KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > svggen > font > SVGFont


1 /*
2
3    Copyright 1999-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17 */

18
19 package org.apache.batik.svggen.font;
20
21 import java.io.FileOutputStream JavaDoc;
22 import java.io.PrintStream JavaDoc;
23
24 import org.apache.batik.svggen.font.table.CmapFormat;
25 import org.apache.batik.svggen.font.table.Feature;
26 import org.apache.batik.svggen.font.table.FeatureTags;
27 import org.apache.batik.svggen.font.table.GsubTable;
28 import org.apache.batik.svggen.font.table.KernSubtable;
29 import org.apache.batik.svggen.font.table.KernTable;
30 import org.apache.batik.svggen.font.table.KerningPair;
31 import org.apache.batik.svggen.font.table.LangSys;
32 import org.apache.batik.svggen.font.table.PostTable;
33 import org.apache.batik.svggen.font.table.Script;
34 import org.apache.batik.svggen.font.table.ScriptTags;
35 import org.apache.batik.svggen.font.table.SingleSubst;
36 import org.apache.batik.svggen.font.table.Table;
37 import org.apache.batik.util.SVGConstants;
38 import org.apache.batik.util.XMLConstants;
39
40 /**
41  * Converts a TrueType font to an SVG embedded font.
42  *
43  * @version $Id: SVGFont.java,v 1.10 2005/03/27 08:58:36 cam Exp $
44  * @author <a HREF="mailto:david@steadystate.co.uk">David Schweinsberg</a>
45  */

