001: /*
002: Copyright © 2006,2007 Stefano Chizzolini. http://clown.stefanochizzolini.it
003:
004: Contributors:
005: * Stefano Chizzolini (original code developer, http://www.stefanochizzolini.it):
006: contributed code is Copyright © 2006,2007 by Stefano Chizzolini.
007:
008: This file should be part of the source code distribution of "PDF Clown library"
009: (the Program): see the accompanying README files for more info.
010:
011: This Program is free software; you can redistribute it and/or modify it under
012: the terms of the GNU General Public License as published by the Free Software
013: Foundation; either version 2 of the License, or (at your option) any later version.
014:
015: This Program is distributed in the hope that it will be useful, but WITHOUT ANY
016: WARRANTY, either expressed or implied; without even the implied warranty of
017: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.
018:
019: You should have received a copy of the GNU General Public License along with this
020: Program (see README files); if not, go to the GNU website (http://www.gnu.org/).
021:
022: Redistribution and use, with or without modification, are permitted provided that such
023: redistributions retain the above copyright notice, license and disclaimer, along with
024: this list of conditions.
025: */
026:
027: package it.stefanochizzolini.clown.documents.contents.fonts;
028:
029: import it.stefanochizzolini.clown.documents.Document;
030: import it.stefanochizzolini.clown.files.File;
031: import it.stefanochizzolini.clown.objects.IPdfNumber;
032: import it.stefanochizzolini.clown.objects.PdfArray;
033: import it.stefanochizzolini.clown.objects.PdfDictionary;
034: import it.stefanochizzolini.clown.objects.PdfDirectObject;
035: import it.stefanochizzolini.clown.objects.PdfName;
036: import it.stefanochizzolini.clown.objects.PdfObjectWrapper;
037: import it.stefanochizzolini.clown.objects.PdfReference;
038: import it.stefanochizzolini.clown.objects.PdfStream;
039: import it.stefanochizzolini.clown.util.NotImplementedException;
040:
041: import java.util.EnumSet;
042: import java.util.Hashtable;
043:
044: /**
045: Abstract font [PDF:1.6:5.4].
046: @version 0.0.4
047: */
048: public abstract class Font extends PdfObjectWrapper<PdfDictionary> {
049: // <class>
050: // <classes>
051: /**
052: Font descriptor flags [PDF:1.6:5.7.1].
053: */
054: public enum FlagsEnum {
055: // <class>
056: // <static>
057: // <fields>
058: /**
059: All glyphs have the same width.
060: */
061: FixedPitch(0x1),
062: /**
063: Glyphs have serifs.
064: */
065: Serif(0x2),
066: /**
067: Font contains glyphs outside the Adobe standard Latin character set.
068: */
069: Symbolic(0x4),
070: /**
071: Glyphs resemble cursive handwriting.
072: */
073: Script(0x8),
074: /**
075: Font uses the Adobe standard Latin character set.
076: */
077: Nonsymbolic(0x20),
078: /**
079: Glyphs have dominant vertical strokes that are slanted.
080: */
081: Italic(0x40),
082: /**
083: Font contains no lowercase letters.
084: */
085: AllCap(0x10000),
086: /**
087: Font contains both uppercase and lowercase letters.
088: */
089: SmallCap(0x20000),
090: /**
091: Thicken bold glyphs at small text sizes.
092: */
093: ForceBold(0x40000);
094: // </fields>
095: // </static>
096:
097: // <dynamic>
098: // <fields>
099: /**
100: <h3>Remarks</h3>
101: <p>Bitwise code MUST be explicitly distinct from the ordinal position of the enum constant
102: as they don't coincide.</p>
103: */
104: private int code;
105:
106: // </fields>
107:
108: // <constructors>
109: private FlagsEnum(int code) {
110: this .code = code;
111: }
112:
113: // </constructors>
114:
115: // <interface>
116: // <public>
117: public int getCode() {
118: return code;
119: }
120: // </public>
121: // </interface>
122: // </dynamic>
123: // </class>
124: }
125:
126: // </classes>
127:
128: // <static>
129: // <interface>
130: // <public>
131: /**
132: Gets the scaling factor to be applied to unscaled metrics to get actual measures.
133: */
134: public static final double getScalingFactor(double size) {
135: return (0.001 * size);
136: }
137:
138: /**
139: Wraps a font reference into a font object.
140: @param reference Reference to a font object.
141: @return Font object associated to the reference.
142: */
143: public static final Font wrap(PdfReference reference) {
144: /*
145: NOTE: This is a factory method for any font-derived object.
146: */
147: if (reference == null)
148: return null;
149:
150: PdfDictionary fontData = (PdfDictionary) reference
151: .getDataObject();
152: PdfName fontType = (PdfName) fontData.get(PdfName.Subtype);
153:
154: {
155: // Has the font been already instantiated?
156: /*
157: NOTE: Font structures are reified as complex objects, both IO- and CPU-intensive to load.
158: So, it's convenient to retrieve them from a common cache whenever possible.
159: */
160: Hashtable cache = reference.getIndirectObject().getFile()
161: .getDocument().cache;
162: if (cache.containsKey(reference)) {
163: return (Font) cache.get(reference);
164: }
165: }
166:
167: // Is it a Type 1 font?
168: if (fontType.equals(PdfName.Type1)) // Type 1.
169: {
170: // Is it a standard Type 1 font?
171: if (!fontData.containsKey(PdfName.FontDescriptor)) // Standard Type 1.
172: {
173: return new StandardType1Font(reference);
174: } else // Custom Type 1.
175: {
176: PdfDictionary fontDescriptor = (PdfDictionary) File
177: .resolve(fontData.get(PdfName.FontDescriptor));
178: // Is it an OpenType-wrapped CFF (Compact Font Format) Type 1 font?
179: if (fontDescriptor.containsKey(PdfName.FontFile3)
180: && ((PdfName) ((PdfStream) File
181: .resolve(fontDescriptor
182: .get(PdfName.FontFile3)))
183: .getHeader().get(PdfName.Subtype))
184: .equals(PdfName.OpenType)) // OpenType-wrapped CFF.
185: {
186: return new OpenTypeFont(reference);
187: } else // Non-OpenType Type 1 font.
188: {
189: return new Type1Font(reference);
190: }
191: }
192: }
193: // Is it a TrueType font?
194: else if (fontType.equals(PdfName.TrueType)) // TrueType.
195: {
196: return new OpenTypeFont(reference);
197: } else // Unknown.
198: {
199: return null;
200: }
201: }
202:
203: // </public>
204: // </interface>
205: // </static>
206:
207: // <dynamic>
208: // <constructors>
209: protected Font(Document context) {
210: super (context.getFile(), new PdfDictionary(
211: new PdfName[] { PdfName.Type },
212: new PdfDirectObject[] { PdfName.Font }));
213: initialize();
214: }
215:
216: protected Font(PdfDirectObject baseObject) {
217: super (baseObject, null // NO container. NOTE: this is a simplification (the spec [PDF:1.6] doesn't apparently prescribe the use of an indirect object for font dictionary, whilst the general practice is as such. If an exception occurs, you'll need to specify the proper container).
218: );
219: initialize();
220: }
221:
222: // </constructors>
223:
224: // <interface>
225: // <public>
226: public final boolean equals(Font object) {
227: return (getType() == object.getType() && getName() == object
228: .getName());
229: }
230:
231: /**
232: Gets the vertical offset from the baseline to the ascender line (ascent).
233: */
234: public double getAscent(double size) {
235: return (((IPdfNumber) File.resolve(((PdfDictionary) File
236: .resolve(getBaseDataObject()
237: .get(PdfName.FontDescriptor)))
238: .get(PdfName.Ascent))).getNumberValue())
239: * getScalingFactor(size);
240: }
241:
242: /**
243: Gets the vertical offset from the baseline to the descender line (descent).
244: */
245: public double getDescent(double size) {
246: return (((IPdfNumber) File.resolve(((PdfDictionary) File
247: .resolve(getBaseDataObject()
248: .get(PdfName.FontDescriptor)))
249: .get(PdfName.Descent))).getNumberValue())
250: * getScalingFactor(size);
251: }
252:
253: /**
254: Gets the font descriptor flags.
255: */
256: public EnumSet<FlagsEnum> getFlags() {
257: int flagMask = (int) ((IPdfNumber) File
258: .resolve(((PdfDictionary) File
259: .resolve(getBaseDataObject().get(
260: PdfName.FontDescriptor)))
261: .get(PdfName.Flags))).getNumberValue();
262:
263: EnumSet<FlagsEnum> flags = EnumSet.noneOf(FlagsEnum.class);
264: for (FlagsEnum flag : FlagsEnum.values()) {
265: if ((flag.getCode() & flagMask) != 0) {
266: flags.add(flag);
267: }
268: }
269:
270: return flags;
271: }
272:
273: /**
274: Gets the width (kerning inclusive) of the given text, scaled to the given font size.
275: */
276: public final double getKernedWidth(String text, double size) {
277: return (getWidth(text) + getKerning(text))
278: * getScalingFactor(size);
279: }
280:
281: /**
282: Gets the unscaled kerning width between two given characters.
283: */
284: public abstract int getKerning(char textChar1, char textChar2);
285:
286: /**
287: Gets the unscaled kerning width inside the given text.
288: */
289: public abstract int getKerning(String text);
290:
291: /**
292: Gets the kerning width inside the given text, scaled to the given font size.
293: */
294: public final double getKerning(String text, double size) {
295: return getKerning(text) * getScalingFactor(size);
296: }
297:
298: /**
299: Gets the actual line height.
300: */
301: public double getLineHeight(double size) {
302: PdfDictionary descriptor = (PdfDictionary) File
303: .resolve(getBaseDataObject()
304: .get(PdfName.FontDescriptor));
305:
306: return (((IPdfNumber) File.resolve(descriptor
307: .get(PdfName.Ascent))).getNumberValue() + Math
308: .abs(((IPdfNumber) File.resolve(descriptor
309: .get(PdfName.Descent))).getNumberValue()))
310: * getScalingFactor(size);
311: }
312:
313: /**
314: Gets the PostScript name of the font.
315: */
316: public final String getName() {
317: return ((PdfName) getBaseDataObject().get(PdfName.BaseFont))
318: .toString();
319: }
320:
321: /**
322: Gets the font type.
323: */
324: public final String getType() {
325: return ((PdfName) getBaseDataObject().get(PdfName.Subtype))
326: .toString();
327: }
328:
329: /**
330: Gets the unscaled width of the given character.
331: */
332: public abstract int getWidth(char textChar);
333:
334: /**
335: Gets the unscaled width (kerning exclusive) of the given text.
336: */
337: public abstract int getWidth(String text);
338:
339: /**
340: Gets the width (kerning exclusive) of the given text, scaled to the given font size.
341: */
342: public final double getWidth(String text, double size) {
343: return getWidth(text) * getScalingFactor(size);
344: }
345:
346: // </public>
347:
348: // <private>
349: private void initialize() {
350: // Put the newly instantiated font into the common cache!
351: /*
352: NOTE: Font structures are reified as complex objects, both IO- and CPU-intensive to load.
353: So, it's convenient to put them into a common cache for later reuse.
354: */
355: getDocument().cache.put((PdfReference) getBaseObject(), this );
356: }
357: // </private>
358: // </interface>
359: // </dynamic>
360: // </class>
361: }
|