您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

OFFontLoader.java 11KB

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