KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lowagie > text > pdf > Type1Font


1 /*
2  * $Id: Type1Font.java 2509 2006-12-21 16:26:19Z psoares33 $
3  * $Name$
4  *
5  * Copyright 2001-2006 Paulo Soares
6  *
7  * The contents of this file are subject to the Mozilla Public License Version 1.1
8  * (the "License"); you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the License.
14  *
15  * The Original Code is 'iText, a free JAVA-PDF library'.
16  *
17  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
18  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
19  * All Rights Reserved.
20  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
21  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
22  *
23  * Contributor(s): all the names of the contributors are added in the source code
24  * where applicable.
25  *
26  * Alternatively, the contents of this file may be used under the terms of the
27  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
28  * provisions of LGPL are applicable instead of those above. If you wish to
29  * allow use of your version of this file only under the terms of the LGPL
30  * License and not to allow others to use your version of this file under
31  * the MPL, indicate your decision by deleting the provisions above and
32  * replace them with the notice and other provisions required by the LGPL.
33  * If you do not delete the provisions above, a recipient may use your version
34  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
35  *
36  * This library is free software; you can redistribute it and/or modify it
37  * under the terms of the MPL as stated above or under the terms of the GNU
38  * Library General Public License as published by the Free Software Foundation;
39  * either version 2 of the License, or any later version.
40  *
41  * This library is distributed in the hope that it will be useful, but WITHOUT
42  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
43  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
44  * details.
45  *
46  * If you didn't download this code from the following link, you should check if
47  * you aren't using an obsolete version:
48  * http://www.lowagie.com/iText/
49  */

50
51 package com.lowagie.text.pdf;
52
53 import java.io.ByteArrayOutputStream JavaDoc;
54 import java.io.IOException JavaDoc;
55 import java.io.InputStream JavaDoc;
56 import java.util.HashMap JavaDoc;
57 import java.util.StringTokenizer JavaDoc;
58
59 import com.lowagie.text.DocumentException;
60 import com.lowagie.text.pdf.fonts.FontsResourceAnchor;
61
62 /** Reads a Type1 font
63  *
64  * @author Paulo Soares (psoares@consiste.pt)
65  */

