123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- /* $Id$ */
-
- package org.apache.fop.fonts.truetype;
-
- import java.awt.Rectangle;
- import java.io.IOException;
- import java.io.InputStream;
- import java.net.URI;
- import java.util.Map;
- import java.util.Set;
-
- import org.apache.commons.io.IOUtils;
-
- import org.apache.fop.apps.io.InternalResourceResolver;
- import org.apache.fop.fonts.CIDFontType;
- import org.apache.fop.fonts.CMapSegment;
- import org.apache.fop.fonts.EmbeddingMode;
- import org.apache.fop.fonts.EncodingMode;
- import org.apache.fop.fonts.FontLoader;
- import org.apache.fop.fonts.FontType;
- import org.apache.fop.fonts.MultiByteFont;
- import org.apache.fop.fonts.NamedCharacter;
- import org.apache.fop.fonts.SingleByteFont;
- import org.apache.fop.fonts.truetype.OpenFont.PostScriptVersion;
- import org.apache.fop.util.HexEncoder;
-
- /**
- * Loads a TrueType font into memory directly from the original font file.
- */
- public class OFFontLoader extends FontLoader {
-
- private MultiByteFont multiFont;
- private SingleByteFont singleFont;
- private final String subFontName;
- private EncodingMode encodingMode;
- private EmbeddingMode embeddingMode;
-
- /**
- * Default constructor
- * @param fontFileURI the URI representing the font file
- * @param resourceResolver the resource resolver for font URI resolution
- */
- public OFFontLoader(URI fontFileURI, InternalResourceResolver resourceResolver) {
- this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, true, resourceResolver);
- }
-
- /**
- * Additional constructor for TrueType Collections.
- * @param fontFileURI the URI representing the font file
- * @param subFontName the sub-fontname of a font in a TrueType Collection (or null for normal
- * TrueType fonts)
- * @param embedded indicates whether the font is embedded or referenced
- * @param embeddingMode the embedding mode of the font
- * @param encodingMode the requested encoding mode
- * @param useKerning true to enable loading kerning info if available, false to disable
- * @param useAdvanced true to enable loading advanced info if available, false to disable
- * @param resolver the FontResolver for font URI resolution
- */
- public OFFontLoader(URI fontFileURI, String subFontName, boolean embedded,
- EmbeddingMode embeddingMode, EncodingMode encodingMode, boolean useKerning,
- boolean useAdvanced, InternalResourceResolver resolver) {
- super(fontFileURI, embedded, useKerning, useAdvanced, resolver);
- this.subFontName = subFontName;
- this.encodingMode = encodingMode;
- this.embeddingMode = embeddingMode;
- if (this.encodingMode == EncodingMode.AUTO) {
- this.encodingMode = EncodingMode.CID; //Default to CID mode for TrueType
- }
- if (this.embeddingMode == EmbeddingMode.AUTO) {
- this.embeddingMode = EmbeddingMode.SUBSET;
- }
- }
-
- /** {@inheritDoc} */
- protected void read() throws IOException {
- read(this.subFontName);
- }
-
- /**
- * Reads a TrueType font.
- * @param ttcFontName the TrueType sub-font name of TrueType Collection (may be null for
- * normal TrueType fonts)
- * @throws IOException if an I/O error occurs
- */
- private void read(String ttcFontName) throws IOException {
- InputStream in = resourceResolver.getResource(this.fontFileURI);
- try {
- FontFileReader reader = new FontFileReader(in);
- String header = readHeader(reader);
- boolean isCFF = header.equals("OTTO");
- OpenFont otf = (isCFF) ? new OTFFile() : new TTFFile(useKerning, useAdvanced);
- boolean supported = otf.readFont(reader, header, ttcFontName);
- if (!supported) {
- throw new IOException("The font does not have a Unicode cmap table: " + fontFileURI);
- }
- buildFont(otf, ttcFontName);
- loaded = true;
- } finally {
- IOUtils.closeQuietly(in);
- }
- }
-
- public static String readHeader(FontFileReader fontFile) throws IOException {
- if (fontFile != null) {
- fontFile.seekSet(0);
- return fontFile.readTTFString(4); // TTF_FIXED_SIZE (4 bytes)
- }
- return null;
- }
-
- private void buildFont(OpenFont otf, String ttcFontName) {
- boolean isCid = this.embedded;
- if (this.encodingMode == EncodingMode.SINGLE_BYTE) {
- isCid = false;
- }
-
- if (isCid) {
- multiFont = new MultiByteFont(resourceResolver, embeddingMode);
- multiFont.setIsOTFFile(otf instanceof OTFFile);
- returnFont = multiFont;
- multiFont.setTTCName(ttcFontName);
- } else {
- singleFont = new SingleByteFont(resourceResolver);
- returnFont = singleFont;
- }
-
- returnFont.setFontName(otf.getPostScriptName());
- returnFont.setFullName(otf.getFullName());
- returnFont.setFamilyNames(otf.getFamilyNames());
- returnFont.setFontSubFamilyName(otf.getSubFamilyName());
- returnFont.setCapHeight(otf.getCapHeight());
- returnFont.setXHeight(otf.getXHeight());
- returnFont.setAscender(otf.getLowerCaseAscent());
- returnFont.setDescender(otf.getLowerCaseDescent());
- returnFont.setFontBBox(otf.getFontBBox());
- returnFont.setUnderlinePosition(otf.getUnderlinePosition() - otf.getUnderlineThickness() / 2);
- returnFont.setUnderlineThickness(otf.getUnderlineThickness());
- returnFont.setStrikeoutPosition(otf.getStrikeoutPosition() - otf.getStrikeoutThickness() / 2);
- returnFont.setStrikeoutThickness(otf.getStrikeoutThickness());
- returnFont.setFlags(otf.getFlags());
- returnFont.setStemV(Integer.parseInt(otf.getStemV())); //not used for TTF
- returnFont.setItalicAngle(Integer.parseInt(otf.getItalicAngle()));
- returnFont.setMissingWidth(0);
- returnFont.setWeight(otf.getWeightClass());
- returnFont.setEmbeddingMode(this.embeddingMode);
- if (isCid) {
- if (otf instanceof OTFFile) {
- multiFont.setCIDType(CIDFontType.CIDTYPE0);
- } else {
- multiFont.setCIDType(CIDFontType.CIDTYPE2);
- }
- multiFont.setWidthArray(otf.getWidths());
- multiFont.setBBoxArray(otf.getBoundingBoxes());
- } else {
- singleFont.setFontType(FontType.TRUETYPE);
- singleFont.setEncoding(otf.getCharSetName());
- returnFont.setFirstChar(otf.getFirstChar());
- returnFont.setLastChar(otf.getLastChar());
- singleFont.setTrueTypePostScriptVersion(otf.getPostScriptVersion());
- copyGlyphMetricsSingleByte(otf);
- }
- returnFont.setCMap(getCMap(otf));
-
- if (otf.getKerning() != null && useKerning) {
- copyKerning(otf, isCid);
- }
- if (useAdvanced) {
- copyAdvanced(otf);
- }
- if (this.embedded) {
- if (otf.isEmbeddable()) {
- returnFont.setEmbedURI(this.fontFileURI);
- } else {
- String msg = "The font " + this.fontFileURI + " is not embeddable due to a"
- + " licensing restriction.";
- throw new RuntimeException(msg);
- }
- }
- }
-
- private CMapSegment[] getCMap(OpenFont otf) {
- CMapSegment[] array = new CMapSegment[otf.getCMaps().size()];
- return otf.getCMaps().toArray(array);
- }
-
- private void copyGlyphMetricsSingleByte(OpenFont otf) {
- int[] wx = otf.getWidths();
- Rectangle[] bboxes = otf.getBoundingBoxes();
- for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) {
- singleFont.setWidth(i, otf.getCharWidth(i));
- int[] bbox = otf.getBBox(i);
- singleFont.setBoundingBox(i,
- new Rectangle(bbox[0], bbox[1], bbox[2] - bbox[0], bbox[3] - bbox[1]));
- }
-
- for (CMapSegment segment : otf.getCMaps()) {
- if (segment.getUnicodeStart() < 0xFFFE) {
- for (char u = (char)segment.getUnicodeStart(); u <= segment.getUnicodeEnd(); u++) {
- int codePoint = singleFont.getEncoding().mapChar(u);
- if (codePoint <= 0) {
- int glyphIndex = segment.getGlyphStartIndex() + u - segment.getUnicodeStart();
- String glyphName = otf.getGlyphName(glyphIndex);
- if (glyphName.length() == 0 && otf.getPostScriptVersion() != PostScriptVersion.V2) {
- glyphName = "u" + HexEncoder.encode(u);
- }
- if (glyphName.length() > 0) {
- String unicode = Character.toString(u);
- NamedCharacter nc = new NamedCharacter(glyphName, unicode);
- singleFont.addUnencodedCharacter(nc, wx[glyphIndex], bboxes[glyphIndex]);
- }
- }
- }
- }
- }
- }
-
- /**
- * Copy kerning information.
- */
- private void copyKerning(OpenFont otf, boolean isCid) {
-
- // Get kerning
- Set<Integer> kerningSet;
- if (isCid) {
- kerningSet = otf.getKerning().keySet();
- } else {
- kerningSet = otf.getAnsiKerning().keySet();
- }
-
- for (Integer kpx1 : kerningSet) {
- Map<Integer, Integer> h2;
- if (isCid) {
- h2 = otf.getKerning().get(kpx1);
- } else {
- h2 = otf.getAnsiKerning().get(kpx1);
- }
- returnFont.putKerningEntry(kpx1, h2);
- }
- }
-
- /**
- * Copy advanced typographic information.
- */
- private void copyAdvanced(OpenFont otf) {
- if (returnFont instanceof MultiByteFont) {
- MultiByteFont mbf = (MultiByteFont) returnFont;
- mbf.setGDEF(otf.getGDEF());
- mbf.setGSUB(otf.getGSUB());
- mbf.setGPOS(otf.getGPOS());
- }
- }
-
- }
|