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.

CharacterSetBuilder.java 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  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.afp.fonts;
  19. import java.io.FileNotFoundException;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.net.URI;
  23. import java.net.URISyntaxException;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.WeakHashMap;
  27. import org.apache.commons.logging.Log;
  28. import org.apache.commons.logging.LogFactory;
  29. import org.apache.fop.afp.AFPConstants;
  30. import org.apache.fop.afp.util.ResourceAccessor;
  31. import org.apache.fop.afp.util.StructuredFieldReader;
  32. import org.apache.fop.fonts.Typeface;
  33. import org.apache.xmlgraphics.image.loader.util.SoftMapCache;
  34. /**
  35. * The CharacterSetBuilder is responsible building the a CharacterSet instance that holds
  36. * the font metric data. The data is either read from disk and passed to a CharacterSet (*)
  37. * or a FopCharacterSet is instantiated that is composed of a Typeface instance configured
  38. * with this data.<p/>
  39. * -*- For referenced fonts CharacterSetBuilder is responsible for reading the font attributes
  40. * from binary code page files and the character set metric files. In IBM font structure, a
  41. * code page maps each character of text to the characters in a character set.
  42. * Each character is translated into a code point. When the character is
  43. * printed, each code point is matched to a character ID on the code page
  44. * specified. The character ID is then matched to the image (raster pattern or
  45. * outline pattern) of the character in the character set specified. The image
  46. * in the character set is the image that is printed in the document. To be a
  47. * valid code page for a particular character set, all character IDs in the code
  48. * page must be included in that character set. <p/>This class will read the
  49. * font information from the binary code page files and character set metric
  50. * files in order to determine the correct metrics to use when rendering the
  51. * formatted object. <p/>
  52. *
  53. */
  54. public class CharacterSetBuilder {
  55. /**
  56. * Static logging instance
  57. */
  58. protected static final Log LOG = LogFactory.getLog(CharacterSetBuilder.class);
  59. /**
  60. * Singleton reference
  61. */
  62. private static CharacterSetBuilder instance;
  63. /**
  64. * Template used to convert lists to arrays.
  65. */
  66. private static final CharacterSetOrientation[] EMPTY_CSO_ARRAY = new CharacterSetOrientation[0];
  67. /** Codepage MO:DCA structured field. */
  68. private static final byte[] CODEPAGE_SF = new byte[] {
  69. (byte) 0xD3, (byte) 0xA8, (byte) 0x87};
  70. /** Character table MO:DCA structured field. */
  71. private static final byte[] CHARACTER_TABLE_SF = new byte[] {
  72. (byte) 0xD3, (byte) 0x8C, (byte) 0x87};
  73. /** Font descriptor MO:DCA structured field. */
  74. private static final byte[] FONT_DESCRIPTOR_SF = new byte[] {
  75. (byte) 0xD3, (byte) 0xA6, (byte) 0x89 };
  76. /** Font control MO:DCA structured field. */
  77. private static final byte[] FONT_CONTROL_SF = new byte[] {
  78. (byte) 0xD3, (byte) 0xA7, (byte) 0x89 };
  79. /** Font orientation MO:DCA structured field. */
  80. private static final byte[] FONT_ORIENTATION_SF = new byte[] {
  81. (byte) 0xD3, (byte) 0xAE, (byte) 0x89 };
  82. /** Font position MO:DCA structured field. */
  83. private static final byte[] FONT_POSITION_SF = new byte[] {
  84. (byte) 0xD3, (byte) 0xAC, (byte) 0x89 };
  85. /** Font index MO:DCA structured field. */
  86. private static final byte[] FONT_INDEX_SF = new byte[] {
  87. (byte) 0xD3, (byte) 0x8C, (byte) 0x89 };
  88. /**
  89. * The collection of code pages
  90. */
  91. private final Map/*<String, Map<String, String>>*/ codePagesCache
  92. = new WeakHashMap/*<String, Map<String, String>>*/();
  93. /**
  94. * Cache of charactersets
  95. */
  96. private final SoftMapCache characterSetsCache = new SoftMapCache(true);
  97. /** Default constructor. */
  98. protected CharacterSetBuilder() {
  99. }
  100. /**
  101. * Factory method for the single-byte implementation of AFPFontReader.
  102. * @return AFPFontReader
  103. */
  104. public static CharacterSetBuilder getInstance() {
  105. if (instance == null) {
  106. instance = new CharacterSetBuilder();
  107. }
  108. return instance;
  109. }
  110. /**
  111. * Factory method for the double-byte (CID Keyed font (Type 0)) implementation of AFPFontReader.
  112. * @return AFPFontReader
  113. */
  114. public static CharacterSetBuilder getDoubleByteInstance() {
  115. return new DoubleByteLoader();
  116. }
  117. /**
  118. * Returns an InputStream to a given file path and filename
  119. *
  120. * * @param accessor the resource accessor
  121. * @param filename the file name
  122. * @return an inputStream
  123. *
  124. * @throws IOException in the event that an I/O exception of some sort has occurred
  125. */
  126. protected InputStream openInputStream(ResourceAccessor accessor, String filename)
  127. throws IOException {
  128. URI uri;
  129. try {
  130. uri = new URI(filename.trim());
  131. } catch (URISyntaxException e) {
  132. throw new FileNotFoundException("Invalid filename: "
  133. + filename + " (" + e.getMessage() + ")");
  134. }
  135. if (LOG.isDebugEnabled()) {
  136. LOG.debug("Opening " + uri);
  137. }
  138. InputStream inputStream = accessor.createInputStream(uri);
  139. return inputStream;
  140. }
  141. /**
  142. * Closes the inputstream
  143. *
  144. * @param inputStream the inputstream to close
  145. */
  146. protected void closeInputStream(InputStream inputStream) {
  147. try {
  148. if (inputStream != null) {
  149. inputStream.close();
  150. }
  151. } catch (Exception ex) {
  152. // Lets log at least!
  153. LOG.error(ex.getMessage());
  154. }
  155. }
  156. /**
  157. * Load the font details and metrics into the CharacterSetMetric object, this will use the
  158. * actual afp code page and character set files to load the object with the necessary metrics.
  159. *
  160. * @param characterSetName name of the characterset
  161. * @param codePageName name of the code page file
  162. * @param encoding encoding name
  163. * @param accessor used to load codepage and characterset
  164. * @return CharacterSet object
  165. * @throws IOException if an I/O error occurs
  166. */
  167. public CharacterSet build(String characterSetName, String codePageName, String encoding,
  168. ResourceAccessor accessor) throws IOException {
  169. return processFont(characterSetName, codePageName, encoding, false, accessor);
  170. }
  171. /**
  172. * Load the font details and metrics into the CharacterSetMetric object, this will use the
  173. * actual afp code page and character set files to load the object with the necessary metrics.
  174. * This method is to be used for double byte character sets (DBCS).
  175. *
  176. * @param characterSetName name of the characterset
  177. * @param codePageName name of the code page file
  178. * @param encoding encoding name
  179. * @param isEDBCS if this is an EBCDIC double byte character set (DBCS)
  180. * @param accessor used to load codepage and characterset
  181. * @return CharacterSet object
  182. * @throws IOException if an I/O error occurs
  183. */
  184. public CharacterSet buildDBCS(String characterSetName, String codePageName, String encoding,
  185. boolean isEDBCS, ResourceAccessor accessor) throws IOException {
  186. return processFont(characterSetName, codePageName, encoding, isEDBCS, accessor);
  187. }
  188. /**
  189. * Load the font details and metrics into the CharacterSetMetric object, this will use the
  190. * actual afp code page and character set files to load the object with the necessary metrics.
  191. *
  192. * @param characterSetName the CharacterSetMetric object to populate
  193. * @param codePageName the name of the code page to use
  194. * @param encoding name of the encoding in use
  195. * @param typeface base14 font name
  196. * @return CharacterSet object
  197. * @throws IOException if an I/O error occurs
  198. */
  199. public CharacterSet build(String characterSetName, String codePageName, String encoding,
  200. Typeface typeface) throws IOException {
  201. return new FopCharacterSet(codePageName, encoding, characterSetName, typeface);
  202. }
  203. private CharacterSet processFont(String characterSetName, String codePageName, String encoding,
  204. boolean isEDBCS, ResourceAccessor accessor) throws IOException {
  205. // check for cached version of the characterset
  206. String descriptor = characterSetName + "_" + encoding + "_" + codePageName;
  207. CharacterSet characterSet = (CharacterSet)characterSetsCache.get(descriptor);
  208. if (characterSet != null) {
  209. return characterSet;
  210. }
  211. // characterset not in the cache, so recreating
  212. characterSet = new CharacterSet(codePageName, encoding, isEDBCS, characterSetName,
  213. accessor);
  214. InputStream inputStream = null;
  215. try {
  216. /**
  217. * Get the code page which contains the character mapping
  218. * information to map the unicode character id to the graphic
  219. * chracter global identifier.
  220. */
  221. Map/*<String,String>*/ codePage
  222. = (Map/*<String,String>*/)codePagesCache.get(codePageName);
  223. if (codePage == null) {
  224. codePage = loadCodePage(codePageName, encoding, accessor);
  225. codePagesCache.put(codePageName, codePage);
  226. }
  227. inputStream = openInputStream(accessor, characterSetName);
  228. StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream);
  229. // Process D3A689 Font Descriptor
  230. FontDescriptor fontDescriptor = processFontDescriptor(structuredFieldReader);
  231. characterSet.setNominalVerticalSize(fontDescriptor.getNominalFontSizeInMillipoints());
  232. // Process D3A789 Font Control
  233. FontControl fontControl = processFontControl(structuredFieldReader);
  234. if (fontControl != null) {
  235. //process D3AE89 Font Orientation
  236. CharacterSetOrientation[] characterSetOrientations
  237. = processFontOrientation(structuredFieldReader);
  238. int metricNormalizationFactor;
  239. if (fontControl.isRelative()) {
  240. metricNormalizationFactor = 1;
  241. } else {
  242. int dpi = fontControl.getDpi();
  243. metricNormalizationFactor = 1000 * 72000
  244. / fontDescriptor.getNominalFontSizeInMillipoints() / dpi;
  245. }
  246. //process D3AC89 Font Position
  247. processFontPosition(structuredFieldReader, characterSetOrientations,
  248. metricNormalizationFactor);
  249. //process D38C89 Font Index (per orientation)
  250. for (int i = 0; i < characterSetOrientations.length; i++) {
  251. processFontIndex(structuredFieldReader,
  252. characterSetOrientations[i], codePage, metricNormalizationFactor);
  253. characterSet.addCharacterSetOrientation(characterSetOrientations[i]);
  254. }
  255. } else {
  256. throw new IOException("Missing D3AE89 Font Control structured field.");
  257. }
  258. } finally {
  259. closeInputStream(inputStream);
  260. }
  261. characterSetsCache.put(descriptor, characterSet);
  262. return characterSet;
  263. }
  264. /**
  265. * Load the code page information from the appropriate file. The file name
  266. * to load is determined by the code page name and the file extension 'CDP'.
  267. *
  268. * @param codePage
  269. * the code page identifier
  270. * @param encoding
  271. * the encoding to use for the character decoding
  272. * @param accessor the resource accessor
  273. * @return a code page mapping (key: GCGID, value: Unicode character)
  274. * @throws IOException if an I/O exception of some sort has occurred.
  275. */
  276. protected Map/*<String,String>*/ loadCodePage(String codePage, String encoding,
  277. ResourceAccessor accessor) throws IOException {
  278. // Create the HashMap to store code page information
  279. Map/*<String,String>*/ codePages = new java.util.HashMap/*<String,String>*/();
  280. InputStream inputStream = null;
  281. try {
  282. inputStream = openInputStream(accessor, codePage.trim());
  283. StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream);
  284. byte[] data = structuredFieldReader.getNext(CHARACTER_TABLE_SF);
  285. int position = 0;
  286. byte[] gcgiBytes = new byte[8];
  287. byte[] charBytes = new byte[1];
  288. // Read data, ignoring bytes 0 - 2
  289. for (int index = 3; index < data.length; index++) {
  290. if (position < 8) {
  291. // Build the graphic character global identifier key
  292. gcgiBytes[position] = data[index];
  293. position++;
  294. } else if (position == 9) {
  295. position = 0;
  296. // Set the character
  297. charBytes[0] = data[index];
  298. String gcgiString = new String(gcgiBytes,
  299. AFPConstants.EBCIDIC_ENCODING);
  300. //Use the 8-bit char index to find the Unicode character using the Java encoding
  301. //given in the configuration. If the code page and the Java encoding don't
  302. //match, a wrong Unicode character will be associated with the AFP GCGID.
  303. //Idea: we could use IBM's GCGID to Unicode map and build code pages ourselves.
  304. String charString = new String(charBytes, encoding);
  305. codePages.put(gcgiString, charString);
  306. } else {
  307. position++;
  308. }
  309. }
  310. } finally {
  311. closeInputStream(inputStream);
  312. }
  313. return codePages;
  314. }
  315. /**
  316. * Process the font descriptor details using the structured field reader.
  317. *
  318. * @param structuredFieldReader the structured field reader
  319. * @return a class representing the font descriptor
  320. * @throws IOException if an I/O exception of some sort has occurred.
  321. */
  322. protected static FontDescriptor processFontDescriptor(
  323. StructuredFieldReader structuredFieldReader)
  324. throws IOException {
  325. byte[] fndData = structuredFieldReader.getNext(FONT_DESCRIPTOR_SF);
  326. return new FontDescriptor(fndData);
  327. }
  328. /**
  329. * Process the font control details using the structured field reader.
  330. *
  331. * @param structuredFieldReader
  332. * the structured field reader
  333. * @return the FontControl
  334. * @throws IOException if an I/O exception of some sort has occurred.
  335. */
  336. protected FontControl processFontControl(StructuredFieldReader structuredFieldReader)
  337. throws IOException {
  338. byte[] fncData = structuredFieldReader.getNext(FONT_CONTROL_SF);
  339. FontControl fontControl = null;
  340. if (fncData != null) {
  341. fontControl = new FontControl();
  342. if (fncData[7] == (byte) 0x02) {
  343. fontControl.setRelative(true);
  344. }
  345. int metricResolution = getUBIN(fncData, 9);
  346. if (metricResolution == 1000) {
  347. //Special case: 1000 units per em (rather than dpi)
  348. fontControl.setUnitsPerEm(1000);
  349. } else {
  350. fontControl.setDpi(metricResolution / 10);
  351. }
  352. }
  353. return fontControl;
  354. }
  355. /**
  356. * Process the font orientation details from using the structured field
  357. * reader.
  358. *
  359. * @param structuredFieldReader
  360. * the structured field reader
  361. * @return CharacterSetOrientation array
  362. * @throws IOException if an I/O exception of some sort has occurred.
  363. */
  364. protected CharacterSetOrientation[] processFontOrientation(
  365. StructuredFieldReader structuredFieldReader) throws IOException {
  366. byte[] data = structuredFieldReader.getNext(FONT_ORIENTATION_SF);
  367. int position = 0;
  368. byte[] fnoData = new byte[26];
  369. List orientations = new java.util.ArrayList();
  370. // Read data, ignoring bytes 0 - 2
  371. for (int index = 3; index < data.length; index++) {
  372. // Build the font orientation record
  373. fnoData[position] = data[index];
  374. position++;
  375. if (position == 26) {
  376. position = 0;
  377. int orientation = determineOrientation(fnoData[2]);
  378. // Space Increment
  379. int space = ((fnoData[8] & 0xFF ) << 8) + (fnoData[9] & 0xFF);
  380. // Em-Space Increment
  381. int em = ((fnoData[14] & 0xFF ) << 8) + (fnoData[15] & 0xFF);
  382. CharacterSetOrientation cso = new CharacterSetOrientation(orientation);
  383. cso.setSpaceIncrement(space);
  384. cso.setEmSpaceIncrement(em);
  385. orientations.add(cso);
  386. }
  387. }
  388. return (CharacterSetOrientation[]) orientations
  389. .toArray(EMPTY_CSO_ARRAY);
  390. }
  391. /**
  392. * Populate the CharacterSetOrientation object in the suplied array with the
  393. * font position details using the supplied structured field reader.
  394. *
  395. * @param structuredFieldReader
  396. * the structured field reader
  397. * @param characterSetOrientations
  398. * the array of CharacterSetOrientation objects
  399. * @param metricNormalizationFactor factor to apply to the metrics to get normalized
  400. * font metric values
  401. * @throws IOException if an I/O exception of some sort has occurred.
  402. */
  403. protected void processFontPosition(StructuredFieldReader structuredFieldReader,
  404. CharacterSetOrientation[] characterSetOrientations, double metricNormalizationFactor)
  405. throws IOException {
  406. byte[] data = structuredFieldReader.getNext(FONT_POSITION_SF);
  407. int position = 0;
  408. byte[] fpData = new byte[26];
  409. int characterSetOrientationIndex = 0;
  410. // Read data, ignoring bytes 0 - 2
  411. for (int index = 3; index < data.length; index++) {
  412. if (position < 22) {
  413. // Build the font orientation record
  414. fpData[position] = data[index];
  415. if (position == 9) {
  416. CharacterSetOrientation characterSetOrientation
  417. = characterSetOrientations[characterSetOrientationIndex];
  418. int xHeight = getSBIN(fpData, 2);
  419. int capHeight = getSBIN(fpData, 4);
  420. int ascHeight = getSBIN(fpData, 6);
  421. int dscHeight = getSBIN(fpData, 8);
  422. dscHeight = dscHeight * -1;
  423. characterSetOrientation.setXHeight(
  424. (int)Math.round(xHeight * metricNormalizationFactor));
  425. characterSetOrientation.setCapHeight(
  426. (int)Math.round(capHeight * metricNormalizationFactor));
  427. characterSetOrientation.setAscender(
  428. (int)Math.round(ascHeight * metricNormalizationFactor));
  429. characterSetOrientation.setDescender(
  430. (int)Math.round(dscHeight * metricNormalizationFactor));
  431. }
  432. } else if (position == 22) {
  433. position = 0;
  434. characterSetOrientationIndex++;
  435. fpData[position] = data[index];
  436. }
  437. position++;
  438. }
  439. }
  440. /**
  441. * Process the font index details for the character set orientation.
  442. *
  443. * @param structuredFieldReader the structured field reader
  444. * @param cso the CharacterSetOrientation object to populate
  445. * @param codepage the map of code pages
  446. * @param metricNormalizationFactor factor to apply to the metrics to get normalized
  447. * font metric values
  448. * @throws IOException if an I/O exception of some sort has occurred.
  449. */
  450. protected void processFontIndex(StructuredFieldReader structuredFieldReader,
  451. CharacterSetOrientation cso, Map/*<String,String>*/ codepage,
  452. double metricNormalizationFactor)
  453. throws IOException {
  454. byte[] data = structuredFieldReader.getNext(FONT_INDEX_SF);
  455. int position = 0;
  456. byte[] gcgid = new byte[8];
  457. byte[] fiData = new byte[20];
  458. char lowest = 255;
  459. char highest = 0;
  460. String firstABCMismatch = null;
  461. // Read data, ignoring bytes 0 - 2
  462. for (int index = 3; index < data.length; index++) {
  463. if (position < 8) {
  464. gcgid[position] = data[index];
  465. position++;
  466. } else if (position < 27) {
  467. fiData[position - 8] = data[index];
  468. position++;
  469. } else if (position == 27) {
  470. fiData[position - 8] = data[index];
  471. position = 0;
  472. String gcgiString = new String(gcgid, AFPConstants.EBCIDIC_ENCODING);
  473. String idx = (String) codepage.get(gcgiString);
  474. if (idx != null) {
  475. char cidx = idx.charAt(0);
  476. int width = getUBIN(fiData, 0);
  477. int a = getSBIN(fiData, 10);
  478. int b = getUBIN(fiData, 12);
  479. int c = getSBIN(fiData, 14);
  480. int abc = a + b + c;
  481. int diff = Math.abs(abc - width);
  482. if (diff != 0 && width != 0) {
  483. double diffPercent = 100 * diff / (double)width;
  484. if (diffPercent > 2) {
  485. if (LOG.isTraceEnabled()) {
  486. LOG.trace(gcgiString + ": "
  487. + a + " + " + b + " + " + c + " = " + (a + b + c)
  488. + " but found: " + width);
  489. }
  490. if (firstABCMismatch == null) {
  491. firstABCMismatch = gcgiString;
  492. }
  493. }
  494. }
  495. if (cidx < lowest) {
  496. lowest = cidx;
  497. }
  498. if (cidx > highest) {
  499. highest = cidx;
  500. }
  501. int normalizedWidth = (int)Math.round(width * metricNormalizationFactor);
  502. cso.setWidth(cidx, normalizedWidth);
  503. }
  504. }
  505. }
  506. cso.setFirstChar(lowest);
  507. cso.setLastChar(highest);
  508. if (LOG.isDebugEnabled() && firstABCMismatch != null) {
  509. //Debug level because it usually is no problem.
  510. LOG.debug("Font has metrics inconsitencies where A+B+C doesn't equal the"
  511. + " character increment. The first such character found: "
  512. + firstABCMismatch);
  513. }
  514. }
  515. private static int getUBIN(byte[] data, int start) {
  516. return ((data[start] & 0xFF) << 8) + (data[start + 1] & 0xFF);
  517. }
  518. private static int getSBIN(byte[] data, int start) {
  519. int ubin = ((data[start] & 0xFF) << 8) + (data[start + 1] & 0xFF);
  520. if ((ubin & 0x8000) != 0) {
  521. //extend sign
  522. return ubin | 0xFFFF0000;
  523. } else {
  524. return ubin;
  525. }
  526. }
  527. private class FontControl {
  528. private int dpi;
  529. private int unitsPerEm;
  530. private boolean isRelative = false;
  531. public int getDpi() {
  532. return dpi;
  533. }
  534. public void setDpi(int i) {
  535. dpi = i;
  536. }
  537. public int getUnitsPerEm() {
  538. return this.unitsPerEm;
  539. }
  540. public void setUnitsPerEm(int value) {
  541. this.unitsPerEm = value;
  542. }
  543. public boolean isRelative() {
  544. return isRelative;
  545. }
  546. public void setRelative(boolean b) {
  547. isRelative = b;
  548. }
  549. }
  550. private static class FontDescriptor {
  551. private byte[] data;
  552. public FontDescriptor(byte[] data) {
  553. this.data = data;
  554. }
  555. public int getNominalFontSizeInMillipoints() {
  556. int nominalFontSize = 100 * getUBIN(data, 39);
  557. return nominalFontSize;
  558. }
  559. }
  560. /**
  561. * Double-byte (CID Keyed font (Type 0)) implementation of AFPFontReader.
  562. */
  563. private static class DoubleByteLoader extends CharacterSetBuilder {
  564. protected Map/*<String,String>*/ loadCodePage(String codePage, String encoding,
  565. ResourceAccessor accessor) throws IOException {
  566. // Create the HashMap to store code page information
  567. Map/*<String,String>*/ codePages = new java.util.HashMap/*<String,String>*/();
  568. InputStream inputStream = null;
  569. try {
  570. inputStream = openInputStream(accessor, codePage.trim());
  571. StructuredFieldReader structuredFieldReader
  572. = new StructuredFieldReader(inputStream);
  573. byte[] data;
  574. while ((data = structuredFieldReader.getNext(CHARACTER_TABLE_SF)) != null) {
  575. int position = 0;
  576. byte[] gcgiBytes = new byte[8];
  577. byte[] charBytes = new byte[2];
  578. // Read data, ignoring bytes 0 - 2
  579. for (int index = 3; index < data.length; index++) {
  580. if (position < 8) {
  581. // Build the graphic character global identifier key
  582. gcgiBytes[position] = data[index];
  583. position++;
  584. } else if (position == 9) {
  585. // Set the character
  586. charBytes[0] = data[index];
  587. position++;
  588. } else if (position == 10) {
  589. position = 0;
  590. // Set the character
  591. charBytes[1] = data[index];
  592. String gcgiString = new String(gcgiBytes,
  593. AFPConstants.EBCIDIC_ENCODING);
  594. String charString = new String(charBytes, encoding);
  595. codePages.put(gcgiString, charString);
  596. }
  597. else {
  598. position++;
  599. }
  600. }
  601. }
  602. } finally {
  603. closeInputStream(inputStream);
  604. }
  605. return codePages;
  606. }
  607. }
  608. private static int determineOrientation(byte orientation) {
  609. int degrees = 0;
  610. switch (orientation) {
  611. case 0x00:
  612. degrees = 0;
  613. break;
  614. case 0x2D:
  615. degrees = 90;
  616. break;
  617. case 0x5A:
  618. degrees = 180;
  619. break;
  620. case (byte) 0x87:
  621. degrees = 270;
  622. break;
  623. default:
  624. throw new IllegalStateException("Invalid orientation: " + orientation);
  625. }
  626. return degrees;
  627. }
  628. }