66 class Type1Font extends BaseFont
67 {
68     private static FontsResourceAnchor resourceAnchor;
69     
70     /** The PFB file if the input was made with a <CODE>byte</CODE> array.
71      */

72     protected byte pfb[];
73 /** The Postscript font name.
74  */

75     private String JavaDoc FontName;
76 /** The full name of the font.
77  */

78     private String JavaDoc FullName;
79 /** The family name of the font.
80  */

81     private String JavaDoc FamilyName;
82 /** The weight of the font: normal, bold, etc.
83  */

84     private String JavaDoc Weight = "";
85 /** The italic angle of the font, usually 0.0 or negative.
86  */

87     private float ItalicAngle = 0.0f;
88 /** <CODE>true</CODE> if all the characters have the same
89  * width.
90  */

91     private boolean IsFixedPitch = false;
92 /** The character set of the font.
93  */

94     private String JavaDoc CharacterSet;
95 /** The llx of the FontBox.
96  */

97     private int llx = -50;
98 /** The lly of the FontBox.
99  */

100     private int lly = -200;
101 /** The lurx of the FontBox.
102  */

103     private int urx = 1000;
104 /** The ury of the FontBox.
105  */

106     private int ury = 900;
107 /** The underline position.
108  */

109     private int UnderlinePosition = -100;
110 /** The underline thickness.
111  */

112     private int UnderlineThickness = 50;
113 /** The font's encoding name. This encoding is 'StandardEncoding' or
114  * 'AdobeStandardEncoding' for a font that can be totally encoded
115  * according to the characters names. For all other names the
116  * font is treated as symbolic.
117  */

118     private String JavaDoc EncodingScheme = "FontSpecific";
119 /** A variable.
120  */

121     private int CapHeight = 700;
122 /** A variable.
123  */

124     private int XHeight = 480;
125 /** A variable.
126  */

127     private int Ascender = 800;
128 /** A variable.
129  */

130     private int Descender = -200;
131 /** A variable.
132  */

133     private int StdHW;
134 /** A variable.
135  */

136     private int StdVW = 80;
137     
138 /** Represents the section CharMetrics in the AFM file. Each
139  * value of this array contains a <CODE>Object[4]</CODE> with an
140  * Integer, Integer, String and int[]. This is the code, width, name and char bbox.
141  * The key is the name of the char and also an Integer with the char number.
142  */

143     private HashMap JavaDoc CharMetrics = new HashMap JavaDoc();
144 /** Represents the section KernPairs in the AFM file. The key is
145  * the name of the first character and the value is a <CODE>Object[]</CODE>
146  * with 2 elements for each kern pair. Position 0 is the name of
147  * the second character and position 1 is the kerning distance. This is
148  * repeated for all the pairs.
149  */

150     private HashMap JavaDoc KernPairs = new HashMap JavaDoc();
151 /** The file in use.
152  */

153     private String JavaDoc fileName;
154 /** <CODE>true</CODE> if this font is one of the 14 built in fonts.
155  */

156     private boolean builtinFont = false;
157 /** Types of records in a PFB file. ASCII is 1 and BINARY is 2.
158  * They have to appear in the PFB file in this sequence.
159  */

160     private static final int PFB_TYPES[] = {1, 2, 1};
161     
162     /** Creates a new Type1 font.
163      * @param ttfAfm the AFM file if the input is made with a <CODE>byte</CODE> array
164      * @param pfb the PFB file if the input is made with a <CODE>byte</CODE> array
165      * @param afmFile the name of one of the 14 built-in fonts or the location of an AFM file. The file must end in '.afm'
166      * @param enc the encoding to be applied to this font
167      * @param emb true if the font is to be embedded in the PDF
168      * @throws DocumentException the AFM file is invalid
169      * @throws IOException the AFM file could not be read
170      */

171     Type1Font(String JavaDoc afmFile, String JavaDoc enc, boolean emb, byte ttfAfm[], byte pfb[]) throws DocumentException, IOException JavaDoc
172     {
173         if (emb && ttfAfm != null && pfb == null)
174             throw new DocumentException("Two byte arrays are needed if the Type1 font is embedded.");
175         if (emb && ttfAfm != null)
176             this.pfb = pfb;
177         encoding = enc;
178         embedded = emb;
179         fileName = afmFile;
180         fontType = FONT_TYPE_T1;
181         RandomAccessFileOrArray rf = null;
182         InputStream JavaDoc is = null;
183         if (BuiltinFonts14.containsKey(afmFile)) {
184             embedded = false;
185             builtinFont = true;
186             byte buf[] = new byte[1024];
187             try {
188                 if (resourceAnchor == null)
189                     resourceAnchor = new FontsResourceAnchor();
190                 is = getResourceStream(RESOURCE_PATH + afmFile + ".afm", resourceAnchor.getClass().getClassLoader());
191                 if (is == null) {
192                     String JavaDoc msg = afmFile + " not found as resource. (The *.afm files must exist as resources in the package com.lowagie.text.pdf.fonts)";
193                     System.err.println(msg);
194                     throw new DocumentException(msg);
195                 }
196                 ByteArrayOutputStream JavaDoc out = new ByteArrayOutputStream JavaDoc();
197                 while (true) {
198                     int size = is.read(buf);
199                     if (size < 0)
200                         break;
201                     out.write(buf, 0, size);
202                 }
203                 buf = out.toByteArray();
204             }
205             finally {
206                 if (is != null) {
207                     try {
208                         is.close();
209                     }
210                     catch (Exception JavaDoc e) {
211                         // empty on purpose
212
}
213                 }
214             }
215             try {
216                 rf = new RandomAccessFileOrArray(buf);
217                 process(rf);
218             }
219             finally {
220                 if (rf != null) {
221                     try {
222                         rf.close();
223                     }
224                     catch (Exception JavaDoc e) {
225                         // empty on purpose
226
}
227                 }
228             }
229         }
230         else if (afmFile.toLowerCase().endsWith(".afm")) {
231             try {
232                 if (ttfAfm == null)
233                     rf = new RandomAccessFileOrArray(afmFile);
234                 else
235                     rf = new RandomAccessFileOrArray(ttfAfm);
236                 process(rf);
237             }
238             finally {
239                 if (rf != null) {
240                     try {
241                         rf.close();
242                     }
243                     catch (Exception JavaDoc e) {
244                         // empty on purpose
245
}
246                 }
247             }
248         }
249         else if (afmFile.toLowerCase().endsWith(".pfm")) {
250             try {
251                 ByteArrayOutputStream JavaDoc ba = new ByteArrayOutputStream JavaDoc();
252                 if (ttfAfm == null)
253                     rf = new RandomAccessFileOrArray(afmFile);
254                 else
255                     rf = new RandomAccessFileOrArray(ttfAfm);
256                 Pfm2afm.convert(rf, ba);
257                 rf.close();
258                 rf = new RandomAccessFileOrArray(ba.toByteArray());
259                 process(rf);
260             }
261             finally {
262                 if (rf != null) {
263                     try {
264                         rf.close();
265                     }
266                     catch (Exception JavaDoc e) {
267                         // empty on purpose
268
}
269                 }
270             }
271         }
272         else
273             throw new DocumentException(afmFile + " is not an AFM or PFM font file.");
274
275         EncodingScheme = EncodingScheme.trim();
276         if (EncodingScheme.equals("AdobeStandardEncoding") || EncodingScheme.equals("StandardEncoding")) {
277             fontSpecific = false;
278         }
279         if (!encoding.startsWith("#"))
280             PdfEncodings.convertToBytes(" ", enc); // check if the encoding exists
281
createEncoding();
282     }
283     
284 /** Gets the width from the font according to the <CODE>name</CODE> or,
285  * if the <CODE>name</CODE> is null, meaning it is a symbolic font,
286  * the char <CODE>c</CODE>.
287  * @param c the char if the font is symbolic
288  * @param name the glyph name
289  * @return the width of the char
290  */

291     int getRawWidth(int c, String JavaDoc name) {
292         Object JavaDoc metrics[];
293         if (name == null) { // font specific
294
metrics = (Object JavaDoc[])CharMetrics.get(new Integer JavaDoc(c));
295         }
296         else {
297             if (name.equals(".notdef"))
298                 return 0;
299             metrics = (Object JavaDoc[])CharMetrics.get(name);
300         }
301         if (metrics != null)
302             return ((Integer JavaDoc)(metrics[1])).intValue();
303         return 0;
304     }
305     
306 /** Gets the kerning between two Unicode characters. The characters
307  * are converted to names and this names are used to find the kerning
308  * pairs in the <CODE>HashMap</CODE> <CODE>KernPairs</CODE>.
309  * @param char1 the first char
310  * @param char2 the second char
311  * @return the kerning to be applied
312  */

313     public int getKerning(char char1, char char2)
314     {
315         String JavaDoc first = GlyphList.unicodeToName((int)char1);
316         if (first == null)
317             return 0;
318         String JavaDoc second = GlyphList.unicodeToName((int)char2);
319         if (second == null)
320             return 0;
321         Object JavaDoc obj[] = (Object JavaDoc[])KernPairs.get(first);
322         if (obj == null)
323             return 0;
324         for (int k = 0; k < obj.length; k += 2) {
325             if (second.equals(obj[k]))
326                 return ((Integer JavaDoc)obj[k + 1]).intValue();
327         }
328         return 0;
329     }
330     
331     
332     /** Reads the font metrics
333      * @param rf the AFM file
334      * @throws DocumentException the AFM file is invalid
335      * @throws IOException the AFM file could not be read
336      */

337     public void process(RandomAccessFileOrArray rf) throws DocumentException, IOException JavaDoc
338     {
339         String JavaDoc line;
340         boolean isMetrics = false;
341         while ((line = rf.readLine()) != null)
342         {
343             StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(line, " ,\n\r\t\f");
344             if (!tok.hasMoreTokens())
345                 continue;
346             String JavaDoc ident = tok.nextToken();
347             if (ident.equals("FontName"))
348                 FontName = tok.nextToken("\u00ff").substring(1);
349             else if (ident.equals("FullName"))
350                 FullName = tok.nextToken("\u00ff").substring(1);
351             else if (ident.equals("FamilyName"))
352                 FamilyName = tok.nextToken("\u00ff").substring(1);
353             else if (ident.equals("Weight"))
354                 Weight = tok.nextToken("\u00ff").substring(1);
355             else if (ident.equals("ItalicAngle"))
356                 ItalicAngle = Float.parseFloat(tok.nextToken());
357             else if (ident.equals("IsFixedPitch"))
358                 IsFixedPitch = tok.nextToken().equals("true");
359             else if (ident.equals("CharacterSet"))
360                 CharacterSet = tok.nextToken("\u00ff").substring(1);
361             else if (ident.equals("FontBBox"))
362             {
363                 llx = (int)Float.parseFloat(tok.nextToken());
364                 lly = (int)Float.parseFloat(tok.nextToken());
365                 urx = (int)Float.parseFloat(tok.nextToken());
366                 ury = (int)Float.parseFloat(tok.nextToken());
367             }
368             else if (ident.equals("UnderlinePosition"))
369                 UnderlinePosition = (int)Float.parseFloat(tok.nextToken());
370             else if (ident.equals("UnderlineThickness"))
371                 UnderlineThickness = (int)Float.parseFloat(tok.nextToken());
372             else if (ident.equals("EncodingScheme"))
373                 EncodingScheme = tok.nextToken("\u00ff").substring(1);
374             else if (ident.equals("CapHeight"))
375                 CapHeight = (int)Float.parseFloat(tok.nextToken());
376             else if (ident.equals("XHeight"))
377                 XHeight = (int)Float.parseFloat(tok.nextToken());
378             else if (ident.equals("Ascender"))
379                 Ascender = (int)Float.parseFloat(tok.nextToken());
380             else if (ident.equals("Descender"))
381                 Descender = (int)Float.parseFloat(tok.nextToken());
382             else if (ident.equals("StdHW"))
383                 StdHW = (int)Float.parseFloat(tok.nextToken());
384             else if (ident.equals("StdVW"))
385                 StdVW = (int)Float.parseFloat(tok.nextToken());
386             else if (ident.equals("StartCharMetrics"))
387             {
388                 isMetrics = true;
389                 break;
390             }
391         }
392         if (!isMetrics)
393             throw new DocumentException("Missing StartCharMetrics in " + fileName);
394         while ((line = rf.readLine()) != null)
395         {
396             StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(line);
397             if (!tok.hasMoreTokens())
398                 continue;
399             String JavaDoc ident = tok.nextToken();
400             if (ident.equals("EndCharMetrics"))
401             {
402                 isMetrics = false;
403                 break;
404             }
405             Integer JavaDoc C = new Integer JavaDoc(-1);
406             Integer JavaDoc WX = new Integer JavaDoc(250);
407             String JavaDoc N = "";
408             int B[] = null;
409
410             tok = new StringTokenizer JavaDoc(line, ";");
411             while (tok.hasMoreTokens())
412             {
413                 StringTokenizer JavaDoc tokc = new StringTokenizer JavaDoc(tok.nextToken());
414                 if (!tokc.hasMoreTokens())
415                     continue;
416                 ident = tokc.nextToken();
417                 if (ident.equals("C"))
418                     C = Integer.valueOf(tokc.nextToken());
419                 else if (ident.equals("WX"))
420                     WX = new Integer JavaDoc((int)Float.parseFloat(tokc.nextToken()));
421                 else if (ident.equals("N"))
422                     N = tokc.nextToken();
423                 else if (ident.equals("B")) {
424                     B = new int[]{Integer.parseInt(tokc.nextToken()),
425                                          Integer.parseInt(tokc.nextToken()),
426                                          Integer.parseInt(tokc.nextToken()),
427                                          Integer.parseInt(tokc.nextToken())};
428                 }
429             }
430             Object JavaDoc metrics[] = new Object JavaDoc[]{C, WX, N, B};
431             if (C.intValue() >= 0)
432                 CharMetrics.put(C, metrics);
433             CharMetrics.put(N, metrics);
434         }
435         if (isMetrics)
436             throw new DocumentException("Missing EndCharMetrics in " + fileName);
437         if (!CharMetrics.containsKey("nonbreakingspace")) {
438             Object JavaDoc[] space = (Object JavaDoc[])CharMetrics.get("space");
439             if (space != null)
440                 CharMetrics.put("nonbreakingspace", space);
441         }
442         while ((line = rf.readLine()) != null)
443         {
444             StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(line);
445             if (!tok.hasMoreTokens())
446                 continue;
447             String JavaDoc ident = tok.nextToken();
448             if (ident.equals("EndFontMetrics"))
449                 return;
450             if (ident.equals("StartKernPairs"))
451             {
452                 isMetrics = true;
453                 break;
454             }
455         }
456         if (!isMetrics)
457             throw new DocumentException("Missing EndFontMetrics in " + fileName);
458         while ((line = rf.readLine()) != null)
459         {
460             StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(line);
461             if (!tok.hasMoreTokens())
462                 continue;
463             String JavaDoc ident = tok.nextToken();
464             if (ident.equals("KPX"))
465             {
466                 String JavaDoc first = tok.nextToken();
467                 String JavaDoc second = tok.nextToken();
468                 Integer JavaDoc width = new Integer JavaDoc((int)Float.parseFloat(tok.nextToken()));
469                 Object JavaDoc relates[] = (Object JavaDoc[])KernPairs.get(first);
470                 if (relates == null)
471                     KernPairs.put(first, new Object JavaDoc[]{second, width});
472                 else
473                 {
474                     int n = relates.length;
475                     Object JavaDoc relates2[] = new Object JavaDoc[n + 2];
476                     System.arraycopy(relates, 0, relates2, 0, n);
477                     relates2[n] = second;
478                     relates2[n + 1] = width;
479                     KernPairs.put(first, relates2);
480                 }
481             }
482             else if (ident.equals("EndKernPairs"))
483             {
484                 isMetrics = false;
485                 break;
486             }
487         }
488         if (isMetrics)
489             throw new DocumentException("Missing EndKernPairs in " + fileName);
490         rf.close();
491     }
492     
493 /** If the embedded flag is <CODE>false</CODE> or if the font is
494  * one of the 14 built in types, it returns <CODE>null</CODE>,
495  * otherwise the font is read and output in a PdfStream object.
496  * @return the PdfStream containing the font or <CODE>null</CODE>
497  * @throws DocumentException if there is an error reading the font
498  */

499     private PdfStream getFontStream() throws DocumentException
500     {
501         if (builtinFont || !embedded)
502             return null;
503         RandomAccessFileOrArray rf = null;
504         try {
505             String JavaDoc filePfb = fileName.substring(0, fileName.length() - 3) + "pfb";
506             if (pfb == null)
507                 rf = new RandomAccessFileOrArray(filePfb);
508             else
509                 rf = new RandomAccessFileOrArray(pfb);
510             int fileLength = rf.length();
511             byte st[] = new byte[fileLength - 18];
512             int lengths[] = new int[3];
513             int bytePtr = 0;
514             for (int k = 0; k < 3; ++k) {
515                 if (rf.read() != 0x80)
516                     throw new DocumentException("Start marker missing in " + filePfb);
517                 if (rf.read() != PFB_TYPES[k])
518                     throw new DocumentException("Incorrect segment type in " + filePfb);
519                 int size = rf.read();
520                 size += rf.read() << 8;
521                 size += rf.read() << 16;
522                 size += rf.read() << 24;
523                 lengths[k] = size;
524                 while (size != 0) {
525                     int got = rf.read(st, bytePtr, size);
526                     if (got < 0)
527                         throw new DocumentException("Premature end in " + filePfb);
528                     bytePtr += got;
529                     size -= got;
530                 }
531             }
532             return new StreamFont(st, lengths);
533         }
534         catch (Exception JavaDoc e) {
535             throw new DocumentException(e);
536         }
537         finally {
538             if (rf != null) {
539                 try {
540                     rf.close();
541                 }
542                 catch (Exception JavaDoc e) {
543                     // empty on purpose
544
}
545             }
546         }
547     }
548     
549 /** Generates the font descriptor for this font or <CODE>null</CODE> if it is
550  * one of the 14 built in fonts.
551  * @param fontStream the indirect reference to a PdfStream containing the font or <CODE>null</CODE>
552  * @return the PdfDictionary containing the font descriptor or <CODE>null</CODE>
553  */

554     private PdfDictionary getFontDescriptor(PdfIndirectReference fontStream)
555     {
556         if (builtinFont)
557             return null;
558         PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR);
559         dic.put(PdfName.ASCENT, new PdfNumber(Ascender));
560         dic.put(PdfName.CAPHEIGHT, new PdfNumber(CapHeight));
561         dic.put(PdfName.DESCENT, new PdfNumber(Descender));
562         dic.put(PdfName.FONTBBOX, new PdfRectangle(llx, lly, urx, ury));
563         dic.put(PdfName.FONTNAME, new PdfName(FontName));
564         dic.put(PdfName.ITALICANGLE, new PdfNumber(ItalicAngle));
565         dic.put(PdfName.STEMV, new PdfNumber(StdVW));
566         if (fontStream != null)
567             dic.put(PdfName.FONTFILE, fontStream);
568         int flags = 0;
569         if (IsFixedPitch)
570             flags |= 1;
571         flags |= fontSpecific ? 4 : 32;
572         if (ItalicAngle < 0)
573             flags |= 64;
574         if (FontName.indexOf("Caps") >= 0 || FontName.endsWith("SC"))
575             flags |= 131072;
576         if (Weight.equals("Bold"))
577             flags |= 262144;
578         dic.put(PdfName.FLAGS, new PdfNumber(flags));
579         
580         return dic;
581     }
582     
583     /** Generates the font dictionary for this font.
584      * @return the PdfDictionary containing the font dictionary
585      * @param firstChar the first valid character
586      * @param lastChar the last valid character
587      * @param shortTag a 256 bytes long <CODE>byte</CODE> array where each unused byte is represented by 0
588      * @param fontDescriptor the indirect reference to a PdfDictionary containing the font descriptor or <CODE>null</CODE>
589      */

