📄 calloutemitter.java
字号:
package com.nwalsh.saxon;import java.util.Stack;import java.util.StringTokenizer;import org.xml.sax.*;import org.w3c.dom.*;import javax.xml.transform.TransformerException;import com.icl.saxon.Controller;import com.icl.saxon.om.NamePool;import com.icl.saxon.output.Emitter;import com.icl.saxon.tree.AttributeCollection;/** * <p>Saxon extension to decorate a result tree fragment with callouts.</p> * * <p>$Id: CalloutEmitter.java 5907 2006-04-27 08:26:47Z xmldoc $</p> * * <p>Copyright (C) 2000 Norman Walsh.</p> * * <p>This class provides the guts of a * <a href="http://saxon.sourceforge.net/">Saxon 6.*</a> * implementation of callouts for verbatim environments. (It is used * by the Verbatim class.)</p> * * <p>The general design is this: the stylesheets construct a result tree * fragment for some verbatim environment. The Verbatim class initializes * a CalloutEmitter with information about the callouts that should be applied * to the verbatim environment in question. Then the result tree fragment * is "replayed" through the CalloutEmitter; the CalloutEmitter builds a * new result tree fragment from this event stream, decorated with callouts, * and that is returned.</p> * * <p><b>Change Log:</b></p> * <dl> * <dt>1.0</dt> * <dd><p>Initial release.</p></dd> * </dl> * * @see Verbatim * * @author Norman Walsh * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a> * * @version $Id: CalloutEmitter.java 5907 2006-04-27 08:26:47Z xmldoc $ * */public class CalloutEmitter extends CopyEmitter { /** A stack for the preserving information about open elements. */ protected Stack elementStack = null; /** A stack for holding information about temporarily closed elements. */ protected Stack tempStack = null; /** Is the next element absolutely the first element in the fragment? */ protected boolean firstElement = false; /** The FO namespace name. */ protected static String foURI = "http://www.w3.org/1999/XSL/Format"; /** The XHTML namespace name. */ protected static String xhURI = "http://www.w3.org/1999/xhtml"; /** The default column for callouts that specify only a line. */ protected int defaultColumn = 60; /** Is the stylesheet currently running an FO stylesheet? */ protected boolean foStylesheet = false; /** The current line number. */ private static int lineNumber = 0; /** The current column number. */ private static int colNumber = 0; /** The (sorted) array of callouts obtained from the areaspec. */ private static Callout callout[] = null; /** The number of callouts in the callout array. */ private static int calloutCount = 0; /** A pointer used to keep track of our position in the callout array. */ private static int calloutPos = 0; /** The FormatCallout object to use for formatting callouts. */ private static FormatCallout fCallout = null; /** <p>Constructor for the CalloutEmitter.</p> * * @param controller * @param namePool The name pool to use for constructing elements and attributes. * @param defaultColumn The default column for callouts. * @param foStylesheet Is this an FO stylesheet? * @param fCallout */ public CalloutEmitter(Controller controller, NamePool namePool, int defaultColumn, boolean foStylesheet, FormatCallout fCallout) { super(controller, namePool); elementStack = new Stack(); firstElement = true; this.defaultColumn = defaultColumn; this.foStylesheet = foStylesheet; this.fCallout = fCallout; } /** * <p>Examine the areaspec and determine the number and position of * callouts.</p> * * <p>The <code><a href="http://docbook.org/tdg/html/areaspec.html">areaspecNodeSet</a></code> * is examined and a sorted list of the callouts is constructed.</p> * * <p>This data structure is used to augment the result tree fragment * with callout bullets.</p> * * @param areaspecNodeList The source document <areaspec> element. */ public void setupCallouts (NodeList areaspecNodeList) { callout = new Callout[10]; calloutCount = 0; calloutPos = 0; lineNumber = 1; colNumber = 1; // First we walk through the areaspec to calculate the position // of the callouts // <areaspec> // <areaset id="ex.plco.const" coords=""> // <area id="ex.plco.c1" coords="4"/> // <area id="ex.plco.c2" coords="8"/> // </areaset> // <area id="ex.plco.ret" coords="12"/> // <area id="ex.plco.dest" coords="12"/> // </areaspec> int pos = 0; int coNum = 0; boolean inAreaSet = false; Node areaspec = areaspecNodeList.item(0); NodeList children = areaspec.getChildNodes(); for (int count = 0; count < children.getLength(); count++) { Node node = children.item(count); if (node.getNodeType() == Node.ELEMENT_NODE) { if (node.getNodeName().equalsIgnoreCase("areaset")) { coNum++; NodeList areas = node.getChildNodes(); for (int acount = 0; acount < areas.getLength(); acount++) { Node area = areas.item(acount); if (area.getNodeType() == Node.ELEMENT_NODE) { if (area.getNodeName().equalsIgnoreCase("area")) { addCallout(coNum, area, defaultColumn); } else { System.out.println("Unexpected element in areaset: " + area.getNodeName()); } } } } else if (node.getNodeName().equalsIgnoreCase("area")) { coNum++; addCallout(coNum, node, defaultColumn); } else { System.out.println("Unexpected element in areaspec: " + node.getNodeName()); } } } // Now sort them java.util.Arrays.sort(callout, 0, calloutCount); } /** Process characters. */ public void characters(char[] chars, int start, int len) throws TransformerException { // If we hit characters, then there's no first element... firstElement = false; if (lineNumber == 0) { // if there are any text nodes, there's at least one line lineNumber++; colNumber = 1; } // Walk through the text node looking for callout positions char[] newChars = new char[len]; int pos = 0; for (int count = start; count < start+len; count++) { if (calloutPos < calloutCount && callout[calloutPos].getLine() == lineNumber && callout[calloutPos].getColumn() == colNumber) { if (pos > 0) { rtfEmitter.characters(newChars, 0, pos); pos = 0; } closeOpenElements(rtfEmitter); while (calloutPos < calloutCount && callout[calloutPos].getLine() == lineNumber && callout[calloutPos].getColumn() == colNumber) { fCallout.formatCallout(rtfEmitter, callout[calloutPos]); calloutPos++; } openClosedElements(rtfEmitter); } if (chars[count] == '\n') { // What if we need to pad this line? if (calloutPos < calloutCount && callout[calloutPos].getLine() == lineNumber && callout[calloutPos].getColumn() > colNumber) { if (pos > 0) { rtfEmitter.characters(newChars, 0, pos); pos = 0; } closeOpenElements(rtfEmitter); while (calloutPos < calloutCount && callout[calloutPos].getLine() == lineNumber && callout[calloutPos].getColumn() > colNumber) { formatPad(callout[calloutPos].getColumn() - colNumber); colNumber = callout[calloutPos].getColumn(); while (calloutPos < calloutCount && callout[calloutPos].getLine() == lineNumber && callout[calloutPos].getColumn() == colNumber) { fCallout.formatCallout(rtfEmitter, callout[calloutPos]); calloutPos++; } } openClosedElements(rtfEmitter); } lineNumber++; colNumber = 1; } else { colNumber++; } newChars[pos++] = chars[count]; } if (pos > 0) { rtfEmitter.characters(newChars, 0, pos); } } /** * <p>Add blanks to the result tree fragment.</p> * * <p>This method adds <tt>numBlanks</tt> to the result tree fragment. * It's used to pad lines when callouts occur after the last existing * characater in a line.</p> * * @param numBlanks The number of blanks to add. */ protected void formatPad(int numBlanks) { char chars[] = new char[numBlanks]; for (int count = 0; count < numBlanks; count++) { chars[count] = ' '; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -