123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- /* $Id$ */
-
- package org.apache.fop.afp.fonts;
-
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.FilenameFilter;
- import java.io.IOException;
- import java.io.InputStream;
- import java.net.MalformedURLException;
- import java.net.URL;
- import java.util.List;
- import java.util.Map;
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.apache.fop.afp.AFPConstants;
- import org.apache.fop.afp.util.StructuredFieldReader;
-
- /**
- * The AFPFontReader is responsible for reading the font attributes from binary
- * code page files and the character set metric files. In IBM font structure, a
- * code page maps each character of text to the characters in a character set.
- * Each character is translated into a code point. When the character is
- * printed, each code point is matched to a character ID on the code page
- * specified. The character ID is then matched to the image (raster pattern or
- * outline pattern) of the character in the character set specified. The image
- * in the character set is the image that is printed in the document. To be a
- * valid code page for a particular character set, all character IDs in the code
- * page must be included in that character set. <p/>This class will read the
- * font information from the binary code page files and character set metric
- * files in order to determine the correct metrics to use when rendering the
- * formatted object. <p/>
- *
- * @author <a href="mailto:pete@townsend.uk.com">Pete Townsend </a>
- */
- public final class AFPFontReader {
-
- /**
- * Static logging instance
- */
- protected static final Log log = LogFactory.getLog("org.apache.xmlgraphics.afp.fonts");
-
- /**
- * Template used to convert lists to arrays.
- */
- private static final CharacterSetOrientation[] EMPTY_CSO_ARRAY = new CharacterSetOrientation[0];
-
- /** Codepage MO:DCA structured field. */
- private static final byte[] CODEPAGE_SF = new byte[] {
- (byte) 0xD3, (byte) 0xA8, (byte) 0x87};
-
- /** Character table MO:DCA structured field. */
- private static final byte[] CHARACTER_TABLE_SF = new byte[] {
- (byte) 0xD3, (byte) 0x8C, (byte) 0x87};
-
- /** Font control MO:DCA structured field. */
- private static final byte[] FONT_CONTROL_SF = new byte[] {
- (byte) 0xD3, (byte) 0xA7, (byte) 0x89 };
-
- /** Font orientation MO:DCA structured field. */
- private static final byte[] FONT_ORIENTATION_SF = new byte[] {
- (byte) 0xD3, (byte) 0xAE, (byte) 0x89 };
-
- /** Font position MO:DCA structured field. */
- private static final byte[] FONT_POSITION_SF = new byte[] {
- (byte) 0xD3, (byte) 0xAC, (byte) 0x89 };
-
- /** Font index MO:DCA structured field. */
- private static final byte[] FONT_INDEX_SF = new byte[] {
- (byte) 0xD3, (byte) 0x8C, (byte) 0x89 };
-
- /**
- * The conversion factor to millipoints for 240 dpi
- */
- private static final int FOP_100_DPI_FACTOR = 1;
-
- /**
- * The conversion factor to millipoints for 240 dpi
- */
- private static final int FOP_240_DPI_FACTOR = 300000;
-
- /**
- * The conversion factor to millipoints for 300 dpi
- */
- private static final int FOP_300_DPI_FACTOR = 240000;
-
- /**
- * The encoding to use to convert from EBCIDIC to ASCII
- */
- private static final String ASCII_ENCODING = "UTF8";
-
- /**
- * The collection of code pages
- */
- private final Map/*<String, Map<String, String>>*/ codePages
- = new java.util.HashMap/*<String, Map<String, String>>*/();
-
- /**
- * Returns an InputStream to a given file path and filename
- *
- * @param path the file path
- * @param filename the file name
- * @return an inputStream
- *
- * @throws IOException in the event that an I/O exception of some sort has occurred
- */
- private InputStream openInputStream(String path, String filename) throws IOException {
- ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
- if (classLoader == null) {
- classLoader = AFPFontReader.class.getClassLoader();
- }
-
- URL url = classLoader.getResource(path);
-
- if (url == null) {
- try {
- File file = new File(path);
- url = file.toURI().toURL();
- if (url == null) {
- String msg = "file not found " + filename + " in classpath: " + path;
- log.error(msg);
- throw new FileNotFoundException(msg);
- }
- } catch (MalformedURLException ex) {
- String msg = "file not found " + filename + " in classpath: " + path;
- log.error(msg);
- throw new FileNotFoundException(msg);
- }
- }
-
- File directory = new File(url.getPath());
- if (!directory.canRead()) {
- String msg = "Failed to read directory " + url.getPath();
- log.error(msg);
- throw new FileNotFoundException(msg);
- }
-
- final String filterpattern = filename.trim();
- FilenameFilter filter = new FilenameFilter() {
- public boolean accept(File dir, String name) {
- return name.startsWith(filterpattern);
- }
- };
-
- File[] files = directory.listFiles(filter);
-
- if (files.length < 1) {
- String msg = "file search for " + filename + " located "
- + files.length + " files";
- log.error(msg);
- throw new FileNotFoundException(msg);
- } else if (files.length > 1) {
- String msg = "file search for " + filename + " located "
- + files.length + " files";
- log.warn(msg);
- }
-
- InputStream inputStream = files[0].toURI().toURL().openStream();
-
- if (inputStream == null) {
- String msg = "AFPFontReader:: getInputStream():: file not found for " + filename;
- log.error(msg);
- throw new FileNotFoundException(msg);
- }
-
- return inputStream;
- }
-
- /**
- * Closes the inputstream
- *
- * @param inputStream the inputstream to close
- */
- private void closeInputStream(InputStream inputStream) {
- try {
- if (inputStream != null) {
- inputStream.close();
- }
- } catch (Exception ex) {
- // Lets log at least!
- log.error(ex.getMessage());
- }
- }
-
- /**
- * Load the font details and metrics into the CharacterSetMetric object,
- * this will use the actual afp code page and character set files to load
- * the object with the necessary metrics.
- *
- * @param characterSet the CharacterSetMetric object to populate
- * @throws IOException if an I/O exception of some sort has occurred.
- */
- public void loadCharacterSetMetric(CharacterSet characterSet) throws IOException {
-
- InputStream inputStream = null;
-
- try {
-
- /**
- * Get the code page which contains the character mapping
- * information to map the unicode character id to the graphic
- * chracter global identifier.
- */
- String codePageId = new String(characterSet.getCodePage());
- String path = characterSet.getPath();
-
- Map/*<String,String>*/ codePage = (Map/*<String,String>*/)codePages.get(codePageId);
-
- if (codePage == null) {
- codePage = loadCodePage(codePageId, characterSet.getEncoding(), path);
- codePages.put(codePageId, codePage);
- }
-
- /**
- * Load the character set metric information, no need to cache this
- * information as it should be cached by the objects that wish to
- * load character set metric information.
- */
- final String characterSetName = characterSet.getName();
-
- inputStream = openInputStream(path, characterSetName);
-
- StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream);
-
- // Process D3A789 Font Control
- FontControl fontControl = processFontControl(structuredFieldReader);
-
- if (fontControl != null) {
- //process D3AE89 Font Orientation
- CharacterSetOrientation[] characterSetOrientations
- = processFontOrientation(structuredFieldReader);
-
- int dpi = fontControl.getDpi();
-
- //process D3AC89 Font Position
- processFontPosition(structuredFieldReader, characterSetOrientations, dpi);
-
- //process D38C89 Font Index (per orientation)
- for (int i = 0; i < characterSetOrientations.length; i++) {
- processFontIndex(structuredFieldReader,
- characterSetOrientations[i], codePage, dpi);
- characterSet.addCharacterSetOrientation(characterSetOrientations[i]);
- }
- } else {
- throw new IOException(
- "Failed to read font control structured field in character set "
- + characterSetName);
- }
-
- } finally {
- closeInputStream(inputStream);
- }
-
- }
-
- /**
- * Load the code page information from the appropriate file. The file name
- * to load is determined by the code page name and the file extension 'CDP'.
- *
- * @param codePage
- * the code page identifier
- * @param encoding
- * the encoding to use for the character decoding
- * @returns a code page mapping
- */
- private Map/*<String,String>*/ loadCodePage(String codePage, String encoding,
- String path) throws IOException {
-
- // Create the HashMap to store code page information
- Map/*<String,String>*/ codePages = new java.util.HashMap/*<String,String>*/();
-
- InputStream inputStream = null;
- try {
- inputStream = openInputStream(path, codePage.trim());
-
- StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream);
- byte[] data = structuredFieldReader.getNext(CHARACTER_TABLE_SF);
-
- int position = 0;
- byte[] gcgiBytes = new byte[8];
- byte[] charBytes = new byte[1];
-
- // Read data, ignoring bytes 0 - 2
- for (int index = 3; index < data.length; index++) {
- if (position < 8) {
- // Build the graphic character global identifier key
- gcgiBytes[position] = data[index];
- position++;
- } else if (position == 9) {
- position = 0;
- // Set the character
- charBytes[0] = data[index];
- String gcgiString = new String(gcgiBytes,
- AFPConstants.EBCIDIC_ENCODING);
- String charString = new String(charBytes, encoding);
- // int value = charString.charAt(0);
- codePages.put(gcgiString, charString);
- } else {
- position++;
- }
- }
- } finally {
- closeInputStream(inputStream);
- }
-
- return codePages;
- }
-
- /**
- * Process the font control details using the structured field reader.
- *
- * @param structuredFieldReader
- * the structured field reader
- */
- private FontControl processFontControl(StructuredFieldReader structuredFieldReader)
- throws IOException {
-
- byte[] fncData = structuredFieldReader.getNext(FONT_CONTROL_SF);
-
- // int position = 0;
- FontControl fontControl = null;
- if (fncData != null) {
- fontControl = new FontControl();
-
- if (fncData[7] == (byte) 0x02) {
- fontControl.setRelative(true);
- }
-
- int dpi = (((fncData[9] & 0xFF) << 8) + (fncData[10] & 0xFF)) / 10;
-
- fontControl.setDpi(dpi);
- }
- return fontControl;
- }
-
- /**
- * Process the font orientation details from using the structured field
- * reader.
- *
- * @param structuredFieldReader
- * the structured field reader
- */
- private CharacterSetOrientation[] processFontOrientation(
- StructuredFieldReader structuredFieldReader) throws IOException {
-
- byte[] data = structuredFieldReader.getNext(FONT_ORIENTATION_SF);
-
- int position = 0;
- byte[] fnoData = new byte[26];
-
- List orientations = new java.util.ArrayList();
-
- // Read data, ignoring bytes 0 - 2
- for (int index = 3; index < data.length; index++) {
- // Build the font orientation record
- fnoData[position] = data[index];
- position++;
-
- if (position == 26) {
-
- position = 0;
-
- int orientation = 0;
-
- switch (fnoData[2]) {
- case 0x00:
- orientation = 0;
- break;
- case 0x2D:
- orientation = 90;
- break;
- case 0x5A:
- orientation = 180;
- break;
- case (byte) 0x87:
- orientation = 270;
- break;
- default:
- System.out.println("ERROR: Oriantation");
- }
-
- CharacterSetOrientation cso = new CharacterSetOrientation(
- orientation);
- orientations.add(cso);
-
- }
- }
-
- return (CharacterSetOrientation[]) orientations
- .toArray(EMPTY_CSO_ARRAY);
- }
-
- /**
- * Populate the CharacterSetOrientation object in the suplied array with the
- * font position details using the supplied structured field reader.
- *
- * @param structuredFieldReader
- * the structured field reader
- * @param characterSetOrientations
- * the array of CharacterSetOrientation objects
- */
- private void processFontPosition(StructuredFieldReader structuredFieldReader,
- CharacterSetOrientation[] characterSetOrientations, int dpi) throws IOException {
-
- byte[] data = structuredFieldReader.getNext(FONT_POSITION_SF);
-
- int position = 0;
- byte[] fpData = new byte[26];
-
- int characterSetOrientationIndex = 0;
- int fopFactor = 0;
-
- switch (dpi) {
- case 100:
- fopFactor = FOP_100_DPI_FACTOR;
- break;
- case 240:
- fopFactor = FOP_240_DPI_FACTOR;
- break;
- case 300:
- fopFactor = FOP_300_DPI_FACTOR;
- break;
- default:
- String msg = "Unsupported font resolution of " + dpi + " dpi.";
- log.error(msg);
- throw new IOException(msg);
- }
-
- // Read data, ignoring bytes 0 - 2
- for (int index = 3; index < data.length; index++) {
- if (position < 22) {
- // Build the font orientation record
- fpData[position] = data[index];
- } else if (position == 22) {
-
- position = 0;
-
- CharacterSetOrientation characterSetOrientation
- = characterSetOrientations[characterSetOrientationIndex];
-
- int xHeight = ((fpData[2] & 0xFF) << 8) + (fpData[3] & 0xFF);
- int capHeight = ((fpData[4] & 0xFF) << 8) + (fpData[5] & 0xFF);
- int ascHeight = ((fpData[6] & 0xFF) << 8) + (fpData[7] & 0xFF);
- int dscHeight = ((fpData[8] & 0xFF) << 8) + (fpData[9] & 0xFF);
-
- dscHeight = dscHeight * -1;
-
- characterSetOrientation.setXHeight(xHeight * fopFactor);
- characterSetOrientation.setCapHeight(capHeight * fopFactor);
- characterSetOrientation.setAscender(ascHeight * fopFactor);
- characterSetOrientation.setDescender(dscHeight * fopFactor);
-
- characterSetOrientationIndex++;
-
- fpData[position] = data[index];
-
- }
-
- position++;
- }
-
- }
-
- /**
- * Process the font index details for the character set orientation.
- *
- * @param structuredFieldReader
- * the structured field reader
- * @param cso
- * the CharacterSetOrientation object to populate
- * @param codepage
- * the map of code pages
- */
- private void processFontIndex(StructuredFieldReader structuredFieldReader,
- CharacterSetOrientation cso, Map/*<String,String>*/ codepage, int dpi)
- throws IOException {
-
- byte[] data = structuredFieldReader.getNext(FONT_INDEX_SF);
-
- int fopFactor = 0;
-
- switch (dpi) {
- case 100:
- fopFactor = FOP_100_DPI_FACTOR;
- break;
- case 240:
- fopFactor = FOP_240_DPI_FACTOR;
- break;
- case 300:
- fopFactor = FOP_300_DPI_FACTOR;
- break;
- default:
- String msg = "Unsupported font resolution of " + dpi + " dpi.";
- log.error(msg);
- throw new IOException(msg);
- }
-
- int position = 0;
-
- byte[] gcgid = new byte[8];
- byte[] fiData = new byte[20];
-
- int lowest = 255;
- int highest = 0;
-
- // Read data, ignoring bytes 0 - 2
- for (int index = 3; index < data.length; index++) {
- if (position < 8) {
- gcgid[position] = data[index];
- position++;
- } else if (position < 27) {
- fiData[position - 8] = data[index];
- position++;
- } else if (position == 27) {
-
- fiData[position - 8] = data[index];
-
- position = 0;
-
- String gcgiString = new String(gcgid, AFPConstants.EBCIDIC_ENCODING);
-
- String idx = (String) codepage.get(gcgiString);
-
- if (idx != null) {
-
- int cidx = idx.charAt(0);
- int width = ((fiData[0] & 0xFF) << 8) + (fiData[1] & 0xFF);
-
- if (cidx < lowest) {
- lowest = cidx;
- }
-
- if (cidx > highest) {
- highest = cidx;
- }
-
- int a = (width * fopFactor);
-
- cso.setWidth(cidx, a);
-
- }
-
- }
- }
-
- cso.setFirstChar(lowest);
- cso.setLastChar(highest);
-
- }
-
- private class FontControl {
-
- private int dpi;
-
- private boolean isRelative = false;
-
- public int getDpi() {
- return dpi;
- }
-
- public void setDpi(int i) {
- dpi = i;
- }
-
- public boolean isRelative() {
- return isRelative;
- }
-
- public void setRelative(boolean b) {
- isRelative = b;
- }
- }
-
- }
|