View Javadoc

1   // Copyright 2001-2005, FreeHEP.
2   package org.freehep.graphicsio.ps;
3   
4   import java.awt.Font;
5   import java.awt.font.FontRenderContext;
6   import java.awt.font.TextAttribute;
7   import java.io.IOException;
8   import java.io.OutputStream;
9   import java.io.PrintStream;
10  import java.util.HashSet;
11  import java.util.Map;
12  import java.util.Properties;
13  
14  import org.freehep.graphics2d.font.CharTable;
15  import org.freehep.graphics2d.font.FontUtilities;
16  import org.freehep.graphics2d.font.Lookup;
17  import org.freehep.graphicsio.FontConstants;
18  import org.freehep.graphicsio.font.FontEmbedderType1;
19  import org.freehep.graphicsio.font.FontIncluder;
20  import org.freehep.graphicsio.font.FontTable;
21  
22  /**
23   * FontTable for PS files. The fonts name is used as a reference for the font.
24   * When the font is first used, it is embedded to the file if it is not a
25   * standard font. If it is unknown it is not substituted.
26   * 
27   * @author Simon Fischer
28   * @version $Id: PSFontTable.java 10516 2007-02-06 21:11:19Z duns $
29   */
30  public class PSFontTable extends FontTable {
31  
32      private OutputStream out;
33  
34      private FontRenderContext context;
35  
36      public PSFontTable(OutputStream out, FontRenderContext context) {
37          super();
38          this.out = out;
39          this.context = context;
40      }
41  
42      public CharTable getEncodingTable() {
43          return Lookup.getInstance().getTable("STDLatin");
44      }
45  
46      protected void firstRequest(Entry e, boolean embed, String embedAs)
47              throws IOException {
48          FontIncluder fontIncluder = null;
49          e.setWritten(true);
50  
51          // There are NO standard fonts in PS.
52          // if (isStandardFont(e.getFont())) return;
53  
54          out.flush();
55  
56          if (embed) {
57              if (embedAs.equals(FontConstants.EMBED_FONTS_TYPE3)) {
58                  fontIncluder = new PSFontEmbedder(context, new PrintStream(out));
59              } else if (embedAs.equals(FontConstants.EMBED_FONTS_TYPE1)) {
60                  fontIncluder = new FontEmbedderType1(context, out, true);
61              } else {
62                  System.err
63                          .println("PSFontTable: not a valid value for embedAs: "
64                                  + embedAs);
65              }
66          } else {
67              // FIXME: set the best standard font
68              // e.setReference(standardfontName)
69              return;
70          }
71          
72          fontIncluder
73                  .includeFont(e.getFont(), e.getEncoding(), e.getReference());
74          out.flush();
75      }
76  
77      /**
78       * Java font names -> PS Font names, used by {@link #normalize(java.util.Map)}
79       */
80      private static final Properties replaceFonts = new Properties();
81      static {
82          replaceFonts.setProperty("timesroman", "Times");
83          replaceFonts.setProperty("dialog", "Helvetica");
84          replaceFonts.setProperty("dialoginput", "Courier-New");
85          // FIXME: works well on windows, others?
86          replaceFonts.setProperty("serif", "Times");
87          replaceFonts.setProperty("sansserif", "Helvetica");
88          // FIXME: works well on windows, others?
89          replaceFonts.setProperty("monospaced", "Courier-New");
90          replaceFonts.setProperty("typewriter", "Courier-New");
91      }
92  
93      /**
94       * fonts that have no TextAttribute.WEIGHT and TextAttribute.POSTURE,
95       * used by {@link #createFontReference(java.awt.Font)}
96       */
97      private static final HashSet ignoreAtributes = new HashSet();
98      static {
99          ignoreAtributes.add("Symbol");
100         ignoreAtributes.add("ZapfDingbats");
101     }
102 
103     /**
104      * removes any transformation and superscript, changes the names
105      * to PS font name
106      *
107      * @param font
108      * @return derived font
109      */
110     protected Font substituteFont(Font font) {
111         Map attributes = FontUtilities.getAttributes(font);
112         // change names
113         // normalize(attributes);
114         // remove transformations
115         attributes.remove(TextAttribute.TRANSFORM);
116         attributes.remove(TextAttribute.SUPERSCRIPT);
117         return new Font(attributes);
118     }
119 
120     /**
121      * Uses the font name as a reference. Whitespace is stripped. The font style
122      * (italic/bold) is added as a suffix delimited by a dash.
123      * Uses {@link #normalize(java.util.Map)}
124      */
125     protected String createFontReference(Font font) {
126         Map /*<TextAttribute, ?>*/ attributes = FontUtilities.getAttributes(font);
127         normalize(attributes);
128 
129         // replace the name
130         StringBuffer result = new StringBuffer();
131 
132         // insert family at the end because oft the "-" between
133         // name and TextAttribute
134         String family = (String) attributes.get(TextAttribute.FAMILY);
135 
136         // weight
137         Object weight = ignoreAtributes.contains(family) ?
138             null : attributes.get(TextAttribute.WEIGHT);
139 
140         if (TextAttribute.WEIGHT_BOLD.equals(weight)) {
141             result.append("Bold");
142         } else if (TextAttribute.WEIGHT_DEMIBOLD.equals(weight)) {
143             result.append("DemiBold");
144         } else if (TextAttribute.WEIGHT_DEMILIGHT.equals(weight)) {
145             result.append("DemiLight");
146         } else if (TextAttribute.WEIGHT_EXTRA_LIGHT.equals(weight)) {
147             result.append("ExtraLight");
148         } else if (TextAttribute.WEIGHT_EXTRABOLD.equals(weight)) {
149             result.append("ExtraBold");
150         } else if (TextAttribute.WEIGHT_HEAVY.equals(weight)) {
151             result.append("Heavy");
152         } else if (TextAttribute.WEIGHT_LIGHT.equals(weight)) {
153             result.append("Light");
154         } else if (TextAttribute.WEIGHT_MEDIUM.equals(weight)) {
155             result.append("Medium");
156         } else if (TextAttribute.WEIGHT_REGULAR.equals(weight)) {
157             // result.append("WRegular");
158         } else if (TextAttribute.WEIGHT_SEMIBOLD.equals(weight)) {
159             result.append("SemiBold");
160         } else if (TextAttribute.WEIGHT_ULTRABOLD.equals(weight)) {
161             result.append("UltraBold");
162         }
163 
164         // italic
165         Object posture = ignoreAtributes.contains(family) ?
166             null : attributes.get(TextAttribute.POSTURE);
167 
168         if (TextAttribute.POSTURE_OBLIQUE.equals(posture)) {
169             if (family.equals("Times")) {
170                 result.append("Italic");
171             } else {
172                 result.append("Oblique");
173             }
174         } else if (TextAttribute.POSTURE_REGULAR.equals(posture)) {
175             // result.append("IRegular");
176         }
177 
178         // Times -> Times-Roman
179         if (family.equals("Times") && result.length() == 0) {
180             result.append("Roman");
181         }
182 
183         // underline is not a specific font
184         // Object ul = font.getAttributes().get(TextAttribute.UNDERLINE);
185         // if (TextAttribute.UNDERLINE_LOW_DASHED.equals(ul)) {
186         //     result.append("UnderlineLowDashed");
187         // } else if (TextAttribute.UNDERLINE_LOW_DOTTED.equals(ul)) {
188         //     result.append("UnderlineLowDotted");
189         // } else if (TextAttribute.UNDERLINE_LOW_GRAY.equals(ul)) {
190         //     result.append("UnderlineLowGray");
191         // } else if (TextAttribute.UNDERLINE_LOW_ONE_PIXEL.equals(ul)) {
192         //     result.append("UnderlineLowOnePixel");
193         // } else if (TextAttribute.UNDERLINE_ON.equals(ul)) {
194         //     result.append("Underline");
195         // }
196 
197         // strike through is not a specific font
198         // if (font.getAttributes().get(TextAttribute.STRIKETHROUGH) != null) {
199         //     result.append("StrikeThrough");
200         // }
201 
202         // width is not a specific font
203         // Object width = font.getAttributes().get(TextAttribute.WIDTH);
204         // if (TextAttribute.WIDTH_CONDENSED.equals(width)) {
205         //     result.append("Condensed");
206         // } else if (TextAttribute.WIDTH_EXTENDED.equals(width)) {
207         //     result.append("Extended");
208         // } else if (TextAttribute.WIDTH_REGULAR.equals(width)) {
209         //    // result.append("WRegular");
210         // } else if (TextAttribute.WIDTH_SEMI_CONDENSED.equals(width)) {
211         //     result.append("SemiCondensed");
212         // } else if (TextAttribute.WIDTH_SEMI_EXTENDED.equals(width)) {
213         //     result.append("SemiExtended");
214         // }
215 
216         // insert "name-" at the beginning or return plain "name"
217         if (result.length() > 0) {
218             result.insert(0, "-");
219             result.insert(0, attributes.get(TextAttribute.FAMILY));
220         } else {
221             result.append(attributes.get(TextAttribute.FAMILY));
222         }
223 
224         return result.toString();
225     }
226 
227     /**
228      * Replaces TextAttribute.FAMILY by values of replaceFonts.
229      * Whitespace is family name stripped. When a
230      * font created using the result of this method the transformation would be:
231      *
232      * <code>java.awt.Font[family=SansSerif,name=SansSerif,style=plain,size=30]</code><BR>
233      * will result to:<BR>
234      * <code>java.awt.Font[family=SansSerif,name=Helvetica,style=plain,size=30]</code><BR><BR>
235      *
236      * Uses {@link FontTable#normalize(java.util.Map)} first.
237      *
238      * @param attributes with font name to change
239      */
240     public static void normalize(Map /*<TextAttribute, ?>*/ attributes) {
241         // dialog.bold -> Dialog with TextAttribute.WEIGHT_BOLD
242         FontTable.normalize(attributes);
243 
244         // get replaced font family name (Yes it's right, not the name!)
245         String family = replaceFonts.getProperty(
246             ((String) attributes.get(TextAttribute.FAMILY)).toLowerCase());
247         if (family == null) {
248             family = (String) attributes.get(TextAttribute.FAMILY);
249         }
250 
251         // remove spaces
252         family = family.replaceAll(" ", "");
253 
254         // store family
255         attributes.put(TextAttribute.FAMILY, family);
256     }
257 }