KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > it > stefanochizzolini > clown > documents > contents > fonts > Type1Font


1 /*
2   Copyright © 2007 Stefano Chizzolini. http://clown.stefanochizzolini.it
3
4   Contributors:
5     * Stefano Chizzolini (original code developer, info@stefanochizzolini.it):
6       contributed code is Copyright © 2007 by Stefano Chizzolini.
7
8   This file should be part of the source code distribution of "PDF Clown library"
9   (the Program): see the accompanying README files for more info.
10
11   This Program is free software; you can redistribute it and/or modify it under
12   the terms of the GNU General Public License as published by the Free Software
13   Foundation; either version 2 of the License, or (at your option) any later version.
14
15   This Program is distributed in the hope that it will be useful, but WITHOUT ANY
16   WARRANTY, either expressed or implied; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.
18
19   You should have received a copy of the GNU General Public License along with this
20   Program (see README files); if not, go to the GNU website (http://www.gnu.org/).
21
22   Redistribution and use, with or without modification, are permitted provided that such
23   redistributions retain the above copyright notice, license and disclaimer, along with
24   this list of conditions.
25 */

26
27 package it.stefanochizzolini.clown.documents.contents.fonts;
28
29 import it.stefanochizzolini.clown.bytes.Buffer;
30 import it.stefanochizzolini.clown.bytes.IInputStream;
31 import it.stefanochizzolini.clown.documents.Document;
32 import it.stefanochizzolini.clown.objects.PdfArray;
33 import it.stefanochizzolini.clown.objects.PdfDictionary;
34 import it.stefanochizzolini.clown.objects.PdfDirectObject;
35 import it.stefanochizzolini.clown.objects.PdfIndirectObject;
36 import it.stefanochizzolini.clown.objects.PdfInteger;
37 import it.stefanochizzolini.clown.objects.PdfName;
38 import it.stefanochizzolini.clown.objects.PdfReal;
39 import it.stefanochizzolini.clown.objects.PdfRectangle;
40 import it.stefanochizzolini.clown.objects.PdfStream;
41 import it.stefanochizzolini.clown.util.NotImplementedException;
42
43 import java.io.BufferedReader JavaDoc;
44 import java.io.InputStreamReader JavaDoc;
45 import java.util.EnumSet JavaDoc;
46 import java.util.Hashtable JavaDoc;
47 import java.util.regex.Matcher JavaDoc;
48 import java.util.regex.Pattern JavaDoc;
49
50 /**
51   Type 1 font [PDF:1.6:5;AFM:4.1].
52   <h3>Remarks</h3>
53   <p>Embedding is currently not supported.</p>
54   <p>Current version is instrumental in supporting standard type 1 fonts only.</p>
55 */

