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.

AFMParser.java 24KB

Merged revisions 638842-642143 via svnmerge from https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk ........ r640089 | clay | 2008-03-22 22:54:27 +0100 (Sa, 22 Mrz 2008) | 1 line updates to Forrest web site (expand menu structure). ........ r640242 | adelmelle | 2008-03-23 20:36:36 +0100 (So, 23 Mrz 2008) | 11 lines Cleanup and minor refactoring: * consolidate addId() in AbstractLayoutManager * replace getPSLM().addIdToPage(getXXX().getId()) in various LMs Smaller changes/cleanup in the affected LMs include: * removal of some redundant casts * simplified conditionals * avoid duplicating the reference to the FObj as much as possible: the reference is already stored in AbstractLM, yet every subclass seems to add another reference... ........ r641742 | jeremias | 2008-03-27 09:49:41 +0100 (Do, 27 Mrz 2008) | 6 lines Added support for addressing all glyphs available in a Type 1 font, not just the ones in the font's primary encoding. Typeface: getEncoding() changed to getEncodingName() to make clearer what is held here. Some cleanup in the font classes to put the various things in more appropriate places. Created a common base class for all Base 14 fonts (makes the hierarchy clearer). Made PDFTextUtil more universally useful and made use of it in PDFRenderer, too. Made PDFStream.add(String) more efficient. The encoding converter is not called for each invocation anymore as the whole thing get buffered by a BufferedWriter (as suggested by the javadoc of OutputStreamWriter). ........ r641764 | maxberger | 2008-03-27 11:02:25 +0100 (Do, 27 Mrz 2008) | 1 line Added my key ........ r641827 | jeremias | 2008-03-27 15:29:44 +0100 (Do, 27 Mrz 2008) | 2 lines When a JPEG image is embedded, an optionally embedded color profile is filtered out as it's already embedded separately in the PDF file. Worked around a problem (PDF renderer) with JPEG image containing RGB color profiles which are not sRGB. The images drifted into yellow. The color profile is simply disabled in this case. Please let us know if you know what the problem could be. ........ git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_ProcessingFeedback@642155 13f79535-47bb-0310-9956-ffa450edef68
16 years ago
Merged revisions 638842-642143 via svnmerge from https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk ........ r640089 | clay | 2008-03-22 22:54:27 +0100 (Sa, 22 Mrz 2008) | 1 line updates to Forrest web site (expand menu structure). ........ r640242 | adelmelle | 2008-03-23 20:36:36 +0100 (So, 23 Mrz 2008) | 11 lines Cleanup and minor refactoring: * consolidate addId() in AbstractLayoutManager * replace getPSLM().addIdToPage(getXXX().getId()) in various LMs Smaller changes/cleanup in the affected LMs include: * removal of some redundant casts * simplified conditionals * avoid duplicating the reference to the FObj as much as possible: the reference is already stored in AbstractLM, yet every subclass seems to add another reference... ........ r641742 | jeremias | 2008-03-27 09:49:41 +0100 (Do, 27 Mrz 2008) | 6 lines Added support for addressing all glyphs available in a Type 1 font, not just the ones in the font's primary encoding. Typeface: getEncoding() changed to getEncodingName() to make clearer what is held here. Some cleanup in the font classes to put the various things in more appropriate places. Created a common base class for all Base 14 fonts (makes the hierarchy clearer). Made PDFTextUtil more universally useful and made use of it in PDFRenderer, too. Made PDFStream.add(String) more efficient. The encoding converter is not called for each invocation anymore as the whole thing get buffered by a BufferedWriter (as suggested by the javadoc of OutputStreamWriter). ........ r641764 | maxberger | 2008-03-27 11:02:25 +0100 (Do, 27 Mrz 2008) | 1 line Added my key ........ r641827 | jeremias | 2008-03-27 15:29:44 +0100 (Do, 27 Mrz 2008) | 2 lines When a JPEG image is embedded, an optionally embedded color profile is filtered out as it's already embedded separately in the PDF file. Worked around a problem (PDF renderer) with JPEG image containing RGB color profiles which are not sRGB. The images drifted into yellow. The color profile is simply disabled in this case. Please let us know if you know what the problem could be. ........ git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_ProcessingFeedback@642155 13f79535-47bb-0310-9956-ffa450edef68
16 years ago
Merged revisions 638842-642143 via svnmerge from https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk ........ r640089 | clay | 2008-03-22 22:54:27 +0100 (Sa, 22 Mrz 2008) | 1 line updates to Forrest web site (expand menu structure). ........ r640242 | adelmelle | 2008-03-23 20:36:36 +0100 (So, 23 Mrz 2008) | 11 lines Cleanup and minor refactoring: * consolidate addId() in AbstractLayoutManager * replace getPSLM().addIdToPage(getXXX().getId()) in various LMs Smaller changes/cleanup in the affected LMs include: * removal of some redundant casts * simplified conditionals * avoid duplicating the reference to the FObj as much as possible: the reference is already stored in AbstractLM, yet every subclass seems to add another reference... ........ r641742 | jeremias | 2008-03-27 09:49:41 +0100 (Do, 27 Mrz 2008) | 6 lines Added support for addressing all glyphs available in a Type 1 font, not just the ones in the font's primary encoding. Typeface: getEncoding() changed to getEncodingName() to make clearer what is held here. Some cleanup in the font classes to put the various things in more appropriate places. Created a common base class for all Base 14 fonts (makes the hierarchy clearer). Made PDFTextUtil more universally useful and made use of it in PDFRenderer, too. Made PDFStream.add(String) more efficient. The encoding converter is not called for each invocation anymore as the whole thing get buffered by a BufferedWriter (as suggested by the javadoc of OutputStreamWriter). ........ r641764 | maxberger | 2008-03-27 11:02:25 +0100 (Do, 27 Mrz 2008) | 1 line Added my key ........ r641827 | jeremias | 2008-03-27 15:29:44 +0100 (Do, 27 Mrz 2008) | 2 lines When a JPEG image is embedded, an optionally embedded color profile is filtered out as it's already embedded separately in the PDF file. Worked around a problem (PDF renderer) with JPEG image containing RGB color profiles which are not sRGB. The images drifted into yellow. The color profile is simply disabled in this case. Please let us know if you know what the problem could be. ........ git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_ProcessingFeedback@642155 13f79535-47bb-0310-9956-ffa450edef68
16 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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.type1;
  19. import java.awt.Rectangle;
  20. import java.beans.Statement;
  21. import java.io.BufferedReader;
  22. import java.io.File;
  23. import java.io.IOException;
  24. import java.io.InputStream;
  25. import java.io.Reader;
  26. import java.util.Map;
  27. import java.util.Stack;
  28. import org.apache.commons.io.IOUtils;
  29. import org.apache.fop.fonts.NamedCharacter;
  30. /**
  31. * Parses the contents of a Type 1 AFM font metrics file into an object structure ({@link AFMFile}).
  32. */
  33. public class AFMParser {
  34. private static final String START_FONT_METRICS = "StartFontMetrics";
  35. //private static final String END_FONT_METRICS = "EndFontMetrics";
  36. private static final String FONT_NAME = "FontName";
  37. private static final String FULL_NAME = "FullName";
  38. private static final String FAMILY_NAME = "FamilyName";
  39. private static final String WEIGHT = "Weight";
  40. private static final String FONT_BBOX = "FontBBox";
  41. private static final String ENCODING_SCHEME = "EncodingScheme";
  42. private static final String CHARACTER_SET = "CharacterSet";
  43. private static final String IS_BASE_FONT = "IsBaseFont";
  44. private static final String IS_CID_FONT = "IsCIDFont";
  45. private static final String CAP_HEIGHT = "CapHeight";
  46. private static final String X_HEIGHT = "XHeight";
  47. private static final String ASCENDER = "Ascender";
  48. private static final String DESCENDER = "Descender";
  49. private static final String STDHW = "StdHW";
  50. private static final String STDVW = "StdVW";
  51. private static final String UNDERLINE_POSITION = "UnderlinePosition";
  52. private static final String UNDERLINE_THICKNESS = "UnderlineThickness";
  53. private static final String ITALIC_ANGLE = "ItalicAngle";
  54. private static final String IS_FIXED_PITCH = "IsFixedPitch";
  55. private static final String START_DIRECTION = "StartDirection";
  56. private static final String END_DIRECTION = "EndDirection";
  57. private static final String START_CHAR_METRICS = "StartCharMetrics";
  58. private static final String END_CHAR_METRICS = "EndCharMetrics";
  59. private static final String C = "C";
  60. private static final String CH = "CH";
  61. private static final String WX = "WX";
  62. private static final String W0X = "W0X";
  63. private static final String W1X = "W1X";
  64. private static final String WY = "WY";
  65. private static final String W0Y = "W0Y";
  66. private static final String W1Y = "W1Y";
  67. private static final String W = "W";
  68. private static final String W0 = "W0";
  69. private static final String W1 = "W1";
  70. private static final String N = "N";
  71. private static final String B = "B";
  72. private static final String START_TRACK_KERN = "StartTrackKern";
  73. private static final String END_TRACK_KERN = "EndTrackKern";
  74. //private static final String START_KERN_PAIRS = "StartKernPairs";
  75. //private static final String START_KERN_PAIRS0 = "StartKernPairs0";
  76. private static final String START_KERN_PAIRS1 = "StartKernPairs1";
  77. //private static final String END_KERN_PAIRS = "EndKernPairs";
  78. private static final String KP = "KP";
  79. private static final String KPH = "KPH";
  80. private static final String KPX = "KPX";
  81. private static final String KPY = "KPY";
  82. private static final int PARSE_NORMAL = 0;
  83. private static final int PARSE_CHAR_METRICS = 1;
  84. private static final Map VALUE_PARSERS;
  85. private static final Map PARSE_MODE_CHANGES;
  86. static {
  87. VALUE_PARSERS = new java.util.HashMap();
  88. VALUE_PARSERS.put(START_FONT_METRICS, new StartFontMetrics());
  89. VALUE_PARSERS.put(FONT_NAME, new StringSetter(FONT_NAME));
  90. VALUE_PARSERS.put(FULL_NAME, new StringSetter(FULL_NAME));
  91. VALUE_PARSERS.put(FAMILY_NAME, new StringSetter(FAMILY_NAME));
  92. VALUE_PARSERS.put(WEIGHT, new StringSetter(WEIGHT));
  93. VALUE_PARSERS.put(ENCODING_SCHEME, new StringSetter(ENCODING_SCHEME));
  94. VALUE_PARSERS.put(FONT_BBOX, new FontBBox());
  95. VALUE_PARSERS.put(CHARACTER_SET, new StringSetter(CHARACTER_SET));
  96. VALUE_PARSERS.put(IS_BASE_FONT, new IsBaseFont());
  97. VALUE_PARSERS.put(IS_CID_FONT, new IsCIDFont());
  98. VALUE_PARSERS.put(CAP_HEIGHT, new NumberSetter(CAP_HEIGHT));
  99. VALUE_PARSERS.put(X_HEIGHT, new NumberSetter(X_HEIGHT));
  100. VALUE_PARSERS.put(ASCENDER, new NumberSetter(ASCENDER));
  101. VALUE_PARSERS.put(DESCENDER, new NumberSetter(DESCENDER));
  102. VALUE_PARSERS.put(STDHW, new NumberSetter(STDHW));
  103. VALUE_PARSERS.put(STDVW, new NumberSetter(STDVW));
  104. VALUE_PARSERS.put(START_DIRECTION, new StartDirection());
  105. VALUE_PARSERS.put(END_DIRECTION, new EndDirection());
  106. VALUE_PARSERS.put(UNDERLINE_POSITION, new WritingDirNumberSetter(UNDERLINE_POSITION));
  107. VALUE_PARSERS.put(UNDERLINE_THICKNESS, new WritingDirNumberSetter(UNDERLINE_THICKNESS));
  108. VALUE_PARSERS.put(ITALIC_ANGLE, new WritingDirDoubleSetter(ITALIC_ANGLE));
  109. VALUE_PARSERS.put(IS_FIXED_PITCH, new WritingDirBooleanSetter(IS_FIXED_PITCH));
  110. VALUE_PARSERS.put(C, new IntegerSetter("CharCode"));
  111. VALUE_PARSERS.put(CH, new NotImplementedYet(CH));
  112. VALUE_PARSERS.put(WX, new DoubleSetter("WidthX"));
  113. VALUE_PARSERS.put(W0X, new DoubleSetter("WidthX"));
  114. VALUE_PARSERS.put(W1X, new NotImplementedYet(W1X));
  115. VALUE_PARSERS.put(WY, new DoubleSetter("WidthY"));
  116. VALUE_PARSERS.put(W0Y, new DoubleSetter("WidthY"));
  117. VALUE_PARSERS.put(W1Y, new NotImplementedYet(W1Y));
  118. VALUE_PARSERS.put(W, new NotImplementedYet(W));
  119. VALUE_PARSERS.put(W0, new NotImplementedYet(W0));
  120. VALUE_PARSERS.put(W1, new NotImplementedYet(W1));
  121. VALUE_PARSERS.put(N, new NamedCharacterSetter("Character"));
  122. VALUE_PARSERS.put(B, new CharBBox());
  123. VALUE_PARSERS.put(START_TRACK_KERN, new NotImplementedYet(START_TRACK_KERN));
  124. VALUE_PARSERS.put(END_TRACK_KERN, new NotImplementedYet(END_TRACK_KERN));
  125. VALUE_PARSERS.put(START_KERN_PAIRS1, new NotImplementedYet(START_KERN_PAIRS1));
  126. VALUE_PARSERS.put(KP, new NotImplementedYet(KP));
  127. VALUE_PARSERS.put(KPH, new NotImplementedYet(KPH));
  128. VALUE_PARSERS.put(KPX, new KPXHandler());
  129. VALUE_PARSERS.put(KPY, new NotImplementedYet(KPY));
  130. PARSE_MODE_CHANGES = new java.util.HashMap();
  131. PARSE_MODE_CHANGES.put(START_CHAR_METRICS, new Integer(PARSE_CHAR_METRICS));
  132. PARSE_MODE_CHANGES.put(END_CHAR_METRICS, new Integer(PARSE_NORMAL));
  133. }
  134. /**
  135. * Main constructor.
  136. */
  137. public AFMParser() {
  138. //nop
  139. }
  140. /**
  141. * Parses an AFM file from a local file.
  142. * @param afmFile the AFM file
  143. * @return the parsed AFM file
  144. * @throws IOException if an I/O error occurs
  145. */
  146. public AFMFile parse(File afmFile) throws IOException {
  147. InputStream in = new java.io.FileInputStream(afmFile);
  148. try {
  149. return parse(in);
  150. } finally {
  151. IOUtils.closeQuietly(in);
  152. }
  153. }
  154. /**
  155. * Parses an AFM file from a stream.
  156. * @param in the stream to read from
  157. * @return the parsed AFM file
  158. * @throws IOException if an I/O error occurs
  159. */
  160. public AFMFile parse(InputStream in) throws IOException {
  161. Reader reader = new java.io.InputStreamReader(in, "US-ASCII");
  162. try {
  163. return parse(new BufferedReader(reader));
  164. } finally {
  165. IOUtils.closeQuietly(reader);
  166. }
  167. }
  168. /**
  169. * Parses an AFM file from a BufferedReader.
  170. * @param reader the BufferedReader instance to read from
  171. * @return the parsed AFM file
  172. * @throws IOException if an I/O error occurs
  173. */
  174. public AFMFile parse(BufferedReader reader) throws IOException {
  175. Stack stack = new Stack();
  176. int parseMode = PARSE_NORMAL;
  177. while (true) {
  178. String line = reader.readLine();
  179. if (line == null) {
  180. break;
  181. }
  182. String key = null;
  183. switch (parseMode) {
  184. case PARSE_NORMAL:
  185. key = parseLine(line, stack);
  186. break;
  187. case PARSE_CHAR_METRICS:
  188. key = parseCharMetrics(line, stack);
  189. break;
  190. default:
  191. throw new IllegalStateException("Invalid parse mode");
  192. }
  193. Integer newParseMode = (Integer)PARSE_MODE_CHANGES.get(key);
  194. if (newParseMode != null) {
  195. parseMode = newParseMode.intValue();
  196. }
  197. }
  198. return (AFMFile)stack.pop();
  199. }
  200. private String parseLine(String line, Stack stack) throws IOException {
  201. int startpos = 0;
  202. //Find key
  203. startpos = skipToNonWhiteSpace(line, startpos);
  204. int endpos = skipToWhiteSpace(line, startpos);
  205. String key = line.substring(startpos, endpos);
  206. //Parse value
  207. startpos = skipToNonWhiteSpace(line, endpos);
  208. ValueHandler vp = (ValueHandler)VALUE_PARSERS.get(key);
  209. if (vp != null) {
  210. vp.parse(line, startpos, stack);
  211. }
  212. return key;
  213. }
  214. private String parseCharMetrics(String line, Stack stack) throws IOException {
  215. int startpos = 0;
  216. AFMCharMetrics chm = new AFMCharMetrics();
  217. stack.push(chm);
  218. while (true) {
  219. //Find key
  220. startpos = skipToNonWhiteSpace(line, startpos);
  221. int endpos = skipToWhiteSpace(line, startpos);
  222. String key = line.substring(startpos, endpos);
  223. if (END_CHAR_METRICS.equals(key)) {
  224. stack.pop(); //Pop and forget unused AFMCharMetrics instance
  225. return key;
  226. } else if (key.length() == 0) {
  227. //EOL: No more key so break
  228. break;
  229. }
  230. //Extract value
  231. startpos = skipToNonWhiteSpace(line, endpos);
  232. endpos = skipToSemicolon(line, startpos);
  233. String value = line.substring(startpos, endpos).trim();
  234. startpos = endpos + 1;
  235. //Parse value
  236. ValueHandler vp = (ValueHandler)VALUE_PARSERS.get(key);
  237. if (vp != null) {
  238. vp.parse(value, 0, stack);
  239. }
  240. if (false) {
  241. break;
  242. }
  243. }
  244. stack.pop();
  245. AFMFile afm = (AFMFile)stack.peek();
  246. afm.addCharMetrics(chm);
  247. return null;
  248. }
  249. private static int skipToNonWhiteSpace(String line, int startpos) {
  250. int pos = startpos;
  251. while (pos < line.length() && isWhitespace(line.charAt(pos))) {
  252. pos++;
  253. }
  254. return pos;
  255. }
  256. private static int skipToWhiteSpace(String line, int startpos) {
  257. int pos = startpos;
  258. while (pos < line.length() && !isWhitespace(line.charAt(pos))) {
  259. pos++;
  260. }
  261. return pos;
  262. }
  263. private static int skipToSemicolon(String line, int startpos) {
  264. int pos = startpos;
  265. while (pos < line.length() && ';' != line.charAt(pos)) {
  266. pos++;
  267. }
  268. return pos;
  269. }
  270. private static boolean isWhitespace(char ch) {
  271. return ch == ' '
  272. || ch == '\t';
  273. }
  274. // ---------------- Value Handlers ---------------------------
  275. private interface ValueHandler {
  276. void parse(String line, int startpos, Stack stack) throws IOException;
  277. }
  278. private abstract static class AbstractValueHandler implements ValueHandler {
  279. protected int findValue(String line, int startpos) {
  280. return skipToWhiteSpace(line, startpos);
  281. }
  282. protected String getStringValue(String line, int startpos) {
  283. return line.substring(startpos);
  284. }
  285. protected Number getNumberValue(String line, int startpos) {
  286. try {
  287. return new Integer(getIntegerValue(line, startpos));
  288. } catch (NumberFormatException nfe) {
  289. return new Double(getDoubleValue(line, startpos));
  290. }
  291. }
  292. protected int getIntegerValue(String line, int startpos) {
  293. int endpos = findValue(line, startpos);
  294. return Integer.parseInt(line.substring(startpos, endpos));
  295. }
  296. protected double getDoubleValue(String line, int startpos) {
  297. int endpos = findValue(line, startpos);
  298. return Double.parseDouble(line.substring(startpos, endpos));
  299. }
  300. protected Boolean getBooleanValue(String line, int startpos) {
  301. return Boolean.valueOf(getStringValue(line, startpos));
  302. }
  303. }
  304. private static class StartFontMetrics extends AbstractValueHandler {
  305. public void parse(String line, int startpos, Stack stack) throws IOException {
  306. int endpos = findValue(line, startpos);
  307. double version = Double.parseDouble(line.substring(startpos, endpos));
  308. if (version < 2) {
  309. throw new IOException(
  310. "AFM version must be at least 2.0 but it is " + version + "!");
  311. }
  312. AFMFile afm = new AFMFile();
  313. stack.push(afm);
  314. }
  315. }
  316. private abstract static class BeanSetter extends AbstractValueHandler {
  317. private String method;
  318. public BeanSetter(String variable) {
  319. this.method = "set" + variable;
  320. }
  321. protected void setValue(Object target, Object value) {
  322. //Uses Java Beans API
  323. Statement statement = new Statement(target, method, new Object[] {value});
  324. try {
  325. statement.execute();
  326. } catch (Exception e) {
  327. //Should never happen
  328. throw new RuntimeException("Bean error: " + e.getMessage());
  329. }
  330. }
  331. }
  332. private static class StringSetter extends BeanSetter {
  333. public StringSetter(String variable) {
  334. super(variable);
  335. }
  336. public void parse(String line, int startpos, Stack stack) throws IOException {
  337. String s = getStringValue(line, startpos);
  338. Object obj = stack.peek();
  339. setValue(obj, s);
  340. }
  341. }
  342. private static class NamedCharacterSetter extends BeanSetter {
  343. public NamedCharacterSetter(String variable) {
  344. super(variable);
  345. }
  346. public void parse(String line, int startpos, Stack stack) throws IOException {
  347. NamedCharacter ch = new NamedCharacter(getStringValue(line, startpos));
  348. Object obj = stack.peek();
  349. setValue(obj, ch);
  350. }
  351. }
  352. private static class NumberSetter extends BeanSetter {
  353. public NumberSetter(String variable) {
  354. super(variable);
  355. }
  356. protected Object getContextObject(Stack stack) {
  357. return stack.peek();
  358. }
  359. public void parse(String line, int startpos, Stack stack) throws IOException {
  360. Number num = getNumberValue(line, startpos);
  361. setValue(getContextObject(stack), num);
  362. }
  363. }
  364. private static class IntegerSetter extends NumberSetter {
  365. public IntegerSetter(String variable) {
  366. super(variable);
  367. }
  368. public void parse(String line, int startpos, Stack stack) throws IOException {
  369. int value = getIntegerValue(line, startpos);
  370. setValue(getContextObject(stack), new Integer(value));
  371. }
  372. }
  373. private static class DoubleSetter extends NumberSetter {
  374. public DoubleSetter(String variable) {
  375. super(variable);
  376. }
  377. public void parse(String line, int startpos, Stack stack) throws IOException {
  378. double value = getDoubleValue(line, startpos);
  379. setValue(getContextObject(stack), new Double(value));
  380. }
  381. }
  382. private static class WritingDirNumberSetter extends NumberSetter {
  383. public WritingDirNumberSetter(String variable) {
  384. super(variable);
  385. }
  386. protected Object getContextObject(Stack stack) {
  387. if (stack.peek() instanceof AFMWritingDirectionMetrics) {
  388. return (AFMWritingDirectionMetrics)stack.peek();
  389. } else {
  390. AFMFile afm = (AFMFile)stack.peek();
  391. AFMWritingDirectionMetrics wdm = afm.getWritingDirectionMetrics(0);
  392. if (wdm == null) {
  393. wdm = new AFMWritingDirectionMetrics();
  394. afm.setWritingDirectionMetrics(0, wdm);
  395. }
  396. return wdm;
  397. }
  398. }
  399. }
  400. private static class WritingDirDoubleSetter extends WritingDirNumberSetter {
  401. public WritingDirDoubleSetter(String variable) {
  402. super(variable);
  403. }
  404. public void parse(String line, int startpos, Stack stack) throws IOException {
  405. double value = getDoubleValue(line, startpos);
  406. setValue(getContextObject(stack), new Double(value));
  407. }
  408. }
  409. private static class BooleanSetter extends AbstractValueHandler {
  410. private String method;
  411. public BooleanSetter(String variable) {
  412. this.method = "set" + variable.substring(2); //Cut "Is" in front
  413. }
  414. protected Object getContextObject(Stack stack) {
  415. return (AFMFile)stack.peek();
  416. }
  417. public void parse(String line, int startpos, Stack stack) throws IOException {
  418. Boolean b = getBooleanValue(line, startpos);
  419. //Uses Java Beans API
  420. Statement statement = new Statement(getContextObject(stack),
  421. method, new Object[] {b});
  422. try {
  423. statement.execute();
  424. } catch (Exception e) {
  425. //Should never happen
  426. throw new RuntimeException("Bean error: " + e.getMessage());
  427. }
  428. }
  429. }
  430. private static class WritingDirBooleanSetter extends BooleanSetter {
  431. public WritingDirBooleanSetter(String variable) {
  432. super(variable);
  433. }
  434. protected Object getContextObject(Stack stack) {
  435. if (stack.peek() instanceof AFMWritingDirectionMetrics) {
  436. return (AFMWritingDirectionMetrics)stack.peek();
  437. } else {
  438. AFMFile afm = (AFMFile)stack.peek();
  439. AFMWritingDirectionMetrics wdm = afm.getWritingDirectionMetrics(0);
  440. if (wdm == null) {
  441. wdm = new AFMWritingDirectionMetrics();
  442. afm.setWritingDirectionMetrics(0, wdm);
  443. }
  444. return wdm;
  445. }
  446. }
  447. }
  448. private static class FontBBox extends AbstractValueHandler {
  449. public void parse(String line, int startpos, Stack stack) throws IOException {
  450. Rectangle rect = parseBBox(line, startpos);
  451. AFMFile afm = (AFMFile)stack.peek();
  452. afm.setFontBBox(rect);
  453. }
  454. protected Rectangle parseBBox(String line, int startpos) {
  455. Rectangle rect = new Rectangle();
  456. int endpos;
  457. endpos = findValue(line, startpos);
  458. rect.x = Integer.parseInt(line.substring(startpos, endpos));
  459. startpos = skipToNonWhiteSpace(line, endpos);
  460. endpos = findValue(line, startpos);
  461. rect.y = Integer.parseInt(line.substring(startpos, endpos));
  462. startpos = skipToNonWhiteSpace(line, endpos);
  463. endpos = findValue(line, startpos);
  464. int v = Integer.parseInt(line.substring(startpos, endpos));
  465. rect.width = v - rect.x;
  466. startpos = skipToNonWhiteSpace(line, endpos);
  467. endpos = findValue(line, startpos);
  468. v = Integer.parseInt(line.substring(startpos, endpos));
  469. rect.height = v - rect.y;
  470. startpos = skipToNonWhiteSpace(line, endpos);
  471. return rect;
  472. }
  473. }
  474. private static class CharBBox extends FontBBox {
  475. public void parse(String line, int startpos, Stack stack) throws IOException {
  476. Rectangle rect = parseBBox(line, startpos);
  477. AFMCharMetrics metrics = (AFMCharMetrics)stack.peek();
  478. metrics.setBBox(rect);
  479. }
  480. }
  481. private static class IsBaseFont extends AbstractValueHandler {
  482. public void parse(String line, int startpos, Stack stack) throws IOException {
  483. if (getBooleanValue(line, startpos).booleanValue()) {
  484. throw new IOException("Only base fonts are currently supported!");
  485. }
  486. }
  487. }
  488. private static class IsCIDFont extends AbstractValueHandler {
  489. public void parse(String line, int startpos, Stack stack) throws IOException {
  490. if (getBooleanValue(line, startpos).booleanValue()) {
  491. throw new IOException("CID fonts are currently not supported!");
  492. }
  493. }
  494. }
  495. private static class NotImplementedYet extends AbstractValueHandler {
  496. private String key;
  497. public NotImplementedYet(String key) {
  498. this.key = key;
  499. }
  500. public void parse(String line, int startpos, Stack stack) throws IOException {
  501. throw new IOException("Support for '" + key
  502. + "' has not been implemented, yet! Font is not supported.");
  503. }
  504. }
  505. private static class StartDirection extends AbstractValueHandler {
  506. public void parse(String line, int startpos, Stack stack) throws IOException {
  507. int index = getIntegerValue(line, startpos);
  508. AFMWritingDirectionMetrics wdm = new AFMWritingDirectionMetrics();
  509. AFMFile afm = (AFMFile)stack.peek();
  510. afm.setWritingDirectionMetrics(index, wdm);
  511. stack.push(wdm);
  512. }
  513. }
  514. private static class EndDirection extends AbstractValueHandler {
  515. public void parse(String line, int startpos, Stack stack) throws IOException {
  516. if (!(stack.pop() instanceof AFMWritingDirectionMetrics)) {
  517. throw new IOException("AFM format error: nesting incorrect");
  518. }
  519. }
  520. }
  521. private static class KPXHandler extends AbstractValueHandler {
  522. public void parse(String line, int startpos, Stack stack) throws IOException {
  523. AFMFile afm = (AFMFile)stack.peek();
  524. int endpos;
  525. endpos = findValue(line, startpos);
  526. String name1 = line.substring(startpos, endpos);
  527. startpos = skipToNonWhiteSpace(line, endpos);
  528. endpos = findValue(line, startpos);
  529. String name2 = line.substring(startpos, endpos);
  530. startpos = skipToNonWhiteSpace(line, endpos);
  531. endpos = findValue(line, startpos);
  532. double kx = Double.parseDouble(line.substring(startpos, endpos));
  533. startpos = skipToNonWhiteSpace(line, endpos);
  534. afm.addXKerning(name1, name2, kx);
  535. }
  536. }
  537. }