View Javadoc

1   // Copyright 2001-2005 freehep
2   package org.freehep.graphicsio.font;
3   
4   import java.awt.Font;
5   import java.awt.Shape;
6   import java.awt.font.FontRenderContext;
7   import java.awt.font.GlyphMetrics;
8   import java.awt.font.GlyphVector;
9   import java.awt.geom.GeneralPath;
10  import java.awt.geom.Rectangle2D;
11  import java.io.IOException;
12  
13  import org.freehep.graphics2d.font.CharTable;
14  
15  /**
16   * A FontIncluder that also embeds all glyphs. Subclasses must implement the
17   * <tt>writeGlyph</tt> method which is called for all defined (up to 256)
18   * characters and the notdefined character. These method calls are bracketed by
19   * <tt>openGlyphs()</tt> and <tt>closeGlyph()</tt>. All invocations of
20   * methods that are abstract in this class succeed the method calls of the
21   * superclass <tt>FontIncluder</tt> (especially <tt>closeIncludeFont()</tt>!)
22   * All of these calls are again succeeded by <tt>closeEmbedFont</tt>. <br>
23   * The abstract methods are called in the following order:
24   * <ul>
25   * <li><tt>openIncludeFont</tt>
26   * <li><tt>writeEncoding</tt>
27   * <li><tt>closeIncludeFont</tt>
28   * <li><tt>writeWidths</tt>
29   * <li><tt>openGlyphs</tt>
30   * <li>loop over all glyphs: <tt>openGlyphs</tt>
31   * <li><tt>closeGlyphs</tt>
32   * <li><tt>closeEmbedFont</tt>
33   * </ul>
34   * 
35   * @author Simon Fischer
36   * @version $Id: FontEmbedder.java 8584 2006-08-10 23:06:37Z duns $
37   */
38  public abstract class FontEmbedder extends FontIncluder {
39  
40      public static final String NOTDEF = ".notdef";
41  
42      /**
43       * Writes a single glyph to the file. A null value for <tt>glyphMetrics</tt>
44       * indicates the undefined character. In this case the value of
45       * <tt>unicodeName</tt> equals the value of
46       * <tt>NOTDEF</TT> (=<tt>.notdef</tt>).
47       *
48       * @param unicodeName the character's name according to the unicode standard
49       * @param glyph the shape that represents this glyph
50       * @param glyphMetrics the metrics of this glyph
51       */
52      protected abstract void writeGlyph(String unicodeName, Shape glyph,
53              GlyphMetrics glyphMetrics) throws IOException;
54  
55      /** Writes the character widths to the file. */
56      protected abstract void writeWidths(double[] widths) throws IOException;
57  
58      /**
59       * Called before the glyph loop starts. Does nothing by default but can be
60       * implemented.
61       */
62      protected void openGlyphs() throws IOException {
63      }
64  
65      /**
66       * Called after the glyph loop ends. Does nothing by default but can be
67       * implemented.
68       */
69      protected void closeGlyphs() throws IOException {
70      }
71  
72      protected abstract void closeEmbedFont() throws IOException;
73  
74      private double[] widths;
75  
76      private GlyphVector glyphs;
77  
78      private Font font; // FONTHACK
79  
80      public FontEmbedder(FontRenderContext context) {
81          super(context);
82      }
83  
84      protected double[] getAdvanceWidths() {
85          if (widths == null) {
86              // figure out the widths of the characters if not yet done
87              widths = new double[256];
88              for (int i = 0; i < widths.length; i++) {
89                  widths[i] = glyphs.getGlyphMetrics(i).getAdvance();
90                  // in case of undefined character set to width of undefined
91                  // symbol
92                  if (getCharName(i) == null) {
93                      widths[i] = getUndefinedWidth();
94                  }
95              }
96          }
97          return widths;
98      }
99  
100     protected double getAdvanceWidth(int character) {
101         return getAdvanceWidths()[character];
102     }
103 
104     protected Shape getGlyph(int i) {
105         // This one-line implementation produces different results under JDK 1.3
106         // and 1.4
107         // return glyphs.getGlyphOutline(i);
108 
109         // The substitute code attempts to work around this by using defensive
110         // programming
111         // See code marked FONTHACK elsewhere in this file
112         // Create a GlyphVector for this single character.
113         FontRenderContext orig = getContext();
114         FontRenderContext frc = new FontRenderContext(null, orig
115                 .isAntiAliased(), orig.usesFractionalMetrics());
116         Shape shape = font.createGlyphVector(frc, new char[] { getUnicode(i) })
117                 .getGlyphOutline(0);
118         return orig.getTransform().createTransformedShape(shape);
119     }
120 
121     protected GlyphMetrics getGlyphMetrics(int i) {
122         return glyphs.getGlyphMetrics(i);
123     }
124 
125     public void includeFont(Font font, CharTable charTable, String name)
126             throws IOException {
127 
128         glyphs = null;
129         widths = null;
130         // FONTHACK: Needed by hacked version of getGlyph()
131         this.font = font;
132 
133         super.includeFont(font, charTable, name);
134 
135         this.glyphs = font.createGlyphVector(getContext(), getUnicode());
136 
137         writeWidths(getAdvanceWidths());
138 
139         try {
140 
141             openGlyphs();
142 
143             // write the glyphs
144             for (int i = 0; i < 256; i++) {
145                 if (getCharName(i) != null) {
146                     writeGlyph(getCharName(i), getGlyph(i), getGlyphMetrics(i));
147                 }
148             }
149 	    writeGlyph(NOTDEF, createUndefined(), null);
150 
151             closeGlyphs();
152             closeEmbedFont();
153 
154         } catch (Exception e) {
155             e.printStackTrace();
156         }
157     }
158 
159     private Shape createUndefined() {
160         GeneralPath ud = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 10);
161         ud.append(new Rectangle2D.Double(0, 0, FONT_SIZE, FONT_SIZE), false);
162         ud.append(new Rectangle2D.Double(FONT_SIZE / 20, FONT_SIZE / 20,
163                 18 * FONT_SIZE / 20, 18 * FONT_SIZE / 20), false);
164         return ud;
165     }
166 }