1001 lines · 30.4 KB · Java
RawDownload
/* * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.text.rtf; import java.lang.*;import java.util.*;import java.awt.Color;import java.awt.Font;import java.io.OutputStream;import java.io.IOException; import javax.swing.text.*; /** * Generates an RTF output stream (java.io.OutputStream) from rich text * (handed off through a series of LTTextAcceptor calls).  Can be used to * generate RTF from any object which knows how to write to a text acceptor * (e.g., LTAttributedText and LTRTFFilter). * * <p>Note that this is a lossy conversion since RTF's model of * text does not exactly correspond with LightText's. * * @see LTAttributedText * @see LTRTFFilter * @see LTTextAcceptor * @see java.io.OutputStream */ class RTFGenerator extends Object{    /* These dictionaries map Colors, font names, or Style objects       to Integers */    Dictionary<Object, Integer> colorTable;    int colorCount;    Dictionary<String, Integer> fontTable;    int fontCount;    Dictionary<AttributeSet, Integer> styleTable;    int styleCount;     /* where all the text is going */    OutputStream outputStream;     boolean afterKeyword;     MutableAttributeSet outputAttributes;     /* the value of the last \\ucN keyword emitted */    int unicodeCount;     /* for efficiency's sake (ha) */    private Segment workingSegment;     int[] outputConversion;     /** The default color, used for text without an explicit color     *  attribute. */    static public final Color defaultRTFColor = Color.black;     static public final float defaultFontSize = 12f;     static public final String defaultFontFamily = "Helvetica";     /* constants so we can avoid allocating objects in inner loops */    final static private Object MagicToken;     /* An array of character-keyword pairs. This could be done       as a dictionary (and lookup would be quicker), but that       would require allocating an object for every character       written (slow!). */    static class CharacterKeywordPair      { public char character; public String keyword; }    static protected CharacterKeywordPair[] textKeywords;     static {        MagicToken = new Object();         Dictionary textKeywordDictionary = RTFReader.textKeywords;        Enumeration keys = textKeywordDictionary.keys();        Vector<CharacterKeywordPair> tempPairs = new Vector<CharacterKeywordPair>();        while(keys.hasMoreElements()) {            CharacterKeywordPair pair = new CharacterKeywordPair();            pair.keyword = (String)keys.nextElement();            pair.character = ((String)textKeywordDictionary.get(pair.keyword)).charAt(0);            tempPairs.addElement(pair);        }        textKeywords = new CharacterKeywordPair[tempPairs.size()];        tempPairs.copyInto(textKeywords);    }     static final char[] hexdigits = { '0', '1', '2', '3', '4', '5', '6', '7',                                      '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; static public void writeDocument(Document d, OutputStream to)    throws IOException{    RTFGenerator gen = new RTFGenerator(to);    Element root = d.getDefaultRootElement();     gen.examineElement(root);    gen.writeRTFHeader();    gen.writeDocumentProperties(d);     /* TODO this assumes a particular element structure; is there       a way to iterate more generically ? */    int max = root.getElementCount();    for(int idx = 0; idx < max; idx++)        gen.writeParagraphElement(root.getElement(idx));     gen.writeRTFTrailer();} public RTFGenerator(OutputStream to){    colorTable = new Hashtable<Object, Integer>();    colorTable.put(defaultRTFColor, Integer.valueOf(0));    colorCount = 1;     fontTable = new Hashtable<String, Integer>();    fontCount = 0;     styleTable = new Hashtable<AttributeSet, Integer>();    /* TODO: put default style in style table */    styleCount = 0;     workingSegment = new Segment();     outputStream = to;     unicodeCount = 1;} public void examineElement(Element el){    AttributeSet a = el.getAttributes();    String fontName;    Object foregroundColor, backgroundColor;     tallyStyles(a);     if (a != null) {        /* TODO: default color must be color 0! */         foregroundColor = StyleConstants.getForeground(a);        if (foregroundColor != null &&            colorTable.get(foregroundColor) == null) {            colorTable.put(foregroundColor, new Integer(colorCount));            colorCount ++;        }         backgroundColor = a.getAttribute(StyleConstants.Background);        if (backgroundColor != null &&            colorTable.get(backgroundColor) == null) {            colorTable.put(backgroundColor, new Integer(colorCount));            colorCount ++;        }         fontName = StyleConstants.getFontFamily(a);         if (fontName == null)            fontName = defaultFontFamily;         if (fontName != null &&            fontTable.get(fontName) == null) {            fontTable.put(fontName, new Integer(fontCount));            fontCount ++;        }    }     int el_count = el.getElementCount();    for(int el_idx = 0; el_idx < el_count; el_idx ++) {        examineElement(el.getElement(el_idx));    }} private void tallyStyles(AttributeSet a) {    while (a != null) {        if (a instanceof Style) {            Integer aNum = styleTable.get(a);            if (aNum == null) {                styleCount = styleCount + 1;                aNum = new Integer(styleCount);                styleTable.put(a, aNum);            }        }        a = a.getResolveParent();    }} private Style findStyle(AttributeSet a){    while(a != null) {        if (a instanceof Style) {            Object aNum = styleTable.get(a);            if (aNum != null)                return (Style)a;        }        a = a.getResolveParent();    }    return null;} private Integer findStyleNumber(AttributeSet a, String domain){    while(a != null) {        if (a instanceof Style) {            Integer aNum = styleTable.get(a);            if (aNum != null) {                if (domain == null ||                    domain.equals(a.getAttribute(Constants.StyleType)))                    return aNum;            }         }        a = a.getResolveParent();    }    return null;} static private Object attrDiff(MutableAttributeSet oldAttrs,                               AttributeSet newAttrs,                               Object key,                               Object dfl){    Object oldValue, newValue;     oldValue = oldAttrs.getAttribute(key);    newValue = newAttrs.getAttribute(key);     if (newValue == oldValue)        return null;    if (newValue == null) {        oldAttrs.removeAttribute(key);        if (dfl != null && !dfl.equals(oldValue))            return dfl;        else            return null;    }    if (oldValue == null ||        !equalArraysOK(oldValue, newValue)) {        oldAttrs.addAttribute(key, newValue);        return newValue;    }    return null;} static private boolean equalArraysOK(Object a, Object b){    Object[] aa, bb;    if (a == b)        return true;    if (a == null || b == null)        return false;    if (a.equals(b))        return true;    if (!(a.getClass().isArray() && b.getClass().isArray()))        return false;    aa = (Object[])a;    bb = (Object[])b;    if (aa.length != bb.length)        return false;     int i;    int l = aa.length;    for(i = 0; i < l; i++) {        if (!equalArraysOK(aa[i], bb[i]))            return false;    }     return true;} /* Writes a line break to the output file, for ease in debugging */public void writeLineBreak()    throws IOException{    writeRawString("\n");    afterKeyword = false;}  public void writeRTFHeader()    throws IOException{    int index;     /* TODO: Should the writer attempt to examine the text it's writing       and pick a character set which will most compactly represent the       document? (currently the writer always uses the ansi character       set, which is roughly ISO-8859 Latin-1, and uses Unicode escapes       for all other characters. However Unicode is a relatively       recent addition to RTF, and not all readers will understand it.) */    writeBegingroup();    writeControlWord("rtf", 1);    writeControlWord("ansi");    outputConversion = outputConversionForName("ansi");    writeLineBreak();     /* write font table */    String[] sortedFontTable = new String[fontCount];    Enumeration<String> fonts = fontTable.keys();    String font;    while(fonts.hasMoreElements()) {        font = fonts.nextElement();        Integer num = fontTable.get(font);        sortedFontTable[num.intValue()] = font;    }    writeBegingroup();    writeControlWord("fonttbl");    for(index = 0; index < fontCount; index ++) {        writeControlWord("f", index);        writeControlWord("fnil");  /* TODO: supply correct font style */        writeText(sortedFontTable[index]);        writeText(";");    }    writeEndgroup();    writeLineBreak();     /* write color table */    if (colorCount > 1) {        Color[] sortedColorTable = new Color[colorCount];        Enumeration colors = colorTable.keys();        Color color;        while(colors.hasMoreElements()) {            color = (Color)colors.nextElement();            Integer num = colorTable.get(color);            sortedColorTable[num.intValue()] = color;        }        writeBegingroup();        writeControlWord("colortbl");        for(index = 0; index < colorCount; index ++) {            color = sortedColorTable[index];            if (color != null) {                writeControlWord("red", color.getRed());                writeControlWord("green", color.getGreen());                writeControlWord("blue", color.getBlue());            }            writeRawString(";");        }        writeEndgroup();        writeLineBreak();    }     /* write the style sheet */    if (styleCount > 1) {        writeBegingroup();        writeControlWord("stylesheet");        Enumeration<AttributeSet> styles = styleTable.keys();        while(styles.hasMoreElements()) {            Style style = (Style)styles.nextElement();            int styleNumber = styleTable.get(style).intValue();            writeBegingroup();            String styleType = (String)style.getAttribute(Constants.StyleType);            if (styleType == null)                styleType = Constants.STParagraph;            if (styleType.equals(Constants.STCharacter)) {                writeControlWord("*");                writeControlWord("cs", styleNumber);            } else if(styleType.equals(Constants.STSection)) {                writeControlWord("*");                writeControlWord("ds", styleNumber);            } else {                writeControlWord("s", styleNumber);            }             AttributeSet basis = style.getResolveParent();            MutableAttributeSet goat;            if (basis == null) {                goat = new SimpleAttributeSet();            } else {                goat = new SimpleAttributeSet(basis);            }             updateSectionAttributes(goat, style, false);            updateParagraphAttributes(goat, style, false);            updateCharacterAttributes(goat, style, false);             basis = style.getResolveParent();            if (basis != null && basis instanceof Style) {                Integer basedOn = styleTable.get(basis);                if (basedOn != null) {                    writeControlWord("sbasedon", basedOn.intValue());                }            }             Style nextStyle = (Style)style.getAttribute(Constants.StyleNext);            if (nextStyle != null) {                Integer nextNum = styleTable.get(nextStyle);                if (nextNum != null) {                    writeControlWord("snext", nextNum.intValue());                }            }             Boolean hidden = (Boolean)style.getAttribute(Constants.StyleHidden);            if (hidden != null && hidden.booleanValue())                writeControlWord("shidden");             Boolean additive = (Boolean)style.getAttribute(Constants.StyleAdditive);            if (additive != null && additive.booleanValue())                writeControlWord("additive");              writeText(style.getName());            writeText(";");            writeEndgroup();        }        writeEndgroup();        writeLineBreak();    }     outputAttributes = new SimpleAttributeSet();} void writeDocumentProperties(Document doc)    throws IOException{    /* Write the document properties */    int i;    boolean wroteSomething = false;     for(i = 0; i < RTFAttributes.attributes.length; i++) {        RTFAttribute attr = RTFAttributes.attributes[i];        if (attr.domain() != RTFAttribute.D_DOCUMENT)            continue;        Object prop = doc.getProperty(attr.swingName());        boolean ok = attr.writeValue(prop, this, false);        if (ok)            wroteSomething = true;    }     if (wroteSomething)        writeLineBreak();} public void writeRTFTrailer()    throws IOException{    writeEndgroup();    writeLineBreak();} protected void checkNumericControlWord(MutableAttributeSet currentAttributes,                                       AttributeSet newAttributes,                                       Object attrName,                                       String controlWord,                                       float dflt, float scale)    throws IOException{    Object parm;     if ((parm = attrDiff(currentAttributes, newAttributes,                         attrName, MagicToken)) != null) {        float targ;        if (parm == MagicToken)            targ = dflt;        else            targ = ((Number)parm).floatValue();        writeControlWord(controlWord, Math.round(targ * scale));    }} protected void checkControlWord(MutableAttributeSet currentAttributes,                                AttributeSet newAttributes,                                RTFAttribute word)    throws IOException{    Object parm;     if ((parm = attrDiff(currentAttributes, newAttributes,                         word.swingName(), MagicToken)) != null) {        if (parm == MagicToken)            parm = null;        word.writeValue(parm, this, true);    }} protected void checkControlWords(MutableAttributeSet currentAttributes,                                 AttributeSet newAttributes,                                 RTFAttribute words[],

Lines 1–500 of 1001.