56 //TODO:IMPL font embedding to be supported!!!
57
public class Type1Font
58   extends Font
59 {
60   // <class>
61
// <classes>
62
/**
63     Font header (Global font information).
64   */

65   private static final class FontMetrics
66   {
67     public boolean isCustomEncoding;
68     public String JavaDoc fontName;
69     public String JavaDoc weight;
70     public float italicAngle;
71     public boolean isFixedPitch;
72     public short xMin;
73     public short yMin;
74     public short xMax;
75     public short yMax;
76     public short underlinePosition;
77     public short underlineThickness;
78     public short capHeight;
79     public short xHeight;
80     public short ascender;
81     public short descender;
82     public short stemH;
83     public short stemV;
84   }
85
86   /**
87     Font data parser.
88   */

89   private final class Parser
90   {
91     private BufferedReader JavaDoc fontMetricsData;
92
93     private Parser(
94       BufferedReader JavaDoc fontMetricsData
95       )
96     {
97       this.fontMetricsData = fontMetricsData;
98
99       load();
100     }
101
102     private void load(
103       )
104     {
105       Type1Font.this.metrics = new FontMetrics();
106
107       load_fontHeader();
108       load_charMetrics();
109       load_kerningData();
110     }
111
112     /**
113       Loads the font header [AFM:4.1:3,4,4.1,4.2].
114     */

115     private void load_fontHeader(
116       )
117     {
118       String JavaDoc line;
119       Pattern JavaDoc linePattern = Pattern.compile("(\\S+)\\s+(.+)");
120       try
121       {
122         while((line = fontMetricsData.readLine()) != null)
123         {
124           Matcher JavaDoc lineMatcher = linePattern.matcher(line);
125           if(!lineMatcher.find())
126             continue;
127
128           String JavaDoc key = lineMatcher.group(1);
129           if(key.equals("FontName"))
130           {metrics.fontName = lineMatcher.group(2);}
131           else if (key.equals("Weight"))
132           {metrics.weight = lineMatcher.group(2);}
133           else if (key.equals("ItalicAngle"))
134           {metrics.italicAngle = Float.valueOf(lineMatcher.group(2));}
135           else if (key.equals("IsFixedPitch"))
136           {metrics.isFixedPitch = lineMatcher.group(2).equals("true");}
137           else if (key.equals("FontBBox"))
138           {
139             String JavaDoc[] coordinates = lineMatcher.group(2).split("\\s+");
140             metrics.xMin = Short.valueOf(coordinates[0]);
141             metrics.yMin = Short.valueOf(coordinates[1]);
142             metrics.xMax = Short.valueOf(coordinates[2]);
143             metrics.yMax = Short.valueOf(coordinates[3]);
144           }
145           else if (key.equals("UnderlinePosition"))
146           {metrics.underlinePosition = Short.valueOf(lineMatcher.group(2));}
147           else if (key.equals("UnderlineThickness"))
148           {metrics.underlineThickness = Short.valueOf(lineMatcher.group(2));}
149           else if (key.equals("EncodingScheme"))
150           {metrics.isCustomEncoding = lineMatcher.group(2).equals("FontSpecific");}
151           else if (key.equals("CapHeight"))
152           {metrics.capHeight = Short.valueOf(lineMatcher.group(2));}
153           else if (key.equals("XHeight"))
154           {metrics.xHeight = Short.valueOf(lineMatcher.group(2));}
155           else if (key.equals("Ascender"))
156           {metrics.ascender = Short.valueOf(lineMatcher.group(2));}
157           else if (key.equals("Descender"))
158           {metrics.descender = Short.valueOf(lineMatcher.group(2));}
159           else if (key.equals("StdHW"))
160           {metrics.stemH = Short.valueOf(lineMatcher.group(2));}
161           else if (key.equals("StdVW"))
162           {metrics.stemV = Short.valueOf(lineMatcher.group(2));}
163           else if (key.equals("StartCharMetrics"))
164           {break;}
165         }
166       }
167       catch(Exception JavaDoc e)
168       {throw new RuntimeException JavaDoc(e);}
169     }
170
171     /**
172       Loads individual character metrics [AFM:4.1:3,4,4.4,8].
173     */

174     private void load_charMetrics(
175       )
176     {
177       Type1Font.this.widths = new Hashtable JavaDoc<Integer JavaDoc,Integer JavaDoc>();
178
179       try
180       {
181         /*
182           NOTE: Customly-encoded fonts rely on character codes,
183           while standardly-encoded fonts rely on character names.
184         */

185         if(metrics.isCustomEncoding) // Custom encoding.
186
{
187           String JavaDoc line;
188           Pattern JavaDoc linePattern = Pattern.compile("C (\\S+) ; WX (\\S+)");
189           while((line = fontMetricsData.readLine()) != null)
190           {
191             Matcher JavaDoc lineMatcher = linePattern.matcher(line);
192             if(!lineMatcher.find())
193             {
194               if(line.equals("EndCharMetrics"))
195                 break;
196
197               continue;
198             }
199
200             int code = Integer.valueOf(lineMatcher.group(1));
201             int width = Integer.valueOf(lineMatcher.group(2));
202
203             widths.put(code,width);
204           }
205         }
206         else // Standard encoding.
207
{
208           String JavaDoc line;
209           Pattern JavaDoc linePattern = Pattern.compile(" WX (\\S+) ; N (\\S+)");
210           while((line = fontMetricsData.readLine()) != null)
211           {
212             Matcher JavaDoc lineMatcher = linePattern.matcher(line);
213             if(!lineMatcher.find())
214             {
215               if(line.equals("EndCharMetrics"))
216                 break;
217
218               continue;
219             }
220
221             String JavaDoc name = lineMatcher.group(2);
222             int width = Integer.valueOf(lineMatcher.group(1));
223
224             widths.put(
225               GlyphMapping.nameToCode(name),
226               width
227               );
228           }
229         }
230       }
231       catch(Exception JavaDoc e)
232       {throw new RuntimeException JavaDoc(e);}
233     }
234
235     /**
236       Loads kerning data [AFM:4.1:3,4,4.5,9].
237     */

238     private void load_kerningData(
239       )
240     {
241       Type1Font.this.kernings = new Hashtable JavaDoc<Integer JavaDoc,Integer JavaDoc>();
242
243       try
244       {
245         String JavaDoc line;
246         while((line = fontMetricsData.readLine()) != null)
247         {
248           if(line.startsWith("StartKernPairs"))
249             break;
250         }
251
252         Pattern JavaDoc linePattern = Pattern.compile("KPX (\\S+) (\\S+) (\\S+)");
253         while((line = fontMetricsData.readLine()) != null)
254         {
255           Matcher JavaDoc lineMatcher = linePattern.matcher(line);
256           if(!lineMatcher.find())
257           {
258             if(line.equals("EndKernPairs"))
259               break;
260
261             continue;
262           }
263
264           int code1 = GlyphMapping.nameToCode(lineMatcher.group(1));
265           int code2 = GlyphMapping.nameToCode(lineMatcher.group(2));
266           int value = Integer.valueOf(lineMatcher.group(3));
267           int pair = code1 << 16 + code2;
268
269           kernings.put(pair,value);
270         }
271       }
272       catch(Exception JavaDoc e)
273       {throw new RuntimeException JavaDoc(e);}
274     }
275   }
276
277   /**
278     Adobe standard glyph mapping (unicode-encoding against glyph-naming) [PDF:1.6:D;AGL:2.0].
279   */

280   private static final class GlyphMapping
281   {
282     private static Hashtable JavaDoc<String JavaDoc,Integer JavaDoc> codes = new Hashtable JavaDoc<String JavaDoc,Integer JavaDoc>();
283
284     static
285     {load();}
286
287     public static int nameToCode(
288       String JavaDoc name
289       )
290     {return codes.get(name);}
291
292     /**
293       Loads the glyph list mapping character names to character codes (unicode encoding).
294     */

295     private static void load(
296       )
297     {
298       BufferedReader JavaDoc glyphListStream = null;
299       try
300       {
301         // Open the glyph list!
302
/*
303           NOTE: The Adobe Glyph List [AGL:2.0] represents the reference name-to-unicode map
304           for consumer applications.
305         */

306         glyphListStream = new BufferedReader JavaDoc(
307           new InputStreamReader JavaDoc(
308             GlyphMapping.class.getResourceAsStream("/fonts/AGL20.scsv")
309             )
310           );
311
312         // Parsing the glyph list...
313
String JavaDoc line;
314         Pattern JavaDoc linePattern = Pattern.compile("^(\\w+);([A-F0-9]+)$");
315         while((line = glyphListStream.readLine()) != null)
316         {
317           Matcher JavaDoc lineMatcher = linePattern.matcher(line);
318           if(!lineMatcher.find())
319             continue;
320
321           String JavaDoc name = lineMatcher.group(1);
322           int code = Integer.parseInt(lineMatcher.group(2),16);
323
324           // Associate the character name with its corresponding character code!
325
codes.put(name,code);
326         }
327       }
328       catch(Exception JavaDoc e)
329       {throw new RuntimeException JavaDoc(e);}
330       finally
331       {
332         try
333         {
334           // Close the glyph list!
335
if(glyphListStream != null)
336           {glyphListStream.close();}
337         }
338         catch(Exception JavaDoc e)
339         {throw new RuntimeException JavaDoc(e);}
340       }
341     }
342   }
343   // </classes>
344

345   // <dynamic>
346
// <fields>
347
private FontMetrics metrics;
348
349   private Hashtable JavaDoc<Integer JavaDoc,Integer JavaDoc> kernings;
350   private Hashtable JavaDoc<Integer JavaDoc,Integer JavaDoc> widths;
351   // </fields>
352

353   // <constructors>
354
//TODO:IMPL define outlines data support (PFB)!!!
355
// public Type1Font(
356
// Document context,
357
// IInputStream fontMetricsData,
358
// IInputStream fontOutlinesData
359
// )
360
// {
361
// super(context);
362
//
363
// throw new NotImplementedException("Outlines (PFB file) are to be supported yet.");
364
// load(fontMetricsData);
365
// }
366

367   /**
368     <h3>Remarks</h3>
369     <p>For internal use only.</p>
370   */

371 //TODO:IMPL manage loading of already embedded font metrics to allow editing!!!
372
public Type1Font(
373     PdfDirectObject baseObject
374     )
375   {super(baseObject);}
376
377   protected Type1Font(
378     Document context
379     )
380   {super(context);}
381   // </constructors>
382

383   // <interface>
384
// <public>
385
public Object JavaDoc clone(
386     Document context
387     )
388   {throw new NotImplementedException();}
389
390   @Override JavaDoc
391   public double getAscent(
392     double size
393     )
394   {
395     /*
396       NOTE: Standard Type 1 fonts SHOULD omit extended font descriptions [PDF:1.6:5.5.1],
397       so we are forced to deal with this circumstance.
398     */

399     if(getBaseDataObject().containsKey(PdfName.FontDescriptor))
400     {return super.getAscent(size);}
401     else
402     {return (metrics.ascender * getScalingFactor(size));}
403   }
404
405   @Override JavaDoc
406   public double getDescent(
407     double size
408     )
409   {
410     /*
411       NOTE: Standard Type 1 fonts SHOULD omit extended font descriptions [PDF:1.6:5.5.1],
412       so we are forced to deal with this circumstance.
413     */

414     if(getBaseDataObject().containsKey(PdfName.FontDescriptor))
415     {return super.getDescent(size);}
416     else
417     {return (metrics.descender * getScalingFactor(size));}
418   }
419
420   @Override JavaDoc
421   public EnumSet JavaDoc<FlagsEnum> getFlags(
422     )
423   {
424     /*
425       NOTE: Standard Type 1 fonts SHOULD omit extended font descriptions [PDF:1.6:5.5.1],
426       so we are forced to deal with this circumstance.
427     */

428     if(getBaseDataObject().containsKey(PdfName.FontDescriptor))
429     {return super.getFlags();}
430     else
431     {
432       //TODO:IMPL!!!
433
return EnumSet.noneOf(FlagsEnum.class);
434     }
435   }
436
437   @Override JavaDoc
438   public int getKerning(
439     char textChar1,
440     char textChar2
441     )
442   {
443     try
444     {
445       return kernings.get(
446         ((int)textChar1) << 16 // Left-hand character code.
447
+ ((int)textChar2) // Right-hand character code.
448
);
449     }
450     catch(Exception JavaDoc e)
451     {return 0;}
452   }
453
454   @Override JavaDoc
455   public int getKerning(
456     String JavaDoc text
457     )
458   {
459     int kerning = 0;
460     // Are kerning pairs available?
461
if(kernings != null)
462     {
463       char textChars[] = text.toCharArray();
464       for(
465         int index = 0,
466           length = text.length() - 1;
467         index < length;
468         index++
469         )
470       {
471         kerning += getKerning(
472           textChars[index],
473           textChars[index + 1]
474           );
475       }
476     }
477
478     return kerning;
479   }
480
481   @Override JavaDoc
482   public double getLineHeight(
483     double size
484     )
485   {
486     /*
487       NOTE: Standard Type 1 fonts SHOULD omit extended font descriptions [PDF:1.6:5.5.1],
488       so we are forced to deal with this circumstance.
489     */

490     if(getBaseDataObject().containsKey(PdfName.FontDescriptor))
491     {return super.getLineHeight(size);}
492     else
493     {return ((metrics.ascender + Math.abs(metrics.descender)) * getScalingFactor(size));}
494   }
495
496   @Override JavaDoc
497   public int getWidth(
498     char textChar
499     )
500   {return widths.get((int)textChar);}
501
502   @Override JavaDoc
503   public int getWidth(
504     String JavaDoc text
505     )
506   {
507     int width = 0;
508     char textChars[] = text.toCharArray();
509     for(
510       int index = 0,
511         length = text.length();
512       index < length;
513       index++
514       )
515     {width += getWidth(textChars[index]);}
516
517     return width;
518   }
519   // </public>
520

521   // <private>
522
/**
523     Loads the font data.
524   */

525   protected void load(
526     BufferedReader JavaDoc fontMetricsData,
527     boolean standard
528     )
529   {
530     try
531     {
532       Parser parser = new Parser(fontMetricsData);
533
534       // Subtype.
535
getBaseDataObject().put(
536         PdfName.Subtype,
537         PdfName.Type1
538         );
539       // BaseFont.
540
getBaseDataObject().put(
541         PdfName.BaseFont,
542         new PdfName(metrics.fontName)
543         );
544
545       // Is the font a standard one?
546
/*
547         NOTE: Standard Type 1 fonts SHOULD omit extended font descriptions [PDF:1.6:5.5.1].
548       */

549       if(standard)
550         return;
551
552       throw new NotImplementedException("Extended font descriptions are currently not supported.");
553       // Encoding.
554
// FirstChar.
555
// LastChar.
556
// Widths.
557
// FontDescriptor.
558
}
559     catch(Exception JavaDoc e)
560     {throw new RuntimeException JavaDoc(e);}
561   }
562
563 // TODO:IMPL!!!
564
// /**
565
// Creates the font descriptor.
566
// */
567
// private PdfIndirectObject load_createFontDescriptor(
568
// )
569
// {
570
// PdfDictionary fontDescriptor = new PdfDictionary();
571
// // Type.
572
// fontDescriptor.put(
573
// PdfName.Type,
574
// PdfName.FontDescriptor
575
// );
576
// // FontName.
577
// fontDescriptor.put(
578
// PdfName.FontName,
579
// getBaseDataObject().get(PdfName.BaseFont)
580
// );
581
// // Flags [PDF:1.6:5.7.1].
582
// // TODO:IMPL see OpenTypeFont flags!!!
583
// fontDescriptor.put(
584
// PdfName.Flags,
585
// new PdfInteger(flags)
586
// );
587
// // FontBBox.
588
// fontDescriptor.put(
589
// PdfName.FontBBox,
590
// new PdfRectangle(
591
// metrics.xMin,
592
// metrics.yMin,
593
// metrics.xMax,
594
// metrics.yMax
595
// )
596
// );
597
// ItalicAngle.
598
// fontDescriptor.put(
599
// PdfName.ItalicAngle,
600
// new PdfReal(metrics.italicAngle)
601
// );
602
// // Ascent.
603
// fontDescriptor.put(
604
// PdfName.Ascent,
605
// new PdfReal(metrics.ascender)
606
// );
607
// // Descent.
608
// fontDescriptor.put(
609
// PdfName.Descent,
610
// new PdfReal(metrics.descender)
611
// );
612
// // CapHeight.
613
// fontDescriptor.put(
614
// PdfName.CapHeight,
615
// new PdfInteger(metrics.capHeight)
616
// );
617
// // StemV.
618
// fontDescriptor.put(
619
// PdfName.StemV,
620
// new PdfInteger(metrics.stemV)
621
// );
622
// // FontFile.
623
// PdfIndirectObject fontFile = getFile().getIndirectObjects().add(
624
// new PdfStream(
625
// new Buffer(fontMetricsData.toByteArray())
626
// )
627
// );
628
// fontDescriptor.put(
629
// PdfName.FontFile,
630
// fontFile.getReference()
631
// );
632
//
633
// return getFile().getIndirectObjects().add(fontDescriptor);
634
// }
635
// </private>
636
// </interface>
637
// </dynamic>
638
// </class>
639
}
Popular Tags