590     private PdfDictionary getFontBaseType(PdfIndirectReference fontDescriptor, int firstChar, int lastChar, byte shortTag[])
591     {
592         PdfDictionary dic = new PdfDictionary(PdfName.FONT);
593         dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
594         dic.put(PdfName.BASEFONT, new PdfName(FontName));
595         boolean stdEncoding = encoding.equals("Cp1252") || encoding.equals("MacRoman");
596         if (!fontSpecific || specialMap != null) {
597             for (int k = firstChar; k <= lastChar; ++k) {
598                 if (!differences[k].equals(notdef)) {
599                     firstChar = k;
600                     break;
601                 }
602             }
603             if (stdEncoding)
604                 dic.put(PdfName.ENCODING, encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING : PdfName.MAC_ROMAN_ENCODING);
605             else {
606                 PdfDictionary enc = new PdfDictionary(PdfName.ENCODING);
607                 PdfArray dif = new PdfArray();
608                 boolean gap = true;
609                 for (int k = firstChar; k <= lastChar; ++k) {
610                     if (shortTag[k] != 0) {
611                         if (gap) {
612                             dif.add(new PdfNumber(k));
613                             gap = false;
614                         }
615                         dif.add(new PdfName(differences[k]));
616                     }
617                     else
618                         gap = true;
619                 }
620                 enc.put(PdfName.DIFFERENCES, dif);
621                 dic.put(PdfName.ENCODING, enc);
622             }
623         }
624         if (specialMap != null || forceWidthsOutput || !(builtinFont && (fontSpecific || stdEncoding))) {
625             dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar));
626             dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar));
627             PdfArray wd = new PdfArray();
628             for (int k = firstChar; k <= lastChar; ++k) {
629                 if (shortTag[k] == 0)
630                     wd.add(new PdfNumber(0));
631                 else
632                     wd.add(new PdfNumber(widths[k]));
633             }
634             dic.put(PdfName.WIDTHS, wd);
635         }
636         if (!builtinFont && fontDescriptor != null)
637             dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor);
638         return dic;
639     }
640     
641     /** Outputs to the writer the font dictionaries and streams.
642      * @param writer the writer for this document
643      * @param ref the font indirect reference
644      * @param params several parameters that depend on the font type
645      * @throws IOException on error
646      * @throws DocumentException error in generating the object
647      */

