# PDType1FontEmbedder.java

    /*
     * 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 java.io.IOException;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.List;
    import org.apache.fontbox.afm.FontMetrics;
    import org.apache.fontbox.encoding.BuiltInEncoding;
    import org.apache.fontbox.pfb.PfbParser;
    import org.apache.fontbox.type1.Type1Font;
    import org.apache.fontbox.util.BoundingBox;
    import org.apache.pdfbox.cos.COSArray;
    import org.apache.pdfbox.cos.COSDictionary;
    import org.apache.pdfbox.cos.COSName;
    import org.apache.pdfbox.io.IOUtils;
    import org.apache.pdfbox.pdmodel.PDDocument;
    import org.apache.pdfbox.pdmodel.common.PDRectangle;
    import org.apache.pdfbox.pdmodel.common.PDStream;
    import org.apache.pdfbox.pdmodel.font.encoding.Encoding;
    import org.apache.pdfbox.pdmodel.font.encoding.GlyphList;
    import org.apache.pdfbox.pdmodel.font.encoding.Type1Encoding;

    /**
     * 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 Type1Font type1;

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

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

            if (encoding == null)
            {
                fontEncoding = Type1Encoding.fromFontBox(type1.getEncoding());
            }
            else
            {
                fontEncoding = encoding;
            }

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

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

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

            // widths
            List<Integer> widths = new ArrayList<>(256);
            for (int code = 0; code <= 255; code++)
            {
                String name = fontEncoding.getName(code);
                int width = Math.round(type1.getWidth(name));
                widths.add(width);
            }

            dict.setInt(COSName.FIRST_CHAR, 0);
            dict.setInt(COSName.LAST_CHAR, 255);
            dict.setItem(COSName.WIDTHS, COSArray.ofCOSIntegers(widths));
            dict.setItem(COSName.ENCODING, encoding);
        }

        /**
         * Returns a PDFontDescriptor for the given PFB.
         *
         * @throws IOException if the font bounding box isn't available
         */
        static PDFontDescriptor buildFontDescriptor(Type1Font type1) throws IOException
        {
            boolean isSymbolic = type1.getEncoding() instanceof BuiltInEncoding;
            BoundingBox bbox = type1.getFontBBox();
            PDFontDescriptor fd = new PDFontDescriptor();

            fd.setFontName(type1.getName());
            fd.setFontFamily(type1.getFamilyName());
            fd.setNonSymbolic(!isSymbolic);
            fd.setSymbolic(isSymbolic);
            fd.setFontBoundingBox(new PDRectangle(bbox));
            fd.setItalicAngle(type1.getItalicAngle());
            fd.setAscent(bbox.getUpperRightY());
            fd.setDescent(bbox.getLowerLeftY());
            fd.setCapHeight(type1.getBlueValues().get(2).floatValue());
            fd.setStemV(0); // for PDF/A
            return fd;
        }

        /**
         * Returns a PDFontDescriptor for the given AFM. Used only for Standard 14 fonts.
         *
         * @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());
            fd.setStemV(0); // for PDF/A
            return fd;
        }

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

        /**
         * Returns the font's glyph list.
         */
        public GlyphList getGlyphList()
        {
            return GlyphList.getAdobeGlyphList();
        }

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