/************************************************************************ * * LaTeXDocumentPortion.java * * Copyright: 2002-2014 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 <http://www.gnu.org/licenses/>. * * Version 1.4 (2014-09-19) * */ package writer2latex.latex; import java.io.OutputStreamWriter; import java.io.IOException; import java.util.Vector; import writer2latex.util.Misc; /** This class represents a portion of a LaTeX document. A portion is any number of lines, and may include subportions. */ public class LaTeXDocumentPortion { private Vector<Object> nodes; // The collection of all nodes in this portion private StringBuilder curText; // The currently active node (always the last node) private boolean bEmpty; // Is the active node empty? private boolean bWrap; // Do we allow line wrap in this portion? /** Construct a new empty <code>LaTeXDocumentPortion</code> * * @param bWrap set to true if lines may be wrapped on writing */ public LaTeXDocumentPortion(boolean bWrap){ this.bWrap = bWrap; nodes = new Vector<Object>(); curText = new StringBuilder(); bEmpty = true; } /** Add another portion to the end of this portion * * @param ldp The <code>LaTeXDocuemtPortion</code> to add * @return a reference to this <code>LaTeXDocumentPortion</code> (not the appended one) */ public LaTeXDocumentPortion append(LaTeXDocumentPortion ldp) { if (!bEmpty) { // add the current node to the node list and create new current node nodes.add(curText); curText = new StringBuilder(); bEmpty = true; } nodes.add(ldp); return this; } /** Add a string to the end of this portion * * @param s the string to add * @return a reference to this <code>LaTeXDocumentPortion</code> */ public LaTeXDocumentPortion append(String s){ curText.append(s); bEmpty = false; // even if this is the empty string! return this; } /** Add an integer to the end of this portion * * @param n the integer to add * @return a reference to this <code>LaTeXDocumentPortion</code> */ public LaTeXDocumentPortion append(int n){ curText.append(n); bEmpty = false; return this; } /** Add a newline to the end of this portion * * @return a reference to this <code>LaTeXDocumentPortion</code> */ public LaTeXDocumentPortion nl(){ curText.append("\n"); bEmpty = false; return this; } /** write a segment of text (eg. a word) to the output */ private void writeSegment(String s, int nStart, int nEnd, OutputStreamWriter osw) throws IOException { for (int i=nStart; i<nEnd; i++) { osw.write(s.charAt(i)); } } /** write the contents of a StringBuilder to the output */ private void writeBuffer(StringBuilder text, OutputStreamWriter osw, int nLineLen, String sNewline) throws IOException { String s = text.toString(); int nLen = s.length(); int[] nBreakPoints = new int[100]; int nLastBPIndex = 99; int nStart = 0; while (nStart<nLen) { // identify line and breakpoints int nBPIndex = 0; boolean bEscape = false; boolean bComment = false; int nNewline = nStart; char c; while (nNewline<nLen) { if (nBPIndex==nLastBPIndex) { nBreakPoints = Misc.doubleIntArray(nBreakPoints); nLastBPIndex = nBreakPoints.length-1; } c = s.charAt(nNewline); if (c=='\n') { nBreakPoints[nBPIndex++] = nNewline; break; } if (bEscape) { bEscape = false; } else if (c=='\\') { bEscape = true; } else if (c=='%') { bComment = true; } else if (!bComment && c==' ') { nBreakPoints[nBPIndex++] = nNewline; } nNewline++; } if (nBPIndex==nLastBPIndex) { nBreakPoints = Misc.doubleIntArray(nBreakPoints); nLastBPIndex = nBreakPoints.length-1; } if (nNewline==nLen) { nBreakPoints[nBPIndex++] = nNewline; } // write out line int nCurLineLen = nBreakPoints[0]-nStart; writeSegment(s,nStart,nBreakPoints[0],osw); for (int i=0; i<nBPIndex-1; i++) { int nSegmentLen = nBreakPoints[i+1]-nBreakPoints[i]; if (nSegmentLen+nCurLineLen>nLineLen) { // break line before this segment osw.write(sNewline); nCurLineLen = nSegmentLen; } else { // segment fits in current line osw.write(" "); nCurLineLen += nSegmentLen; } writeSegment(s,nBreakPoints[i]+1,nBreakPoints[i+1],osw); } osw.write(sNewline); nStart = nNewline+1; } } /** write the contents of a StringBuilder to the output without wrap */ private void writeBuffer(StringBuilder text, OutputStreamWriter osw, String sNewline) throws IOException { String s = text.toString(); int nLen = s.length(); int nStart = 0; while (nStart<nLen) { // identify line int nNewline = nStart; while (nNewline<nLen) { if (s.charAt(nNewline)=='\n') { break; } nNewline++; } // write out line writeSegment(s,nStart,nNewline,osw); osw.write(sNewline); nStart = nNewline+1; } } /** Write this portion to the output * * @param osw an <code>OutputStreamWriter</code> to write to * @param nLineLen the line length after which automatic line breaks should occur if allowed (nLineLen=0 means no wrap) * @param sNewline the newline character(s) to use * @throws IOException if an exception occurs writing to to osw */ public void write(OutputStreamWriter osw, int nLineLen, String sNewline) throws IOException { int n = nodes.size(); for (int i=0; i<n; i++) { if (nodes.get(i) instanceof LaTeXDocumentPortion) { ((LaTeXDocumentPortion) nodes.get(i)).write(osw,nLineLen,sNewline); } else if (bWrap && nLineLen>0) { writeBuffer((StringBuilder) nodes.get(i),osw,nLineLen,sNewline); } else { writeBuffer((StringBuilder) nodes.get(i),osw,sNewline); } } if (!bEmpty) { // write current node as well if (bWrap && nLineLen>0) { writeBuffer(curText,osw,nLineLen,sNewline); } else { writeBuffer(curText,osw,sNewline); } } } /** Return the content of this LaTeXDocumentPortion as a string * * @return a string representation of the <code>LaTeXDocumentPortion</code> */ public String toString() { StringBuilder buf = new StringBuilder(); int n = nodes.size(); for (int i=0; i<n; i++) { if (nodes.get(i) instanceof LaTeXDocumentPortion) { buf.append(((LaTeXDocumentPortion) nodes.get(i)).toString()); } else { buf.append((StringBuilder) nodes.get(i)); } } if (!bEmpty) { // write current node as well buf.append(curText.toString()); } return buf.toString(); } }