KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > render > afp > fonts > AFPFontReader


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
18 /* $Id: AFPFontReader.java 426576 2006-07-28 15:44:37Z jeremias $ */
19
20 package org.apache.fop.render.afp.fonts;
21
22 import java.io.File JavaDoc;
23 import java.io.FileNotFoundException JavaDoc;
24 import java.io.FilenameFilter JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.net.MalformedURLException JavaDoc;
28 import java.net.URL JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.HashMap JavaDoc;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.apache.fop.render.afp.exceptions.FontRuntimeException;
35 import org.apache.fop.render.afp.modca.AFPConstants;
36 import org.apache.fop.render.afp.tools.StructuredFieldReader;
37
38 /**
39  * The AFPFontReader is responsible for reading the font attributes from binary
40  * 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  * @author <a HREF="mailto:pete@townsend.uk.com">Pete Townsend </a>
54  */

55 public final class AFPFontReader {
56
57     /**
58      * Static logging instance
59      */

60     protected static final Log log = LogFactory.getLog("org.apache.fop.render.afp.fonts");
61
62     /**
63      * Template used to convert lists to arrays.
64      */

65     private static final CharacterSetOrientation[] EMPTY_CSO_ARRAY = new CharacterSetOrientation[0];
66
67     /** Codepage MO:DCA structured field. */
68     private static final byte[] CODEPAGE_SF = new byte[] { (byte) 0xD3,
69         (byte) 0xA8, (byte) 0x87 };
70
71     /** Character table MO:DCA structured field. */
72     private static final byte[] CHARACTER_TABLE_SF = new byte[] { (byte) 0xD3,
73         (byte) 0x8C, (byte) 0x87 };
74
75     /** Font control MO:DCA structured field. */
76     private static final byte[] FONT_CONTROL_SF = new byte[] { (byte) 0xD3,
77         (byte) 0xA7, (byte) 0x89 };
78
79     /** Font orientation MO:DCA structured field. */
80     private static final byte[] FONT_ORIENTATION_SF = new byte[] { (byte) 0xD3,
81         (byte) 0xAE, (byte) 0x89 };
82
83     /** Font position MO:DCA structured field. */
84     private static final byte[] FONT_POSITION_SF = new byte[] { (byte) 0xD3,
85         (byte) 0xAC, (byte) 0x89 };
86
87     /** Font index MO:DCA structured field. */
88     private static final byte[] FONT_INDEX_SF = new byte[] { (byte) 0xD3,
89         (byte) 0x8C, (byte) 0x89 };
90
91     /**
92      * The conversion factor to millipoints for 240 dpi
93      */

94     private static final int FOP_100_DPI_FACTOR = 1;
95
96     /**
97      * The conversion factor to millipoints for 240 dpi
98      */

99     private static final int FOP_240_DPI_FACTOR = 300000;
100
101     /**
102      * The conversion factor to millipoints for 300 dpi
103      */

104     private static final int FOP_300_DPI_FACTOR = 240000;
105
106     /**
107      * The encoding to use to convert from EBCIDIC to ASCII
108      */

109     private static final String JavaDoc ASCII_ENCODING = "UTF8";
110
111     /**
112      * The collection of code pages
113      */

114     private static HashMap JavaDoc _codePages = new HashMap JavaDoc();
115
116     /**
117      * Load the font details and metrics into the CharacterSetMetric object,
118      * this will use the actual afp code page and character set files to load
119      * the object with the necessary metrics.
120      *
121      * @param characterSet the CharacterSetMetric object to populate
122      */

123     public static void loadCharacterSetMetric(CharacterSet characterSet) {
124
125         InputStream JavaDoc inputStream = null;
126
127         try {
128
129             /**
130              * Get the code page which contains the character mapping
131              * information to map the unicode character id to the graphic
132              * chracter global identifier.
133              */

134             String JavaDoc cp = new String JavaDoc(characterSet.getCodePage());
135             String JavaDoc path = characterSet.getPath();
136
137             HashMap JavaDoc codepage = (HashMap JavaDoc) _codePages.get(cp);
138
139             if (codepage == null) {
140                 codepage = loadCodePage(cp, characterSet.getEncoding(), path);
141                 _codePages.put(cp, codepage);
142             }
143
144             /**
145              * Load the character set metric information, no need to cache this
146              * information as it should be cached by the objects that wish to
147              * load character set metric information.
148              */

149             final String JavaDoc characterset = characterSet.getName();
150
151             ClassLoader JavaDoc classLoader = Thread.currentThread().getContextClassLoader();
152             if (classLoader == null) {
153                 classLoader = AFPFontReader.class.getClassLoader();
154             }
155
156             URL JavaDoc url = classLoader.getResource(path);
157             if (url == null) {
158                 try {
159                     File JavaDoc file = new File JavaDoc(path);
160                     url = file.toURL();
161                     if (url == null) {
162                         String JavaDoc msg = "CharacterSet file not found for "
163                             + characterset + " in classpath: " + path;
164                         log.error(msg);
165                         throw new FileNotFoundException JavaDoc(msg);
166                     }
167                 } catch (MalformedURLException JavaDoc ex) {
168                     String JavaDoc msg = "CharacterSet file not found for "
169                         + characterset + " in classpath: " + path;
170                     log.error(msg);
171                     throw new FileNotFoundException JavaDoc(msg);
172                 }
173
174             }
175
176             File JavaDoc directory = new File JavaDoc(url.getPath());
177
178             final String JavaDoc filterpattern = characterset.trim();
179             FilenameFilter JavaDoc filter = new FilenameFilter JavaDoc() {
180                 public boolean accept(File JavaDoc dir, String JavaDoc name) {
181                     return name.startsWith(filterpattern);
182                 }
183             };
184
185             File JavaDoc[] csfont = directory.listFiles(filter);
186             if (csfont.length < 1) {
187                 String JavaDoc msg = "CharacterSet file search for " + characterset
188                     + " located " + csfont.length + " files";
189                 log.error(msg);
190                 throw new FileNotFoundException JavaDoc(msg);
191             } else if (csfont.length > 1) {
192                 String JavaDoc msg = "CharacterSet file search for " + characterset
193                     + " located " + csfont.length + " files";
194                 log.warn(msg);
195             }
196
197             inputStream = inputStream = csfont[0].toURL().openStream();
198             if (inputStream == null) {
199                 String JavaDoc msg = "Failed to open character set resource "
200                     + characterset;
201                 log.error(msg);
202                 throw new FileNotFoundException JavaDoc(msg);
203             }
204
205             StructuredFieldReader sfr = new StructuredFieldReader(inputStream);
206
207             // Process D3A789 Font Control
208
FontControl fnc = processFontControl(sfr);
209
210             //process D3AE89 Font Orientation
211
CharacterSetOrientation[] csoArray = processFontOrientation(sfr);
212
213             //process D3AC89 Font Position
214
processFontPosition(sfr, csoArray, fnc.getDpi());
215
216             //process D38C89 Font Index (per orientation)
217
for (int i = 0; i < csoArray.length; i++) {
218                 processFontIndex(sfr, csoArray[i], codepage, fnc.getDpi());
219                 characterSet.addCharacterSetOrientation(csoArray[i]);
220             }
221
222         } catch (Exception JavaDoc ex) {
223             throw new FontRuntimeException(
224                 "Failed to load the character set metrics for code page "
225                 + characterSet.getCodePage(), ex);
226         } finally {
227             try {
228                 inputStream.close();
229             } catch (Exception JavaDoc ex) {
230                 // Ignore
231
}
232         }
233
234     }
235
236     /**
237      * Load the code page information from the appropriate file. The file name
238      * to load is determined by the code page name and the file extension 'CDP'.
239      *
240      * @param codePage
241      * the code page identifier
242      * @param encoding
243      * the encoding to use for the character decoding
244      */

245     private static HashMap JavaDoc loadCodePage(String JavaDoc codePage, String JavaDoc encoding,
246         String JavaDoc path) throws IOException JavaDoc, FileNotFoundException JavaDoc {
247
248         // Create the HashMap to store code page information
249
HashMap JavaDoc codepages = new HashMap JavaDoc();
250
251         ClassLoader JavaDoc classLoader = Thread.currentThread().getContextClassLoader();
252         if (classLoader == null) {
253             classLoader = AFPFontReader.class.getClassLoader();
254         }
255
256         URL JavaDoc url = classLoader.getResource(path);
257
258         if (url == null) {
259             try {
260                 File JavaDoc file = new File JavaDoc(path);
261                 url = file.toURL();
262                 if (url == null) {
263                     String JavaDoc msg = "CodePage file not found for " + codePage
264                         + " in classpath: " + path;
265                     log.error(msg);
266                     throw new FileNotFoundException JavaDoc(msg);
267                 }
268             } catch (MalformedURLException JavaDoc ex) {
269                 String JavaDoc msg = "CodePage file not found for " + codePage
270                     + " in classpath: " + path;
271                 log.error(msg);
272                 throw new FileNotFoundException JavaDoc(msg);
273             }
274
275         }
276
277         File JavaDoc directory = new File JavaDoc(url.getPath());
278
279         final String JavaDoc filterpattern = codePage.trim();
280         FilenameFilter JavaDoc filter = new FilenameFilter JavaDoc() {
281             public boolean accept(File JavaDoc dir, String JavaDoc name) {
282                 return name.startsWith(filterpattern);
283             }
284         };
285
286         File JavaDoc[] codepage = directory.listFiles(filter);
287
288         if (codepage.length < 1) {
289             String JavaDoc msg = "CodePage file search for " + codePage + " located "
290                 + codepage.length + " files";
291             log.error(msg);
292             throw new FileNotFoundException JavaDoc(msg);
293         } else if (codepage.length > 1) {
294             String JavaDoc msg = "CodePage file search for " + codePage + " located "
295                 + codepage.length + " files";
296             log.warn(msg);
297         }
298
299         InputStream JavaDoc is = codepage[0].toURL().openStream();
300
301         if (is == null) {
302             String JavaDoc msg = "AFPFontReader:: loadCodePage(String):: code page file not found for "
303                 + codePage;
304             log.error(msg);
305             throw new FileNotFoundException JavaDoc(msg);
306         }
307
308         StructuredFieldReader sfr = new StructuredFieldReader(is);
309         byte[] data = sfr.getNext(CHARACTER_TABLE_SF);
310
311         int position = 0;
312         byte[] gcgiBytes = new byte[8];
313         byte[] charBytes = new byte[1];
314
315         // Read data, ignoring bytes 0 - 2
316
for (int index = 3; index < data.length; index++) {
317             if (position < 8) {
318                 // Build the graphic character global identifier key
319
gcgiBytes[position] = data[index];
320                 position++;
321             } else if (position == 9) {
322                 position = 0;
323                 // Set the character
324
charBytes[0] = data[index];
325                 String JavaDoc gcgiString = new String JavaDoc(gcgiBytes,
326                     AFPConstants.EBCIDIC_ENCODING);
327                 String JavaDoc charString = new String JavaDoc(charBytes, encoding);
328                 int value = charString.charAt(0);
329                 codepages.put(gcgiString, charString);
330             } else {
331                 position++;
332             }
333         }
334
335         try {
336             is.close();
337         } catch (Exception JavaDoc ex) {
338             // Ignore
339
}
340
341         return codepages;
342
343     }
344
345     /**
346      * Process the font control details using the structured field reader.
347      *
348      * @param sfr
349      * the structured field reader
350      */

351     private static FontControl processFontControl(StructuredFieldReader sfr)
352     throws IOException JavaDoc {
353
354         byte[] fncData = sfr.getNext(FONT_CONTROL_SF);
355
356         int position = 0;
357
358         FontControl fontControl = new AFPFontReader().new FontControl();
359
360         if (fncData[7] == (byte) 0x02) {
361             fontControl.setRelative(true);
362         }
363
364         int dpi = (((fncData[9] & 0xFF) << 8) + (fncData[10] & 0xFF)) / 10;
365
366         fontControl.setDpi(dpi);
367
368         return fontControl;
369
370     }
371
372     /**
373      * Process the font orientation details from using the structured field
374      * reader.
375      *
376      * @param sfr
377      * the structured field reader
378      */

379     private static CharacterSetOrientation[] processFontOrientation(
380         StructuredFieldReader sfr) throws IOException JavaDoc {
381
382         byte[] data = sfr.getNext(FONT_ORIENTATION_SF);
383
384         int position = 0;
385         byte[] fnoData = new byte[26];
386
387         ArrayList JavaDoc orientations = new ArrayList JavaDoc();
388
389         // Read data, ignoring bytes 0 - 2
390
for (int index = 3; index < data.length; index++) {
391             // Build the font orientation record
392
fnoData[position] = data[index];
393             position++;
394
395             if (position == 26) {
396
397                 position = 0;
398
399                 int orientation = 0;
400
401                 switch (fnoData[2]) {
402                     case 0x00:
403                         orientation = 0;
404                         break;
405                     case 0x2D:
406                         orientation = 90;
407                         break;
408                     case 0x5A:
409                         orientation = 180;
410                         break;
411                     case (byte) 0x87:
412                         orientation = 270;
413                         break;
414                     default:
415                         System.out.println("ERROR: Oriantation");
416                 }
417
418                 CharacterSetOrientation cso = new CharacterSetOrientation(
419                     orientation);
420                 orientations.add(cso);
421
422             }
423         }
424
425         return (CharacterSetOrientation[]) orientations
426             .toArray(EMPTY_CSO_ARRAY);
427     }
428
429     /**
430      * Populate the CharacterSetOrientation object in the suplied array with the
431      * font position details using the supplied structured field reader.
432      *
433      * @param sfr
434      * the structured field reader
435      * @param csoArray
436      * the array of CharacterSetOrientation objects
437      */

438     private static void processFontPosition(StructuredFieldReader sfr,
439         CharacterSetOrientation[] csoArray, int dpi) throws IOException JavaDoc {
440
441         byte[] data = sfr.getNext(FONT_POSITION_SF);
442
443         int position = 0;
444         byte[] fpData = new byte[26];
445
446         int csoIndex = 0;
447         int fopFactor = 0;
448
449         switch (dpi) {
450             case 100:
451                 fopFactor = FOP_100_DPI_FACTOR;
452                 break;
453             case 240:
454                 fopFactor = FOP_240_DPI_FACTOR;
455                 break;
456             case 300:
457                 fopFactor = FOP_300_DPI_FACTOR;
458                 break;
459             default:
460                 String JavaDoc msg = "Unsupported font resolution of " + dpi + " dpi.";
461                 log.error(msg);
462                 throw new IOException JavaDoc(msg);
463         }
464
465         // Read data, ignoring bytes 0 - 2
466
for (int index = 3; index < data.length; index++) {
467             if (position < 22) {
468                 // Build the font orientation record
469
fpData[position] = data[index];
470             } else if (position == 22) {
471
472                 position = 0;
473
474                 CharacterSetOrientation cso = csoArray[csoIndex];
475
476                 int xHeight = ((fpData[2] & 0xFF) << 8) + (fpData[3] & 0xFF);
477                 int capHeight = ((fpData[4] & 0xFF) << 8) + (fpData[5] & 0xFF);
478                 int ascHeight = ((fpData[6] & 0xFF) << 8) + (fpData[7] & 0xFF);
479                 int dscHeight = ((fpData[8] & 0xFF) << 8) + (fpData[9] & 0xFF);
480
481                 dscHeight = dscHeight * -1;
482
483                 cso.setXHeight(xHeight * fopFactor);
484                 cso.setCapHeight(capHeight * fopFactor);
485                 cso.setAscender(ascHeight * fopFactor);
486                 cso.setDescender(dscHeight * fopFactor);
487
488                 csoIndex++;
489
490                 fpData[position] = data[index];
491
492             }
493
494             position++;
495         }
496
497     }
498
499     /**
500      * Process the font index details for the character set orientation.
501      *
502      * @param sfr
503      * the structured field reader
504      * @param cso
505      * the CharacterSetOrientation object to populate
506      * @param codepage
507      * the map of code pages
508      */

509     private static void processFontIndex(StructuredFieldReader sfr,
510         CharacterSetOrientation cso, HashMap JavaDoc codepage, int dpi)
511         throws IOException JavaDoc {
512
513         byte[] data = sfr.getNext(FONT_INDEX_SF);
514
515         int fopFactor = 0;
516
517         switch (dpi) {
518             case 100:
519                 fopFactor = FOP_100_DPI_FACTOR;
520                 break;
521             case 240:
522                 fopFactor = FOP_240_DPI_FACTOR;
523                 break;
524             case 300:
525                 fopFactor = FOP_300_DPI_FACTOR;
526                 break;
527             default:
528                 String JavaDoc msg = "Unsupported font resolution of " + dpi + " dpi.";
529                 log.error(msg);
530                 throw new IOException JavaDoc(msg);
531         }
532
533         int position = 0;
534
535         byte[] gcgid = new byte[8];
536         byte[] fiData = new byte[20];
537
538         int lowest = 255;
539         int highest = 0;
540
541         // Read data, ignoring bytes 0 - 2
542
for (int index = 3; index < data.length; index++) {
543             if (position < 8) {
544                 gcgid[position] = (byte) data[index];
545                 position++;
546             } else if (position < 27) {
547                 fiData[position - 8] = (byte) data[index];
548                 position++;
549             } else if (position == 27) {
550
551                 fiData[position - 8] = (byte) data[index];
552
553                 position = 0;
554
555                 String JavaDoc gcgiString = new String JavaDoc(gcgid, AFPConstants.EBCIDIC_ENCODING);
556
557                 String JavaDoc idx = (String JavaDoc) codepage.get(gcgiString);
558
559                 if (idx != null) {
560
561                     int cidx = idx.charAt(0);
562                     int width = ((fiData[0] & 0xFF) << 8) + (fiData[1] & 0xFF);
563
564                     if (cidx < lowest) {
565                         lowest = cidx;
566                     }
567
568                     if (cidx > highest) {
569                         highest = cidx;
570                     }
571
572                     int a = (width * fopFactor);
573
574                     cso.setWidth(cidx, a);
575
576                 }
577
578             }
579         }
580
581         cso.setFirstChar(lowest);
582         cso.setLastChar(highest);
583
584     }
585
586     private class FontControl {
587
588         private int _dpi;
589
590         private boolean isRelative = false;
591
592         public int getDpi() {
593             return _dpi;
594         }
595
596         public void setDpi(int i) {
597             _dpi = i;
598         }
599
600         public boolean isRelative() {
601             return isRelative;
602         }
603
604         public void setRelative(boolean b) {
605             isRelative = b;
606         }
607     }
608
609 }
Popular Tags