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 12KB

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