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.

AFPFontConfig.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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.render.afp;
  19. import java.io.IOException;
  20. import java.util.ArrayList;
  21. import java.util.Collections;
  22. import java.util.List;
  23. import org.apache.avalon.framework.configuration.Configuration;
  24. import org.apache.avalon.framework.configuration.ConfigurationException;
  25. import org.apache.commons.logging.Log;
  26. import org.apache.commons.logging.LogFactory;
  27. import org.apache.fop.afp.AFPEventProducer;
  28. import org.apache.fop.afp.fonts.AFPFont;
  29. import org.apache.fop.afp.fonts.AFPFontInfo;
  30. import org.apache.fop.afp.fonts.CharacterSet;
  31. import org.apache.fop.afp.fonts.CharacterSetBuilder;
  32. import org.apache.fop.afp.fonts.CharacterSetType;
  33. import org.apache.fop.afp.fonts.DoubleByteFont;
  34. import org.apache.fop.afp.fonts.OutlineFont;
  35. import org.apache.fop.afp.fonts.RasterFont;
  36. import org.apache.fop.afp.util.AFPResourceAccessor;
  37. import org.apache.fop.apps.FOPException;
  38. import org.apache.fop.apps.io.InternalResourceResolver;
  39. import org.apache.fop.events.EventProducer;
  40. import org.apache.fop.fonts.FontConfig;
  41. import org.apache.fop.fonts.FontManager;
  42. import org.apache.fop.fonts.FontManagerConfigurator;
  43. import org.apache.fop.fonts.FontTriplet;
  44. import org.apache.fop.fonts.FontTriplet.Matcher;
  45. import org.apache.fop.fonts.FontUtil;
  46. import org.apache.fop.fonts.Typeface;
  47. /**
  48. * The config object for AFP fonts, these differ from the the more generic fonts (TTF and Type1).
  49. */
  50. public final class AFPFontConfig implements FontConfig {
  51. private static final Log LOG = LogFactory.getLog(AFPFontConfig.class);
  52. private final List<AFPFontConfigData> fontsConfig;
  53. private AFPFontConfig() {
  54. fontsConfig = new ArrayList<AFPFontConfigData>();
  55. }
  56. /**
  57. * Returns a list of AFP font configuration data.
  58. * @return the AFP font config data
  59. */
  60. public List<AFPFontConfigData> getFontConfig() {
  61. return fontsConfig;
  62. }
  63. /**
  64. * The parser for AFP font data.
  65. */
  66. static final class AFPFontInfoConfigParser implements FontConfigParser {
  67. /** {@inheritDoc}} */
  68. public AFPFontConfig parse(Configuration cfg, FontManager fontManager, boolean strict,
  69. EventProducer eventProducer) throws FOPException {
  70. try {
  71. return new ParserHelper(cfg, fontManager, strict,
  72. (AFPEventProducer) eventProducer).fontConfig;
  73. } catch (ConfigurationException ce) {
  74. throw new FOPException(ce);
  75. }
  76. }
  77. AFPFontConfig getEmptyConfig() {
  78. return new AFPFontConfig();
  79. }
  80. }
  81. private static final class AggregateMatcher implements Matcher {
  82. private final List<Matcher> matchers;
  83. private AggregateMatcher(Matcher... matchers) {
  84. this.matchers = new ArrayList<Matcher>();
  85. for (Matcher matcher : matchers) {
  86. if (matcher != null) {
  87. this.matchers.add(matcher);
  88. }
  89. }
  90. }
  91. public boolean matches(FontTriplet triplet) {
  92. for (Matcher matcher : matchers) {
  93. if (matcher.matches(triplet)) {
  94. return true;
  95. }
  96. }
  97. return false;
  98. }
  99. }
  100. private static final class ParserHelper {
  101. private static final Log LOG = LogFactory.getLog(ParserHelper.class);
  102. private final AFPFontConfig fontConfig;
  103. private final Matcher matcher;
  104. private ParserHelper(Configuration cfg, FontManager fontManager, boolean strict,
  105. AFPEventProducer eventProducer) throws FOPException, ConfigurationException {
  106. Configuration fonts = cfg.getChild("fonts");
  107. Matcher localMatcher = null;
  108. Configuration referencedFontsCfg = fonts.getChild("referenced-fonts", false);
  109. if (referencedFontsCfg != null) {
  110. localMatcher = FontManagerConfigurator.createFontsMatcher(referencedFontsCfg, strict);
  111. }
  112. matcher = new AggregateMatcher(fontManager.getReferencedFontsMatcher(), localMatcher);
  113. fontConfig = new AFPFontConfig();
  114. for (Configuration font : fonts.getChildren("font")) {
  115. buildFont(font, eventProducer);
  116. }
  117. }
  118. private void buildFont(Configuration fontCfg, AFPEventProducer eventProducer)
  119. throws ConfigurationException {
  120. //FontManager fontManager = this.userAgent.getFontManager();
  121. Configuration[] triplets = fontCfg.getChildren("font-triplet");
  122. List<FontTriplet> tripletList = new ArrayList<FontTriplet>();
  123. if (triplets.length == 0) {
  124. eventProducer.fontConfigMissing(this, "<font-triplet...", fontCfg.getLocation());
  125. return;
  126. }
  127. for (Configuration triplet : triplets) {
  128. int weight = FontUtil.parseCSS2FontWeight(triplet.getAttribute("weight"));
  129. FontTriplet fontTriplet = new FontTriplet(triplet.getAttribute("name"),
  130. triplet.getAttribute("style"), weight);
  131. tripletList.add(fontTriplet);
  132. }
  133. //build the fonts
  134. Configuration[] config = fontCfg.getChildren("afp-font");
  135. if (config.length == 0) {
  136. eventProducer.fontConfigMissing(this, "<afp-font...", fontCfg.getLocation());
  137. return;
  138. }
  139. Configuration afpFontCfg = config[0];
  140. String uri = afpFontCfg.getAttribute("base-uri", null);
  141. try {
  142. String type = afpFontCfg.getAttribute("type");
  143. if (type == null) {
  144. eventProducer.fontConfigMissing(this, "type attribute", fontCfg.getLocation());
  145. return;
  146. }
  147. String codepage = afpFontCfg.getAttribute("codepage");
  148. if (codepage == null) {
  149. eventProducer.fontConfigMissing(this, "codepage attribute",
  150. fontCfg.getLocation());
  151. return;
  152. }
  153. String encoding = afpFontCfg.getAttribute("encoding");
  154. if (encoding == null) {
  155. eventProducer.fontConfigMissing(this, "encoding attribute",
  156. fontCfg.getLocation());
  157. return;
  158. }
  159. fontFromType(tripletList, type, codepage, encoding, afpFontCfg, eventProducer, uri);
  160. } catch (ConfigurationException ce) {
  161. eventProducer.invalidConfiguration(this, ce);
  162. }
  163. }
  164. private void fontFromType(List<FontTriplet> fontTriplets, String type, String codepage,
  165. String encoding, Configuration cfg, AFPEventProducer eventProducer, String embedURI)
  166. throws ConfigurationException {
  167. AFPFontConfigData config = null;
  168. if ("raster".equalsIgnoreCase(type)) {
  169. config = getRasterFont(fontTriplets, type, codepage, encoding, cfg, eventProducer,
  170. embedURI);
  171. } else if ("outline".equalsIgnoreCase(type)) {
  172. config = getOutlineFont(fontTriplets, type, codepage, encoding, cfg, eventProducer,
  173. embedURI);
  174. } else if ("CIDKeyed".equalsIgnoreCase(type)) {
  175. config = getCIDKeyedFont(fontTriplets, type, codepage, encoding, cfg,
  176. eventProducer,
  177. embedURI);
  178. } else {
  179. LOG.error("No or incorrect type attribute: " + type);
  180. }
  181. if (config != null) {
  182. fontConfig.fontsConfig.add(config);
  183. }
  184. }
  185. private CIDKeyedFontConfig getCIDKeyedFont(List<FontTriplet> fontTriplets, String type,
  186. String codepage, String encoding, Configuration cfg, AFPEventProducer eventProducer,
  187. String uri) throws ConfigurationException {
  188. String characterset = cfg.getAttribute("characterset");
  189. if (characterset == null) {
  190. eventProducer.fontConfigMissing(this, "characterset attribute",
  191. cfg.getLocation());
  192. return null;
  193. }
  194. String name = cfg.getAttribute("name", characterset);
  195. CharacterSetType charsetType = cfg.getAttributeAsBoolean("ebcdic-dbcs", false)
  196. ? CharacterSetType.DOUBLE_BYTE_LINE_DATA : CharacterSetType.DOUBLE_BYTE;
  197. return new CIDKeyedFontConfig(fontTriplets, type, codepage, encoding, characterset,
  198. name, charsetType, isEmbbedable(fontTriplets), uri);
  199. }
  200. private OutlineFontConfig getOutlineFont(List<FontTriplet> fontTriplets, String type,
  201. String codepage, String encoding, Configuration cfg,
  202. AFPEventProducer eventProducer, String uri) throws ConfigurationException {
  203. String characterset = cfg.getAttribute("characterset");
  204. if (characterset == null) {
  205. eventProducer.fontConfigMissing(this, "characterset attribute",
  206. cfg.getLocation());
  207. return null;
  208. }
  209. String name = cfg.getAttribute("name", characterset);
  210. String base14 = cfg.getAttribute("base14-font", null);
  211. return new OutlineFontConfig(fontTriplets, type, codepage, encoding, characterset,
  212. name, base14, isEmbbedable(fontTriplets), uri);
  213. }
  214. private RasterFontConfig getRasterFont(List<FontTriplet> triplets, String type,
  215. String codepage, String encoding, Configuration cfg,
  216. AFPEventProducer eventProducer, String uri)
  217. throws ConfigurationException {
  218. String name = cfg.getAttribute("name", "Unknown");
  219. // Create a new font object
  220. Configuration[] rasters = cfg.getChildren("afp-raster-font");
  221. if (rasters.length == 0) {
  222. eventProducer.fontConfigMissing(this, "<afp-raster-font...",
  223. cfg.getLocation());
  224. return null;
  225. }
  226. List<RasterCharactersetData> charsetData = new ArrayList<RasterCharactersetData>();
  227. for (Configuration rasterCfg : rasters) {
  228. String characterset = rasterCfg.getAttribute("characterset");
  229. if (characterset == null) {
  230. eventProducer.fontConfigMissing(this, "characterset attribute",
  231. cfg.getLocation());
  232. return null;
  233. }
  234. float size = rasterCfg.getAttributeAsFloat("size");
  235. int sizeMpt = (int) (size * 1000);
  236. String base14 = rasterCfg.getAttribute("base14-font", null);
  237. charsetData.add(new RasterCharactersetData(characterset, sizeMpt, base14));
  238. }
  239. return new RasterFontConfig(triplets, type, codepage, encoding, null, name, uri, charsetData,
  240. isEmbbedable(triplets));
  241. }
  242. private boolean isEmbbedable(List<FontTriplet> triplets) {
  243. for (FontTriplet triplet : triplets) {
  244. if (matcher.matches(triplet)) {
  245. return false;
  246. }
  247. }
  248. return true;
  249. }
  250. }
  251. abstract static class AFPFontConfigData {
  252. private final List<FontTriplet> triplets;
  253. private final String codePage;
  254. private final String encoding;
  255. private final String name;
  256. private final boolean embeddable;
  257. private final String uri;
  258. AFPFontConfigData(List<FontTriplet> triplets, String type, String codePage,
  259. String encoding, String name, boolean embeddable, String uri) {
  260. this.triplets = Collections.unmodifiableList(triplets);
  261. this.codePage = codePage;
  262. this.encoding = encoding;
  263. this.name = name;
  264. this.embeddable = embeddable;
  265. this.uri = uri;
  266. }
  267. static AFPFontInfo getFontInfo(AFPFont font, AFPFontConfigData config) {
  268. return font != null ? new AFPFontInfo(font, config.triplets) : null;
  269. }
  270. abstract AFPFontInfo getFontInfo(InternalResourceResolver resourceResolver,
  271. AFPEventProducer eventProducer) throws IOException;
  272. AFPResourceAccessor getAccessor(InternalResourceResolver resourceResolver) {
  273. return new AFPResourceAccessor(resourceResolver, uri);
  274. }
  275. }
  276. static final class CIDKeyedFontConfig extends AFPFontConfigData {
  277. private final CharacterSetType charsetType;
  278. private final String characterset;
  279. private CIDKeyedFontConfig(List<FontTriplet> triplets, String type, String codePage, String encoding,
  280. String characterset, String name, CharacterSetType charsetType, boolean embeddable, String uri) {
  281. super(triplets, type, codePage, encoding, name, embeddable, uri);
  282. this.characterset = characterset;
  283. this.charsetType = charsetType;
  284. }
  285. @Override
  286. AFPFontInfo getFontInfo(InternalResourceResolver resourceResolver, AFPEventProducer eventProducer)
  287. throws IOException {
  288. AFPResourceAccessor accessor = getAccessor(resourceResolver);
  289. CharacterSet characterSet = CharacterSetBuilder.getDoubleByteInstance().buildDBCS(
  290. characterset, super.codePage, super.encoding, charsetType, accessor, eventProducer);
  291. return getFontInfo(new DoubleByteFont(super.codePage, super.embeddable, characterSet,
  292. eventProducer), this);
  293. }
  294. }
  295. static final class OutlineFontConfig extends AFPFontConfigData {
  296. private final String base14;
  297. private final String characterset;
  298. private OutlineFontConfig(List<FontTriplet> triplets, String type, String codePage,
  299. String encoding, String characterset, String name, String base14, boolean embeddable, String uri) {
  300. super(triplets, type, codePage, encoding, name, embeddable, uri);
  301. this.characterset = characterset;
  302. this.base14 = base14;
  303. }
  304. @Override
  305. AFPFontInfo getFontInfo(InternalResourceResolver resourceResolver, AFPEventProducer eventProducer)
  306. throws IOException {
  307. CharacterSet characterSet = null;
  308. if (base14 != null) {
  309. try {
  310. Typeface tf = getTypeFace(base14);
  311. characterSet = CharacterSetBuilder.getSingleByteInstance()
  312. .build(characterset, super.codePage,
  313. super.encoding, tf, eventProducer);
  314. } catch (ClassNotFoundException cnfe) {
  315. String msg = "The base 14 font class for " + characterset
  316. + " could not be found";
  317. LOG.error(msg);
  318. }
  319. } else {
  320. AFPResourceAccessor accessor = getAccessor(resourceResolver);
  321. characterSet = CharacterSetBuilder.getSingleByteInstance().buildSBCS(
  322. characterset, super.codePage, super.encoding, accessor, eventProducer);
  323. }
  324. return getFontInfo(new OutlineFont(super.name, super.embeddable, characterSet,
  325. eventProducer), this);
  326. }
  327. }
  328. private static Typeface getTypeFace(String base14Name) throws ClassNotFoundException {
  329. try {
  330. Class<? extends Typeface> clazz = Class.forName("org.apache.fop.fonts.base14."
  331. + base14Name).asSubclass(Typeface.class);
  332. return clazz.newInstance();
  333. } catch (IllegalAccessException iae) {
  334. LOG.error(iae.getMessage());
  335. } catch (ClassNotFoundException cnfe) {
  336. LOG.error(cnfe.getMessage());
  337. } catch (InstantiationException ie) {
  338. LOG.error(ie.getMessage());
  339. }
  340. throw new ClassNotFoundException("Couldn't load file for AFP font with base14 name: "
  341. + base14Name);
  342. }
  343. static final class RasterFontConfig extends AFPFontConfigData {
  344. private final List<RasterCharactersetData> charsets;
  345. private RasterFontConfig(List<FontTriplet> triplets, String type, String codePage,
  346. String encoding, String characterset, String name, String uri,
  347. List<RasterCharactersetData> csetData, boolean embeddable) {
  348. super(triplets, type, codePage, encoding, name, embeddable, uri);
  349. this.charsets = Collections.unmodifiableList(csetData);
  350. }
  351. @Override
  352. AFPFontInfo getFontInfo(InternalResourceResolver resourceResolver, AFPEventProducer eventProducer)
  353. throws IOException {
  354. RasterFont rasterFont = new RasterFont(super.name, super.embeddable);
  355. for (RasterCharactersetData charset : charsets) {
  356. if (charset.base14 != null) {
  357. try {
  358. Typeface tf = getTypeFace(charset.base14);
  359. rasterFont.addCharacterSet(charset.size,
  360. CharacterSetBuilder.getSingleByteInstance().build(
  361. charset.characterset, super.codePage, super.encoding,
  362. tf, eventProducer));
  363. } catch (ClassNotFoundException cnfe) {
  364. String msg = "The base 14 font class for " + charset.characterset
  365. + " could not be found";
  366. LOG.error(msg);
  367. } catch (IOException ie) {
  368. String msg = "The base 14 font class " + charset.characterset
  369. + " could not be instantiated";
  370. LOG.error(msg);
  371. }
  372. } else {
  373. AFPResourceAccessor accessor = getAccessor(resourceResolver);
  374. rasterFont.addCharacterSet(charset.size,
  375. CharacterSetBuilder.getSingleByteInstance().buildSBCS(charset.characterset,
  376. super.codePage, super.encoding, accessor, eventProducer));
  377. }
  378. }
  379. return getFontInfo(rasterFont, this);
  380. }
  381. }
  382. static final class RasterCharactersetData {
  383. private final String characterset;
  384. private final int size;
  385. private final String base14;
  386. private RasterCharactersetData(String characterset, int size, String base14) {
  387. this.characterset = characterset;
  388. this.size = size;
  389. this.base14 = base14;
  390. }
  391. }
  392. }