1 /*
2 * $Id: Type1Font.java 3718 2009-02-23 16:56:55Z blowagie $
3 *
4 * Copyright 2001-2006 Paulo Soares
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * (the "License"); you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the License.
13 *
14 * The Original Code is 'iText, a free JAVA-PDF library'.
15 *
16 * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17 * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18 * All Rights Reserved.
19 * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20 * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21 *
22 * Contributor(s): all the names of the contributors are added in the source code
23 * where applicable.
24 *
25 * Alternatively, the contents of this file may be used under the terms of the
26 * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27 * provisions of LGPL are applicable instead of those above. If you wish to
28 * allow use of your version of this file only under the terms of the LGPL
29 * License and not to allow others to use your version of this file under
30 * the MPL, indicate your decision by deleting the provisions above and
31 * replace them with the notice and other provisions required by the LGPL.
32 * If you do not delete the provisions above, a recipient may use your version
33 * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34 *
35 * This library is free software; you can redistribute it and/or modify it
36 * under the terms of the MPL as stated above or under the terms of the GNU
37 * Library General Public License as published by the Free Software Foundation;
38 * either version 2 of the License, or any later version.
39 *
40 * This library is distributed in the hope that it will be useful, but WITHOUT
41 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42 * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43 * details.
44 *
45 * If you didn't download this code from the following link, you should check if
46 * you aren't using an obsolete version:
47 * http://www.lowagie.com/iText/
48 */
49
50 package com.lowagie.text.pdf;
51
52 import java.io.ByteArrayOutputStream;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.util.HashMap;
56 import java.util.StringTokenizer;
57
58 import com.lowagie.text.Document;
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 FontName;
76 /** The full name of the font.
77 */
78 private String FullName;
79 /** The family name of the font.
80 */
81 private String FamilyName;
82 /** The weight of the font: normal, bold, etc.
83 */
84 private String 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 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 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 CharMetrics = new HashMap();
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 KernPairs = new HashMap();
151 /** The file in use.
152 */
153 private String 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 * @since 2.1.5
171 */
172 Type1Font(String afmFile, String enc, boolean emb, byte ttfAfm[], byte pfb[], boolean forceRead)
173 throws DocumentException, IOException {
174 if (emb && ttfAfm != null && pfb == null)
175 throw new DocumentException("Two byte arrays are needed if the Type1 font is embedded.");
176 if (emb && ttfAfm != null)
177 this.pfb = pfb;
178 encoding = enc;
179 embedded = emb;
180 fileName = afmFile;
181 fontType = FONT_TYPE_T1;
182 RandomAccessFileOrArray rf = null;
183 InputStream is = null;
184 if (BuiltinFonts14.containsKey(afmFile)) {
185 embedded = false;
186 builtinFont = true;
187 byte buf[] = new byte[1024];
188 try {
189 if (resourceAnchor == null)
190 resourceAnchor = new FontsResourceAnchor();
191 is = getResourceStream(RESOURCE_PATH + afmFile + ".afm", resourceAnchor.getClass().getClassLoader());
192 if (is == null) {
193 String msg = afmFile + " not found as resource. (The *.afm files must exist as resources in the package com.lowagie.text.pdf.fonts)";
194 System.err.println(msg);
195 throw new DocumentException(msg);
196 }
197 ByteArrayOutputStream out = new ByteArrayOutputStream();
198 while (true) {
199 int size = is.read(buf);
200 if (size < 0)
201 break;
202 out.write(buf, 0, size);
203 }
204 buf = out.toByteArray();
205 }
206 finally {
207 if (is != null) {
208 try {
209 is.close();
210 }
211 catch (Exception e) {
212 // empty on purpose
213 }
214 }
215 }
216 try {
217 rf = new RandomAccessFileOrArray(buf);
218 process(rf);
219 }
220 finally {
221 if (rf != null) {
222 try {
223 rf.close();
224 }
225 catch (Exception e) {
226 // empty on purpose
227 }
228 }
229 }
230 }
231 else if (afmFile.toLowerCase().endsWith(".afm")) {
232 try {
233 if (ttfAfm == null)
234 rf = new RandomAccessFileOrArray(afmFile, forceRead, Document.plainRandomAccess);
235 else
236 rf = new RandomAccessFileOrArray(ttfAfm);
237 process(rf);
238 }
239 finally {
240 if (rf != null) {
241 try {
242 rf.close();
243 }
244 catch (Exception e) {
245 // empty on purpose
246 }
247 }
248 }
249 }
250 else if (afmFile.toLowerCase().endsWith(".pfm")) {
251 try {
252 ByteArrayOutputStream ba = new ByteArrayOutputStream();
253 if (ttfAfm == null)
254 rf = new RandomAccessFileOrArray(afmFile, forceRead, Document.plainRandomAccess);
255 else
256 rf = new RandomAccessFileOrArray(ttfAfm);
257 Pfm2afm.convert(rf, ba);
258 rf.close();
259 rf = new RandomAccessFileOrArray(ba.toByteArray());
260 process(rf);
261 }
262 finally {
263 if (rf != null) {
264 try {
265 rf.close();
266 }
267 catch (Exception e) {
268 // empty on purpose
269 }
270 }
271 }
272 }
273 else
274 throw new DocumentException(afmFile + " is not an AFM or PFM font file.");
275
276 EncodingScheme = EncodingScheme.trim();
277 if (EncodingScheme.equals("AdobeStandardEncoding") || EncodingScheme.equals("StandardEncoding")) {
278 fontSpecific = false;
279 }
280 if (!encoding.startsWith("#"))
281 PdfEncodings.convertToBytes(" ", enc); // check if the encoding exists
282 createEncoding();
283 }
284
285 /** Gets the width from the font according to the <CODE>name</CODE> or,
286 * if the <CODE>name</CODE> is null, meaning it is a symbolic font,
287 * the char <CODE>c</CODE>.
288 * @param c the char if the font is symbolic
289 * @param name the glyph name
290 * @return the width of the char
291 */
292 int getRawWidth(int c, String name) {
293 Object metrics[];
294 if (name == null) { // font specific
295 metrics = (Object[])CharMetrics.get(new Integer(c));
296 }
297 else {
298 if (name.equals(".notdef"))
299 return 0;
300 metrics = (Object[])CharMetrics.get(name);
301 }
302 if (metrics != null)
303 return ((Integer)(metrics[1])).intValue();
304 return 0;
305 }
306
307 /** Gets the kerning between two Unicode characters. The characters
308 * are converted to names and this names are used to find the kerning
309 * pairs in the <CODE>HashMap</CODE> <CODE>KernPairs</CODE>.
310 * @param char1 the first char
311 * @param char2 the second char
312 * @return the kerning to be applied
313 */
314 public int getKerning(int char1, int char2)
315 {
316 String first = GlyphList.unicodeToName(char1);
317 if (first == null)
318 return 0;
319 String second = GlyphList.unicodeToName(char2);
320 if (second == null)
321 return 0;
322 Object obj[] = (Object[])KernPairs.get(first);
323 if (obj == null)
324 return 0;
325 for (int k = 0; k < obj.length; k += 2) {
326 if (second.equals(obj[k]))
327 return ((Integer)obj[k + 1]).intValue();
328 }
329 return 0;
330 }
331
332
333 /** Reads the font metrics
334 * @param rf the AFM file
335 * @throws DocumentException the AFM file is invalid
336 * @throws IOException the AFM file could not be read
337 */
338 public void process(RandomAccessFileOrArray rf) throws DocumentException, IOException
339 {
340 String line;
341 boolean isMetrics = false;
342 while ((line = rf.readLine()) != null)
343 {
344 StringTokenizer tok = new StringTokenizer(line, " ,\n\r\t\f");
345 if (!tok.hasMoreTokens())
346 continue;
347 String ident = tok.nextToken();
348 if (ident.equals("FontName"))
349 FontName = tok.nextToken("\u00ff").substring(1);
350 else if (ident.equals("FullName"))
351 FullName = tok.nextToken("\u00ff").substring(1);
352 else if (ident.equals("FamilyName"))
353 FamilyName = tok.nextToken("\u00ff").substring(1);
354 else if (ident.equals("Weight"))
355 Weight = tok.nextToken("\u00ff").substring(1);
356 else if (ident.equals("ItalicAngle"))
357 ItalicAngle = Float.parseFloat(tok.nextToken());
358 else if (ident.equals("IsFixedPitch"))
359 IsFixedPitch = tok.nextToken().equals("true");
360 else if (ident.equals("CharacterSet"))
361 CharacterSet = tok.nextToken("\u00ff").substring(1);
362 else if (ident.equals("FontBBox"))
363 {
364 llx = (int)Float.parseFloat(tok.nextToken());
365 lly = (int)Float.parseFloat(tok.nextToken());
366 urx = (int)Float.parseFloat(tok.nextToken());
367 ury = (int)Float.parseFloat(tok.nextToken());
368 }
369 else if (ident.equals("UnderlinePosition"))
370 UnderlinePosition = (int)Float.parseFloat(tok.nextToken());
371 else if (ident.equals("UnderlineThickness"))
372 UnderlineThickness = (int)Float.parseFloat(tok.nextToken());
373 else if (ident.equals("EncodingScheme"))
374 EncodingScheme = tok.nextToken("\u00ff").substring(1);
375 else if (ident.equals("CapHeight"))
376 CapHeight = (int)Float.parseFloat(tok.nextToken());
377 else if (ident.equals("XHeight"))
378 XHeight = (int)Float.parseFloat(tok.nextToken());
379 else if (ident.equals("Ascender"))
380 Ascender = (int)Float.parseFloat(tok.nextToken());
381 else if (ident.equals("Descender"))
382 Descender = (int)Float.parseFloat(tok.nextToken());
383 else if (ident.equals("StdHW"))
384 StdHW = (int)Float.parseFloat(tok.nextToken());
385 else if (ident.equals("StdVW"))
386 StdVW = (int)Float.parseFloat(tok.nextToken());
387 else if (ident.equals("StartCharMetrics"))
388 {
389 isMetrics = true;
390 break;
391 }
392 }
393 if (!isMetrics)
394 throw new DocumentException("Missing StartCharMetrics in " + fileName);
395 while ((line = rf.readLine()) != null)
396 {
397 StringTokenizer tok = new StringTokenizer(line);
398 if (!tok.hasMoreTokens())
399 continue;
400 String ident = tok.nextToken();
401 if (ident.equals("EndCharMetrics"))
402 {
403 isMetrics = false;
404 break;
405 }
406 Integer C = new Integer(-1);
407 Integer WX = new Integer(250);
408 String N = "";
409 int B[] = null;
410
411 tok = new StringTokenizer(line, ";");
412 while (tok.hasMoreTokens())
413 {
414 StringTokenizer tokc = new StringTokenizer(tok.nextToken());
415 if (!tokc.hasMoreTokens())
416 continue;
417 ident = tokc.nextToken();
418 if (ident.equals("C"))
419 C = Integer.valueOf(tokc.nextToken());
420 else if (ident.equals("WX"))
421 WX = new Integer((int)Float.parseFloat(tokc.nextToken()));
422 else if (ident.equals("N"))
423 N = tokc.nextToken();
424 else if (ident.equals("B")) {
425 B = new int[]{Integer.parseInt(tokc.nextToken()),
426 Integer.parseInt(tokc.nextToken()),
427 Integer.parseInt(tokc.nextToken()),
428 Integer.parseInt(tokc.nextToken())};
429 }
430 }
431 Object metrics[] = new Object[]{C, WX, N, B};
432 if (C.intValue() >= 0)
433 CharMetrics.put(C, metrics);
434 CharMetrics.put(N, metrics);
435 }
436 if (isMetrics)
437 throw new DocumentException("Missing EndCharMetrics in " + fileName);
438 if (!CharMetrics.containsKey("nonbreakingspace")) {
439 Object[] space = (Object[])CharMetrics.get("space");
440 if (space != null)
441 CharMetrics.put("nonbreakingspace", space);
442 }
443 while ((line = rf.readLine()) != null)
444 {
445 StringTokenizer tok = new StringTokenizer(line);
446 if (!tok.hasMoreTokens())
447 continue;
448 String ident = tok.nextToken();
449 if (ident.equals("EndFontMetrics"))
450 return;
451 if (ident.equals("StartKernPairs"))
452 {
453 isMetrics = true;
454 break;
455 }
456 }
457 if (!isMetrics)
458 throw new DocumentException("Missing EndFontMetrics in " + fileName);
459 while ((line = rf.readLine()) != null)
460 {
461 StringTokenizer tok = new StringTokenizer(line);
462 if (!tok.hasMoreTokens())
463 continue;
464 String ident = tok.nextToken();
465 if (ident.equals("KPX"))
466 {
467 String first = tok.nextToken();
468 String second = tok.nextToken();
469 Integer width = new Integer((int)Float.parseFloat(tok.nextToken()));
470 Object relates[] = (Object[])KernPairs.get(first);
471 if (relates == null)
472 KernPairs.put(first, new Object[]{second, width});
473 else
474 {
475 int n = relates.length;
476 Object relates2[] = new Object[n + 2];
477 System.arraycopy(relates, 0, relates2, 0, n);
478 relates2[n] = second;
479 relates2[n + 1] = width;
480 KernPairs.put(first, relates2);
481 }
482 }
483 else if (ident.equals("EndKernPairs"))
484 {
485 isMetrics = false;
486 break;
487 }
488 }
489 if (isMetrics)
490 throw new DocumentException("Missing EndKernPairs in " + fileName);
491 rf.close();
492 }
493
494 /** If the embedded flag is <CODE>false</CODE> or if the font is
495 * one of the 14 built in types, it returns <CODE>null</CODE>,
496 * otherwise the font is read and output in a PdfStream object.
497 * @return the PdfStream containing the font or <CODE>null</CODE>
498 * @throws DocumentException if there is an error reading the font
499 * @since 2.1.3
500 */
501 public PdfStream getFullFontStream() throws DocumentException
502 {
503 if (builtinFont || !embedded)
504 return null;
505 RandomAccessFileOrArray rf = null;
506 try {
507 String filePfb = fileName.substring(0, fileName.length() - 3) + "pfb";
508 if (pfb == null)
509 rf = new RandomAccessFileOrArray(filePfb, true, Document.plainRandomAccess);
510 else
511 rf = new RandomAccessFileOrArray(pfb);
512 int fileLength = rf.length();
513 byte st[] = new byte[fileLength - 18];
514 int lengths[] = new int[3];
515 int bytePtr = 0;
516 for (int k = 0; k < 3; ++k) {
517 if (rf.read() != 0x80)
518 throw new DocumentException("Start marker missing in " + filePfb);
519 if (rf.read() != PFB_TYPES[k])
520 throw new DocumentException("Incorrect segment type in " + filePfb);
521 int size = rf.read();
522 size += rf.read() << 8;
523 size += rf.read() << 16;
524 size += rf.read() << 24;
525 lengths[k] = size;
526 while (size != 0) {
527 int got = rf.read(st, bytePtr, size);
528 if (got < 0)
529 throw new DocumentException("Premature end in " + filePfb);
530 bytePtr += got;
531 size -= got;
532 }
533 }
534 return new StreamFont(st, lengths, compressionLevel);
535 }
536 catch (Exception e) {
537 throw new DocumentException(e);
538 }
539 finally {
540 if (rf != null) {
541 try {
542 rf.close();
543 }
544 catch (Exception e) {
545 // empty on purpose
546 }
547 }
548 }
549 }
550
551 /** Generates the font descriptor for this font or <CODE>null</CODE> if it is
552 * one of the 14 built in fonts.
553 * @param fontStream the indirect reference to a PdfStream containing the font or <CODE>null</CODE>
554 * @return the PdfDictionary containing the font descriptor or <CODE>null</CODE>
555 */
556 private PdfDictionary getFontDescriptor(PdfIndirectReference fontStream)
557 {
558 if (builtinFont)
559 return null;
560 PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR);
561 dic.put(PdfName.ASCENT, new PdfNumber(Ascender));
562 dic.put(PdfName.CAPHEIGHT, new PdfNumber(CapHeight));
563 dic.put(PdfName.DESCENT, new PdfNumber(Descender));
564 dic.put(PdfName.FONTBBOX, new PdfRectangle(llx, lly, urx, ury));
565 dic.put(PdfName.FONTNAME, new PdfName(FontName));
566 dic.put(PdfName.ITALICANGLE, new PdfNumber(ItalicAngle));
567 dic.put(PdfName.STEMV, new PdfNumber(StdVW));
568 if (fontStream != null)
569 dic.put(PdfName.FONTFILE, fontStream);
570 int flags = 0;
571 if (IsFixedPitch)
572 flags |= 1;
573 flags |= fontSpecific ? 4 : 32;
574 if (ItalicAngle < 0)
575 flags |= 64;
576 if (FontName.indexOf("Caps") >= 0 || FontName.endsWith("SC"))
577 flags |= 131072;
578 if (Weight.equals("Bold"))
579 flags |= 262144;
580 dic.put(PdfName.FLAGS, new PdfNumber(flags));
581
582 return dic;
583 }
584
585 /** Generates the font dictionary for this font.
586 * @return the PdfDictionary containing the font dictionary
587 * @param firstChar the first valid character
588 * @param lastChar the last valid character
589 * @param shortTag a 256 bytes long <CODE>byte</CODE> array where each unused byte is represented by 0
590 * @param fontDescriptor the indirect reference to a PdfDictionary containing the font descriptor or <CODE>null</CODE>
591 */
592 private PdfDictionary getFontBaseType(PdfIndirectReference fontDescriptor, int firstChar, int lastChar, byte shortTag[])
593 {
594 PdfDictionary dic = new PdfDictionary(PdfName.FONT);
595 dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
596 dic.put(PdfName.BASEFONT, new PdfName(FontName));
597 boolean stdEncoding = encoding.equals("Cp1252") || encoding.equals("MacRoman");
598 if (!fontSpecific || specialMap != null) {
599 for (int k = firstChar; k <= lastChar; ++k) {
600 if (!differences[k].equals(notdef)) {
601 firstChar = k;
602 break;
603 }
604 }
605 if (stdEncoding)
606 dic.put(PdfName.ENCODING, encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING : PdfName.MAC_ROMAN_ENCODING);
607 else {
608 PdfDictionary enc = new PdfDictionary(PdfName.ENCODING);
609 PdfArray dif = new PdfArray();
610 boolean gap = true;
611 for (int k = firstChar; k <= lastChar; ++k) {
612 if (shortTag[k] != 0) {
613 if (gap) {
614 dif.add(new PdfNumber(k));
615 gap = false;
616 }
617 dif.add(new PdfName(differences[k]));
618 }
619 else
620 gap = true;
621 }
622 enc.put(PdfName.DIFFERENCES, dif);
623 dic.put(PdfName.ENCODING, enc);
624 }
625 }
626 if (specialMap != null || forceWidthsOutput || !(builtinFont && (fontSpecific || stdEncoding))) {
627 dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar));
628 dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar));
629 PdfArray wd = new PdfArray();
630 for (int k = firstChar; k <= lastChar; ++k) {
631 if (shortTag[k] == 0)
632 wd.add(new PdfNumber(0));
633 else
634 wd.add(new PdfNumber(widths[k]));
635 }
636 dic.put(PdfName.WIDTHS, wd);
637 }
638 if (!builtinFont && fontDescriptor != null)
639 dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor);
640 return dic;
641 }
642
643 /** Outputs to the writer the font dictionaries and streams.
644 * @param writer the writer for this document
645 * @param ref the font indirect reference
646 * @param params several parameters that depend on the font type
647 * @throws IOException on error
648 * @throws DocumentException error in generating the object
649 */
650 void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException {
651 int firstChar = ((Integer)params[0]).intValue();
652 int lastChar = ((Integer)params[1]).intValue();
653 byte shortTag[] = (byte[])params[2];
654 boolean subsetp = ((Boolean)params[3]).booleanValue() && subset;
655 if (!subsetp) {
656 firstChar = 0;
657 lastChar = shortTag.length - 1;
658 for (int k = 0; k < shortTag.length; ++k)
659 shortTag[k] = 1;
660 }
661 PdfIndirectReference ind_font = null;
662 PdfObject pobj = null;
663 PdfIndirectObject obj = null;
664 pobj = getFullFontStream();
665 if (pobj != null){
666 obj = writer.addToBody(pobj);
667 ind_font = obj.getIndirectReference();
668 }
669 pobj = getFontDescriptor(ind_font);
670 if (pobj != null){
671 obj = writer.addToBody(pobj);
672 ind_font = obj.getIndirectReference();
673 }
674 pobj = getFontBaseType(ind_font, firstChar, lastChar, shortTag);
675 writer.addToBody(pobj, ref);
676 }
677
678 /** Gets the font parameter identified by <CODE>key</CODE>. Valid values
679 * for <CODE>key</CODE> are <CODE>ASCENT</CODE>, <CODE>CAPHEIGHT</CODE>, <CODE>DESCENT</CODE>,
680 * <CODE>ITALICANGLE</CODE>, <CODE>BBOXLLX</CODE>, <CODE>BBOXLLY</CODE>, <CODE>BBOXURX</CODE>
681 * and <CODE>BBOXURY</CODE>.
682 * @param key the parameter to be extracted
683 * @param fontSize the font size in points
684 * @return the parameter in points
685 */
686 public float getFontDescriptor(int key, float fontSize) {
687 switch (key) {
688 case AWT_ASCENT:
689 case ASCENT:
690 return Ascender * fontSize / 1000;
691 case CAPHEIGHT:
692 return CapHeight * fontSize / 1000;
693 case AWT_DESCENT:
694 case DESCENT:
695 return Descender * fontSize / 1000;
696 case ITALICANGLE:
697 return ItalicAngle;
698 case BBOXLLX:
699 return llx * fontSize / 1000;
700 case BBOXLLY:
701 return lly * fontSize / 1000;
702 case BBOXURX:
703 return urx * fontSize / 1000;
704 case BBOXURY:
705 return ury * fontSize / 1000;
706 case AWT_LEADING:
707 return 0;
708 case AWT_MAXADVANCE:
709 return (urx - llx) * fontSize / 1000;
710 case UNDERLINE_POSITION:
711 return UnderlinePosition * fontSize / 1000;
712 case UNDERLINE_THICKNESS:
713 return UnderlineThickness * fontSize / 1000;
714 }
715 return 0;
716 }
717
718 /** Gets the postscript font name.
719 * @return the postscript font name
720 */
721 public String getPostscriptFontName() {
722 return FontName;
723 }
724
725 /** Gets the full name of the font. If it is a True Type font
726 * each array element will have {Platform ID, Platform Encoding ID,
727 * Language ID, font name}. The interpretation of this values can be
728 * found in the Open Type specification, chapter 2, in the 'name' table.<br>
729 * For the other fonts the array has a single element with {"", "", "",
730 * font name}.
731 * @return the full name of the font
732 */
733 public String[][] getFullFontName() {
734 return new String[][]{{"", "", "", FullName}};
735 }
736
737 /** Gets all the entries of the names-table. If it is a True Type font
738 * each array element will have {Name ID, Platform ID, Platform Encoding ID,
739 * Language ID, font name}. The interpretation of this values can be
740 * found in the Open Type specification, chapter 2, in the 'name' table.<br>
741 * For the other fonts the array has a single element with {"4", "", "", "",
742 * font name}.
743 * @return the full name of the font
744 */
745 public String[][] getAllNameEntries() {
746 return new String[][]{{"4", "", "", "", FullName}};
747 }
748
749 /** Gets the family name of the font. If it is a True Type font
750 * each array element will have {Platform ID, Platform Encoding ID,
751 * Language ID, font name}. The interpretation of this values can be
752 * found in the Open Type specification, chapter 2, in the 'name' table.<br>
753 * For the other fonts the array has a single element with {"", "", "",
754 * font name}.
755 * @return the family name of the font
756 */
757 public String[][] getFamilyFontName() {
758 return new String[][]{{"", "", "", FamilyName}};
759 }
760
761 /** Checks if the font has any kerning pairs.
762 * @return <CODE>true</CODE> if the font has any kerning pairs
763 */
764 public boolean hasKernPairs() {
765 return !KernPairs.isEmpty();
766 }
767
768 /**
769 * Sets the font name that will appear in the pdf font dictionary.
770 * Use with care as it can easily make a font unreadable if not embedded.
771 * @param name the new font name
772 */
773 public void setPostscriptFontName(String name) {
774 FontName = name;
775 }
776
777 /**
778 * Sets the kerning between two Unicode chars.
779 * @param char1 the first char
780 * @param char2 the second char
781 * @param kern the kerning to apply in normalized 1000 units
782 * @return <code>true</code> if the kerning was applied, <code>false</code> otherwise
783 */
784 public boolean setKerning(int char1, int char2, int kern) {
785 String first = GlyphList.unicodeToName(char1);
786 if (first == null)
787 return false;
788 String second = GlyphList.unicodeToName(char2);
789 if (second == null)
790 return false;
791 Object obj[] = (Object[])KernPairs.get(first);
792 if (obj == null) {
793 obj = new Object[]{second, new Integer(kern)};
794 KernPairs.put(first, obj);
795 return true;
796 }
797 for (int k = 0; k < obj.length; k += 2) {
798 if (second.equals(obj[k])) {
799 obj[k + 1] = new Integer(kern);
800 return true;
801 }
802 }
803 int size = obj.length;
804 Object obj2[] = new Object[size + 2];
805 System.arraycopy(obj, 0, obj2, 0, size);
806 obj2[size] = second;
807 obj2[size + 1] = new Integer(kern);
808 KernPairs.put(first, obj2);
809 return true;
810 }
811
812 protected int[] getRawCharBBox(int c, String name) {
813 Object metrics[];
814 if (name == null) { // font specific
815 metrics = (Object[])CharMetrics.get(new Integer(c));
816 }
817 else {
818 if (name.equals(".notdef"))
819 return null;
820 metrics = (Object[])CharMetrics.get(name);
821 }
822 if (metrics != null)
823 return ((int[])(metrics[3]));
824 return null;
825 }
826
827 }
828