KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > text > rtf > RTFGenerator


1 /*
2  * @(#)RTFGenerator.java 1.13 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing.text.rtf;
8
9 import java.lang.*;
10 import java.util.*;
11 import java.awt.Color JavaDoc;
12 import java.awt.Font JavaDoc;
13 import java.io.OutputStream JavaDoc;
14 import java.io.IOException JavaDoc;
15
16 import javax.swing.text.*;
17
18 /**
19  * Generates an RTF output stream (java.io.OutputStream) from rich text
20  * (handed off through a series of LTTextAcceptor calls). Can be used to
21  * generate RTF from any object which knows how to write to a text acceptor
22  * (e.g., LTAttributedText and LTRTFFilter).
23  *
24  * <p>Note that this is a lossy conversion since RTF's model of
25  * text does not exactly correspond with LightText's.
26  *
27  * @see LTAttributedText
28  * @see LTRTFFilter
29  * @see LTTextAcceptor
30  * @see java.io.OutputStream
31  */

32
33 class RTFGenerator extends Object JavaDoc
34 {
35     /* These dictionaries map Colors, font names, or Style objects
36        to Integers */

37     Dictionary colorTable;
38     int colorCount;
39     Dictionary fontTable;
40     int fontCount;
41     Dictionary styleTable;
42     int styleCount;
43
44     /* where all the text is going */
45     OutputStream JavaDoc outputStream;
46
47     boolean afterKeyword;
48
49     MutableAttributeSet outputAttributes;
50
51     /* the value of the last \\ucN keyword emitted */
52     int unicodeCount;
53
54     /* for efficiency's sake (ha) */
55     private Segment workingSegment;
56
57     int[] outputConversion;
58
59     /** The default color, used for text without an explicit color
60      * attribute. */

61     static public final Color JavaDoc defaultRTFColor = Color.black;
62
63     static public final float defaultFontSize = 12f;
64
65     static public final String JavaDoc defaultFontFamily = "Helvetica";
66
67     /* constants so we can avoid allocating objects in inner loops */
68     /* these should all be final, but javac seems to be a bit buggy */
69     static protected Integer JavaDoc One, Zero;
70     static protected Boolean JavaDoc False;
71     static protected Float JavaDoc ZeroPointZero;
72     static private Object JavaDoc MagicToken;
73
74     /* An array of character-keyword pairs. This could be done
75        as a dictionary (and lookup would be quicker), but that
76        would require allocating an object for every character
77        written (slow!). */

78     static class CharacterKeywordPair
79       { public char character; public String JavaDoc keyword; };
80     static protected CharacterKeywordPair[] textKeywords;
81
82     static {
83     One = new Integer JavaDoc(1);
84     Zero = new Integer JavaDoc(0);
85     False = Boolean.valueOf(false);
86     MagicToken = new Object JavaDoc();
87     ZeroPointZero = new Float JavaDoc(0);
88
89     Dictionary textKeywordDictionary = RTFReader.textKeywords;
90         Enumeration keys = textKeywordDictionary.keys();
91     Vector tempPairs = new Vector();
92     while(keys.hasMoreElements()) {
93         CharacterKeywordPair pair = new CharacterKeywordPair();
94         pair.keyword = (String JavaDoc)keys.nextElement();
95         pair.character = ((String JavaDoc)textKeywordDictionary.get(pair.keyword)).charAt(0);
96         tempPairs.addElement(pair);
97     }
98     textKeywords = new CharacterKeywordPair[tempPairs.size()];
99     tempPairs.copyInto(textKeywords);
100     }
101
102     static final char[] hexdigits = { '0', '1', '2', '3', '4', '5', '6', '7',
103                       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
104
105 static public void writeDocument(Document d, OutputStream JavaDoc to)
106     throws IOException JavaDoc
107 {
108     RTFGenerator JavaDoc gen = new RTFGenerator JavaDoc(to);
109     Element root = d.getDefaultRootElement();
110
111     gen.examineElement(root);
112     gen.writeRTFHeader();
113     gen.writeDocumentProperties(d);
114
115     /* TODO this assumes a particular element structure; is there
116        a way to iterate more generically ? */

117     int max = root.getElementCount();
118     for(int idx = 0; idx < max; idx++)
119     gen.writeParagraphElement(root.getElement(idx));
120
121     gen.writeRTFTrailer();
122 }
123
124 public RTFGenerator(OutputStream JavaDoc to)
125 {
126     colorTable = new Hashtable();
127     colorTable.put(defaultRTFColor, new Integer JavaDoc(0));
128     colorCount = 1;
129
130     fontTable = new Hashtable();
131     fontCount = 0;
132
133     styleTable = new Hashtable();
134     /* TODO: put default style in style table */
135     styleCount = 0;
136
137     workingSegment = new Segment();
138
139     outputStream = to;
140
141     unicodeCount = 1;
142 }
143
144 public void examineElement(Element el)
145 {
146     AttributeSet a = el.getAttributes();
147     String JavaDoc fontName;
148     Object JavaDoc foregroundColor, backgroundColor;
149
150     tallyStyles(a);
151
152     if (a != null) {
153     /* TODO: default color must be color 0! */
154     
155     foregroundColor = StyleConstants.getForeground(a);
156     if (foregroundColor != null &&
157         colorTable.get(foregroundColor) == null) {
158         colorTable.put(foregroundColor, new Integer JavaDoc(colorCount));
159         colorCount ++;
160     }
161     
162     backgroundColor = a.getAttribute(StyleConstants.Background);
163     if (backgroundColor != null &&
164         colorTable.get(backgroundColor) == null) {
165         colorTable.put(backgroundColor, new Integer JavaDoc(colorCount));
166         colorCount ++;
167     }
168     
169     fontName = StyleConstants.getFontFamily(a);
170     
171     if (fontName == null)
172         fontName = defaultFontFamily;
173
174     if (fontName != null &&
175         fontTable.get(fontName) == null) {
176         fontTable.put(fontName, new Integer JavaDoc(fontCount));
177         fontCount ++;
178     }
179     }
180
181     int el_count = el.getElementCount();
182     for(int el_idx = 0; el_idx < el_count; el_idx ++) {
183     examineElement(el.getElement(el_idx));
184     }
185 }
186
187 private void tallyStyles(AttributeSet a) {
188     while (a != null) {
189         if (a instanceof Style) {
190         Integer JavaDoc aNum = (Integer JavaDoc)styleTable.get(a);
191         if (aNum == null) {
192         styleCount = styleCount + 1;
193             aNum = new Integer JavaDoc(styleCount);
194         styleTable.put(a, aNum);
195         }
196     }
197     a = a.getResolveParent();
198     }
199 }
200
201 private Style findStyle(AttributeSet a)
202 {
203     while(a != null) {
204         if (a instanceof Style) {
205         Object JavaDoc aNum = styleTable.get(a);
206         if (aNum != null)
207             return (Style)a;
208     }
209     a = a.getResolveParent();
210     }
211     return null;
212 }
213
214 private Integer JavaDoc findStyleNumber(AttributeSet a, String JavaDoc domain)
215 {
216     while(a != null) {
217         if (a instanceof Style) {
218         Integer JavaDoc aNum = (Integer JavaDoc)styleTable.get(a);
219         if (aNum != null) {
220         if (domain == null ||
221             domain.equals(a.getAttribute(Constants.StyleType)))
222             return aNum;
223         }
224           
225     }
226     a = a.getResolveParent();
227     }
228     return null;
229 }
230
231 static private Object JavaDoc attrDiff(MutableAttributeSet oldAttrs,
232                    AttributeSet newAttrs,
233                    Object JavaDoc key,
234                    Object JavaDoc dfl)
235 {
236     Object JavaDoc oldValue, newValue;
237
238     oldValue = oldAttrs.getAttribute(key);
239     newValue = newAttrs.getAttribute(key);
240
241     if (newValue == oldValue)
242     return null;
243     if (newValue == null) {
244     oldAttrs.removeAttribute(key);
245     if (dfl != null && !dfl.equals(oldValue))
246         return dfl;
247         else
248         return null;
249     }
250     if (oldValue == null ||
251     !equalArraysOK(oldValue, newValue)) {
252     oldAttrs.addAttribute(key, newValue);
253     return newValue;
254     }
255     return null;
256 }
257
258 static private boolean equalArraysOK(Object JavaDoc a, Object JavaDoc b)
259 {
260     Object JavaDoc[] aa, bb;
261     if (a == b)
262     return true;
263     if (a == null || b == null)
264     return false;
265     if (a.equals(b))
266     return true;
267     if (!(a.getClass().isArray() && b.getClass().isArray()))
268     return false;
269     aa = (Object JavaDoc[])a;
270     bb = (Object JavaDoc[])b;
271     if (aa.length != bb.length)
272     return false;
273     
274     int i;
275     int l = aa.length;
276     for(i = 0; i < l; i++) {
277     if (!equalArraysOK(aa[i], bb[i]))
278         return false;
279     }
280
281     return true;
282 }
283     
284 /* Writes a line break to the output file, for ease in debugging */
285 public void writeLineBreak()
286     throws IOException JavaDoc
287 {
288     writeRawString("\n");
289     afterKeyword = false;
290 }
291
292
293 public void writeRTFHeader()
294     throws IOException JavaDoc
295 {
296     int index;
297
298     /* TODO: Should the writer attempt to examine the text it's writing
299        and pick a character set which will most compactly represent the
300        document? (currently the writer always uses the ansi character
301        set, which is roughly ISO-8859 Latin-1, and uses Unicode escapes
302        for all other characters. However Unicode is a relatively
303        recent addition to RTF, and not all readers will understand it.) */

304     writeBegingroup();
305     writeControlWord("rtf", 1);
306     writeControlWord("ansi");
307     outputConversion = outputConversionForName("ansi");
308     writeLineBreak();
309
310     /* write font table */
311     String JavaDoc[] sortedFontTable = new String JavaDoc[fontCount];
312     Enumeration fonts = fontTable.keys();
313     String JavaDoc font;
314     while(fonts.hasMoreElements()) {
315     font = (String JavaDoc)fonts.nextElement();
316     Integer JavaDoc num = (Integer JavaDoc)(fontTable.get(font));
317     sortedFontTable[num.intValue()] = font;
318     }
319     writeBegingroup();
320     writeControlWord("fonttbl");
321     for(index = 0; index < fontCount; index ++) {
322     writeControlWord("f", index);
323     writeControlWord("fnil"); /* TODO: supply correct font style */
324     writeText(sortedFontTable[index]);
325     writeText(";");
326     }
327     writeEndgroup();
328     writeLineBreak();
329
330     /* write color table */
331     if (colorCount > 1) {
332     Color JavaDoc[] sortedColorTable = new Color JavaDoc[colorCount];
333     Enumeration colors = colorTable.keys();
334     Color JavaDoc color;
335     while(colors.hasMoreElements()) {
336         color = (Color JavaDoc)colors.nextElement();
337         Integer JavaDoc num = (Integer JavaDoc)(colorTable.get(color));
338         sortedColorTable[num.intValue()] = color;
339     }
340     writeBegingroup();
341     writeControlWord("colortbl");
342     for(index = 0; index < colorCount; index ++) {
343         color = sortedColorTable[index];
344         if (color != null) {
345         writeControlWord("red", color.getRed());
346         writeControlWord("green", color.getGreen());
347         writeControlWord("blue", color.getBlue());
348         }
349         writeRawString(";");
350     }
351     writeEndgroup();
352     writeLineBreak();
353     }
354
355     /* write the style sheet */
356     if (styleCount > 1) {
357     writeBegingroup();
358     writeControlWord("stylesheet");
359     Enumeration styles = styleTable.keys();
360     while(styles.hasMoreElements()) {
361         Style style = (Style)styles.nextElement();
362         int styleNumber = ((Integer JavaDoc)styleTable.get(style)).intValue();
363         writeBegingroup();
364         String JavaDoc styleType = (String JavaDoc)style.getAttribute(Constants.StyleType);
365         if (styleType == null)
366             styleType = Constants.STParagraph;
367         if (styleType.equals(Constants.STCharacter)) {
368             writeControlWord("*");
369         writeControlWord("cs", styleNumber);
370         } else if(styleType.equals(Constants.STSection)) {
371             writeControlWord("*");
372         writeControlWord("ds", styleNumber);
373         } else {
374             writeControlWord("s", styleNumber);
375         }
376         
377         AttributeSet basis = style.getResolveParent();
378         MutableAttributeSet goat;
379         if (basis == null) {
380             goat = new SimpleAttributeSet();
381         } else {
382             goat = new SimpleAttributeSet(basis);
383         }
384
385         updateSectionAttributes(goat, style, false);
386         updateParagraphAttributes(goat, style, false);
387         updateCharacterAttributes(goat, style, false);
388
389         basis = style.getResolveParent();
390         if (basis != null && basis instanceof Style) {
391             Integer JavaDoc basedOn = (Integer JavaDoc)styleTable.get(basis);
392         if (basedOn != null) {
393             writeControlWord("sbasedon", basedOn.intValue());
394         }
395         }
396         
397         Style nextStyle = (Style)style.getAttribute(Constants.StyleNext);
398         if (nextStyle != null) {
399             Integer JavaDoc nextNum = (Integer JavaDoc)styleTable.get(nextStyle);
400         if (nextNum != null) {
401             writeControlWord("snext", nextNum.intValue());
402         }
403         }
404         
405         Boolean JavaDoc hidden = (Boolean JavaDoc)style.getAttribute(Constants.StyleHidden);
406         if (hidden != null && hidden.booleanValue())
407             writeControlWord("shidden");
408
409         Boolean JavaDoc additive = (Boolean JavaDoc)style.getAttribute(Constants.StyleAdditive);
410         if (additive != null && additive.booleanValue())
411             writeControlWord("additive");
412
413         
414         writeText(style.getName());
415         writeText(";");
416         writeEndgroup();
417     }
418     writeEndgroup();
419     writeLineBreak();
420     }
421
422     outputAttributes = new SimpleAttributeSet();
423 }
424
425 void writeDocumentProperties(Document doc)
426     throws IOException JavaDoc
427 {
428     /* Write the document properties */
429     int i;
430     boolean wroteSomething = false;
431     
432     for(i = 0; i < RTFAttributes.attributes.length; i++) {
433         RTFAttribute JavaDoc attr = RTFAttributes.attributes[i];
434     if (attr.domain() != RTFAttribute.D_DOCUMENT)
435         continue;
436     Object JavaDoc prop = doc.getProperty(attr.swingName());
437     boolean ok = attr.writeValue(prop, this, false);
438     if (ok)
439         wroteSomething = true;
440     }
441
442     if (wroteSomething)
443         writeLineBreak();
444 }
445
446 public void writeRTFTrailer()
447     throws IOException JavaDoc
448 {
449     writeEndgroup();
450     writeLineBreak();
451 }
452
453 protected void checkNumericControlWord(MutableAttributeSet currentAttributes,
454                        AttributeSet newAttributes,
455                        Object JavaDoc attrName,
456                        String JavaDoc controlWord,
457                        float dflt, float scale)
458     throws IOException JavaDoc
459 {
460     Object JavaDoc parm;
461
462     if ((parm = attrDiff(currentAttributes, newAttributes,
463              attrName, MagicToken)) != null) {
464     float targ;
465     if (parm == MagicToken)
466         targ = dflt;
467     else
468         targ = ((Number JavaDoc)parm).floatValue();
469     writeControlWord(controlWord, Math.round(targ * scale));
470     }
471 }
472
473 protected void checkControlWord(MutableAttributeSet currentAttributes,
474                 AttributeSet newAttributes,
475                 RTFAttribute JavaDoc word)
476     throws IOException JavaDoc
477 {
478     Object JavaDoc parm;
479
480     if ((parm = attrDiff(currentAttributes, newAttributes,
481              word.swingName(), MagicToken)) != null) {
482         if (parm == MagicToken)
483         parm = null;
484     word.writeValue(parm, this, true);
485     }
486 }
487
488 protected void checkControlWords(MutableAttributeSet currentAttributes,
489                  AttributeSet newAttributes,
490                  RTFAttribute JavaDoc words[],
491                  int domain)
492     throws IOException JavaDoc
493 {
494     int wordIndex;
495     int wordCount = words.length;
496     for(wordIndex = 0; wordIndex < wordCount; wordIndex++) {
497         RTFAttribute JavaDoc attr = words[wordIndex];
498     if (attr.domain() == domain)
499         checkControlWord(currentAttributes, newAttributes, attr);
500     }
501 }
502
503 void updateSectionAttributes(MutableAttributeSet current,
504                  AttributeSet newAttributes,
505                  boolean emitStyleChanges)
506     throws IOException JavaDoc
507 {
508     if (emitStyleChanges) {
509     Object JavaDoc oldStyle = current.getAttribute("sectionStyle");
510     Object JavaDoc newStyle = findStyleNumber(newAttributes, Constants.STSection);
511     if (oldStyle != newStyle) {
512         if (oldStyle != null) {
513         resetSectionAttributes(current);
514         }
515         if (newStyle != null) {
516         writeControlWord("ds", ((Integer JavaDoc)newStyle).intValue());
517         current.addAttribute("sectionStyle", newStyle);
518         } else {
519         current.removeAttribute("sectionStyle");
520         }
521     }
522     }
523     
524     checkControlWords(current, newAttributes,
525               RTFAttributes.attributes, RTFAttribute.D_SECTION);
526 }
527
528 protected void resetSectionAttributes(MutableAttributeSet currentAttributes)
529     throws IOException JavaDoc
530 {
531     writeControlWord("sectd");
532
533     int wordIndex;
534     int wordCount = RTFAttributes.attributes.length;
535     for(wordIndex = 0; wordIndex < wordCount; wordIndex++) {
536         RTFAttribute JavaDoc attr = RTFAttributes.attributes[wordIndex];
537     if (attr.domain() == RTFAttribute.D_SECTION)
538         attr.setDefault(currentAttributes);
539     }
540
541     currentAttributes.removeAttribute("sectionStyle");
542 }
543
544 void updateParagraphAttributes(MutableAttributeSet current,
545                    AttributeSet newAttributes,
546                    boolean emitStyleChanges)
547     throws IOException JavaDoc
548 {
549     Object JavaDoc parm;
550     Object JavaDoc oldStyle, newStyle;
551     
552     /* The only way to get rid of tabs or styles is with the \pard keyword,
553        emitted by resetParagraphAttributes(). Ideally we should avoid
554        emitting \pard if the new paragraph's tabs are a superset of the old
555        paragraph's tabs. */

556
557     if (emitStyleChanges) {
558     oldStyle = current.getAttribute("paragraphStyle");
559     newStyle = findStyleNumber(newAttributes, Constants.STParagraph);
560     if (oldStyle != newStyle) {
561         if (oldStyle != null) {
562         resetParagraphAttributes(current);
563         oldStyle = null;
564         }
565     }
566     } else {
567     oldStyle = null;
568     newStyle = null;
569     }
570     
571     Object JavaDoc oldTabs = current.getAttribute(Constants.Tabs);
572     Object JavaDoc newTabs = newAttributes.getAttribute(Constants.Tabs);
573     if (oldTabs != newTabs) {
574     if (oldTabs != null) {
575         resetParagraphAttributes(current);
576         oldTabs = null;
577         oldStyle = null;
578     }
579     }
580
581     if (oldStyle != newStyle && newStyle != null) {
582     writeControlWord("s", ((Integer JavaDoc)newStyle).intValue());
583     current.addAttribute("paragraphStyle", newStyle);
584     }
585
586     checkControlWords(current, newAttributes,
587               RTFAttributes.attributes, RTFAttribute.D_PARAGRAPH);
588
589     if (oldTabs != newTabs && newTabs != null) {
590     TabStop tabs[] = (TabStop[])newTabs;
591     int index;
592     for(index = 0; index < tabs.length; index ++) {
593         TabStop tab = tabs[index];
594         switch (tab.getAlignment()) {
595           case TabStop.ALIGN_LEFT:
596           case TabStop.ALIGN_BAR:
597         break;
598           case TabStop.ALIGN_RIGHT:
599         writeControlWord("tqr");
600         break;
601           case TabStop.ALIGN_CENTER:
602         writeControlWord("tqc");
603         break;
604           case TabStop.ALIGN_DECIMAL:
605         writeControlWord("tqdec");
606         break;
607         }
608         switch (tab.getLeader()) {
609           case TabStop.LEAD_NONE:
610         break;
611           case TabStop.LEAD_DOTS:
612         writeControlWord("tldot");
613         break;
614           case TabStop.LEAD_HYPHENS:
615         writeControlWord("tlhyph");
616         break;
617           case TabStop.LEAD_UNDERLINE:
618         writeControlWord("tlul");
619         break;
620           case TabStop.LEAD_THICKLINE:
621         writeControlWord("tlth");
622         break;
623           case TabStop.LEAD_EQUALS:
624         writeControlWord("tleq");
625         break;
626         }
627         int twips = Math.round(20f * tab.getPosition());
628         if (tab.getAlignment() == TabStop.ALIGN_BAR) {
629         writeControlWord("tb", twips);
630         } else {
631         writeControlWord("tx", twips);
632         }
633     }
634     current.addAttribute(Constants.Tabs, tabs);
635     }
636 }
637
638 public void writeParagraphElement(Element el)
639     throws IOException JavaDoc
640 {
641     updateParagraphAttributes(outputAttributes, el.getAttributes(), true);
642
643     int sub_count = el.getElementCount();
644     for(int idx = 0; idx < sub_count; idx ++) {
645     writeTextElement(el.getElement(idx));
646     }
647
648     writeControlWord("par");
649     writeLineBreak(); /* makes the raw file more readable */
650 }
651
652 /* debugging. TODO: remove.
653 private static String tabdump(Object tso)
654 {
655     String buf;
656     int i;
657
658     if (tso == null)
659     return "[none]";
660
661     TabStop[] ts = (TabStop[])tso;
662
663     buf = "[";
664     for(i = 0; i < ts.length; i++) {
665     buf = buf + ts[i].toString();
666     if ((i+1) < ts.length)
667         buf = buf + ",";
668     }
669     return buf + "]";
670 }
671 */

672
673 protected void resetParagraphAttributes(MutableAttributeSet currentAttributes)
674     throws IOException JavaDoc
675 {
676     writeControlWord("pard");
677
678     currentAttributes.addAttribute(StyleConstants.Alignment, Zero);
679
680     int wordIndex;
681     int wordCount = RTFAttributes.attributes.length;
682     for(wordIndex = 0; wordIndex < wordCount; wordIndex++) {
683         RTFAttribute JavaDoc attr = RTFAttributes.attributes[wordIndex];
684     if (attr.domain() == RTFAttribute.D_PARAGRAPH)
685         attr.setDefault(currentAttributes);
686     }
687
688     currentAttributes.removeAttribute("paragraphStyle");
689     currentAttributes.removeAttribute(Constants.Tabs);
690 }
691
692 void updateCharacterAttributes(MutableAttributeSet current,
693                    AttributeSet newAttributes,
694                    boolean updateStyleChanges)
695     throws IOException JavaDoc
696 {
697     Object JavaDoc parm;
698
699     if (updateStyleChanges) {
700     Object JavaDoc oldStyle = current.getAttribute("characterStyle");
701     Object JavaDoc newStyle = findStyleNumber(newAttributes,
702                       Constants.STCharacter);
703     if (oldStyle != newStyle) {
704         if (oldStyle != null) {
705         resetCharacterAttributes(current);
706         }
707         if (newStyle != null) {
708         writeControlWord("cs", ((Integer JavaDoc)newStyle).intValue());
709         current.addAttribute("characterStyle", newStyle);
710         } else {
711         current.removeAttribute("characterStyle");
712         }
713     }
714     }
715
716     if ((parm = attrDiff(current, newAttributes,
717              StyleConstants.FontFamily, null)) != null) {
718     Number JavaDoc fontNum = (Number JavaDoc)fontTable.get(parm);
719     writeControlWord("f", fontNum.intValue());
720     }
721
722     checkNumericControlWord(current, newAttributes,
723                 StyleConstants.FontSize, "fs",
724                 defaultFontSize, 2f);
725
726     checkControlWords(current, newAttributes,
727               RTFAttributes.attributes, RTFAttribute.D_CHARACTER);
728
729     checkNumericControlWord(current, newAttributes,
730                 StyleConstants.LineSpacing, "sl",
731                 0, 20f); /* TODO: sl wackiness */
732
733     if ((parm = attrDiff(current, newAttributes,
734              StyleConstants.Background, MagicToken)) != null) {
735     int colorNum;
736     if (parm == MagicToken)
737         colorNum = 0;
738     else
739         colorNum = ((Number JavaDoc)colorTable.get(parm)).intValue();
740     writeControlWord("cb", colorNum);
741     }
742
743     if ((parm = attrDiff(current, newAttributes,
744              StyleConstants.Foreground, null)) != null) {
745     int colorNum;
746     if (parm == MagicToken)
747         colorNum = 0;
748     else
749         colorNum = ((Number JavaDoc)colorTable.get(parm)).intValue();
750     writeControlWord("cf", colorNum);
751     }
752 }
753
754 protected void resetCharacterAttributes(MutableAttributeSet currentAttributes)
755     throws IOException JavaDoc
756 {
757     writeControlWord("plain");
758
759     int wordIndex;
760     int wordCount = RTFAttributes.attributes.length;
761     for(wordIndex = 0; wordIndex < wordCount; wordIndex++) {
762         RTFAttribute JavaDoc attr = RTFAttributes.attributes[wordIndex];
763     if (attr.domain() == RTFAttribute.D_CHARACTER)
764         attr.setDefault(currentAttributes);
765     }
766
767     StyleConstants.setFontFamily(currentAttributes, defaultFontFamily);
768     currentAttributes.removeAttribute(StyleConstants.FontSize); /* =default */
769     currentAttributes.removeAttribute(StyleConstants.Background);
770     currentAttributes.removeAttribute(StyleConstants.Foreground);
771     currentAttributes.removeAttribute(StyleConstants.LineSpacing);
772     currentAttributes.removeAttribute("characterStyle");
773 }
774
775 public void writeTextElement(Element el)
776     throws IOException JavaDoc
777 {
778     updateCharacterAttributes(outputAttributes, el.getAttributes(), true);
779
780     if (el.isLeaf()) {
781     try {
782         el.getDocument().getText(el.getStartOffset(),
783                      el.getEndOffset() - el.getStartOffset(),
784                      this.workingSegment);
785     } catch (BadLocationException ble) {
786         /* TODO is this the correct error to raise? */
787         ble.printStackTrace();
788         throw new InternalError JavaDoc(ble.getMessage());
789     }
790     writeText(this.workingSegment);
791     } else {
792     int sub_count = el.getElementCount();
793     for(int idx = 0; idx < sub_count; idx ++)
794         writeTextElement(el.getElement(idx));
795     }
796 }
797
798 public void writeText(Segment s)
799     throws IOException JavaDoc
800 {
801     int pos, end;
802     char[] array;
803
804     pos = s.offset;
805     end = pos + s.count;
806     array = s.array;
807     for( ; pos < end; pos ++)
808     writeCharacter(array[pos]);
809 }
810
811 public void writeText(String JavaDoc s)
812     throws IOException JavaDoc
813 {
814     int pos, end;
815
816     pos = 0;
817     end = s.length();
818     for( ; pos < end; pos ++)
819     writeCharacter(s.charAt(pos));
820 }
821
822 public void writeRawString(String JavaDoc str)
823     throws IOException JavaDoc
824 {
825     int strlen = str.length();
826     for (int offset = 0; offset < strlen; offset ++)
827     outputStream.write((int)str.charAt(offset));
828 }
829
830 public void writeControlWord(String JavaDoc keyword)
831     throws IOException JavaDoc
832 {
833     outputStream.write('\\');
834     writeRawString(keyword);
835     afterKeyword = true;
836 }
837
838 public void writeControlWord(String JavaDoc keyword, int arg)
839     throws IOException JavaDoc
840 {
841     outputStream.write('\\');
842     writeRawString(keyword);
843     writeRawString(String.valueOf(arg)); /* TODO: correct in all cases? */
844     afterKeyword = true;
845 }
846     
847 public void writeBegingroup()
848     throws IOException JavaDoc
849 {
850     outputStream.write('{');
851     afterKeyword = false;
852 }
853
854 public void writeEndgroup()
855     throws IOException JavaDoc
856 {
857     outputStream.write('}');
858     afterKeyword = false;
859 }
860
861 public void writeCharacter(char ch)
862     throws IOException JavaDoc
863 {
864     /* Nonbreaking space is in most RTF encodings, but the keyword is
865        preferable; same goes for tabs */

866     if (ch == 0xA0) { /* nonbreaking space */
867         outputStream.write(0x5C); /* backslash */
868     outputStream.write(0x7E); /* tilde */
869     afterKeyword = false; /* non-alpha keywords are self-terminating */
870     return;
871     }
872
873     if (ch == 0x09) { /* horizontal tab */
874     writeControlWord("tab");
875     return;
876     }
877
878     if (ch == 10 || ch == 13) { /* newline / paragraph */
879     /* ignore CRs, we'll write a paragraph element soon enough */
880     return;
881     }
882
883     int b = convertCharacter(outputConversion, ch);
884     if (b == 0) {
885         /* Unicode characters which have corresponding RTF keywords */
886         int i;
887     for(i = 0; i < textKeywords.length; i++) {
888         if (textKeywords[i].character == ch) {
889             writeControlWord(textKeywords[i].keyword);
890         return;
891         }
892     }
893         /* In some cases it would be reasonable to check to see if the
894        glyph being written out is in the Symbol encoding, and if so,
895        to switch to the Symbol font for this character. TODO. */

896         /* Currently all unrepresentable characters are written as
897        Unicode escapes. */

898         String JavaDoc approximation = approximationForUnicode(ch);
899     if (approximation.length() != unicodeCount) {
900         unicodeCount = approximation.length();
901         writeControlWord("uc", unicodeCount);
902     }
903     writeControlWord("u", (int)ch);
904     writeRawString(" ");
905     writeRawString(approximation);
906     afterKeyword = false;
907         return;
908     }
909
910     if (b > 127) {
911     int nybble;
912         outputStream.write('\\');
913     outputStream.write('\'');
914     nybble = ( b & 0xF0 ) >>> 4;
915     outputStream.write(hexdigits[nybble]);
916     nybble = ( b & 0x0F );
917     outputStream.write(hexdigits[nybble]);
918     afterKeyword = false;
919     return;
920     }
921
922     switch (b) {
923     case '}':
924     case '{':
925     case '\\':
926         outputStream.write(0x5C); /* backslash */
927     afterKeyword = false; /* in a keyword, actually ... */
928         /* fall through */
929     default:
930     if (afterKeyword) {
931             outputStream.write(0x20); /* space */
932         afterKeyword = false;
933     }
934         outputStream.write(b);
935         break;
936     }
937 }
938
939 String JavaDoc approximationForUnicode(char ch)
940 {
941     /* TODO: Find reasonable approximations for all Unicode characters
942        in all RTF code pages... heh, heh... */

943     return "?";
944 }
945     
946 /** Takes a translation table (a 256-element array of characters)
947  * and creates an output conversion table for use by
948  * convertCharacter(). */

949     /* Not very efficient at all. Could be changed to sort the table
950        for binary search. TODO. (Even though this is inefficient however,
951        writing RTF is still much faster than reading it.) */

952 static int[] outputConversionFromTranslationTable(char[] table)
953 {
954     int[] conversion = new int[2 * table.length];
955
956     int index;
957
958     for(index = 0; index < table.length; index ++) {
959         conversion[index * 2] = table[index];
960     conversion[(index * 2) + 1] = index;
961     }
962
963     return conversion;
964 }
965     
966 static int[] outputConversionForName(String JavaDoc name)
967     throws IOException JavaDoc
968 {
969     char[] table = (char[])RTFReader.getCharacterSet(name);
970     return outputConversionFromTranslationTable(table);
971 }
972
973 /** Takes a char and a conversion table (an int[] in the current
974  * implementation, but conversion tables should be treated as an opaque
975  * type) and returns the
976  * corresponding byte value (as an int, since bytes are signed).
977  */

978     /* Not very efficient. TODO. */
979 static protected int convertCharacter(int[] conversion, char ch)
980 {
981    int index;
982
983    for(index = 0; index < conversion.length; index += 2) {
984        if(conversion[index] == ch)
985        return conversion[index + 1];
986    }
987
988    return 0; /* 0 indicates an unrepresentable character */
989 }
990
991 }
992
Popular Tags