Package org.apache.fop.render.afp.fonts

Source Code of org.apache.fop.render.afp.fonts.AFPFontReader

/*
* 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: AFPFontReader.java 601438 2007-12-05 17:36:12Z acumiskey $ */

package org.apache.fop.render.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.ArrayList;
import java.util.HashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.render.afp.exceptions.FontRuntimeException;
import org.apache.fop.render.afp.modca.AFPConstants;
import org.apache.fop.render.afp.tools.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.fop.render.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 HashMap codePages = new HashMap();

    /**
     * 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
     */
    public void loadCharacterSetMetric(CharacterSet characterSet) {

        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 cp = new String(characterSet.getCodePage());
            String path = characterSet.getPath();

            HashMap codepage = (HashMap) codePages.get(cp);

            if (codepage == null) {
                codepage = loadCodePage(cp, characterSet.getEncoding(), path);
                codePages.put(cp, 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 characterset = characterSet.getName();

            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.toURL();
                    if (url == null) {
                        String msg = "CharacterSet file not found for "
                            + characterset + " in classpath: " + path;
                        log.error(msg);
                        throw new FileNotFoundException(msg);
                    }
                } catch (MalformedURLException ex) {
                    String msg = "CharacterSet file not found for "
                        + characterset + " in classpath: " + path;
                    log.error(msg);
                    throw new FileNotFoundException(msg);
                }

            }

            File directory = new File(url.getPath());

            final String filterpattern = characterset.trim();
            FilenameFilter filter = new FilenameFilter() {
                public boolean accept(File dir, String name) {
                    return name.startsWith(filterpattern);
                }
            };

            File[] csfont = directory.listFiles(filter);
            if (csfont.length < 1) {
                String msg = "CharacterSet file search for " + characterset
                    + " located " + csfont.length + " files";
                log.error(msg);
                throw new FileNotFoundException(msg);
            } else if (csfont.length > 1) {
                String msg = "CharacterSet file search for " + characterset
                    + " located " + csfont.length + " files";
                log.warn(msg);
            }

            inputStream = csfont[0].toURL().openStream();
            if (inputStream == null) {
                String msg = "Failed to open character set resource "
                    + characterset;
                log.error(msg);
                throw new FileNotFoundException(msg);
            }

            StructuredFieldReader sfr = new StructuredFieldReader(inputStream);

            // Process D3A789 Font Control
            FontControl fnc = processFontControl(sfr);

            //process D3AE89 Font Orientation
            CharacterSetOrientation[] csoArray = processFontOrientation(sfr);

            //process D3AC89 Font Position
            processFontPosition(sfr, csoArray, fnc.getDpi());

            //process D38C89 Font Index (per orientation)
            for (int i = 0; i < csoArray.length; i++) {
                processFontIndex(sfr, csoArray[i], codepage, fnc.getDpi());
                characterSet.addCharacterSetOrientation(csoArray[i]);
            }

        } catch (Exception ex) {
            throw new FontRuntimeException(
                "Failed to load the character set metrics for code page "
                + characterSet.getCodePage(), ex);
        } finally {
            try {
                inputStream.close();
            } catch (Exception ex) {
                // Ignore
            }
        }

    }

    /**
     * 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
     */
    private static HashMap loadCodePage(String codePage, String encoding,
        String path) throws IOException {

        // Create the HashMap to store code page information
        HashMap codepages = new HashMap();

        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.toURL();
                if (url == null) {
                    String msg = "CodePage file not found for " + codePage
                        + " in classpath: " + path;
                    log.error(msg);
                    throw new FileNotFoundException(msg);
                }
            } catch (MalformedURLException ex) {
                String msg = "CodePage file not found for " + codePage
                    + " 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 = codePage.trim();
        FilenameFilter filter = new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.startsWith(filterpattern);
            }
        };

        File[] codepage = directory.listFiles(filter);

        if (codepage.length < 1) {
            String msg = "CodePage file search for " + codePage + " located "
                + codepage.length + " files";
            log.error(msg);
            throw new FileNotFoundException(msg);
        } else if (codepage.length > 1) {
            String msg = "CodePage file search for " + codePage + " located "
                + codepage.length + " files";
            log.warn(msg);
        }

        InputStream is = codepage[0].toURL().openStream();

        if (is == null) {
            String msg = "AFPFontReader:: loadCodePage(String):: code page file not found for "
                + codePage;
            log.error(msg);
            throw new FileNotFoundException(msg);
        }

        StructuredFieldReader sfr = new StructuredFieldReader(is);
        byte[] data = sfr.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++;
            }
        }

        try {
            is.close();
        } catch (Exception ex) {
            // Ignore
        }

        return codepages;

    }

    /**
     * Process the font control details using the structured field reader.
     *
     * @param sfr
     *            the structured field reader
     */
    private static FontControl processFontControl(StructuredFieldReader sfr)
    throws IOException {

        byte[] fncData = sfr.getNext(FONT_CONTROL_SF);

//        int position = 0;

        FontControl fontControl = new AFPFontReader().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 sfr
     *            the structured field reader
     */
    private static CharacterSetOrientation[] processFontOrientation(
        StructuredFieldReader sfr) throws IOException {

        byte[] data = sfr.getNext(FONT_ORIENTATION_SF);

        int position = 0;
        byte[] fnoData = new byte[26];

        ArrayList orientations = new 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 sfr
     *            the structured field reader
     * @param csoArray
     *            the array of CharacterSetOrientation objects
     */
    private static void processFontPosition(StructuredFieldReader sfr,
        CharacterSetOrientation[] csoArray, int dpi) throws IOException {

        byte[] data = sfr.getNext(FONT_POSITION_SF);

        int position = 0;
        byte[] fpData = new byte[26];

        int csoIndex = 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 cso = csoArray[csoIndex];

                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;

                cso.setXHeight(xHeight * fopFactor);
                cso.setCapHeight(capHeight * fopFactor);
                cso.setAscender(ascHeight * fopFactor);
                cso.setDescender(dscHeight * fopFactor);

                csoIndex++;

                fpData[position] = data[index];

            }

            position++;
        }

    }

    /**
     * Process the font index details for the character set orientation.
     *
     * @param sfr
     *            the structured field reader
     * @param cso
     *            the CharacterSetOrientation object to populate
     * @param codepage
     *            the map of code pages
     */
    private static void processFontIndex(StructuredFieldReader sfr,
        CharacterSetOrientation cso, HashMap codepage, int dpi)
        throws IOException {

        byte[] data = sfr.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] = (byte) data[index];
                position++;
            } else if (position < 27) {
                fiData[position - 8] = (byte) data[index];
                position++;
            } else if (position == 27) {

                fiData[position - 8] = (byte) 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;
        }
    }

}
TOP

Related Classes of org.apache.fop.render.afp.fonts.AFPFontReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.