648     void writeFont(PdfWriter writer, PdfIndirectReference ref, Object JavaDoc params[]) throws DocumentException, IOException JavaDoc {
649         int firstChar = ((Integer JavaDoc)params[0]).intValue();
650         int lastChar = ((Integer JavaDoc)params[1]).intValue();
651         byte shortTag[] = (byte[])params[2];
652         boolean subsetp = ((Boolean JavaDoc)params[3]).booleanValue() && subset;
653         if (!subsetp) {
654             firstChar = 0;
655             lastChar = shortTag.length - 1;
656             for (int k = 0; k < shortTag.length; ++k)
657                 shortTag[k] = 1;
658         }
659         PdfIndirectReference ind_font = null;
660         PdfObject pobj = null;
661         PdfIndirectObject obj = null;
662         pobj = getFontStream();
663         if (pobj != null){
664             obj = writer.addToBody(pobj);
665             ind_font = obj.getIndirectReference();
666         }
667         pobj = getFontDescriptor(ind_font);
668         if (pobj != null){
669             obj = writer.addToBody(pobj);
670             ind_font = obj.getIndirectReference();
671         }
672         pobj = getFontBaseType(ind_font, firstChar, lastChar, shortTag);
673         writer.addToBody(pobj, ref);
674     }
675     
676     /** Gets the font parameter identified by <CODE>key</CODE>. Valid values
677      * for <CODE>key</CODE> are <CODE>ASCENT</CODE>, <CODE>CAPHEIGHT</CODE>, <CODE>DESCENT</CODE>,
678      * <CODE>ITALICANGLE</CODE>, <CODE>BBOXLLX</CODE>, <CODE>BBOXLLY</CODE>, <CODE>BBOXURX</CODE>
679      * and <CODE>BBOXURY</CODE>.
680      * @param key the parameter to be extracted
681      * @param fontSize the font size in points
682      * @return the parameter in points
683      */