46 public class SVGFont implements XMLConstants, SVGConstants, ScriptTags, FeatureTags {
47     static final String JavaDoc EOL;
48
49     static final String JavaDoc PROPERTY_LINE_SEPARATOR = "line.separator";
50     static final String JavaDoc PROPERTY_LINE_SEPARATOR_DEFAULT = "\n";
51
52     static final int DEFAULT_FIRST = 32;
53     static final int DEFAULT_LAST = 128;
54
55     static {
56         String JavaDoc temp;
57         try {
58             temp = System.getProperty (PROPERTY_LINE_SEPARATOR,
59                                        PROPERTY_LINE_SEPARATOR_DEFAULT);
60         } catch (SecurityException JavaDoc e) {
61             temp = PROPERTY_LINE_SEPARATOR_DEFAULT;
62         }
63         EOL = temp;
64     }
65     
66     static private String JavaDoc QUOT_EOL = XML_CHAR_QUOT + EOL;
67
68     /**
69      * Defines the application arguments.
70      */

71     static private String JavaDoc CONFIG_USAGE =
72         "SVGFont.config.usage";
73
74     /**
75      * Defines the start of the generated SVG document
76      * {0} SVG public ID
77      * {1} SVG system ID
78      */

79     static private String JavaDoc CONFIG_SVG_BEGIN =
80         "SVGFont.config.svg.begin";
81
82     /**
83      * Defines the SVG start fragment that exercise the generated
84      * Font.
85      */

86     static private String JavaDoc CONFIG_SVG_TEST_CARD_START =
87         "SVGFont.config.svg.test.card.start";
88
89     /**
90      * Defines the end of the SVG fragment that exercise the generated
91      * Font.
92      */

93     static private String JavaDoc CONFIG_SVG_TEST_CARD_END =
94         "SVGFont.config.svg.test.card.end";
95
96     protected static String JavaDoc encodeEntities(String JavaDoc s) {
97         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
98         for (int i = 0; i < s.length(); i++) {
99             if (s.charAt(i) == XML_CHAR_LT) {
100                 sb.append(XML_ENTITY_LT);
101             } else if (s.charAt(i) == XML_CHAR_GT) {
102                 sb.append(XML_ENTITY_GT);
103             } else if (s.charAt(i) == XML_CHAR_AMP) {
104                 sb.append(XML_ENTITY_AMP);
105             } else if (s.charAt(i) == XML_CHAR_APOS) {
106                 sb.append(XML_ENTITY_APOS);
107             } else if(s.charAt(i) == XML_CHAR_QUOT) {
108                 sb.append(XML_ENTITY_QUOT);
109             } else {
110                 sb.append(s.charAt(i));
111             }
112         }
113         return sb.toString();
114     }
115
116     protected static String JavaDoc getContourAsSVGPathData(Glyph glyph, int startIndex, int count) {
117
118         // If this is a single point on it's own, we can't do anything with it
119
if (glyph.getPoint(startIndex).endOfContour) {
120             return "";
121         }
122
123         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
124         int offset = 0;
125
126         while (offset < count) {
127             Point point = glyph.getPoint(startIndex + offset%count);
128             Point point_plus1 = glyph.getPoint(startIndex + (offset+1)%count);
129             Point point_plus2 = glyph.getPoint(startIndex + (offset+2)%count);
130
131             if (offset == 0) {
132                 sb.append(PATH_MOVE)
133                 .append(String.valueOf(point.x))
134                 .append(XML_SPACE)
135                 .append(String.valueOf(point.y));
136             }
137
138             if (point.onCurve && point_plus1.onCurve) {
139                 if (point_plus1.x == point.x) { // This is a vertical line
140
sb.append(PATH_VERTICAL_LINE_TO)
141                     .append(String.valueOf(point_plus1.y));
142                 } else if (point_plus1.y == point.y) { // This is a horizontal line
143
sb.append(PATH_HORIZONTAL_LINE_TO)
144                     .append(String.valueOf(point_plus1.x));
145                 } else {
146                     sb.append(PATH_LINE_TO)
147                     .append(String.valueOf(point_plus1.x))
148                     .append(XML_SPACE)
149                     .append(String.valueOf(point_plus1.y));
150                 }
151                 offset++;
152             } else if (point.onCurve && !point_plus1.onCurve && point_plus2.onCurve) {
153                 // This is a curve with no implied points
154
sb.append(PATH_QUAD_TO)
155                 .append(String.valueOf(point_plus1.x))
156                 .append(XML_SPACE)
157                 .append(String.valueOf(point_plus1.y))
158                 .append(XML_SPACE)
159                 .append(String.valueOf(point_plus2.x))
160                 .append(XML_SPACE)
161                 .append(String.valueOf(point_plus2.y));
162                 offset+=2;
163             } else if (point.onCurve && !point_plus1.onCurve && !point_plus2.onCurve) {
164                 // This is a curve with one implied point
165
sb.append(PATH_QUAD_TO)
166                 .append(String.valueOf(point_plus1.x))
167                 .append(XML_SPACE)
168                 .append(String.valueOf(point_plus1.y))
169                 .append(XML_SPACE)
170                 .append(String.valueOf(midValue(point_plus1.x, point_plus2.x)))
171                 .append(XML_SPACE)
172                 .append(String.valueOf(midValue(point_plus1.y, point_plus2.y)));
173                 offset+=2;
174             } else if (!point.onCurve && !point_plus1.onCurve) {
175                 // This is a curve with two implied points
176
sb.append(PATH_SMOOTH_QUAD_TO)
177                 .append(String.valueOf(midValue(point.x, point_plus1.x)))
178                 .append(XML_SPACE)
179                 .append(String.valueOf(midValue(point.y, point_plus1.y)));
180                 offset++;
181             } else if (!point.onCurve && point_plus1.onCurve) {
182                 sb.append(PATH_SMOOTH_QUAD_TO)
183                 .append(String.valueOf(point_plus1.x))
184                 .append(XML_SPACE)
185                 .append(String.valueOf(point_plus1.y));
186                 offset++;
187             } else {
188                 System.out.println("drawGlyph case not catered for!!");
189                 break;
190             }
191         }
192         sb.append(PATH_CLOSE);
193
194         return sb.toString();
195     }
196
197     protected static String JavaDoc getSVGFontFaceElement(Font font) {
198         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
199         String JavaDoc fontFamily = font.getNameTable().getRecord(Table.nameFontFamilyName);
200         short unitsPerEm = font.getHeadTable().getUnitsPerEm();
201         String JavaDoc panose = font.getOS2Table().getPanose().toString();
202         short ascent = font.getHheaTable().getAscender();
203         short descent = font.getHheaTable().getDescender();
204         int baseline = 0; // bit 0 of head.flags will indicate if this is true
205

206     // <!ELEMENT font-face (%descTitleMetadata;,font-face-src?,definition-src?) >
207
// <!ATTLIST font-face
208
// %stdAttrs;
209
// font-family CDATA #IMPLIED
210
// font-style CDATA #IMPLIED
211
// font-variant CDATA #IMPLIED
212
// font-weight CDATA #IMPLIED
213
// font-stretch CDATA #IMPLIED
214
// font-size CDATA #IMPLIED
215
// unicode-range CDATA #IMPLIED
216
// units-per-em %Number; #IMPLIED
217
// panose-1 CDATA #IMPLIED
218
// stemv %Number; #IMPLIED
219
// stemh %Number; #IMPLIED
220
// slope %Number; #IMPLIED
221
// cap-height %Number; #IMPLIED
222
// x-height %Number; #IMPLIED
223
// accent-height %Number; #IMPLIED
224
// ascent %Number; #IMPLIED
225
// descent %Number; #IMPLIED
226
// widths CDATA #IMPLIED
227
// bbox CDATA #IMPLIED
228
// ideographic %Number; #IMPLIED
229
// alphabetic %Number; #IMPLIED
230
// mathematical %Number; #IMPLIED
231
// hanging %Number; #IMPLIED
232
// v-ideographic %Number; #IMPLIED
233
// v-alphabetic %Number; #IMPLIED
234
// v-mathematical %Number; #IMPLIED
235
// v-hanging %Number; #IMPLIED
236
// underline-position %Number; #IMPLIED
237
// underline-thickness %Number; #IMPLIED
238
// strikethrough-position %Number; #IMPLIED
239
// strikethrough-thickness %Number; #IMPLIED
240
// overline-position %Number; #IMPLIED
241
// overline-thickness %Number; #IMPLIED >
242

243         sb.append(XML_OPEN_TAG_START).append(SVG_FONT_FACE_TAG).append(EOL)
244             .append(XML_TAB).append(SVG_FONT_FAMILY_ATTRIBUTE).append(XML_EQUAL_QUOT).append(fontFamily).append(QUOT_EOL)
245             // .append(" font-family=\"").append(fontFamily).append("\"\r\n")
246
.append(XML_TAB).append(SVG_UNITS_PER_EM_ATTRIBUTE).append(XML_EQUAL_QUOT).append(unitsPerEm).append(QUOT_EOL)
247             //.append(" units-per-em=\"").append(unitsPerEm).append("\"\r\n")
248
.append(XML_TAB).append(SVG_PANOSE_1_ATTRIBUTE).append(XML_EQUAL_QUOT).append(panose).append(QUOT_EOL)
249             // .append(" panose-1=\"").append(panose).append("\"\r\n")
250
.append(XML_TAB).append(SVG_ASCENT_ATTRIBUTE).append(XML_EQUAL_QUOT).append(ascent).append(QUOT_EOL)
251             // .append(" ascent=\"").append(ascent).append("\"\r\n")
252
.append(XML_TAB).append(SVG_DESCENT_ATTRIBUTE).append(XML_EQUAL_QUOT).append(descent).append(QUOT_EOL)
253             // .append(" descent=\"").append(descent).append("\"\r\n")
254
.append(XML_TAB).append(SVG_ALPHABETIC_ATTRIBUTE).append(XML_EQUAL_QUOT).append(baseline).append(XML_CHAR_QUOT)
255             .append(XML_OPEN_TAG_END_NO_CHILDREN).append(EOL);
256             //.append(" baseline=\"").append(baseline).append("\"/>\r\n");
257

258         return sb.toString();
259     }
260
261     /**
262      * Returns a &lt;font&gt;&#x2e;&#x2e;&#x2e;&lt;/font&gt; block,
263      * defining the specified font.
264      *
265      * @param font The TrueType font to be converted to SVG
266      * @param id An XML id attribute for the font element
267      * @param first The first character in the output range
268      * @param last The last character in the output range
269      * @param forceAscii Force the use of the ASCII character map
270      */

271     protected static void writeFontAsSVGFragment(PrintStream JavaDoc ps, Font font, String JavaDoc id, int first, int last, boolean autoRange, boolean forceAscii)
272     throws Exception JavaDoc {
273         // StringBuffer sb = new StringBuffer();
274
// int horiz_advance_x = font.getHmtxTable().getAdvanceWidth(
275
// font.getHheaTable().getNumberOfHMetrics() - 1);
276
int horiz_advance_x = font.getOS2Table().getAvgCharWidth();
277
278         ps.print(XML_OPEN_TAG_START);
279         ps.print(SVG_FONT_TAG);
280         ps.print(XML_SPACE);
281         // ps.print("<font ");
282
if (id != null) {
283             ps.print(SVG_ID_ATTRIBUTE);
284             ps.print(XML_EQUAL_QUOT);
285             // ps.print("id=\"");
286
ps.print(id);
287             ps.print(XML_CHAR_QUOT);
288             ps.print(XML_SPACE);
289             // ps.print("\" ");
290
}
291
292         ps.print(SVG_HORIZ_ADV_X_ATTRIBUTE);
293         ps.print(XML_EQUAL_QUOT);
294         // ps.print("horiz-adv-x=\"");
295
ps.print(horiz_advance_x);
296         ps.print(XML_CHAR_QUOT);
297         ps.print(XML_OPEN_TAG_END_CHILDREN);
298         // ps.println("\">");
299

300         ps.print(getSVGFontFaceElement(font));
301
302         // Decide upon a cmap table to use for our character to glyph look-up
303
CmapFormat cmapFmt = null;
304         if (forceAscii) {
305             // We've been asked to use the ASCII/Macintosh cmap format
306
cmapFmt = font.getCmapTable().getCmapFormat(
307                 Table.platformMacintosh,
308                 Table.encodingRoman );
309         } else {
310             // The default behaviour is to use the Unicode cmap encoding
311
cmapFmt = font.getCmapTable().getCmapFormat(
312                 Table.platformMicrosoft,
313                 Table.encodingUGL );
314             if (cmapFmt == null) {
315                 // This might be a symbol font, so we'll look for an "undefined" encoding
316
cmapFmt = font.getCmapTable().getCmapFormat(
317                     Table.platformMicrosoft,
318                     Table.encodingUndefined );
319             }
320         }
321         if (cmapFmt == null) {
322             throw new Exception JavaDoc("Cannot find a suitable cmap table");
323         }
324
325         // If this font includes arabic script, we want to specify
326
// substitutions for initial, medial, terminal & isolated
327
// cases.
328
GsubTable gsub = (GsubTable) font.getTable(Table.GSUB);
329         SingleSubst initialSubst = null;
330         SingleSubst medialSubst = null;
331         SingleSubst terminalSubst = null;
332         if (gsub != null) {
333             Script s = gsub.getScriptList().findScript(SCRIPT_TAG_ARAB);
334             if (s != null) {
335                 LangSys ls = s.getDefaultLangSys();
336                 if (ls != null) {
337                     Feature init = gsub.getFeatureList().findFeature(ls, FEATURE_TAG_INIT);
338                     Feature medi = gsub.getFeatureList().findFeature(ls, FEATURE_TAG_MEDI);
339                     Feature fina = gsub.getFeatureList().findFeature(ls, FEATURE_TAG_FINA);
340                     
341                     initialSubst = (SingleSubst)
342                         gsub.getLookupList().getLookup(init, 0).getSubtable(0);
343                     medialSubst = (SingleSubst)
344                         gsub.getLookupList().getLookup(medi, 0).getSubtable(0);
345                     terminalSubst = (SingleSubst)
346                         gsub.getLookupList().getLookup(fina, 0).getSubtable(0);
347                 }
348             }
349         }
350
351         // Include the missing glyph
352
ps.println(getGlyphAsSVG(font, font.getGlyph(0), 0, horiz_advance_x,
353             initialSubst, medialSubst, terminalSubst, ""));
354
355         try {
356             if (first == -1) {
357                 if (!autoRange) first = DEFAULT_FIRST;
358                 else first = cmapFmt.getFirst();
359             }
360             if (last == -1) {
361                 if (!autoRange) last = DEFAULT_LAST;
362                 else last = cmapFmt.getLast();
363             }
364
365             // Include our requested range
366
for (int i = first; i <= last; i++) {
367                 int glyphIndex = cmapFmt.mapCharCode(i);
368                 // ps.println(String.valueOf(i) + " -> " + String.valueOf(glyphIndex));
369
// if (font.getGlyphs()[glyphIndex] != null)
370
// sb.append(font.getGlyphs()[glyphIndex].toString() + "\n");
371

372                 if (glyphIndex > 0) {
373                     ps.println(getGlyphAsSVG(
374                         font,
375                         font.getGlyph(glyphIndex),
376                         glyphIndex,
377                         horiz_advance_x,
378                         initialSubst, medialSubst, terminalSubst,
379                         (32 <= i && i <= 127) ?
380                         encodeEntities("" + (char) i) :
381                         XML_CHAR_REF_PREFIX + Integer.toHexString(i) + XML_CHAR_REF_SUFFIX));
382                 }
383
384             }
385             
386             // Output kerning pairs from the requested range
387
KernTable kern = (KernTable) font.getTable(Table.kern);
388             if (kern != null) {
389                 KernSubtable kst = kern.getSubtable(0);
390                 PostTable post = (PostTable) font.getTable(Table.post);
391                 for (int i = 0; i < kst.getKerningPairCount(); i++) {
392                     ps.println(getKerningPairAsSVG(kst.getKerningPair(i), post));
393                 }
394             }
395         } catch (Exception JavaDoc e) {
396             System.err.println(e.getMessage());
397         }
398
399         ps.print(XML_CLOSE_TAG_START);
400         ps.print(SVG_FONT_TAG);
401         ps.println(XML_CLOSE_TAG_END);
402         // ps.println("</font>");
403
}
404
405     protected static String JavaDoc getGlyphAsSVG(
406             Font font,
407             Glyph glyph,
408             int glyphIndex,
409             int defaultHorizAdvanceX,
410             String JavaDoc attrib,
411             String JavaDoc code) {
412
413         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
414         int firstIndex = 0;
415         int count = 0;
416         int i;
417         int horiz_advance_x;
418
419         horiz_advance_x = font.getHmtxTable().getAdvanceWidth(glyphIndex);
420
421         if (glyphIndex == 0) {
422             sb.append(XML_OPEN_TAG_START);
423             sb.append(SVG_MISSING_GLYPH_TAG);
424             // sb.append("<missing-glyph");
425
} else {
426
427             // Unicode value
428
sb.append(XML_OPEN_TAG_START)
429                 .append(SVG_GLYPH_TAG).append(XML_SPACE).append(SVG_UNICODE_ATTRIBUTE)
430                 .append(XML_EQUAL_QUOT).append(code).append(XML_CHAR_QUOT);
431             // sb.append("<glyph unicode=\"").append(code).append("\"");
432

433             // Glyph name
434
sb.append(XML_SPACE).append(SVG_GLYPH_NAME_ATTRIBUTE).append(XML_EQUAL_QUOT)
435                 // sb.append(" glyph-name=\"")
436
.append(font.getPostTable().getGlyphName(glyphIndex))
437                 // .append("\"");
438
.append(XML_CHAR_QUOT);
439         }
440         if (horiz_advance_x != defaultHorizAdvanceX) {
441             sb.append(XML_SPACE).append(SVG_HORIZ_ADV_X_ATTRIBUTE).append(XML_EQUAL_QUOT)
442                 .append(horiz_advance_x).append(XML_CHAR_QUOT);
443             // sb.append(" horiz-adv-x=\"").append(horiz_advance_x).append("\"");
444
}
445
446         if (attrib != null) {
447             sb.append(attrib);
448         }
449
450         if (glyph != null) {
451             // sb.append(" d=\"");
452
sb.append(XML_SPACE).append(SVG_D_ATTRIBUTE).append(XML_EQUAL_QUOT);
453             for (i = 0; i < glyph.getPointCount(); i++) {
454                 count++;
455                 if (glyph.getPoint(i).endOfContour) {
456                     sb.append(getContourAsSVGPathData(glyph, firstIndex, count));
457                     firstIndex = i + 1;
458                     count = 0;
459                 }
460             }
461             // sb.append("\"");
462
sb.append(XML_CHAR_QUOT);
463         }
464
465         sb.append(XML_OPEN_TAG_END_NO_CHILDREN);
466         // sb.append("/>");
467

468         // Chop-up the string into 255 character lines
469
chopUpStringBuffer(sb);
470
471         return sb.toString();
472     }
473
474     protected static String JavaDoc getGlyphAsSVG(
475             Font font,
476             Glyph glyph,
477             int glyphIndex,
478             int defaultHorizAdvanceX,
479             SingleSubst arabInitSubst,
480             SingleSubst arabMediSubst,
481             SingleSubst arabTermSubst,
482             String JavaDoc code) {
483
484         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
485         boolean substituted = false;
486         
487         // arabic = "initial | medial | terminal | isolated"
488
int arabInitGlyphIndex = glyphIndex;
489         int arabMediGlyphIndex = glyphIndex;
490         int arabTermGlyphIndex = glyphIndex;
491         if (arabInitSubst != null) {
492             arabInitGlyphIndex = arabInitSubst.substitute(glyphIndex);
493         }
494         if (arabMediSubst != null) {
495             arabMediGlyphIndex = arabMediSubst.substitute(glyphIndex);
496         }
497         if (arabTermSubst != null) {
498             arabTermGlyphIndex = arabTermSubst.substitute(glyphIndex);
499         }
500         
501         if (arabInitGlyphIndex != glyphIndex) {
502             sb.append(getGlyphAsSVG(
503                 font,
504                 font.getGlyph(arabInitGlyphIndex),
505                 arabInitGlyphIndex,
506                 defaultHorizAdvanceX,
507                 // " arabic-form=\"initial\"",
508
(XML_SPACE + SVG_ARABIC_FORM_ATTRIBUTE + XML_EQUAL_QUOT +
509                  SVG_INITIAL_VALUE + XML_CHAR_QUOT),
510                 code));
511             // sb.append("\r\n");
512
sb.append(EOL);
513             substituted = true;
514         }
515
516         if (arabMediGlyphIndex != glyphIndex) {
517             sb.append(getGlyphAsSVG(
518                 font,
519                 font.getGlyph(arabMediGlyphIndex),
520                 arabMediGlyphIndex,
521                 defaultHorizAdvanceX,
522                 // " arabic-form=\"medial\"",
523
(XML_SPACE + SVG_ARABIC_FORM_ATTRIBUTE + XML_EQUAL_QUOT +
524                  SVG_MEDIAL_VALUE + XML_CHAR_QUOT),
525                 code));
526             // sb.append("\r\n");
527
sb.append(EOL);
528             substituted = true;
529         }
530
531         if (arabTermGlyphIndex != glyphIndex) {
532             sb.append(getGlyphAsSVG(
533                 font,
534                 font.getGlyph(arabTermGlyphIndex),
535                 arabTermGlyphIndex,
536                 defaultHorizAdvanceX,
537                 // " arabic-form=\"terminal\"",
538
(XML_SPACE + SVG_ARABIC_FORM_ATTRIBUTE + XML_EQUAL_QUOT +
539                  SVG_TERMINAL_VALUE + XML_CHAR_QUOT),
540                 code));
541             // sb.append("\r\n");
542
sb.append(EOL);
543             substituted = true;
544         }
545
546         if (substituted) {
547             sb.append(getGlyphAsSVG(
548                 font,
549                 glyph,
550                 glyphIndex,
551                 defaultHorizAdvanceX,
552                 // " arabic-form=\"isolated\"",
553
(XML_SPACE + SVG_ARABIC_FORM_ATTRIBUTE + XML_EQUAL_QUOT +
554                  SVG_ISOLATED_VALUE + XML_CHAR_QUOT),
555                 code));
556         } else {
557             sb.append(getGlyphAsSVG(
558                 font,
559                 glyph,
560                 glyphIndex,
561                 defaultHorizAdvanceX,
562                 null,
563                 code));
564         }
565
566         return sb.toString();
567     }
568
569     protected static String JavaDoc getKerningPairAsSVG(KerningPair kp, PostTable post) {
570         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
571         // sb.append("<hkern g1=\"");
572
sb.append(XML_OPEN_TAG_START).append(SVG_HKERN_TAG).append(XML_SPACE);
573         sb.append(SVG_G1_ATTRIBUTE).append(XML_EQUAL_QUOT);
574
575         sb.append(post.getGlyphName(kp.getLeft()));
576         // sb.append("\" g2=\"");
577
sb.append(XML_CHAR_QUOT).append(XML_SPACE).append(SVG_G2_ATTRIBUTE).append(XML_EQUAL_QUOT);
578
579         sb.append(post.getGlyphName(kp.getRight()));
580         // sb.append("\" k=\"");
581
sb.append(XML_CHAR_QUOT).append(XML_SPACE).append(SVG_K_ATTRIBUTE).append(XML_EQUAL_QUOT);
582
583         // SVG kerning values are inverted from TrueType's.
584
sb.append(-kp.getValue());
585         // sb.append("\"/>");
586
sb.append(XML_CHAR_QUOT).append(XML_OPEN_TAG_END_NO_CHILDREN);
587
588         return sb.toString();
589     }
590 /*
591     protected static String getGlyphAsPath(Glyph glyph) {
592         StringBuffer sb = new StringBuffer();
593         int firstIndex = 0;
594         int count = 0;
595         int i;
596
597         for (i = 0; i < glyph.getPointCount(); i++) {
598             count++;
599             if (glyph.getPoint(i).endOfContour) {
600                 sb.append(getContourAsSVGPathData(glyph, firstIndex, count));
601                 firstIndex = i + 1;
602                 count = 0;
603             }
604         }
605         return sb.toString();
606     }
607
608     protected static void writeTextAsSVGFragment(PrintStream ps, Font f, int size, String text) {
609         CmapFormat cmapFmt = f.getCmapTable().getCmapFormat(Table.platformMicrosoft, Table.encodingUGL);
610         int x = 0;
611         for (short i = 0; i < text.length(); i++) {
612             int glyphIndex = cmapFmt.mapCharCode((short)text.charAt(i));
613             Glyph glyph = f.getGlyph(glyphIndex);
614             if (glyph != null) {
615                 ps.println(translateSVG(x, 0, getGlyphAsSVGPath(glyph)));
616                 x += glyph.getAdvanceWidth();
617             }
618         }
619     }
620 */

621     protected static void writeSvgBegin(PrintStream JavaDoc ps) {
622         // ps.println("<?xml version=\"1.0\" standalone=\"no\"?>");
623
// ps.println("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20001102//EN\"");
624
// ps.println("\"http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd\" >");
625
// ps.println("<svg width=\"100%\" height=\"100%\">");
626
ps.println(Messages.formatMessage(CONFIG_SVG_BEGIN,
627                                           new Object JavaDoc[]{SVG_PUBLIC_ID, SVG_SYSTEM_ID}));
628                    
629     }
630         
631     protected static void writeSvgDefsBegin(PrintStream JavaDoc ps) {
632         // ps.println("<defs>");
633
ps.println(XML_OPEN_TAG_START + SVG_DEFS_TAG + XML_OPEN_TAG_END_CHILDREN);
634     }
635
636     protected static void writeSvgDefsEnd(PrintStream JavaDoc ps) {
637         // ps.println("</defs>");
638
ps.println(XML_CLOSE_TAG_START + SVG_DEFS_TAG + XML_CLOSE_TAG_END);
639     }
640
641     protected static void writeSvgEnd(PrintStream JavaDoc ps) {
642         // ps.println("</svg>");
643
ps.println(XML_CLOSE_TAG_START + SVG_SVG_TAG + XML_CLOSE_TAG_END);
644     }
645
646     protected static void writeSvgTestCard(PrintStream JavaDoc ps, String JavaDoc fontFamily) {
647         ps.println(Messages.formatMessage(CONFIG_SVG_TEST_CARD_START, null));
648         ps.println(fontFamily);
649         ps.println(Messages.formatMessage(CONFIG_SVG_TEST_CARD_END, null));
650
651         /*ps.println("<g style=\"font-family: '" + fontFamily + "'; font-size:18;fill:black\">");
652         ps.println("<text x=\"20\" y=\"60\"> !&quot;#$%&amp;&apos;()*+,-./0123456789:;&lt;&gt;?</text>");
653         ps.println("<text x=\"20\" y=\"120\">@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_</text>");
654         ps.println("<text x=\"20\" y=\"180\">`abcdefghijklmnopqrstuvwxyz{|}~</text>");
655         ps.println("<text x=\"20\" y=\"240\">&#x80;&#x81;&#x82;&#x83;&#x84;&#x85;&#x86;&#x87;&#x88;&#x89;&#x8a;&#x8b;&#x8c;&#x8d;&#x8e;&#x8f;&#x90;&#x91;&#x92;&#x93;&#x94;&#x95;&#x96;&#x97;&#x98;&#x99;&#x9a;&#x9b;&#x9c;&#x9d;&#x9e;&#x9f;</text>");
656         ps.println("<text x=\"20\" y=\"300\">&#xa0;&#xa1;&#xa2;&#xa3;&#xa4;&#xa5;&#xa6;&#xa7;&#xa8;&#xa9;&#xaa;&#xab;&#xac;&#xad;&#xae;&#xaf;&#xb0;&#xb1;&#xb2;&#xb3;&#xb4;&#xb5;&#xb6;&#xb7;&#xb8;&#xb9;&#xba;&#xbb;&#xbc;&#xbd;&#xbe;&#xbf;</text>");
657         ps.println("<text x=\"20\" y=\"360\">&#xc0;&#xc1;&#xc2;&#xc3;&#xc4;&#xc5;&#xc6;&#xc7;&#xc8;&#xc9;&#xca;&#xcb;&#xcc;&#xcd;&#xce;&#xcf;&#xd0;&#xd1;&#xd2;&#xd3;&#xd4;&#xd5;&#xd6;&#xd7;&#xd8;&#xd9;&#xda;&#xdb;&#xdc;&#xdd;&#xde;&#xdf;</text>");
658         ps.println("<text x=\"20\" y=\"420\">&#xe0;&#xe1;&#xe2;&#xe3;&#xe4;&#xe5;&#xe6;&#xe7;&#xe8;&#xe9;&#xea;&#xeb;&#xec;&#xed;&#xee;&#xef;&#xf0;&#xf1;&#xf2;&#xf3;&#xf4;&#xf5;&#xf6;&#xf7;&#xf8;&#xf9;&#xfa;&#xfb;&#xfc;&#xfd;&#xfe;&#xff;</text>");
659         ps.println("</g>");*/

660     }
661
662     public static final char ARG_KEY_START_CHAR = '-';
663     public static final String JavaDoc ARG_KEY_CHAR_RANGE_LOW = "-l";
664     public static final String JavaDoc ARG_KEY_CHAR_RANGE_HIGH = "-h";
665     public static final String JavaDoc ARG_KEY_ID = "-id";
666     public static final String JavaDoc ARG_KEY_ASCII = "-ascii";
667     public static final String JavaDoc ARG_KEY_TESTCARD = "-testcard";
668     public static final String JavaDoc ARG_KEY_AUTO_RANGE = "-autorange";
669     public static final String JavaDoc ARG_KEY_OUTPUT_PATH = "-o";
670
671     /**
672      * Starts the application.
673      * @param args an array of command-line arguments
674      */

675     public static void main(String JavaDoc[] args) {
676         try {
677             String JavaDoc path = parseArgs(args, null);
678             String JavaDoc low = parseArgs(args, ARG_KEY_CHAR_RANGE_LOW);
679             String JavaDoc high = parseArgs(args, ARG_KEY_CHAR_RANGE_HIGH);
680             String JavaDoc id = parseArgs(args, ARG_KEY_ID);
681             String JavaDoc ascii = parseArgs(args, ARG_KEY_ASCII);
682             String JavaDoc testCard = parseArgs(args, ARG_KEY_TESTCARD);
683             String JavaDoc outPath = parseArgs(args, ARG_KEY_OUTPUT_PATH);
684             String JavaDoc autoRange = parseArgs(args, ARG_KEY_AUTO_RANGE);
685             PrintStream JavaDoc ps = null;
686             FileOutputStream JavaDoc fos = null;
687
688             // What are we outputting to?
689
if (outPath != null) {
690                 // If an output path was specified, write to a file
691
fos = new FileOutputStream JavaDoc(outPath);
692                 ps = new PrintStream JavaDoc(fos);
693             } else {
694                 // Otherwise we'll just put it to stdout
695
ps = System.out;
696             }
697
698             // The font path is the only required argument
699
if (path != null) {
700                 Font font = Font.create(path);
701
702                 // Write the various parts of the SVG file
703
writeSvgBegin(ps);
704                 writeSvgDefsBegin(ps);
705                 writeFontAsSVGFragment(
706                     ps,
707                     font,
708                     id,
709                     (low != null ? Integer.parseInt(low) : -1),
710                     (high != null ? Integer.parseInt(high) : -1),
711                     (autoRange != null),
712                     (ascii != null));
713                 writeSvgDefsEnd(ps);
714                 if (testCard != null) {
715                     String JavaDoc fontFamily = font.getNameTable().getRecord(Table.nameFontFamilyName);
716                     writeSvgTestCard(ps, fontFamily);
717                 }
718                 writeSvgEnd(ps);
719
720                 // Close the output stream (if we have one)
721
if (fos != null) {
722                     fos.close();
723                 }
724             } else {
725                 usage();
726             }
727         } catch (Exception JavaDoc e) {
728             e.printStackTrace();
729             System.err.println(e.getMessage());
730             usage();
731         }
732     }
733
734     private static void chopUpStringBuffer(StringBuffer JavaDoc sb) {
735         if (sb.length() < 256) {
736             return;
737         } else {
738             // Being rather simplistic about it, for now we'll insert a newline after
739
// 240 chars
740
for (int i = 240; i < sb.length(); i++) {
741                 if (sb.charAt(i) == ' ') {
742                     sb.setCharAt(i, '\n');
743                     i += 240;
744                 }
745             }
746         }
747     }
748
749     private static int midValue(int a, int b) {
750         return a + (b - a)/2;
751     }
752
753     /*private static String translateSVG(int x, int y, String svgText) {
754         StringBuffer sb = new StringBuffer();
755         sb.append("<g transform=\"translate(")
756             .append(String.valueOf(x))
757             .append(" ")
758             .append(String.valueOf(y))
759             .append(")\">")
760             .append(svgText)
761             .append("</g>");
762         return sb.toString();
763         }*/

764
765     private static String JavaDoc parseArgs(String JavaDoc[] args, String JavaDoc name) {
766         for (int i = 0; i < args.length; i++) {
767             if (name == null) {
768                 if (args[i].charAt(0) != ARG_KEY_START_CHAR) {
769                     return args[i];
770                 }
771             } else if (name.equalsIgnoreCase(args[i])) {
772                 if ((i < args.length - 1) && (args[i+1].charAt(0) != ARG_KEY_START_CHAR)) {
773                     return args[i+1];
774                 } else {
775                     return args[i];
776                 }
777             }
778         }
779         return null;
780     }
781
782     private static void usage() {
783         System.err.println(Messages.formatMessage(CONFIG_USAGE, null));
784     }
785 }
786
Popular Tags