com.sun.pdfview.font.TTFFont Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pdf-renderer Show documentation
Show all versions of pdf-renderer Show documentation
PDF renderer implementation supporting the subset of PDF 1.4 specification.
The newest version!
/*
* $Id: TTFFont.java,v 1.11 2009-03-19 04:03:54 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.font;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Collection;
import java.util.Collections;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.font.ttf.*;
/**
* A true-type font
*/
public class TTFFont extends OutlineFont {
/** the truetype font itself */
private TrueTypeFont font;
/** the number of units per em in the font */
private float unitsPerEm;
public TTFFont (String baseFont, PDFObject fontObj,
PDFFontDescriptor descriptor) throws IOException {
this(baseFont, fontObj, descriptor, null);
}
/**
* create a new TrueTypeFont object based on a description of the
* font from the PDF file. If the description happens to contain
* an in-line true-type font file (under key "FontFile2"), use the
* true type font. Otherwise, parse the description for key information
* and use that to generate an appropriate font.
*/
public TTFFont (String baseFont, PDFObject fontObj,
PDFFontDescriptor descriptor, File fontFile)
throws IOException {
super (baseFont, fontObj, descriptor);
String fontName = descriptor.getFontName ();
PDFObject ttfObj = descriptor.getFontFile2 ();
// try {
// byte[] fontData = ttfObj.getStream();
// java.io.FileOutputStream fis = new java.io.FileOutputStream("/tmp/" + fontName + ".ttf");
// fis.write(fontData);
// fis.flush();
// fis.close();
// } catch (Exception ex) {
// ex.printStackTrace();
// }
if (ttfObj != null || fontFile != null) {
if (ttfObj != null) {
font = TrueTypeFont.parseFont (ttfObj.getStreamBuffer ());
} else {
final RandomAccessFile raFile = fontFile != null ? new RandomAccessFile(fontFile, "r") : null;
final FileChannel fc = raFile.getChannel();
try {
MappedByteBuffer mappedFont = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
font = TrueTypeFont.parseFont(mappedFont);
mappedFont = null;
} finally {
try {
fc.close();
} catch (IOException ioEx) {
// swallow
}
try {
raFile.close();
} catch (IOException ioEx) {
// swallow
}
}
}
// read the units per em from the head table
HeadTable head = (HeadTable) font.getTable ("head");
unitsPerEm = head.getUnitsPerEm ();
} else {
font = null;
}
// System.out.println ("TTFFont: ttfObj: " + ttfObj + ", fontName: " + fontName);
}
public Collection getNames()
{
return font.getNames();
}
/**
* Get the outline of a character given the character code
*/
protected synchronized GeneralPath getOutline (char src, float width) {
// find the cmaps
CmapTable cmap = (CmapTable) font.getTable ("cmap");
// if there are no cmaps, this is (hopefully) a cid-mapped font,
// so just trust the value we were given for src
if (cmap == null) {
return getOutline ((int) src, width);
}
CMap[] maps = cmap.getCMaps ();
// try the maps in order
for (int i = 0; i < maps.length; i++) {
int idx = maps[i].map (src);
if (idx != 0) {
return getOutline (idx, width);
}
}
// not found, return the empty glyph
return getOutline (0, width);
}
/**
* lookup the outline using the CMAPs, as specified in 32000-1:2008,
* 9.6.6.4, when an Encoding is specified.
*
* @param val
* @param width
* @return GeneralPath
*/
protected synchronized GeneralPath getOutlineFromCMaps (char val,
float width) {
// find the cmaps
CmapTable cmap = (CmapTable) font.getTable ("cmap");
if (cmap == null) {
return null;
}
// try maps in required order of (3, 1), (1, 0)
CMap map = cmap.getCMap ((short) 3, (short) 1);
if (map == null) {
map = cmap.getCMap ((short) 1, (short) 0);
}
int idx = map.map (val);
if (idx != 0) {
return getOutline (idx, width);
}
return null;
}
/**
* Get the outline of a character given the character name
*/
protected synchronized GeneralPath getOutline (String name, float width) {
int idx;
PostTable post = (PostTable) font.getTable ("post");
if (post != null) {
idx = post.getGlyphNameIndex (name);
if (idx != 0) {
return getOutline (idx, width);
}
return null;
}
Integer res = AdobeGlyphList.getGlyphNameIndex (name);
if (res != null) {
idx = res;
return getOutlineFromCMaps ((char) idx, width);
}
return null;
}
/**
* Get the outline of a character given the glyph id
*/
protected synchronized GeneralPath getOutline (int glyphId, float width) {
// find the glyph itself
GlyfTable glyf = (GlyfTable) font.getTable ("glyf");
Glyf g = glyf.getGlyph (glyphId);
GeneralPath gp = null;
if (g instanceof GlyfSimple) {
gp = renderSimpleGlyph ((GlyfSimple) g);
} else if (g instanceof GlyfCompound) {
gp = renderCompoundGlyph (glyf, (GlyfCompound) g);
} else {
gp = new GeneralPath ();
}
// calculate the advance
HmtxTable hmtx = (HmtxTable) font.getTable ("hmtx");
float advance = (float) hmtx.getAdvance (glyphId) / (float) unitsPerEm;
// scale the glyph to match the desired advance
float widthfactor = width / advance;
// the base transform scales the glyph to 1x1
AffineTransform at = AffineTransform.getScaleInstance (1 / unitsPerEm,
1 / unitsPerEm);
at.concatenate (AffineTransform.getScaleInstance (widthfactor, 1));
gp.transform (at);
return gp;
}
/**
* Render a simple glyf
*/
protected GeneralPath renderSimpleGlyph (GlyfSimple g) {
// the current contour
int curContour = 0;
// the render state
RenderState rs = new RenderState ();
rs.gp = new GeneralPath ();
for (int i = 0; i < g.getNumPoints (); i++) {
PointRec rec = new PointRec (g, i);
if (rec.onCurve) {
addOnCurvePoint (rec, rs);
} else {
addOffCurvePoint (rec, rs);
}
// see if we just ended a contour
if (i == g.getContourEndPoint (curContour)) {
curContour++;
if (rs.firstOff != null) {
addOffCurvePoint (rs.firstOff, rs);
}
if (rs.firstOn != null) {
addOnCurvePoint (rs.firstOn, rs);
}
rs.firstOn = null;
rs.firstOff = null;
rs.prevOff = null;
}
}
return rs.gp;
}
/**
* Render a compound glyf
*/
protected GeneralPath renderCompoundGlyph (GlyfTable glyf, GlyfCompound g) {
GeneralPath gp = new GeneralPath ();
for (int i = 0; i < g.getNumComponents (); i++) {
// find and render the component glyf
Glyf gl = glyf.getGlyph (g.getGlyphIndex (i));
GeneralPath path = null;
if (gl instanceof GlyfSimple) {
path = renderSimpleGlyph ((GlyfSimple) gl);
} else if (gl instanceof GlyfCompound) {
path = renderCompoundGlyph (glyf, (GlyfCompound) gl);
} else {
throw new RuntimeException (
"Unsupported glyph type " + gl.getClass ().getCanonicalName ());
}
// multiply the translations by units per em
double[] matrix = g.getTransform (i);
// transform the path
path.transform (new AffineTransform (matrix));
// add it to the global path
gp.append (path, false);
}
return gp;
}
/** add a point on the curve */
private void addOnCurvePoint (PointRec rec, RenderState rs) {
// if the point is on the curve, either move to it,
// or draw a line from the previous point
if (rs.firstOn == null) {
rs.firstOn = rec;
rs.gp.moveTo (rec.x, rec.y);
} else if (rs.prevOff != null) {
rs.gp.quadTo (rs.prevOff.x, rs.prevOff.y, rec.x, rec.y);
rs.prevOff = null;
} else {
rs.gp.lineTo (rec.x, rec.y);
}
}
/** add a point off the curve */
private void addOffCurvePoint (PointRec rec, RenderState rs) {
if (rs.prevOff != null) {
PointRec oc = new PointRec ((rec.x + rs.prevOff.x) / 2,
(rec.y + rs.prevOff.y) / 2,
true);
addOnCurvePoint (oc, rs);
} else if (rs.firstOn == null) {
rs.firstOff = rec;
}
rs.prevOff = rec;
}
class RenderState {
// the shape itself
GeneralPath gp;
// the first off and on-curve points in the current segment
PointRec firstOn;
PointRec firstOff;
// the previous off and on-curve points in the current segment
PointRec prevOff;
}
/** a point on the stack of points */
class PointRec {
int x;
int y;
boolean onCurve;
public PointRec (int x, int y, boolean onCurve) {
this.x = x;
this.y = y;
this.onCurve = onCurve;
}
public PointRec (GlyfSimple g, int idx) {
x = g.getXCoord (idx);
y = g.getYCoord (idx);
onCurve = g.onCurve (idx);
}
}
}