684     public float getFontDescriptor(int key, float fontSize) {
685         switch (key) {
686             case AWT_ASCENT:
687             case ASCENT:
688                 return Ascender * fontSize / 1000;
689             case CAPHEIGHT:
690                 return CapHeight * fontSize / 1000;
691             case AWT_DESCENT:
692             case DESCENT:
693                 return Descender * fontSize / 1000;
694             case ITALICANGLE:
695                 return ItalicAngle;
696             case BBOXLLX:
697                 return llx * fontSize / 1000;
698             case BBOXLLY:
699                 return lly * fontSize / 1000;
700             case BBOXURX:
701                 return urx * fontSize / 1000;
702             case BBOXURY:
703                 return ury * fontSize / 1000;
704             case AWT_LEADING:
705                 return 0;
706             case AWT_MAXADVANCE:
707                 return (urx - llx) * fontSize / 1000;
708         }
709         return 0;
710     }
711     
712     /** Gets the postscript font name.
713      * @return the postscript font name
714      */

715     public String JavaDoc getPostscriptFontName() {
716         return FontName;
717     }
718     
719     /** Gets the full name of the font. If it is a True Type font
720      * each array element will have {Platform ID, Platform Encoding ID,
721      * Language ID, font name}. The interpretation of this values can be
722      * found in the Open Type specification, chapter 2, in the 'name' table.<br>
723      * For the other fonts the array has a single element with {"", "", "",
724      * font name}.
725      * @return the full name of the font
726      */

