📄 rtfreader.java
字号:
/* * @(#)RTFReader.java 1.26 05/11/17 * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */package javax.swing.text.rtf;import java.lang.*;import java.util.*;import java.io.*;import java.awt.Font;import java.awt.Color;import javax.swing.text.*;/** * Takes a sequence of RTF tokens and text and appends the text * described by the RTF to a <code>StyledDocument</code> (the <em>target</em>). * The RTF is lexed * from the character stream by the <code>RTFParser</code> which is this class's * superclass. * * This class is an indirect subclass of OutputStream. It must be closed * in order to guarantee that all of the text has been sent to * the text acceptor. * * @see RTFParser * @see java.io.OutputStream */class RTFReader extends RTFParser{ /** The object to which the parsed text is sent. */ StyledDocument target; /** Miscellaneous information about the parser's state. This * dictionary is saved and restored when an RTF group begins * or ends. */ Dictionary parserState; /* Current parser state */ /** This is the "dst" item from parserState. rtfDestination * is the current rtf destination. It is cached in an instance * variable for speed. */ Destination rtfDestination; /** This holds the current document attributes. */ MutableAttributeSet documentAttributes; /** This Dictionary maps Integer font numbers to String font names. */ Dictionary fontTable; /** This array maps color indices to Color objects. */ Color[] colorTable; /** This array maps character style numbers to Style objects. */ Style[] characterStyles; /** This array maps paragraph style numbers to Style objects. */ Style[] paragraphStyles; /** This array maps section style numbers to Style objects. */ Style[] sectionStyles; /** This is the RTF version number, extracted from the \rtf keyword. * The version information is currently not used. */ int rtfversion; /** <code>true</code> to indicate that if the next keyword is unknown, * the containing group should be ignored. */ boolean ignoreGroupIfUnknownKeyword; /** The parameter of the most recently parsed \\ucN keyword, * used for skipping alternative representations after a * Unicode character. */ int skippingCharacters; static private Dictionary straightforwardAttributes; static { straightforwardAttributes = RTFAttributes.attributesByKeyword(); } private MockAttributeSet mockery; /* this should be final, but there's a bug in javac... */ /** textKeywords maps RTF keywords to single-character strings, * for those keywords which simply insert some text. */ static Dictionary textKeywords = null; static { textKeywords = new Hashtable(); textKeywords.put("\\", "\\"); textKeywords.put("{", "{"); textKeywords.put("}", "}"); textKeywords.put(" ", "\u00A0"); /* not in the spec... */ textKeywords.put("~", "\u00A0"); /* nonbreaking space */ textKeywords.put("_", "\u2011"); /* nonbreaking hyphen */ textKeywords.put("bullet", "\u2022"); textKeywords.put("emdash", "\u2014"); textKeywords.put("emspace", "\u2003"); textKeywords.put("endash", "\u2013"); textKeywords.put("enspace", "\u2002"); textKeywords.put("ldblquote", "\u201C"); textKeywords.put("lquote", "\u2018"); textKeywords.put("ltrmark", "\u200E"); textKeywords.put("rdblquote", "\u201D"); textKeywords.put("rquote", "\u2019"); textKeywords.put("rtlmark", "\u200F"); textKeywords.put("tab", "\u0009"); textKeywords.put("zwj", "\u200D"); textKeywords.put("zwnj", "\u200C"); /* There is no Unicode equivalent to an optional hyphen, as far as I can tell. */ textKeywords.put("-", "\u2027"); /* TODO: optional hyphen */ } /* some entries in parserState */ static final String TabAlignmentKey = "tab_alignment"; static final String TabLeaderKey = "tab_leader"; static Dictionary characterSets; static boolean useNeXTForAnsi = false; static { characterSets = new Hashtable(); }/* TODO: per-font font encodings ( \fcharset control word ) ? *//** * Creates a new RTFReader instance. Text will be sent to * the specified TextAcceptor. * * @param destination The TextAcceptor which is to receive the text. */public RTFReader(StyledDocument destination){ int i; target = destination; parserState = new Hashtable(); fontTable = new Hashtable(); rtfversion = -1; mockery = new MockAttributeSet(); documentAttributes = new SimpleAttributeSet();}/** Called when the RTFParser encounters a bin keyword in the * RTF stream. * * @see RTFParser */public void handleBinaryBlob(byte[] data){ if (skippingCharacters > 0) { /* a blob only counts as one character for skipping purposes */ skippingCharacters --; return; } /* someday, someone will want to do something with blobs */}/** * Handles any pure text (containing no control characters) in the input * stream. Called by the superclass. */public void handleText(String text){ if (skippingCharacters > 0) { if (skippingCharacters >= text.length()) { skippingCharacters -= text.length(); return; } else { text = text.substring(skippingCharacters); skippingCharacters = 0; } } if (rtfDestination != null) { rtfDestination.handleText(text); return; } warning("Text with no destination. oops.");}/** The default color for text which has no specified color. */Color defaultColor(){ return Color.black;}/** Called by the superclass when a new RTF group is begun. * This implementation saves the current <code>parserState</code>, and gives * the current destination a chance to save its own state. * @see RTFParser#begingroup */public void begingroup(){ if (skippingCharacters > 0) { /* TODO this indicates an error in the RTF. Log it? */ skippingCharacters = 0; } /* we do this little dance to avoid cloning the entire state stack and immediately throwing it away. */ Object oldSaveState = parserState.get("_savedState"); if (oldSaveState != null) parserState.remove("_savedState"); Dictionary saveState = (Dictionary)((Hashtable)parserState).clone(); if (oldSaveState != null) saveState.put("_savedState", oldSaveState); parserState.put("_savedState", saveState); if (rtfDestination != null) rtfDestination.begingroup();}/** Called by the superclass when the current RTF group is closed. * This restores the parserState saved by <code>begingroup()</code> * as well as invoking the endgroup method of the current * destination. * @see RTFParser#endgroup */public void endgroup(){ if (skippingCharacters > 0) { /* NB this indicates an error in the RTF. Log it? */ skippingCharacters = 0; } Dictionary restoredState = (Dictionary)parserState.get("_savedState"); Destination restoredDestination = (Destination)restoredState.get("dst"); if (restoredDestination != rtfDestination) { rtfDestination.close(); /* allow the destination to clean up */ rtfDestination = restoredDestination; } Dictionary oldParserState = parserState; parserState = restoredState; if (rtfDestination != null) rtfDestination.endgroup(oldParserState);}protected void setRTFDestination(Destination newDestination){ /* Check that setting the destination won't close the current destination (should never happen) */ Dictionary previousState = (Dictionary)parserState.get("_savedState"); if (previousState != null) { if (rtfDestination != previousState.get("dst")) { warning("Warning, RTF destination overridden, invalid RTF."); rtfDestination.close(); } } rtfDestination = newDestination; parserState.put("dst", rtfDestination);}/** Called by the user when there is no more input (<i>i.e.</i>, * at the end of the RTF file.) * * @see OutputStream#close */public void close() throws IOException{ Enumeration docProps = documentAttributes.getAttributeNames(); while(docProps.hasMoreElements()) { Object propName = docProps.nextElement(); target.putProperty(propName, documentAttributes.getAttribute((String)propName)); } /* RTFParser should have ensured that all our groups are closed */ warning("RTF filter done."); super.close();}/** * Handles a parameterless RTF keyword. This is called by the superclass * (RTFParser) when a keyword is found in the input stream. * * @returns <code>true</code> if the keyword is recognized and handled; * <code>false</code> otherwise * @see RTFParser#handleKeyword */public boolean handleKeyword(String keyword){ Object item; boolean ignoreGroupIfUnknownKeywordSave = ignoreGroupIfUnknownKeyword; if (skippingCharacters > 0) { skippingCharacters --; return true; } ignoreGroupIfUnknownKeyword = false; if ((item = textKeywords.get(keyword)) != null) { handleText((String)item); return true; } if (keyword.equals("fonttbl")) { setRTFDestination(new FonttblDestination()); return true; } if (keyword.equals("colortbl")) { setRTFDestination(new ColortblDestination()); return true; } if (keyword.equals("stylesheet")) { setRTFDestination(new StylesheetDestination()); return true; } if (keyword.equals("info")) { setRTFDestination(new InfoDestination()); return false; } if (keyword.equals("mac")) { setCharacterSet("mac"); return true; } if (keyword.equals("ansi")) { if (useNeXTForAnsi) setCharacterSet("NeXT"); else setCharacterSet("ansi"); return true; } if (keyword.equals("next")) { setCharacterSet("NeXT"); return true; } if (keyword.equals("pc")) { setCharacterSet("cpg437"); /* IBM Code Page 437 */ return true; } if (keyword.equals("pca")) { setCharacterSet("cpg850"); /* IBM Code Page 850 */ return true; } if (keyword.equals("*")) { ignoreGroupIfUnknownKeyword = true; return true; } if (rtfDestination != null) { if(rtfDestination.handleKeyword(keyword)) return true; } /* this point is reached only if the keyword is unrecognized */ /* other destinations we don't understand and therefore ignore */ if (keyword.equals("aftncn") || keyword.equals("aftnsep") || keyword.equals("aftnsepc") || keyword.equals("annotation") || keyword.equals("atnauthor") || keyword.equals("atnicn") || keyword.equals("atnid") || keyword.equals("atnref") || keyword.equals("atntime") || keyword.equals("atrfend") || keyword.equals("atrfstart") || keyword.equals("bkmkend") || keyword.equals("bkmkstart") || keyword.equals("datafield") || keyword.equals("do") || keyword.equals("dptxbxtext") || keyword.equals("falt") || keyword.equals("field") || keyword.equals("file") || keyword.equals("filetbl") || keyword.equals("fname") || keyword.equals("fontemb") || keyword.equals("fontfile") || keyword.equals("footer") || keyword.equals("footerf") || keyword.equals("footerl") || keyword.equals("footerr") || keyword.equals("footnote") || keyword.equals("ftncn") || keyword.equals("ftnsep") || keyword.equals("ftnsepc") || keyword.equals("header") || keyword.equals("headerf") || keyword.equals("headerl") || keyword.equals("headerr") || keyword.equals("keycode") || keyword.equals("nextfile") || keyword.equals("object") || keyword.equals("pict") || keyword.equals("pn") || keyword.equals("pnseclvl") || keyword.equals("pntxtb") || keyword.equals("pntxta") || keyword.equals("revtbl") || keyword.equals("rxe") || keyword.equals("tc") || keyword.equals("template") || keyword.equals("txe") || keyword.equals("xe")) { ignoreGroupIfUnknownKeywordSave = true; } if (ignoreGroupIfUnknownKeywordSave) { setRTFDestination(new DiscardingDestination()); } return false;}/** * Handles an RTF keyword and its integer parameter. * This is called by the superclass * (RTFParser) when a keyword is found in the input stream. * * @returns <code>true</code> if the keyword is recognized and handled; * <code>false</code> otherwise * @see RTFParser#handleKeyword */public boolean handleKeyword(String keyword, int parameter){ boolean ignoreGroupIfUnknownKeywordSave = ignoreGroupIfUnknownKeyword; if (skippingCharacters > 0) { skippingCharacters --; return true; } ignoreGroupIfUnknownKeyword = false; if (keyword.equals("uc")) { /* count of characters to skip after a unicode character */ parserState.put("UnicodeSkip", Integer.valueOf(parameter)); return true; } if (keyword.equals("u")) { if (parameter < 0) parameter = parameter + 65536; handleText((char)parameter); Number skip = (Number)(parserState.get("UnicodeSkip")); if (skip != null) { skippingCharacters = skip.intValue(); } else { skippingCharacters = 1; } return true; } if (keyword.equals("rtf")) { rtfversion = parameter; setRTFDestination(new DocumentDestination()); return true; } if (keyword.startsWith("NeXT") || keyword.equals("private")) ignoreGroupIfUnknownKeywordSave = true; if (rtfDestination != null) { if(rtfDestination.handleKeyword(keyword, parameter)) return true; } /* this point is reached only if the keyword is unrecognized */ if (ignoreGroupIfUnknownKeywordSave) { setRTFDestination(new DiscardingDestination()); } return false;}private void setTargetAttribute(String name, Object value){// target.changeAttributes(new LFDictionary(LFArray.arrayWithObject(value), LFArray.arrayWithObject(name)));}/** * setCharacterSet sets the current translation table to correspond with * the named character set. The character set is loaded if necessary. * * @see AbstractFilter */public void setCharacterSet(String name){ Object set; try { set = getCharacterSet(name); } catch (Exception e) { warning("Exception loading RTF character set \"" + name + "\": " + e); set = null; } if (set != null) { translationTable = (char[])set; } else { warning("Unknown RTF character set \"" + name + "\""); if (!name.equals("ansi")) { try { translationTable = (char[])getCharacterSet("ansi"); } catch (IOException e) { throw new InternalError("RTFReader: Unable to find character set resources (" + e + ")"); } } } setTargetAttribute(Constants.RTFCharacterSet, name);}/** Adds a character set to the RTFReader's list * of known character sets */public static voiddefineCharacterSet(String name, char[] table){ if (table.length < 256) throw new IllegalArgumentException("Translation table must have 256 entries."); characterSets.put(name, table);}/** Looks up a named character set. A character set is a 256-entry * array of characters, mapping unsigned byte values to their Unicode * equivalents. The character set is loaded if necessary. * * @returns the character set */public static ObjectgetCharacterSet(final String name) throws IOException{ char[] set; set = (char [])characterSets.get(name); if (set == null) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -