You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

OFFontLoader.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.fonts.truetype;
  19. import java.awt.Rectangle;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.net.URI;
  23. import java.util.Map;
  24. import java.util.Set;
  25. import org.apache.commons.io.IOUtils;
  26. import org.apache.fop.apps.io.InternalResourceResolver;
  27. import org.apache.fop.fonts.CFFToType1Font;
  28. import org.apache.fop.fonts.CIDFontType;
  29. import org.apache.fop.fonts.CMapSegment;
  30. import org.apache.fop.fonts.EmbeddingMode;
  31. import org.apache.fop.fonts.EncodingMode;
  32. import org.apache.fop.fonts.FontLoader;
  33. import org.apache.fop.fonts.FontType;
  34. import org.apache.fop.fonts.MultiByteFont;
  35. import org.apache.fop.fonts.NamedCharacter;
  36. import org.apache.fop.fonts.SingleByteFont;
  37. import org.apache.fop.fonts.truetype.OpenFont.PostScriptVersion;
  38. import org.apache.fop.util.HexEncoder;
  39. /**
  40. * Loads a TrueType font into memory directly from the original font file.
  41. */
  42. public class OFFontLoader extends FontLoader {
  43. private MultiByteFont multiFont;
  44. private SingleByteFont singleFont;
  45. private final String subFontName;
  46. private EncodingMode encodingMode;
  47. private EmbeddingMode embeddingMode;
  48. private boolean embedAsType1;
  49. /**
  50. * Default constructor
  51. * @param fontFileURI the URI representing the font file
  52. * @param resourceResolver the resource resolver for font URI resolution
  53. */
  54. public OFFontLoader(URI fontFileURI, InternalResourceResolver resourceResolver) {
  55. this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, true, resourceResolver, false);
  56. }
  57. /**
  58. * Additional constructor for TrueType Collections.
  59. * @param fontFileURI the URI representing the font file
  60. * @param subFontName the sub-fontname of a font in a TrueType Collection (or null for normal
  61. * TrueType fonts)
  62. * @param embedded indicates whether the font is embedded or referenced
  63. * @param embeddingMode the embedding mode of the font
  64. * @param encodingMode the requested encoding mode
  65. * @param useKerning true to enable loading kerning info if available, false to disable
  66. * @param useAdvanced true to enable loading advanced info if available, false to disable
  67. * @param resolver the FontResolver for font URI resolution
  68. */
  69. public OFFontLoader(URI fontFileURI, String subFontName, boolean embedded,
  70. EmbeddingMode embeddingMode, EncodingMode encodingMode, boolean useKerning,
  71. boolean useAdvanced, InternalResourceResolver resolver, boolean embedAsType1) {
  72. super(fontFileURI, embedded, useKerning, useAdvanced, resolver);
  73. this.subFontName = subFontName;
  74. this.encodingMode = encodingMode;
  75. this.embeddingMode = embeddingMode;
  76. this.embedAsType1 = embedAsType1;
  77. if (this.encodingMode == EncodingMode.AUTO) {
  78. this.encodingMode = EncodingMode.CID; //Default to CID mode for TrueType
  79. }
  80. if (this.embeddingMode == EmbeddingMode.AUTO) {
  81. this.embeddingMode = EmbeddingMode.SUBSET;
  82. }
  83. }
  84. /** {@inheritDoc} */
  85. protected void read() throws IOException {
  86. read(this.subFontName);
  87. }
  88. /**
  89. * Reads a TrueType font.
  90. * @param ttcFontName the TrueType sub-font name of TrueType Collection (may be null for
  91. * normal TrueType fonts)
  92. * @throws IOException if an I/O error occurs
  93. */
  94. private void read(String ttcFontName) throws IOException {
  95. InputStream in = resourceResolver.getResource(this.fontFileURI);
  96. try {
  97. FontFileReader reader = new FontFileReader(in);
  98. String header = readHeader(reader);
  99. boolean isCFF = header.equals("OTTO");
  100. OpenFont otf = (isCFF) ? new OTFFile(useKerning, useAdvanced) : new TTFFile(useKerning, useAdvanced);
  101. boolean supported = otf.readFont(reader, header, ttcFontName);
  102. if (!supported) {
  103. throw new IOException("The font does not have a Unicode cmap table: " + fontFileURI);
  104. }
  105. buildFont(otf, ttcFontName, embedAsType1);
  106. loaded = true;
  107. } finally {
  108. IOUtils.closeQuietly(in);
  109. }
  110. }
  111. public static String readHeader(FontFileReader fontFile) throws IOException {
  112. if (fontFile != null) {
  113. fontFile.seekSet(0);
  114. return fontFile.readTTFString(4); // TTF_FIXED_SIZE (4 bytes)
  115. }
  116. return null;
  117. }
  118. private void buildFont(OpenFont otf, String ttcFontName, boolean embedAsType1) {
  119. boolean isCid = this.embedded;
  120. if (this.encodingMode == EncodingMode.SINGLE_BYTE) {
  121. isCid = false;
  122. }
  123. if (isCid) {
  124. if (otf instanceof OTFFile && embedAsType1) {
  125. multiFont = new CFFToType1Font(resourceResolver, embeddingMode);
  126. } else {
  127. multiFont = new MultiByteFont(resourceResolver, embeddingMode);
  128. }
  129. multiFont.setIsOTFFile(otf instanceof OTFFile);
  130. returnFont = multiFont;
  131. multiFont.setTTCName(ttcFontName);
  132. } else {
  133. singleFont = new SingleByteFont(resourceResolver, embeddingMode);
  134. returnFont = singleFont;
  135. }
  136. returnFont.setFontURI(fontFileURI);
  137. if (!otf.getEmbedFontName().equals("")) {
  138. returnFont.setFontName(otf.getEmbedFontName());
  139. } else {
  140. returnFont.setFontName(otf.getPostScriptName());
  141. }
  142. returnFont.setFullName(otf.getFullName());
  143. returnFont.setFamilyNames(otf.getFamilyNames());
  144. returnFont.setFontSubFamilyName(otf.getSubFamilyName());
  145. returnFont.setCapHeight(otf.getCapHeight());
  146. returnFont.setXHeight(otf.getXHeight());
  147. returnFont.setAscender(otf.getLowerCaseAscent());
  148. returnFont.setDescender(otf.getLowerCaseDescent());
  149. returnFont.setFontBBox(otf.getFontBBox());
  150. returnFont.setUnderlinePosition(otf.getUnderlinePosition() - otf.getUnderlineThickness() / 2);
  151. returnFont.setUnderlineThickness(otf.getUnderlineThickness());
  152. returnFont.setStrikeoutPosition(otf.getStrikeoutPosition() - otf.getStrikeoutThickness() / 2);
  153. returnFont.setStrikeoutThickness(otf.getStrikeoutThickness());
  154. returnFont.setFlags(otf.getFlags());
  155. returnFont.setStemV(Integer.parseInt(otf.getStemV())); //not used for TTF
  156. returnFont.setItalicAngle(Integer.parseInt(otf.getItalicAngle()));
  157. returnFont.setMissingWidth(0);
  158. returnFont.setWeight(otf.getWeightClass());
  159. if (isCid) {
  160. if (otf instanceof OTFFile) {
  161. multiFont.setCIDType(CIDFontType.CIDTYPE0);
  162. } else {
  163. multiFont.setCIDType(CIDFontType.CIDTYPE2);
  164. }
  165. multiFont.setWidthArray(otf.getWidths());
  166. multiFont.setBBoxArray(otf.getBoundingBoxes());
  167. } else {
  168. singleFont.setFontType(FontType.TRUETYPE);
  169. singleFont.setEncoding(otf.getCharSetName());
  170. returnFont.setFirstChar(otf.getFirstChar());
  171. returnFont.setLastChar(otf.getLastChar());
  172. singleFont.setTrueTypePostScriptVersion(otf.getPostScriptVersion());
  173. copyGlyphMetricsSingleByte(otf);
  174. }
  175. returnFont.setCMap(getCMap(otf));
  176. if (otf.getKerning() != null && useKerning) {
  177. copyKerning(otf, isCid);
  178. }
  179. if (useAdvanced) {
  180. copyAdvanced(otf);
  181. }
  182. if (this.embedded) {
  183. if (otf.isEmbeddable()) {
  184. returnFont.setEmbedURI(this.fontFileURI);
  185. } else {
  186. String msg = "The font " + this.fontFileURI + " is not embeddable due to a"
  187. + " licensing restriction.";
  188. throw new RuntimeException(msg);
  189. }
  190. }
  191. }
  192. private CMapSegment[] getCMap(OpenFont otf) {
  193. CMapSegment[] array = new CMapSegment[otf.getCMaps().size()];
  194. return otf.getCMaps().toArray(array);
  195. }
  196. private void copyGlyphMetricsSingleByte(OpenFont otf) {
  197. int[] wx = otf.getWidths();
  198. Rectangle[] bboxes = otf.getBoundingBoxes();
  199. for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) {
  200. singleFont.setWidth(i, otf.getCharWidth(i));
  201. int[] bbox = otf.getBBox(i);
  202. singleFont.setBoundingBox(i,
  203. new Rectangle(bbox[0], bbox[1], bbox[2] - bbox[0], bbox[3] - bbox[1]));
  204. }
  205. for (CMapSegment segment : otf.getCMaps()) {
  206. if (segment.getUnicodeStart() < 0xFFFE) {
  207. for (char u = (char)segment.getUnicodeStart(); u <= segment.getUnicodeEnd(); u++) {
  208. int codePoint = singleFont.getEncoding().mapChar(u);
  209. if (codePoint <= 0) {
  210. int glyphIndex = segment.getGlyphStartIndex() + u - segment.getUnicodeStart();
  211. String glyphName = otf.getGlyphName(glyphIndex);
  212. if (glyphName.length() == 0 && otf.getPostScriptVersion() != PostScriptVersion.V2) {
  213. glyphName = "u" + HexEncoder.encode(u);
  214. }
  215. if (glyphName.length() > 0) {
  216. String unicode = Character.toString(u);
  217. NamedCharacter nc = new NamedCharacter(glyphName, unicode);
  218. singleFont.addUnencodedCharacter(nc, wx[glyphIndex], bboxes[glyphIndex]);
  219. }
  220. }
  221. }
  222. }
  223. }
  224. }
  225. /**
  226. * Copy kerning information.
  227. */
  228. private void copyKerning(OpenFont otf, boolean isCid) {
  229. // Get kerning
  230. Set<Integer> kerningSet;
  231. if (isCid) {
  232. kerningSet = otf.getKerning().keySet();
  233. } else {
  234. kerningSet = otf.getAnsiKerning().keySet();
  235. }
  236. for (Integer kpx1 : kerningSet) {
  237. Map<Integer, Integer> h2;
  238. if (isCid) {
  239. h2 = otf.getKerning().get(kpx1);
  240. } else {
  241. h2 = otf.getAnsiKerning().get(kpx1);
  242. }
  243. returnFont.putKerningEntry(kpx1, h2);
  244. }
  245. }
  246. /**
  247. * Copy advanced typographic information.
  248. */
  249. private void copyAdvanced(OpenFont otf) {
  250. if (returnFont instanceof MultiByteFont) {
  251. MultiByteFont mbf = (MultiByteFont) returnFont;
  252. mbf.setGDEF(otf.getGDEF());
  253. mbf.setGSUB(otf.getGSUB());
  254. mbf.setGPOS(otf.getGPOS());
  255. }
  256. }
  257. }