727     public String JavaDoc[][] getFullFontName() {
728         return new String JavaDoc[][]{{"", "", "", FullName}};
729     }
730     
731     /** Gets the family name of the font. If it is a True Type font
732      * each array element will have {Platform ID, Platform Encoding ID,
733      * Language ID, font name}. The interpretation of this values can be
734      * found in the Open Type specification, chapter 2, in the 'name' table.<br>
735      * For the other fonts the array has a single element with {"", "", "",
736      * font name}.
737      * @return the family name of the font
738      */

739     public String JavaDoc[][] getFamilyFontName() {
740         return new String JavaDoc[][]{{"", "", "", FamilyName}};
741     }
742     
743     /** Checks if the font has any kerning pairs.
744      * @return <CODE>true</CODE> if the font has any kerning pairs
745      */

746     public boolean hasKernPairs() {
747         return !KernPairs.isEmpty();
748     }
749     
750     /**
751      * Sets the font name that will appear in the pdf font dictionary.
752      * Use with care as it can easily make a font unreadable if not embedded.
753      * @param name the new font name
754      */

755     public void setPostscriptFontName(String JavaDoc name) {
756         FontName = name;
757     }
758     
759     /**
760      * Sets the kerning between two Unicode chars.
761      * @param char1 the first char
762      * @param char2 the second char
763      * @param kern the kerning to apply in normalized 1000 units
764      * @return <code>true</code> if the kerning was applied, <code>false</code> otherwise
765      */

