001: /*
002: * $Id: Type1Font.java 2509 2006-12-21 16:26:19Z psoares33 $
003: * $Name$
004: *
005: * Copyright 2001-2006 Paulo Soares
006: *
007: * The contents of this file are subject to the Mozilla Public License Version 1.1
008: * (the "License"); you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the License.
014: *
015: * The Original Code is 'iText, a free JAVA-PDF library'.
016: *
017: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
018: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
019: * All Rights Reserved.
020: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
021: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
022: *
023: * Contributor(s): all the names of the contributors are added in the source code
024: * where applicable.
025: *
026: * Alternatively, the contents of this file may be used under the terms of the
027: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
028: * provisions of LGPL are applicable instead of those above. If you wish to
029: * allow use of your version of this file only under the terms of the LGPL
030: * License and not to allow others to use your version of this file under
031: * the MPL, indicate your decision by deleting the provisions above and
032: * replace them with the notice and other provisions required by the LGPL.
033: * If you do not delete the provisions above, a recipient may use your version
034: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
035: *
036: * This library is free software; you can redistribute it and/or modify it
037: * under the terms of the MPL as stated above or under the terms of the GNU
038: * Library General Public License as published by the Free Software Foundation;
039: * either version 2 of the License, or any later version.
040: *
041: * This library is distributed in the hope that it will be useful, but WITHOUT
042: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
043: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
044: * details.
045: *
046: * If you didn't download this code from the following link, you should check if
047: * you aren't using an obsolete version:
048: * http://www.lowagie.com/iText/
049: */
050:
051: package com.lowagie.text.pdf;
052:
053: import java.io.ByteArrayOutputStream;
054: import java.io.IOException;
055: import java.io.InputStream;
056: import java.util.HashMap;
057: import java.util.StringTokenizer;
058:
059: import com.lowagie.text.DocumentException;
060: import com.lowagie.text.pdf.fonts.FontsResourceAnchor;
061:
062: /** Reads a Type1 font
063: *
064: * @author Paulo Soares (psoares@consiste.pt)
065: */
066: class Type1Font extends BaseFont {
067: private static FontsResourceAnchor resourceAnchor;
068:
069: /** The PFB file if the input was made with a <CODE>byte</CODE> array.
070: */
071: protected byte pfb[];
072: /** The Postscript font name.
073: */
074: private String FontName;
075: /** The full name of the font.
076: */
077: private String FullName;
078: /** The family name of the font.
079: */
080: private String FamilyName;
081: /** The weight of the font: normal, bold, etc.
082: */
083: private String Weight = "";
084: /** The italic angle of the font, usually 0.0 or negative.
085: */
086: private float ItalicAngle = 0.0f;
087: /** <CODE>true</CODE> if all the characters have the same
088: * width.
089: */
090: private boolean IsFixedPitch = false;
091: /** The character set of the font.
092: */
093: private String CharacterSet;
094: /** The llx of the FontBox.
095: */
096: private int llx = -50;
097: /** The lly of the FontBox.
098: */
099: private int lly = -200;
100: /** The lurx of the FontBox.
101: */
102: private int urx = 1000;
103: /** The ury of the FontBox.
104: */
105: private int ury = 900;
106: /** The underline position.
107: */
108: private int UnderlinePosition = -100;
109: /** The underline thickness.
110: */
111: private int UnderlineThickness = 50;
112: /** The font's encoding name. This encoding is 'StandardEncoding' or
113: * 'AdobeStandardEncoding' for a font that can be totally encoded
114: * according to the characters names. For all other names the
115: * font is treated as symbolic.
116: */
117: private String EncodingScheme = "FontSpecific";
118: /** A variable.
119: */
120: private int CapHeight = 700;
121: /** A variable.
122: */
123: private int XHeight = 480;
124: /** A variable.
125: */
126: private int Ascender = 800;
127: /** A variable.
128: */
129: private int Descender = -200;
130: /** A variable.
131: */
132: private int StdHW;
133: /** A variable.
134: */
135: private int StdVW = 80;
136:
137: /** Represents the section CharMetrics in the AFM file. Each
138: * value of this array contains a <CODE>Object[4]</CODE> with an
139: * Integer, Integer, String and int[]. This is the code, width, name and char bbox.
140: * The key is the name of the char and also an Integer with the char number.
141: */
142: private HashMap CharMetrics = new HashMap();
143: /** Represents the section KernPairs in the AFM file. The key is
144: * the name of the first character and the value is a <CODE>Object[]</CODE>
145: * with 2 elements for each kern pair. Position 0 is the name of
146: * the second character and position 1 is the kerning distance. This is
147: * repeated for all the pairs.
148: */
149: private HashMap KernPairs = new HashMap();
150: /** The file in use.
151: */
152: private String fileName;
153: /** <CODE>true</CODE> if this font is one of the 14 built in fonts.
154: */
155: private boolean builtinFont = false;
156: /** Types of records in a PFB file. ASCII is 1 and BINARY is 2.
157: * They have to appear in the PFB file in this sequence.
158: */
159: private static final int PFB_TYPES[] = { 1, 2, 1 };
160:
161: /** Creates a new Type1 font.
162: * @param ttfAfm the AFM file if the input is made with a <CODE>byte</CODE> array
163: * @param pfb the PFB file if the input is made with a <CODE>byte</CODE> array
164: * @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'
165: * @param enc the encoding to be applied to this font
166: * @param emb true if the font is to be embedded in the PDF
167: * @throws DocumentException the AFM file is invalid
168: * @throws IOException the AFM file could not be read
169: */
170: Type1Font(String afmFile, String enc, boolean emb, byte ttfAfm[],
171: byte pfb[]) throws DocumentException, IOException {
172: if (emb && ttfAfm != null && pfb == null)
173: throw new DocumentException(
174: "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 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(
191: RESOURCE_PATH + afmFile + ".afm",
192: resourceAnchor.getClass().getClassLoader());
193: if (is == null) {
194: String msg = afmFile
195: + " not found as resource. (The *.afm files must exist as resources in the package com.lowagie.text.pdf.fonts)";
196: System.err.println(msg);
197: throw new DocumentException(msg);
198: }
199: ByteArrayOutputStream out = new ByteArrayOutputStream();
200: while (true) {
201: int size = is.read(buf);
202: if (size < 0)
203: break;
204: out.write(buf, 0, size);
205: }
206: buf = out.toByteArray();
207: } finally {
208: if (is != null) {
209: try {
210: is.close();
211: } catch (Exception e) {
212: // empty on purpose
213: }
214: }
215: }
216: try {
217: rf = new RandomAccessFileOrArray(buf);
218: process(rf);
219: } finally {
220: if (rf != null) {
221: try {
222: rf.close();
223: } catch (Exception e) {
224: // empty on purpose
225: }
226: }
227: }
228: } else if (afmFile.toLowerCase().endsWith(".afm")) {
229: try {
230: if (ttfAfm == null)
231: rf = new RandomAccessFileOrArray(afmFile);
232: else
233: rf = new RandomAccessFileOrArray(ttfAfm);
234: process(rf);
235: } finally {
236: if (rf != null) {
237: try {
238: rf.close();
239: } catch (Exception e) {
240: // empty on purpose
241: }
242: }
243: }
244: } else if (afmFile.toLowerCase().endsWith(".pfm")) {
245: try {
246: ByteArrayOutputStream ba = new ByteArrayOutputStream();
247: if (ttfAfm == null)
248: rf = new RandomAccessFileOrArray(afmFile);
249: else
250: rf = new RandomAccessFileOrArray(ttfAfm);
251: Pfm2afm.convert(rf, ba);
252: rf.close();
253: rf = new RandomAccessFileOrArray(ba.toByteArray());
254: process(rf);
255: } finally {
256: if (rf != null) {
257: try {
258: rf.close();
259: } catch (Exception e) {
260: // empty on purpose
261: }
262: }
263: }
264: } else
265: throw new DocumentException(afmFile
266: + " is not an AFM or PFM font file.");
267:
268: EncodingScheme = EncodingScheme.trim();
269: if (EncodingScheme.equals("AdobeStandardEncoding")
270: || EncodingScheme.equals("StandardEncoding")) {
271: fontSpecific = false;
272: }
273: if (!encoding.startsWith("#"))
274: PdfEncodings.convertToBytes(" ", enc); // check if the encoding exists
275: createEncoding();
276: }
277:
278: /** Gets the width from the font according to the <CODE>name</CODE> or,
279: * if the <CODE>name</CODE> is null, meaning it is a symbolic font,
280: * the char <CODE>c</CODE>.
281: * @param c the char if the font is symbolic
282: * @param name the glyph name
283: * @return the width of the char
284: */
285: int getRawWidth(int c, String name) {
286: Object metrics[];
287: if (name == null) { // font specific
288: metrics = (Object[]) CharMetrics.get(new Integer(c));
289: } else {
290: if (name.equals(".notdef"))
291: return 0;
292: metrics = (Object[]) CharMetrics.get(name);
293: }
294: if (metrics != null)
295: return ((Integer) (metrics[1])).intValue();
296: return 0;
297: }
298:
299: /** Gets the kerning between two Unicode characters. The characters
300: * are converted to names and this names are used to find the kerning
301: * pairs in the <CODE>HashMap</CODE> <CODE>KernPairs</CODE>.
302: * @param char1 the first char
303: * @param char2 the second char
304: * @return the kerning to be applied
305: */
306: public int getKerning(char char1, char char2) {
307: String first = GlyphList.unicodeToName((int) char1);
308: if (first == null)
309: return 0;
310: String second = GlyphList.unicodeToName((int) char2);
311: if (second == null)
312: return 0;
313: Object obj[] = (Object[]) KernPairs.get(first);
314: if (obj == null)
315: return 0;
316: for (int k = 0; k < obj.length; k += 2) {
317: if (second.equals(obj[k]))
318: return ((Integer) obj[k + 1]).intValue();
319: }
320: return 0;
321: }
322:
323: /** Reads the font metrics
324: * @param rf the AFM file
325: * @throws DocumentException the AFM file is invalid
326: * @throws IOException the AFM file could not be read
327: */
328: public void process(RandomAccessFileOrArray rf)
329: throws DocumentException, IOException {
330: String line;
331: boolean isMetrics = false;
332: while ((line = rf.readLine()) != null) {
333: StringTokenizer tok = new StringTokenizer(line,
334: " ,\n\r\t\f");
335: if (!tok.hasMoreTokens())
336: continue;
337: String ident = tok.nextToken();
338: if (ident.equals("FontName"))
339: FontName = tok.nextToken("\u00ff").substring(1);
340: else if (ident.equals("FullName"))
341: FullName = tok.nextToken("\u00ff").substring(1);
342: else if (ident.equals("FamilyName"))
343: FamilyName = tok.nextToken("\u00ff").substring(1);
344: else if (ident.equals("Weight"))
345: Weight = tok.nextToken("\u00ff").substring(1);
346: else if (ident.equals("ItalicAngle"))
347: ItalicAngle = Float.parseFloat(tok.nextToken());
348: else if (ident.equals("IsFixedPitch"))
349: IsFixedPitch = tok.nextToken().equals("true");
350: else if (ident.equals("CharacterSet"))
351: CharacterSet = tok.nextToken("\u00ff").substring(1);
352: else if (ident.equals("FontBBox")) {
353: llx = (int) Float.parseFloat(tok.nextToken());
354: lly = (int) Float.parseFloat(tok.nextToken());
355: urx = (int) Float.parseFloat(tok.nextToken());
356: ury = (int) Float.parseFloat(tok.nextToken());
357: } else if (ident.equals("UnderlinePosition"))
358: UnderlinePosition = (int) Float.parseFloat(tok
359: .nextToken());
360: else if (ident.equals("UnderlineThickness"))
361: UnderlineThickness = (int) Float.parseFloat(tok
362: .nextToken());
363: else if (ident.equals("EncodingScheme"))
364: EncodingScheme = tok.nextToken("\u00ff").substring(1);
365: else if (ident.equals("CapHeight"))
366: CapHeight = (int) Float.parseFloat(tok.nextToken());
367: else if (ident.equals("XHeight"))
368: XHeight = (int) Float.parseFloat(tok.nextToken());
369: else if (ident.equals("Ascender"))
370: Ascender = (int) Float.parseFloat(tok.nextToken());
371: else if (ident.equals("Descender"))
372: Descender = (int) Float.parseFloat(tok.nextToken());
373: else if (ident.equals("StdHW"))
374: StdHW = (int) Float.parseFloat(tok.nextToken());
375: else if (ident.equals("StdVW"))
376: StdVW = (int) Float.parseFloat(tok.nextToken());
377: else if (ident.equals("StartCharMetrics")) {
378: isMetrics = true;
379: break;
380: }
381: }
382: if (!isMetrics)
383: throw new DocumentException("Missing StartCharMetrics in "
384: + fileName);
385: while ((line = rf.readLine()) != null) {
386: StringTokenizer tok = new StringTokenizer(line);
387: if (!tok.hasMoreTokens())
388: continue;
389: String ident = tok.nextToken();
390: if (ident.equals("EndCharMetrics")) {
391: isMetrics = false;
392: break;
393: }
394: Integer C = new Integer(-1);
395: Integer WX = new Integer(250);
396: String N = "";
397: int B[] = null;
398:
399: tok = new StringTokenizer(line, ";");
400: while (tok.hasMoreTokens()) {
401: StringTokenizer tokc = new StringTokenizer(tok
402: .nextToken());
403: if (!tokc.hasMoreTokens())
404: continue;
405: ident = tokc.nextToken();
406: if (ident.equals("C"))
407: C = Integer.valueOf(tokc.nextToken());
408: else if (ident.equals("WX"))
409: WX = new Integer((int) Float.parseFloat(tokc
410: .nextToken()));
411: else if (ident.equals("N"))
412: N = tokc.nextToken();
413: else if (ident.equals("B")) {
414: B = new int[] { Integer.parseInt(tokc.nextToken()),
415: Integer.parseInt(tokc.nextToken()),
416: Integer.parseInt(tokc.nextToken()),
417: Integer.parseInt(tokc.nextToken()) };
418: }
419: }
420: Object metrics[] = new Object[] { C, WX, N, B };
421: if (C.intValue() >= 0)
422: CharMetrics.put(C, metrics);
423: CharMetrics.put(N, metrics);
424: }
425: if (isMetrics)
426: throw new DocumentException("Missing EndCharMetrics in "
427: + fileName);
428: if (!CharMetrics.containsKey("nonbreakingspace")) {
429: Object[] space = (Object[]) CharMetrics.get("space");
430: if (space != null)
431: CharMetrics.put("nonbreakingspace", space);
432: }
433: while ((line = rf.readLine()) != null) {
434: StringTokenizer tok = new StringTokenizer(line);
435: if (!tok.hasMoreTokens())
436: continue;
437: String ident = tok.nextToken();
438: if (ident.equals("EndFontMetrics"))
439: return;
440: if (ident.equals("StartKernPairs")) {
441: isMetrics = true;
442: break;
443: }
444: }
445: if (!isMetrics)
446: throw new DocumentException("Missing EndFontMetrics in "
447: + fileName);
448: while ((line = rf.readLine()) != null) {
449: StringTokenizer tok = new StringTokenizer(line);
450: if (!tok.hasMoreTokens())
451: continue;
452: String ident = tok.nextToken();
453: if (ident.equals("KPX")) {
454: String first = tok.nextToken();
455: String second = tok.nextToken();
456: Integer width = new Integer((int) Float.parseFloat(tok
457: .nextToken()));
458: Object relates[] = (Object[]) KernPairs.get(first);
459: if (relates == null)
460: KernPairs
461: .put(first, new Object[] { second, width });
462: else {
463: int n = relates.length;
464: Object relates2[] = new Object[n + 2];
465: System.arraycopy(relates, 0, relates2, 0, n);
466: relates2[n] = second;
467: relates2[n + 1] = width;
468: KernPairs.put(first, relates2);
469: }
470: } else if (ident.equals("EndKernPairs")) {
471: isMetrics = false;
472: break;
473: }
474: }
475: if (isMetrics)
476: throw new DocumentException("Missing EndKernPairs in "
477: + fileName);
478: rf.close();
479: }
480:
481: /** If the embedded flag is <CODE>false</CODE> or if the font is
482: * one of the 14 built in types, it returns <CODE>null</CODE>,
483: * otherwise the font is read and output in a PdfStream object.
484: * @return the PdfStream containing the font or <CODE>null</CODE>
485: * @throws DocumentException if there is an error reading the font
486: */
487: private PdfStream getFontStream() throws DocumentException {
488: if (builtinFont || !embedded)
489: return null;
490: RandomAccessFileOrArray rf = null;
491: try {
492: String filePfb = fileName.substring(0,
493: fileName.length() - 3)
494: + "pfb";
495: if (pfb == null)
496: rf = new RandomAccessFileOrArray(filePfb);
497: else
498: rf = new RandomAccessFileOrArray(pfb);
499: int fileLength = rf.length();
500: byte st[] = new byte[fileLength - 18];
501: int lengths[] = new int[3];
502: int bytePtr = 0;
503: for (int k = 0; k < 3; ++k) {
504: if (rf.read() != 0x80)
505: throw new DocumentException(
506: "Start marker missing in " + filePfb);
507: if (rf.read() != PFB_TYPES[k])
508: throw new DocumentException(
509: "Incorrect segment type in " + filePfb);
510: int size = rf.read();
511: size += rf.read() << 8;
512: size += rf.read() << 16;
513: size += rf.read() << 24;
514: lengths[k] = size;
515: while (size != 0) {
516: int got = rf.read(st, bytePtr, size);
517: if (got < 0)
518: throw new DocumentException("Premature end in "
519: + filePfb);
520: bytePtr += got;
521: size -= got;
522: }
523: }
524: return new StreamFont(st, lengths);
525: } catch (Exception e) {
526: throw new DocumentException(e);
527: } finally {
528: if (rf != null) {
529: try {
530: rf.close();
531: } catch (Exception e) {
532: // empty on purpose
533: }
534: }
535: }
536: }
537:
538: /** Generates the font descriptor for this font or <CODE>null</CODE> if it is
539: * one of the 14 built in fonts.
540: * @param fontStream the indirect reference to a PdfStream containing the font or <CODE>null</CODE>
541: * @return the PdfDictionary containing the font descriptor or <CODE>null</CODE>
542: */
543: private PdfDictionary getFontDescriptor(
544: PdfIndirectReference fontStream) {
545: if (builtinFont)
546: return null;
547: PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR);
548: dic.put(PdfName.ASCENT, new PdfNumber(Ascender));
549: dic.put(PdfName.CAPHEIGHT, new PdfNumber(CapHeight));
550: dic.put(PdfName.DESCENT, new PdfNumber(Descender));
551: dic.put(PdfName.FONTBBOX, new PdfRectangle(llx, lly, urx, ury));
552: dic.put(PdfName.FONTNAME, new PdfName(FontName));
553: dic.put(PdfName.ITALICANGLE, new PdfNumber(ItalicAngle));
554: dic.put(PdfName.STEMV, new PdfNumber(StdVW));
555: if (fontStream != null)
556: dic.put(PdfName.FONTFILE, fontStream);
557: int flags = 0;
558: if (IsFixedPitch)
559: flags |= 1;
560: flags |= fontSpecific ? 4 : 32;
561: if (ItalicAngle < 0)
562: flags |= 64;
563: if (FontName.indexOf("Caps") >= 0 || FontName.endsWith("SC"))
564: flags |= 131072;
565: if (Weight.equals("Bold"))
566: flags |= 262144;
567: dic.put(PdfName.FLAGS, new PdfNumber(flags));
568:
569: return dic;
570: }
571:
572: /** Generates the font dictionary for this font.
573: * @return the PdfDictionary containing the font dictionary
574: * @param firstChar the first valid character
575: * @param lastChar the last valid character
576: * @param shortTag a 256 bytes long <CODE>byte</CODE> array where each unused byte is represented by 0
577: * @param fontDescriptor the indirect reference to a PdfDictionary containing the font descriptor or <CODE>null</CODE>
578: */
579: private PdfDictionary getFontBaseType(
580: PdfIndirectReference fontDescriptor, int firstChar,
581: int lastChar, byte shortTag[]) {
582: PdfDictionary dic = new PdfDictionary(PdfName.FONT);
583: dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
584: dic.put(PdfName.BASEFONT, new PdfName(FontName));
585: boolean stdEncoding = encoding.equals("Cp1252")
586: || encoding.equals("MacRoman");
587: if (!fontSpecific || specialMap != null) {
588: for (int k = firstChar; k <= lastChar; ++k) {
589: if (!differences[k].equals(notdef)) {
590: firstChar = k;
591: break;
592: }
593: }
594: if (stdEncoding)
595: dic
596: .put(
597: PdfName.ENCODING,
598: encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING
599: : PdfName.MAC_ROMAN_ENCODING);
600: else {
601: PdfDictionary enc = new PdfDictionary(PdfName.ENCODING);
602: PdfArray dif = new PdfArray();
603: boolean gap = true;
604: for (int k = firstChar; k <= lastChar; ++k) {
605: if (shortTag[k] != 0) {
606: if (gap) {
607: dif.add(new PdfNumber(k));
608: gap = false;
609: }
610: dif.add(new PdfName(differences[k]));
611: } else
612: gap = true;
613: }
614: enc.put(PdfName.DIFFERENCES, dif);
615: dic.put(PdfName.ENCODING, enc);
616: }
617: }
618: if (specialMap != null || forceWidthsOutput
619: || !(builtinFont && (fontSpecific || stdEncoding))) {
620: dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar));
621: dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar));
622: PdfArray wd = new PdfArray();
623: for (int k = firstChar; k <= lastChar; ++k) {
624: if (shortTag[k] == 0)
625: wd.add(new PdfNumber(0));
626: else
627: wd.add(new PdfNumber(widths[k]));
628: }
629: dic.put(PdfName.WIDTHS, wd);
630: }
631: if (!builtinFont && fontDescriptor != null)
632: dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor);
633: return dic;
634: }
635:
636: /** Outputs to the writer the font dictionaries and streams.
637: * @param writer the writer for this document
638: * @param ref the font indirect reference
639: * @param params several parameters that depend on the font type
640: * @throws IOException on error
641: * @throws DocumentException error in generating the object
642: */
643: void writeFont(PdfWriter writer, PdfIndirectReference ref,
644: Object params[]) throws DocumentException, IOException {
645: int firstChar = ((Integer) params[0]).intValue();
646: int lastChar = ((Integer) params[1]).intValue();
647: byte shortTag[] = (byte[]) params[2];
648: boolean subsetp = ((Boolean) params[3]).booleanValue()
649: && subset;
650: if (!subsetp) {
651: firstChar = 0;
652: lastChar = shortTag.length - 1;
653: for (int k = 0; k < shortTag.length; ++k)
654: shortTag[k] = 1;
655: }
656: PdfIndirectReference ind_font = null;
657: PdfObject pobj = null;
658: PdfIndirectObject obj = null;
659: pobj = getFontStream();
660: if (pobj != null) {
661: obj = writer.addToBody(pobj);
662: ind_font = obj.getIndirectReference();
663: }
664: pobj = getFontDescriptor(ind_font);
665: if (pobj != null) {
666: obj = writer.addToBody(pobj);
667: ind_font = obj.getIndirectReference();
668: }
669: pobj = getFontBaseType(ind_font, firstChar, lastChar, shortTag);
670: writer.addToBody(pobj, ref);
671: }
672:
673: /** Gets the font parameter identified by <CODE>key</CODE>. Valid values
674: * for <CODE>key</CODE> are <CODE>ASCENT</CODE>, <CODE>CAPHEIGHT</CODE>, <CODE>DESCENT</CODE>,
675: * <CODE>ITALICANGLE</CODE>, <CODE>BBOXLLX</CODE>, <CODE>BBOXLLY</CODE>, <CODE>BBOXURX</CODE>
676: * and <CODE>BBOXURY</CODE>.
677: * @param key the parameter to be extracted
678: * @param fontSize the font size in points
679: * @return the parameter in points
680: */
681: public float getFontDescriptor(int key, float fontSize) {
682: switch (key) {
683: case AWT_ASCENT:
684: case ASCENT:
685: return Ascender * fontSize / 1000;
686: case CAPHEIGHT:
687: return CapHeight * fontSize / 1000;
688: case AWT_DESCENT:
689: case DESCENT:
690: return Descender * fontSize / 1000;
691: case ITALICANGLE:
692: return ItalicAngle;
693: case BBOXLLX:
694: return llx * fontSize / 1000;
695: case BBOXLLY:
696: return lly * fontSize / 1000;
697: case BBOXURX:
698: return urx * fontSize / 1000;
699: case BBOXURY:
700: return ury * fontSize / 1000;
701: case AWT_LEADING:
702: return 0;
703: case AWT_MAXADVANCE:
704: return (urx - llx) * fontSize / 1000;
705: }
706: return 0;
707: }
708:
709: /** Gets the postscript font name.
710: * @return the postscript font name
711: */
712: public String getPostscriptFontName() {
713: return FontName;
714: }
715:
716: /** Gets the full name of the font. If it is a True Type font
717: * each array element will have {Platform ID, Platform Encoding ID,
718: * Language ID, font name}. The interpretation of this values can be
719: * found in the Open Type specification, chapter 2, in the 'name' table.<br>
720: * For the other fonts the array has a single element with {"", "", "",
721: * font name}.
722: * @return the full name of the font
723: */
724: public String[][] getFullFontName() {
725: return new String[][] { { "", "", "", FullName } };
726: }
727:
728: /** Gets the family name of the font. If it is a True Type font
729: * each array element will have {Platform ID, Platform Encoding ID,
730: * Language ID, font name}. The interpretation of this values can be
731: * found in the Open Type specification, chapter 2, in the 'name' table.<br>
732: * For the other fonts the array has a single element with {"", "", "",
733: * font name}.
734: * @return the family name of the font
735: */
736: public String[][] getFamilyFontName() {
737: return new String[][] { { "", "", "", FamilyName } };
738: }
739:
740: /** Checks if the font has any kerning pairs.
741: * @return <CODE>true</CODE> if the font has any kerning pairs
742: */
743: public boolean hasKernPairs() {
744: return !KernPairs.isEmpty();
745: }
746:
747: /**
748: * Sets the font name that will appear in the pdf font dictionary.
749: * Use with care as it can easily make a font unreadable if not embedded.
750: * @param name the new font name
751: */
752: public void setPostscriptFontName(String name) {
753: FontName = name;
754: }
755:
756: /**
757: * Sets the kerning between two Unicode chars.
758: * @param char1 the first char
759: * @param char2 the second char
760: * @param kern the kerning to apply in normalized 1000 units
761: * @return <code>true</code> if the kerning was applied, <code>false</code> otherwise
762: */
763: public boolean setKerning(char char1, char char2, int kern) {
764: String first = GlyphList.unicodeToName((int) char1);
765: if (first == null)
766: return false;
767: String second = GlyphList.unicodeToName((int) char2);
768: if (second == null)
769: return false;
770: Object obj[] = (Object[]) KernPairs.get(first);
771: if (obj == null) {
772: obj = new Object[] { second, new Integer(kern) };
773: KernPairs.put(first, obj);
774: return true;
775: }
776: for (int k = 0; k < obj.length; k += 2) {
777: if (second.equals(obj[k])) {
778: obj[k + 1] = new Integer(kern);
779: return true;
780: }
781: }
782: int size = obj.length;
783: Object obj2[] = new Object[size + 2];
784: System.arraycopy(obj, 0, obj2, 0, size);
785: obj2[size] = second;
786: obj2[size + 1] = new Integer(kern);
787: KernPairs.put(first, obj2);
788: return true;
789: }
790:
791: protected int[] getRawCharBBox(int c, String name) {
792: Object metrics[];
793: if (name == null) { // font specific
794: metrics = (Object[]) CharMetrics.get(new Integer(c));
795: } else {
796: if (name.equals(".notdef"))
797: return null;
798: metrics = (Object[]) CharMetrics.get(name);
799: }
800: if (metrics != null)
801: return ((int[]) (metrics[3]));
802: return null;
803: }
804:
805: }
|