From 9194604f27aa5a0bbd7b86f8d3e7a241c74429b5 Mon Sep 17 00:00:00 2001 From: henrikjust Date: Thu, 11 Jun 2015 18:39:29 +0000 Subject: [PATCH] w2x: Refactoring of index export git-svn-id: svn://svn.code.sf.net/p/writer2latex/code/trunk@250 f0f2a975-2e09-46c8-9428-3b39399b9f3c --- source/distro/changelog.txt | 8 + .../xhtml/AlphabeticalIndexConverter.java | 164 +++++ .../xhtml/BibliographyConverter.java | 71 ++ source/java/writer2latex/xhtml/Converter.java | 23 +- .../xhtml/HeadingStyleConverter.java | 38 +- .../xhtml/IndexConverterBase.java | 125 ++++ .../java/writer2latex/xhtml/LOFConverter.java | 42 ++ .../java/writer2latex/xhtml/LOTConverter.java | 42 ++ .../java/writer2latex/xhtml/TOCConverter.java | 427 ++++++++++++ .../writer2latex/xhtml/TextConverter.java | 606 ++---------------- 10 files changed, 978 insertions(+), 568 deletions(-) create mode 100644 source/java/writer2latex/xhtml/AlphabeticalIndexConverter.java create mode 100644 source/java/writer2latex/xhtml/BibliographyConverter.java create mode 100644 source/java/writer2latex/xhtml/IndexConverterBase.java create mode 100644 source/java/writer2latex/xhtml/LOFConverter.java create mode 100644 source/java/writer2latex/xhtml/LOTConverter.java create mode 100644 source/java/writer2latex/xhtml/TOCConverter.java diff --git a/source/distro/changelog.txt b/source/distro/changelog.txt index 8b6badc..a59e49e 100644 --- a/source/distro/changelog.txt +++ b/source/distro/changelog.txt @@ -2,6 +2,14 @@ Changelog for Writer2LaTeX version 1.4 -> 1.6 ---------- version 1.5.3 ---------- +[w2x] Added support for semantic inflection in EPUB 3 export for the types toc, index + (http://www.idpf.org/epub/30/spec/epub30-contentdocs.html#sec-xhtml-semantic-inflection). + +[w2x] Improved the semantic markup of the table of contents and the alphabetical index in HTML export using sections, + headings and lists + +[w2x] Added support for background color of alphabetical index and bibliography + [w2x] Bugfix: Export of list-style-type now uses the correct values disc, circle, square (in that order) [w2l] UI strings in the code are now externalized for localization diff --git a/source/java/writer2latex/xhtml/AlphabeticalIndexConverter.java b/source/java/writer2latex/xhtml/AlphabeticalIndexConverter.java new file mode 100644 index 0000000..4488d13 --- /dev/null +++ b/source/java/writer2latex/xhtml/AlphabeticalIndexConverter.java @@ -0,0 +1,164 @@ +/************************************************************************ + * + * AlphabeticalIndexConverter.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-2015 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.6 (2015-06-11) + * + */ +package writer2latex.xhtml; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import writer2latex.office.IndexMark; +import writer2latex.office.OfficeReader; +import writer2latex.office.XMLString; +import writer2latex.util.Misc; + +// Helper class (a struct) to contain information about an alphabetical index entry. +final class AlphabeticalEntry { + String sWord; // the word for the index + int nIndex; // the original index of this entry +} + +/** This class processes alphabetical index marks and the associated index table + */ +public class AlphabeticalIndexConverter extends IndexConverterBase { + + private List index = new ArrayList(); // All words for the index + private int nIndexIndex = -1; // Current index used for id's (of form idxN) + private int nAlphabeticalIndex = -1; // File containing alphabetical index + + public AlphabeticalIndexConverter(OfficeReader ofr, XhtmlConfig config, Converter converter) { + super(ofr,config,converter,XMLString.TEXT_ALPHABETICAL_INDEX_SOURCE,"index"); + } + + /** Return the id of the file containing the alphabetical index + * + * @return the file id + */ + public int getFileIndex() { + return nAlphabeticalIndex; + } + + /** Handle an alphabetical index mark + * + * @param onode a text:alphabetical-index-mark node + * @param hnode the link target will be added to this inline HTML node + */ + public void handleIndexMark(Node onode, Node hnode) { + handleIndexMark(Misc.getAttribute(onode,XMLString.TEXT_STRING_VALUE),hnode); + } + + /** Handle an alphabetical index mark start + * + * @param onode a text:alphabetical-index-mark-start node + * @param hnode the link target will be added to this inline HTML node + */ + public void handleIndexMarkStart(Node onode, Node hnode) { + handleIndexMark(IndexMark.getIndexValue(onode),hnode); + } + + // Create an entry for an index mark + private void handleIndexMark(String sWord, Node hnode) { + if (sWord!=null) { + AlphabeticalEntry entry = new AlphabeticalEntry(); + entry.sWord = sWord; + entry.nIndex = ++nIndexIndex; + index.add(entry); + hnode.appendChild(converter.createTarget("idx"+nIndexIndex)); + } + } + + /** Handle an alphabetical index + * + * @param onode a text:alphabetical-index node + * @param hnode the index will be added to this block HTML node + */ + @Override public void handleIndex(Element onode, Element hnode) { + // Register the file index (we assume that there is only one alphabetical index) + nAlphabeticalIndex = converter.getOutFileIndex(); + converter.setIndexFile(null); + super.handleIndex(onode, hnode); + } + + @Override protected void populateIndex(Element source, Element container) { + sortEntries(source); + String sEntryStyleName = getEntryStyleName(source); + for (int i=0; i<=nIndexIndex; i++) { + AlphabeticalEntry entry = index.get(i); + Element li = converter.createElement("li"); + container.appendChild(li); + Element p = getTextCv().createParagraph(li,sEntryStyleName); + Element a = converter.createLink("idx"+entry.nIndex); + p.appendChild(a); + a.appendChild(converter.createTextNode(entry.sWord)); + } + } + + // Sort the list of words + private void sortEntries(Element source) { + // The index source may define a language to use for sorting + Collator collator; + String sLanguage = Misc.getAttribute(source,XMLString.FO_LANGUAGE); + if (sLanguage==null) { // use default locale + collator = Collator.getInstance(); + } + else { + String sCountry = Misc.getAttribute(source,XMLString.FO_COUNTRY); + if (sCountry==null) { sCountry=""; } + collator = Collator.getInstance(new Locale(sLanguage,sCountry)); + } + // Sort the list of words using the collator + for (int i = 0; i<=nIndexIndex; i++) { + for (int j = i+1; j<=nIndexIndex ; j++) { + AlphabeticalEntry entryi = index.get(i); + AlphabeticalEntry entryj = index.get(j); + if (collator.compare(entryi.sWord, entryj.sWord) > 0) { + index.set(i,entryj); + index.set(j,entryi); + } + } + } + } + + // Get the style name to use for the individual words + private String getEntryStyleName(Element source) { + // TODO: Should read the entire template + Node child = source.getFirstChild(); + while (child!=null) { + if (child.getNodeType() == Node.ELEMENT_NODE + && child.getNodeName().equals(XMLString.TEXT_ALPHABETICAL_INDEX_ENTRY_TEMPLATE)) { + // Note: There are actually three outline-levels: separator, 1, 2 and 3 + int nLevel = Misc.getPosInteger(Misc.getAttribute(child,XMLString.TEXT_OUTLINE_LEVEL),0); + if (nLevel==1) { + return Misc.getAttribute(child,XMLString.TEXT_STYLE_NAME); + } + } + child = child.getNextSibling(); + } + return null; + } + +} diff --git a/source/java/writer2latex/xhtml/BibliographyConverter.java b/source/java/writer2latex/xhtml/BibliographyConverter.java new file mode 100644 index 0000000..2bb6ca8 --- /dev/null +++ b/source/java/writer2latex/xhtml/BibliographyConverter.java @@ -0,0 +1,71 @@ +/************************************************************************ + * + * BibliographyConverter.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-2015 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.6 (2015-06-11) + * + */ +package writer2latex.xhtml; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import writer2latex.office.OfficeReader; +import writer2latex.office.XMLString; +import writer2latex.util.Misc; + +public class BibliographyConverter extends ConverterHelper { + + public BibliographyConverter(OfficeReader ofr, XhtmlConfig config, Converter converter) { + super(ofr,config,converter); + } + + public void handleBibliographyMark(Node onode, Node hnode) { + Element anchor = converter.createLink("bibliography"); + hnode.appendChild(anchor); + getTextCv().traversePCDATA(onode,anchor); + } + + public void handleBibliography (Node onode, Node hnode) { + // Use the content, not the template + // This is a temp. solution. Later we want to be able to create + // hyperlinks from the bib-item to the actual entry in the bibliography, + // so we have to recreate the bibliography from the template. + Node body = Misc.getChildByTagName(onode,XMLString.TEXT_INDEX_BODY); + if (body!=null) { + Element div = converter.createElement("div"); + String sStyleName = Misc.getAttribute(onode,XMLString.TEXT_STYLE_NAME); + if (sStyleName!=null) { + StyleInfo sectionInfo = new StyleInfo(); + getSectionSc().applyStyle(sStyleName,sectionInfo); + applyStyle(sectionInfo,div); + } + + converter.addTarget(div,"bibliography"); + hnode.appendChild(div); + //asapNode = converter.createTarget("bibliography"); + Node title = Misc.getChildByTagName(body,XMLString.TEXT_INDEX_TITLE); + if (title!=null) { getTextCv().traverseBlockText(title,div); } + getTextCv().traverseBlockText(body,div); + } + } + +} diff --git a/source/java/writer2latex/xhtml/Converter.java b/source/java/writer2latex/xhtml/Converter.java index b7d4211..c7293f7 100644 --- a/source/java/writer2latex/xhtml/Converter.java +++ b/source/java/writer2latex/xhtml/Converter.java @@ -16,11 +16,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * - * Copyright: 2002-2014 by Henrik Just + * Copyright: 2002-2015 by Henrik Just * * All Rights Reserved. * - * Version 1.4 (2014-09-18) + * Version 1.6 (2015-06-11) * */ @@ -192,6 +192,8 @@ public class Converter extends ConverterBase { protected MathConverter getMathCv() { return mathCv; } protected int getType() { return nType; } + + public boolean isHTML5() { return nType==XhtmlDocument.HTML5; } protected int getOutFileIndex() { return nOutFileIndex; } @@ -236,7 +238,7 @@ public class Converter extends ConverterBase { public void setOPS(boolean b) { bOPS = true; } public boolean isOPS() { return bOPS; } - + @Override public void convertInner() throws IOException { sTargetFileName = Misc.trimDocumentName(sTargetFileName,XhtmlDocument.getExtension(nType)); @@ -643,6 +645,7 @@ public class Converter extends ConverterBase { htmlDOM = htmlDoc.getContentDOM(); Element rootElement = htmlDOM.getDocumentElement(); styleCv.applyDefaultLanguage(rootElement); + addEpubNs(rootElement); rootElement.insertBefore(htmlDOM.createComment( "This file was converted to xhtml by " + (ofr.isText() ? "Writer" : (ofr.isSpreadsheet() ? "Calc" : "Impress")) @@ -776,6 +779,20 @@ public class Converter extends ConverterBase { return htmlDoc.getContentNode(); } + + // Add epub namespace for the purpose of semantic inflection in EPUB 3 + public void addEpubNs(Element elm) { + if (bOPS && nType==XhtmlDocument.HTML5) { + elm.setAttribute("xmlns:epub", "http://www.idpf.org/2007/ops"); + } + } + + // Add a type from the structural semantics vocabulary of EPUB 3 + public void addEpubType(Element elm, String sType) { + if (bOPS && nType==XhtmlDocument.HTML5 && sType!=null) { + elm.setAttribute("epub:type", sType); + } + } // create a target public Element createTarget(String sId) { diff --git a/source/java/writer2latex/xhtml/HeadingStyleConverter.java b/source/java/writer2latex/xhtml/HeadingStyleConverter.java index 39771cd..85ebaeb 100644 --- a/source/java/writer2latex/xhtml/HeadingStyleConverter.java +++ b/source/java/writer2latex/xhtml/HeadingStyleConverter.java @@ -16,13 +16,19 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * - * Copyright: 2002-2014 by Henrik Just + * Copyright: 2002-2015 by Henrik Just * * All Rights Reserved. * - * Version 1.6 (2014-10-24) + * Version 1.6 (2015-06-10) * - */package writer2latex.xhtml; + */ +package writer2latex.xhtml; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import writer2latex.office.OfficeReader; import writer2latex.office.OfficeStyleFamily; @@ -30,6 +36,9 @@ import writer2latex.office.StyleWithProperties; import writer2latex.util.CSVList; public class HeadingStyleConverter extends StyleConverterHelper { + + // Sets of additional styles (other than the main heading style for the level) + private List> otherLevelStyles; public HeadingStyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { @@ -37,6 +46,10 @@ public class HeadingStyleConverter extends StyleConverterHelper { this.styleMap = config.getXHeadingStyleMap(); this.bConvertStyles = config.xhtmlFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlFormatting()==XhtmlConfig.IGNORE_HARD; this.bConvertHard = config.xhtmlFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlFormatting()==XhtmlConfig.IGNORE_STYLES; + this.otherLevelStyles = new ArrayList>(); + for (int i=0; i<=6; i++) { + otherLevelStyles.add(new HashSet()); + } } @Override @@ -44,6 +57,7 @@ public class HeadingStyleConverter extends StyleConverterHelper { if (bConvertStyles) { StringBuilder buf = new StringBuilder(); for (int i=1; i<=6; i++) { + // Convert main style for this level if (ofr.getHeadingStyle(i)!=null) { CSVList props = new CSVList(";"); getParSc().applyProperties(ofr.getHeadingStyle(i),props,true); @@ -51,6 +65,16 @@ public class HeadingStyleConverter extends StyleConverterHelper { buf.append(sIndent).append("h").append(i) .append(" {").append(props.toString()).append("}").append(config.prettyPrint() ? "\n" : " "); } + // Convert other styles for this level + for (String sDisplayName : otherLevelStyles.get(i)) { + StyleWithProperties style = (StyleWithProperties) + getStyles().getStyleByDisplayName(sDisplayName); + CSVList props = new CSVList(";"); + getParSc().applyProperties(style,props,true); + props.addValue("clear","left"); + buf.append(sIndent).append("h").append(i).append(".").append(styleNames.getExportName(sDisplayName)) + .append(" {").append(props.toString()).append("}").append(config.prettyPrint() ? "\n" : " "); + } } return buf.toString(); } @@ -88,8 +112,12 @@ public class HeadingStyleConverter extends StyleConverterHelper { info.sClass = map.sBlockCss; } } - else { - // TODO: Apply style if different from main style for this level + else if (style!=ofr.getHeadingStyle(nLevel)) { + // This is not the main style for this level, add class and remember + info.sClass = styleNames.getExportName(sDisplayName); + if (1<=nLevel && nLevel<=6) { + otherLevelStyles.get(nLevel).add(sDisplayName); + } } } } diff --git a/source/java/writer2latex/xhtml/IndexConverterBase.java b/source/java/writer2latex/xhtml/IndexConverterBase.java new file mode 100644 index 0000000..8629714 --- /dev/null +++ b/source/java/writer2latex/xhtml/IndexConverterBase.java @@ -0,0 +1,125 @@ +/************************************************************************ + * + * IndexConverterBase.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-2015 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.6 (2015-06-11) + * + */ + +package writer2latex.xhtml; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import writer2latex.office.OfficeReader; +import writer2latex.office.XMLString; +import writer2latex.util.Misc; + +/** This is a base class for conversion of indexes (table of contents, bibliography, alphabetical index, + * list of tables, list of figures) + */ +public abstract class IndexConverterBase extends ConverterHelper { + + private String sEpubType; + private String sSourceName; + + /** Construct a new index converter + * + * @param ofr the office reader used to read the source document + * @param config the configuration + * @param converter the converter + * @param sSourceName the name of the source data element in the index + * @param sEpubType the EPUB 3 semantic type + */ + public IndexConverterBase(OfficeReader ofr, XhtmlConfig config, Converter converter, + String sSourceName, String sEpubType) { + super(ofr,config,converter); + this.sSourceName = sSourceName; + this.sEpubType = sEpubType; + } + + /** Generate the actual contents of the index + * + * @param source the index source + * @param container an ul element to populate with list items + */ + protected abstract void populateIndex(Element source, Element container); + + /** Handle an alphabetical index + * + * @param onode a text:alphabetical-index node + * @param hnode the index will be added to this block HTML node + */ + public void handleIndex(Element onode, Element hnode) { + Element source = Misc.getChildByTagName(onode,sSourceName); + if (source!=null) { + Element container = createContainer(onode, hnode); + generateTitle(source, container); + generateIndex(source, container); + } + } + + // Create a container node for the index + private Element createContainer(Element source, Element hnode) { + Element container = converter.createElement(converter.isHTML5() ? "section" : "div"); + hnode.appendChild(container); + + converter.addEpubType(container, sEpubType); + + String sName = source.getAttribute(XMLString.TEXT_NAME); + if (sName!=null) { + converter.addTarget(container,sName); + } + + String sStyleName = source.getAttribute(XMLString.TEXT_STYLE_NAME); + if (sStyleName!=null) { + StyleInfo sectionInfo = new StyleInfo(); + getSectionSc().applyStyle(sStyleName,sectionInfo); + applyStyle(sectionInfo,container); + } + return container; + } + + // Generate the index title and add it to the container + private void generateTitle(Element source, Element container) { + Node title = Misc.getChildByTagName(source,XMLString.TEXT_INDEX_TITLE_TEMPLATE); + if (title!=null) { + Element h1 = converter.createElement("h1"); + container.appendChild(h1); + String sStyleName = Misc.getAttribute(title,XMLString.TEXT_STYLE_NAME); + StyleInfo info = new StyleInfo(); + info.sTagName = "h1"; + getHeadingSc().applyStyle(1, sStyleName, info); + applyStyle(info,h1); + getTextCv().traversePCDATA(title,h1); + } + } + + // Generate the index and add it to the container + private void generateIndex(Element source, Element container) { + Element ul = converter.createElement("ul"); + // TODO: Support column formatting from the index source + ul.setAttribute("style", "list-style-type:none;margin:0;padding:0"); + container.appendChild(ul); + populateIndex(source,ul); + } + +} diff --git a/source/java/writer2latex/xhtml/LOFConverter.java b/source/java/writer2latex/xhtml/LOFConverter.java new file mode 100644 index 0000000..86af49d --- /dev/null +++ b/source/java/writer2latex/xhtml/LOFConverter.java @@ -0,0 +1,42 @@ +/************************************************************************ + * + * LOFConverter.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-2015 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.6 (2015-06-10) + * + */ +package writer2latex.xhtml; + +import org.w3c.dom.Node; + +import writer2latex.office.OfficeReader; + +public class LOFConverter extends ConverterHelper { + + public LOFConverter(OfficeReader ofr, XhtmlConfig config, Converter converter) { + super(ofr,config,converter); + } + + public void handleLOF(Node onode, Node hnode) { + // TODO + } + +} diff --git a/source/java/writer2latex/xhtml/LOTConverter.java b/source/java/writer2latex/xhtml/LOTConverter.java new file mode 100644 index 0000000..25ecabe --- /dev/null +++ b/source/java/writer2latex/xhtml/LOTConverter.java @@ -0,0 +1,42 @@ +/************************************************************************ + * + * LOTConverter.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-2015 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.6 (2015-06-10) + * + */ +package writer2latex.xhtml; + +import org.w3c.dom.Node; + +import writer2latex.office.OfficeReader; + +public class LOTConverter extends ConverterHelper { + + public LOTConverter(OfficeReader ofr, XhtmlConfig config, Converter converter) { + super(ofr,config,converter); + } + + public void handleLOT(Node onode, Node hnode) { + // TODO + } + +} diff --git a/source/java/writer2latex/xhtml/TOCConverter.java b/source/java/writer2latex/xhtml/TOCConverter.java new file mode 100644 index 0000000..dc2e8ef --- /dev/null +++ b/source/java/writer2latex/xhtml/TOCConverter.java @@ -0,0 +1,427 @@ +/************************************************************************ + * + * TOCConverter.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-2015 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.6 (2015-06-11) + * + */ +package writer2latex.xhtml; + +import java.util.ArrayList; +import java.util.List; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import writer2latex.office.IndexMark; +import writer2latex.office.ListCounter; +import writer2latex.office.OfficeReader; +import writer2latex.office.TocReader; +import writer2latex.office.XMLString; +import writer2latex.util.Misc; +import writer2latex.xhtml.l10n.L10n; + +//Helper class (a struct) to contain information about a toc entry (ie. a heading, other paragraph or toc-mark) +final class TocEntry { + Element onode; // the original node + String sLabel = null; // generated label for the entry + int nFileIndex; // the file index for the generated content + int nOutlineLevel; // the outline level for this heading + int[] nOutlineNumber; // the natural outline number for this heading +} + +//Helper class (a struct) to point back to indexes that should be processed +final class IndexData { + int nOutFileIndex; // the index of the out file containing the index + Element onode; // the original node + Element chapter; // the chapter containing this toc + Element hnode; // a block node where the index should be added +} + +// TODO: This class needs some refactoring + +/** This class processes table of content index marks and the associated table of content + */ +public class TOCConverter extends IndexConverterBase { + + private List indexes = new ArrayList(); // All tables of content + private List tocEntries = new ArrayList(); // All potential(!) toc items + private int nTocFileIndex = -1; // file index for main toc + private Element currentChapter = null; // Node for the current chapter (level 1) heading + private int nTocIndex = -1; // Current index for id's (of form tocN) + private ListCounter naturalOutline = new ListCounter(); // Current "natural" outline number + + private int nExternalTocDepth = 1; // The number of levels to include in the "external" table of contents + + public TOCConverter(OfficeReader ofr, XhtmlConfig config, Converter converter) { + super(ofr,config,converter,XMLString.TEXT_TABLE_OF_CONTENT_SOURCE,"toc"); + nExternalTocDepth = config.externalTocDepth(); + if (nExternalTocDepth==0) { // A value of zero means auto (i.e. determine from split level) + nExternalTocDepth = Math.max(config.getXhtmlSplitLevel(),1); + } + } + + /** Return the id of the file containing the alphabetical index + * + * @return the file id + */ + public int getFileIndex() { + return nTocFileIndex; + } + + /** Handle a heading as a table of content entry + * + * @param onode the text:h element + * @param heading the link target will be added to this inline HTML node + * @param sLabel the numbering label of this heading + */ + public void handleHeading(Element onode, Element heading, String sLabel) { + int nLevel = getTextCv().getOutlineLevel(onode); + String sTarget = "toc"+(++nTocIndex); + converter.addTarget(heading,sTarget); + + this.currentChapter = onode; + + // Add in external content. For single file output we include all level 1 headings + their target + // Targets are added only when the toc level is deeper than the split level + if (nLevel<=nExternalTocDepth) { + converter.addContentEntry(sLabel+converter.getPlainInlineText(onode), nLevel, + nLevel>config.getXhtmlSplitLevel() ? sTarget : null); + } + + // Add to real toc + TocEntry entry = new TocEntry(); + entry.onode = onode; + entry.sLabel = sLabel; + entry.nFileIndex = converter.getOutFileIndex(); + entry.nOutlineLevel = nLevel; + entry.nOutlineNumber = naturalOutline.step(nLevel).getValues(); + tocEntries.add(entry); + } + + // Add in external content. For single file output we include all level 1 headings + their target + // Targets are added only when the toc level is deeper than the split level + public void handleHeadingExternal(Element onode, Element hnode, String sLabel) { + int nLevel = getTextCv().getOutlineLevel(onode); + if (nLevel<=nExternalTocDepth) { + // Add an empty div to use as target, if required + String sTarget = null; + if (nLevel>config.getXhtmlSplitLevel()) { + Element div = converter.createElement("div"); + hnode.appendChild(div); + sTarget = "toc"+(++nTocIndex); + converter.addTarget(div,sTarget); + } + converter.addContentEntry(sLabel+converter.getPlainInlineText(onode), nLevel, sTarget); + } + } + + public void handleParagraph(Element onode, Element par, String sCurrentListLabel) { + String sStyleName = Misc.getAttribute(onode,XMLString.TEXT_STYLE_NAME); + if (ofr.isIndexSourceStyle(getParSc().getRealParStyleName(sStyleName))) { + converter.addTarget(par,"toc"+(++nTocIndex)); + TocEntry entry = new TocEntry(); + entry.onode = (Element) onode; + entry.sLabel = sCurrentListLabel; + entry.nFileIndex = converter.getOutFileIndex(); + tocEntries.add(entry); + } + } + + /** Handle a table of contents mark + * + * @param onode a text:toc-mark or text:toc-mark-start node + * @param hnode the link target will be added to this inline HTML node + */ + public void handleTocMark(Node onode, Node hnode) { + hnode.appendChild(converter.createTarget("toc"+(++nTocIndex))); + TocEntry entry = new TocEntry(); + entry.onode = (Element) onode; + entry.nFileIndex = converter.getOutFileIndex(); + tocEntries.add(entry); + } + + /** Handle a table of contents + * + * @param onode a text:alphabetical-index node + * @param hnode the index will be added to this block HTML node + */ + @Override public void handleIndex(Element onode, Element hnode) { + if (config.includeToc()) { + if (!ofr.getTocReader((Element)onode).isByChapter()) { + nTocFileIndex = converter.getOutFileIndex(); + } + converter.setTocFile(null); + super.handleIndex(onode,hnode); + } + } + + @Override protected void populateIndex(Element source, Element container) { + // Actually we are not populating the index, but collects information to generate it later + IndexData data = new IndexData(); + data.nOutFileIndex = converter.getOutFileIndex(); + data.onode = source; + data.chapter = currentChapter; + data.hnode = container; + indexes.add(data); + } + + /** Generate the content of all tables of content + * + */ + public void generate() { + int nIndexCount = indexes.size(); + for (int i=0; i=0; i--) { + TocEntry entry = tocEntries.get(i); + if (XMLString.TEXT_H.equals(entry.onode.getTagName()) && entry.nFileIndex==nIndex && entry.nOutlineLevel<=nSplit) { + entryCurrent = entry; break; + } + } + + if (entryCurrent==null) { + entryCurrent = fakeEntry; + if (nIndex==0) { bHasFrontMatter=true; } + } + + // Determine the maximum outline level to include + int nMaxLevel = entryCurrent.nOutlineLevel; + if (nMaxLevelnPrevFileIndex+1) { + // Skipping a file index means we have passed an index + for (int k=nPrevFileIndex+1; k0) { + inline.appendChild(converter.createTextNode(entry.sLabel)); + if (!entry.sLabel.endsWith(" ")) { + inline.appendChild(converter.createTextNode(" ")); + } + } + getTextCv().traverseInlineText(entry.onode,inline); + } + } + } + if (nPrevFileIndex indexes = new Vector(); - - // Data used to handle Alphabetical Index - Vector index = new Vector(); // All words for the index - private int nIndexIndex = -1; // Current index used for id's (of form idxN) - private int nAlphabeticalIndex = -1; // File containing alphabetical index - - // Data used to handle Table of Contents - private Vector tocEntries = new Vector(); // All potential(!) toc items - private int nTocFileIndex = -1; // file index for main toc - private Element currentChapter = null; // Node for the current chapter (level 1) heading - private int nTocIndex = -1; // Current index for id's (of form tocN) - private ListCounter naturalOutline = new ListCounter(); // Current "natural" outline number + // Data used to handle all sorts of indexes + private TOCConverter tocCv; + private LOFConverter lofCv; + private LOTConverter lotCv; + private AlphabeticalIndexConverter indexCv; + private BibliographyConverter bibCv; // Style names for foot- and endnotes private String sFntCitBodyStyle = null; @@ -148,17 +106,18 @@ public class TextConverter extends ConverterHelper { // Display hidden text? private boolean bDisplayHiddenText = false; - + public TextConverter(OfficeReader ofr, XhtmlConfig config, Converter converter) { super(ofr,config,converter); + tocCv = new TOCConverter(ofr, config, converter); + lofCv = new LOFConverter(ofr, config, converter); + lotCv = new LOTConverter(ofr, config, converter); + bibCv = new BibliographyConverter(ofr, config, converter); + indexCv = new AlphabeticalIndexConverter(ofr, config, converter); nSplitAfter = 1000*config.splitAfter(); nPageBreakSplit = config.pageBreakSplit(); nSplit = config.getXhtmlSplitLevel(); nRepeatLevels = converter.isOPS() ? 0 : config.getXhtmlRepeatLevels(); // never repeat headings in EPUB - nExternalTocDepth = config.externalTocDepth(); - if (nExternalTocDepth==0) { // A value of zero means auto (i.e. determine from split level) - nExternalTocDepth = Math.max(nSplit,1); - } nFloatMode = ofr.isText() && config.xhtmlFloatObjects() ? DrawConverter.FLOATING : DrawConverter.ABSOLUTE; outlineNumbering = new ListCounter(ofr.getOutlineStyle()); @@ -204,20 +163,21 @@ public class TextConverter extends ConverterHelper { insertEndnotes(hnode); // Generate all indexes - int nIndexCount = indexes.size(); - for (int i=0; i=0; i--) { - TocEntry entry = tocEntries.get(i); - if (XMLString.TEXT_H.equals(entry.onode.getTagName()) && entry.nFileIndex==nIndex && entry.nOutlineLevel<=nSplit) { - entryCurrent = entry; break; - } - } - - if (entryCurrent==null) { - entryCurrent = fakeEntry; - if (nIndex==0) { bHasFrontMatter=true; } - } - - // Determine the maximum outline level to include - int nMaxLevel = entryCurrent.nOutlineLevel; - if (nMaxLevelnPrevFileIndex+1) { - // Skipping a file index means we have passed an index - for (int k=nPrevFileIndex+1; k0) { - inline.appendChild(converter.createTextNode(entry.sLabel)); - if (!entry.sLabel.endsWith(" ")) { - inline.appendChild(converter.createTextNode(" ")); - } - } - traverseInlineText(entry.onode,inline); - } - } - } - if (nPrevFileIndex0) { int nFirst = nLevel-nRepeatLevels; @@ -706,24 +535,7 @@ public class TextConverter extends ConverterHelper { // Add to toc if (!bInToc) { - String sTarget = "toc"+(++nTocIndex); - converter.addTarget(heading,sTarget); - - // Add in external content. For single file output we include all level 1 headings + their target - // Targets are added only when the toc level is deeper than the split level - if (nLevel<=nExternalTocDepth) { - converter.addContentEntry(sLabel+converter.getPlainInlineText(onode), nLevel, - nLevel>nSplit ? sTarget : null); - } - - // Add to real toc - TocEntry entry = new TocEntry(); - entry.onode = onode; - entry.sLabel = sLabel; - entry.nFileIndex = converter.getOutFileIndex(); - entry.nOutlineLevel = nLevel; - entry.nOutlineNumber = naturalOutline.step(nLevel).getValues(); - tocEntries.add(entry); + tocCv.handleHeading(onode,heading,sLabel); } // Convert content @@ -748,19 +560,7 @@ public class TextConverter extends ConverterHelper { } else { if (!bInToc) { - // Add in external content. For single file output we include all level 1 headings + their target - // Targets are added only when the toc level is deeper than the split level - if (nLevel<=nExternalTocDepth) { - // Add an empty div to use as target, if required - String sTarget = null; - if (nLevel>nSplit) { - Element div = converter.createElement("div"); - hnode.appendChild(div); - sTarget = "toc"+(++nTocIndex); - converter.addTarget(div,sTarget); - } - converter.addContentEntry(sLabel+converter.getPlainInlineText(onode), nLevel, sTarget); - } + tocCv.handleHeadingExternal(onode, hnode, sLabel); } // Keep track of current headings for split output currentHeading[nLevel] = null; @@ -799,15 +599,8 @@ public class TextConverter extends ConverterHelper { } // Maybe add to toc - if (ofr.isIndexSourceStyle(getParSc().getRealParStyleName(sStyleName))) { - converter.addTarget(par,"toc"+(++nTocIndex)); - TocEntry entry = new TocEntry(); - entry.onode = (Element) onode; - entry.sLabel = sCurrentListLabel; - entry.nFileIndex = converter.getOutFileIndex(); - tocEntries.add(entry); - } - + tocCv.handleParagraph((Element)onode, par, sCurrentListLabel); + if (!bIsEmpty) { par = createTextBackground(par, sStyleName); if (config.listFormatting()==XhtmlConfig.HARD_LABELS) { @@ -1205,7 +998,7 @@ public class TextConverter extends ConverterHelper { Node rememberNode = hnode; StyleWithProperties style = ofr.getParStyle(Misc.getAttribute(child, XMLString.TEXT_STYLE_NAME)); hnode = maybeSplit(hnode,style,nOutlineLevel); - handleHeading((Element)child, hnode, rememberNode!=hnode, + handleHeading((Element)child, (Element)hnode, rememberNode!=hnode, ofr.getListStyle(sStyleName), nLevel, bUnNumbered, bRestart, nStartValue); nDontSplitLevel--; @@ -1228,284 +1021,7 @@ public class TextConverter extends ConverterHelper { } return hnode; } - - ////////////////////////////////////////////////////////////////////////// - // INDEXES - ////////////////////////////////////////////////////////////////////////// - - /* Process table of contents - */ - private void handleTOC(Node onode, Node hnode) { - if (!config.includeToc()) { return; } - - if (!ofr.getTocReader((Element)onode).isByChapter()) { - nTocFileIndex = converter.getOutFileIndex(); - } - - converter.setTocFile(null); - - Element div = converter.createElement("div"); - hnode.appendChild(div); - - IndexData data = new IndexData(); - data.nOutFileIndex = converter.getOutFileIndex(); - data.onode = (Element) onode; - data.chapter = currentChapter; - data.hnode = (Element) div; - indexes.add(data); // to be processed later with generateTOC - } - - private void generateToc(IndexData data) { - if (!config.includeToc()) { return; } - - Element onode = data.onode; - Element chapter = data.chapter; - Element div = data.hnode; - - int nSaveOutFileIndex = converter.getOutFileIndex(); - converter.changeOutFile(data.nOutFileIndex); - - bInToc = true; - TocReader tocReader = ofr.getTocReader(onode); - - StyleInfo sectionInfo = new StyleInfo(); - getSectionSc().applyStyle(tocReader.getStyleName(),sectionInfo); - applyStyle(sectionInfo,div); - - if (tocReader.getName()!=null) { converter.addTarget(div,tocReader.getName()); } - // Generate title - Element title = tocReader.getIndexTitleTemplate(); - if (title!=null) { - String sStyleName = Misc.getAttribute(title,XMLString.TEXT_STYLE_NAME); - Element p = createParagraph(div,sStyleName); - traversePCDATA(title,p); - } - - // TODO: Read the entire content of the entry templates! - String[] sEntryStyleName = new String[11]; - for (int i=1; i<=10; i++) { - Element entryTemplate = tocReader.getTocEntryTemplate(i); - if (entryTemplate!=null) { - sEntryStyleName[i] = Misc.getAttribute(entryTemplate,XMLString.TEXT_STYLE_NAME); - } - } - - int nStart = 0; - int nLen = tocEntries.size(); - - // Find the chapter - if (tocReader.isByChapter() && chapter!=null) { - for (int i=0; i 0) { - index.set(i,entryj); - index.set(j,entryi); - } - } - } - // Generate the index - Element table = converter.createElement("table"); - table.setAttribute("style","width:100%"); - div.appendChild(table); - Element tr = converter.createElement("tr"); - table.appendChild(tr); - Element[] td = new Element[4]; - for (int i=0; i<4; i++) { - td[i] = converter.createElement("td"); - td[i].setAttribute("style","vertical-align:top"); - tr.appendChild(td[i]); - } - int nColEntries = nIndexIndex/4+1; - int nColIndex = -1; - for (int i=0; i<=nIndexIndex; i++) { - if (i%nColEntries==0) { nColIndex++; } - AlphabeticalEntry entry = index.get(i); - Element p = createParagraph(td[nColIndex],sEntryStyleName); - Element a = converter.createLink("idx"+entry.nIndex); - p.appendChild(a); - a.appendChild(converter.createTextNode(entry.sWord)); - } - } - - } - - /* - * Process Bibliography - */ - private void handleBibliography (Node onode, Node hnode) { - // Use the content, not the template - // This is a temp. solution. Later we want to be able to create - // hyperlinks from the bib-item to the actual entry in the bibliography, - // so we have to recreate the bibliography from the template. - Node body = Misc.getChildByTagName(onode,XMLString.TEXT_INDEX_BODY); - if (body!=null) { - Element div = converter.createElement("div"); - converter.addTarget(div,"bibliography"); - hnode.appendChild(div); - //asapNode = converter.createTarget("bibliography"); - Node title = Misc.getChildByTagName(body,XMLString.TEXT_INDEX_TITLE); - if (title!=null) { traverseBlockText(title,div); } - traverseBlockText(body,div); - } - } - //////////////////////////////////////////////////////////////////////// // INLINE TEXT //////////////////////////////////////////////////////////////////////// @@ -1549,7 +1065,7 @@ public class TextConverter extends ConverterHelper { /* * Process inline text */ - private void traverseInlineText (Node onode,Node hnode) { + protected void traverseInlineText (Node onode,Node hnode) { //String styleName = Misc.getAttribute(onode, XMLString.TEXT_STYLE_NAME); if (onode.hasChildNodes()) { @@ -1662,16 +1178,16 @@ public class TextConverter extends ConverterHelper { handleBookmarkRef(child,hnode); } else if (sName.equals(XMLString.TEXT_ALPHABETICAL_INDEX_MARK)) { - handleAlphabeticalIndexMark(child,hnode); + if (!bInToc) { indexCv.handleIndexMark(child,hnode); } } else if (sName.equals(XMLString.TEXT_ALPHABETICAL_INDEX_MARK_START)) { - handleAlphabeticalIndexMarkStart(child,hnode); + if (!bInToc) { indexCv.handleIndexMarkStart(child,hnode); } } else if (sName.equals(XMLString.TEXT_TOC_MARK)) { - handleTocMark(child,hnode); + tocCv.handleTocMark(child,hnode); } else if (sName.equals(XMLString.TEXT_TOC_MARK_START)) { - handleTocMark(child,hnode); + tocCv.handleTocMark(child,hnode); } else if (sName.equals(XMLString.TEXT_BIBLIOGRAPHY_MARK)) { handleBibliographyMark(child,hnode); @@ -1722,7 +1238,7 @@ public class TextConverter extends ConverterHelper { } } - private void traversePCDATA(Node onode, Node hnode) { + protected void traversePCDATA(Node onode, Node hnode) { if (onode.hasChildNodes()) { NodeList nl = onode.getChildNodes(); int nLen = nl.getLength(); @@ -1946,42 +1462,12 @@ public class TextConverter extends ConverterHelper { createReference(onode,hnode,"bkm"); } - private void handleAlphabeticalIndexMark(Node onode, Node hnode) { - if (bInToc) { return; } - String sWord = Misc.getAttribute(onode,XMLString.TEXT_STRING_VALUE); - if (sWord==null) { return; } - AlphabeticalEntry entry = new AlphabeticalEntry(); - entry.sWord = sWord; entry.nIndex = ++nIndexIndex; - index.add(entry); - hnode.appendChild(converter.createTarget("idx"+nIndexIndex)); - } - - private void handleAlphabeticalIndexMarkStart(Node onode, Node hnode) { - if (bInToc) { return; } - String sWord = IndexMark.getIndexValue(onode); - if (sWord==null) { return; } - AlphabeticalEntry entry = new AlphabeticalEntry(); - entry.sWord = sWord; entry.nIndex = ++nIndexIndex; - index.add(entry); - hnode.appendChild(converter.createTarget("idx"+nIndexIndex)); - } - - private void handleTocMark(Node onode, Node hnode) { - hnode.appendChild(converter.createTarget("toc"+(++nTocIndex))); - TocEntry entry = new TocEntry(); - entry.onode = (Element) onode; - entry.nFileIndex = converter.getOutFileIndex(); - tocEntries.add(entry); - } - private void handleBibliographyMark(Node onode, Node hnode) { if (bInToc) { traversePCDATA(onode,hnode); } else { - Element anchor = converter.createLink("bibliography"); - hnode.appendChild(anchor); - traversePCDATA(onode,anchor); + bibCv.handleBibliographyMark(onode, hnode); } } @@ -2123,7 +1609,7 @@ public class TextConverter extends ConverterHelper { } /* Create a styled paragraph node */ - private Element createParagraph(Element node, String sStyleName) { + protected Element createParagraph(Element node, String sStyleName) { StyleInfo info = new StyleInfo(); getParSc().applyStyle(sStyleName,info); Element par = converter.createElement(info.sTagName); @@ -2182,7 +1668,7 @@ public class TextConverter extends ConverterHelper { return applyAttributes(newNode,ofr.getTextStyle(sStyleName)); } - private int getOutlineLevel(Element node) { + protected int getOutlineLevel(Element node) { return ofr.isOpenDocument() ? Misc.getPosInteger(node.getAttribute(XMLString.TEXT_OUTLINE_LEVEL),1): Misc.getPosInteger(node.getAttribute(XMLString.TEXT_LEVEL),1);