766     public boolean setKerning(char char1, char char2, int kern) {
767         String JavaDoc first = GlyphList.unicodeToName((int)char1);
768         if (first == null)
769             return false;
770         String JavaDoc second = GlyphList.unicodeToName((int)char2);
771         if (second == null)
772             return false;
773         Object JavaDoc obj[] = (Object JavaDoc[])KernPairs.get(first);
774         if (obj == null) {
775             obj = new Object JavaDoc[]{second, new Integer JavaDoc(kern)};
776             KernPairs.put(first, obj);
777             return true;
778         }
779         for (int k = 0; k < obj.length; k += 2) {
780             if (second.equals(obj[k])) {
781                 obj[k + 1] = new Integer JavaDoc(kern);
782                 return true;
783             }
784         }
785         int size = obj.length;
786         Object JavaDoc obj2[] = new Object JavaDoc[size + 2];
787         System.arraycopy(obj, 0, obj2, 0, size);
788         obj2[size] = second;
789         obj2[size + 1] = new Integer JavaDoc(kern);
790         KernPairs.put(first, obj2);
791         return true;
792     }
793     
794     protected int[] getRawCharBBox(int c, String JavaDoc name) {
795         Object JavaDoc metrics[];
796         if (name == null) { // font specific
797
metrics = (Object JavaDoc[])CharMetrics.get(new Integer JavaDoc(c));
798         }
799         else {
800             if (name.equals(".notdef"))
801                 return null;
802             metrics = (Object JavaDoc[])CharMetrics.get(name);
803         }
804         if (metrics != null)
805             return ((int[])(metrics[3]));
806         return null;
807     }
808     
809 }
810
Popular Tags