/************************************************************************
*
* TOCConverter.java
*
* Copyright: 2002-2015 by Henrik Just
*
* This file is part of Writer2LaTeX.
*
* Writer2LaTeX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Writer2LaTeX 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Writer2LaTeX. If not, see .
*
* Version 1.6 (2015-07-23)
*
*/
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
*/
class TOCConverter extends IndexConverterHelper {
private static final String TOC_LINK_PREFIX = "toc";
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
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
*/
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
*/
void handleHeading(Element onode, Element heading, String sLabel) {
int nLevel = getTextCv().getOutlineLevel(onode);
String sTarget = TOC_LINK_PREFIX+(++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
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_LINK_PREFIX+(++nTocIndex);
converter.addTarget(div,sTarget);
}
converter.addContentEntry(sLabel+converter.getPlainInlineText(onode), nLevel, sTarget);
}
}
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_LINK_PREFIX+(++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
*/
void handleTocMark(Node onode, Node hnode) {
hnode.appendChild(converter.createTarget(TOC_LINK_PREFIX+(++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 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 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
*
*/
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