Package org.apache.pdfbox.pdmodel.font

Source Code of org.apache.pdfbox.pdmodel.font.PDType1FontEmbedder

/*
* 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.
*/
package org.apache.pdfbox.pdmodel.font;

import org.apache.fontbox.afm.AFMParser;
import org.apache.fontbox.afm.CharMetric;
import org.apache.fontbox.afm.FontMetrics;
import org.apache.fontbox.pfb.PfbParser;
import org.apache.fontbox.type1.Type1Font;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSInteger;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.font.encoding.DictionaryEncoding;
import org.apache.pdfbox.pdmodel.font.encoding.Encoding;
import org.apache.pdfbox.pdmodel.font.encoding.Type1Encoding;
import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.COSArrayList;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.common.PDStream;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
* Embedded PDType1Font builder. Helper class to populate a PDType1Font from a PFB and AFM.
*
* @author Michael Niedermair
*/
class PDType1FontEmbedder
{
    private final Encoding fontEncoding;
    private final FontMetrics metrics;
    private final Type1Font type1;

    /**
     * This will load a afm and pfb to be embedding into a document.
     *
     * @param doc The PDF document that will hold the embedded font.
     * @param dict The Font dictionary to write to.
     * @param afmStream The afm input.
     * @param pfbStream The pfb input.
     * @throws IOException If there is an error loading the data.
     */
    public PDType1FontEmbedder(PDDocument doc, COSDictionary dict, InputStream afmStream,
                               InputStream pfbStream) throws IOException
    {
        dict.setItem(COSName.SUBTYPE, COSName.TYPE1);

        // read the afm
        AFMParser afmParser = new AFMParser(afmStream);
        metrics = afmParser.parse();
        this.fontEncoding = encodingFromAFM(metrics);

        // build font descriptor
        PDFontDescriptor fd = buildFontDescriptor(metrics);

        // read the pfb
        byte[] pfbBytes = IOUtils.toByteArray(pfbStream);
        PfbParser pfbParser = new PfbParser(new ByteArrayInputStream(pfbBytes));
        type1 = Type1Font.createWithPFB(new ByteArrayInputStream(pfbBytes));

        PDStream fontStream = new PDStream(doc, pfbParser.getInputStream(), false);
        fontStream.getStream().setInt("Length", pfbParser.size());
        for (int i = 0; i < pfbParser.getLengths().length; i++)
        {
            fontStream.getStream().setInt("Length" + (i + 1), pfbParser.getLengths()[i]);
        }
        fontStream.addCompression();
        fd.setFontFile(fontStream);

        // set the values
        dict.setItem(COSName.FONT_DESC, fd);
        dict.setName(COSName.BASE_FONT, metrics.getFontName());

        // get firstchar, lastchar
        int firstchar = 255;
        int lastchar = 0;

        // widths
        List<CharMetric> listmetric = metrics.getCharMetrics();
        Encoding encoding = getFontEncoding();
        int maxWidths = 256;
        List<Integer> widths = new ArrayList<Integer>(maxWidths);
        int zero = 250;

        Iterator<CharMetric> iter = listmetric.iterator();
        for (int i = 0; i < maxWidths; i++)
        {
            widths.add(zero);
        }

        while (iter.hasNext())
        {
            CharMetric m = iter.next();
            int n = m.getCharacterCode();
            if (n > 0)
            {
                firstchar = Math.min(firstchar, n);
                lastchar = Math.max(lastchar, n);
                if (m.getWx() > 0)
                {
                    int width = Math.round(m.getWx());
                    widths.set(n, width);
                }
            }
        }
        dict.setInt(COSName.FIRST_CHAR, 0);
        dict.setInt(COSName.LAST_CHAR, 255);
        dict.setItem(COSName.WIDTHS, COSArrayList.converterToCOSArray(widths));
    }

    /**
     * Returns a PDFontDescriptor for the given AFM.
     *
     * @param metrics AFM
     */
    static PDFontDescriptor buildFontDescriptor(FontMetrics metrics)
    {
        boolean isSymbolic = metrics.getEncodingScheme().equals("FontSpecific");

        PDFontDescriptor fd = new PDFontDescriptor();
        fd.setFontName(metrics.getFontName());
        fd.setFontFamily(metrics.getFamilyName());
        fd.setNonSymbolic(!isSymbolic);
        fd.setSymbolic(isSymbolic);
        fd.setFontBoundingBox(new PDRectangle(metrics.getFontBBox()));
        fd.setItalicAngle(metrics.getItalicAngle());
        fd.setAscent(metrics.getAscender());
        fd.setDescent(metrics.getDescender());
        fd.setCapHeight(metrics.getCapHeight());
        fd.setXHeight(metrics.getXHeight());
        fd.setAverageWidth(metrics.getAverageCharacterWidth());
        fd.setCharacterSet(metrics.getCharacterSet());
        return fd;
    }

    // This will generate a Encoding from the AFM-Encoding, because the AFM-Enconding isn't exported
    // to the pdf and consequently the StandardEncoding is used so that any special character is
    // missing I've copied the code from the pdfbox-forum posted by V0JT4 and made some additions
    // concerning german umlauts see also https://sourceforge.net/forum/message.php?msg_id=4705274
    private DictionaryEncoding encodingFromAFM(FontMetrics metrics) throws IOException
    {
        Type1Encoding encoding = new Type1Encoding(metrics);

        COSArray differences = new COSArray();
        differences.add(COSInteger.ZERO);
        for (int i = 0; i < 256; i++)
        {
            differences.add(COSName.getPDFName(encoding.getName(i)));
        }
        // my AFMPFB-Fonts has no character-codes for german umlauts
        // so that I've to add them here by hand
        differences.set(0337 + 1, COSName.getPDFName("germandbls"));
        differences.set(0344 + 1, COSName.getPDFName("adieresis"));
        differences.set(0366 + 1, COSName.getPDFName("odieresis"));
        differences.set(0374 + 1, COSName.getPDFName("udieresis"));
        differences.set(0304 + 1, COSName.getPDFName("Adieresis"));
        differences.set(0326 + 1, COSName.getPDFName("Odieresis"));
        differences.set(0334 + 1, COSName.getPDFName("Udieresis"));

        return new DictionaryEncoding(COSName.STANDARD_ENCODING, differences);
    }

    /**
     * Returns the font's encoding.
     */
    public Encoding getFontEncoding()
    {
        return fontEncoding;
    }

    /**
     * Returns the font's metrics.
     */
    public FontMetrics getFontMetrics()
    {
        return metrics;
    }

    /**
     * Returns the Type 1 font.
     */
    public Type1Font getType1Font()
    {
        return type1;
    }
}
TOP

Related Classes of org.apache.pdfbox.pdmodel.font.PDType1FontEmbedder

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.