All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpedal.fonts.glyph.PdfJavaGlyphs Maven / Gradle / Ivy

/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/support/
 *
 * (C) Copyright 1997-2017 IDRsolutions and Contributors.
 *
 * This file is part of JPedal/JPDF2HTML5
 *
     This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


 *
 * ---------------
 * PdfJavaGlyphs.java
 * ---------------
 */
package org.jpedal.fonts.glyph;

import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.jpedal.fonts.FontMappings;
import org.jpedal.fonts.StandardFonts;
import org.jpedal.fonts.objects.FontData;
import org.jpedal.fonts.tt.Table;
import org.jpedal.parser.DecoderOptions;
import org.jpedal.render.DynamicVectorRenderer;
import org.jpedal.utils.StringUtils;

public class PdfJavaGlyphs implements PdfGlyphs, Serializable {

    protected boolean hasGIDtoCID;

    /**
     * shapes we have already drawn to speed up plotting, or null if there are none
     */
    private Area[] cachedShapes;
    private AffineTransform[] cachedAt;

    /**
     * lookup to translate CMAP chars
     */
    public int[] CMAP_Translate;

    protected int glyphCount;

    public boolean isFontInstalled;

    /**
     * default font to use in display
     */
    public String defaultFont = "Lucida Sans";

    /**
     * lookup for font names less any + suffix
     */
    public String fontName = "default";

    //some fonts need to be remapped (ie Arial-BoldMT to Arial,Bold)
    public String logicalfontName = "default";

    Map chars = new HashMap();
    Map displayValues = new HashMap();
    Map embeddedChars = new HashMap();

    /**
     * flag is CID font is identity matrix
     */
    private boolean isIdentity;
    private boolean isFontEmbedded;
    private boolean hasWidths = true;
    protected int objID;
    protected boolean remappedCFFFont;

    public void flush() {
        cachedShapes = null;
        cachedAt = null;
    }

    @Override
    public String getBaseFontName() {
        return baseFontName;
    }

    public void setBaseFontName(final String baseFontName) {
        this.baseFontName = baseFontName;
    }

    public String baseFontName = "";

    public boolean isSubsetted;


    /**
     * copy of Trm so we can choose if cache should be flushed
     */
    public float[][] lastTrm = new float[3][3];

    /**
     * current font to plot, or null if not used yet
     */
    private Font unscaledFont;

    public boolean isArialInstalledLocally;
    private int maxCharCount = 255;
    public boolean isCIDFont;

    public String font_family_name;

    public int style;

    int size;

    String weight, testFont;

    /**
     * used to render page by drawing routines
     */
    public static final FontRenderContext frc = new FontRenderContext(null, true, true);

    /**
     * list of installed fonts
     */
    private static String[] fontList;


    /**
     * used for standard non-substituted version
     *
     * @param Trm
     * @param rawInt
     * @param displayValue
     * @param currentWidth
     */
    @Override
    public Area getStandardGlyph(final float[][] Trm, final int rawInt, final String displayValue, final float currentWidth, final boolean isSTD) {

        //either calculate the glyph to draw or reuse if already drawn
        Area transformedGlyph2 = getCachedShape(rawInt);

        if (transformedGlyph2 == null) {

            double dY = -1, dX = 1, y = 0;

            AffineTransform at;

            //allow for text running up the page
            if ((Trm[1][0] < 0 && Trm[0][1] >= 0) || (Trm[0][1] < 0 && Trm[1][0] >= 0)) {
                dX = 1f;
                dY = -1f;
            }

            if (isSTD) {

                transformedGlyph2 = getGlyph(displayValue, currentWidth);

                //hack to fix problem with Java Arial font
                if (transformedGlyph2 != null && rawInt == 146 && isArialInstalledLocally) {
                    y = -(transformedGlyph2.getBounds().height - transformedGlyph2.getBounds().y);
                }
            } else {

                GlyphVector gv1 = null;

                //do not show CID fonts as Lucida unless match
                if (!isCIDFont || isFontInstalled) {
                    gv1 = getUnscaledFont().createGlyphVector(frc, displayValue);
                }

                if (gv1 != null) {

                    transformedGlyph2 = new Area(gv1.getOutline());

                    //put glyph into display position
                    double glyphX = gv1.getOutline().getBounds2D().getX();

                    //ensure inside box
                    if (glyphX < 0) {
                        glyphX = -glyphX;
                        at = AffineTransform.getTranslateInstance(glyphX * 2, 0);
                        transformedGlyph2.transform(at);
                        //x=-glyphX*2;
                    }

                    final double glyphWidth = gv1.getVisualBounds().getWidth() + (glyphX * 2);
                    final double scaleFactor = currentWidth / glyphWidth;
                    if (scaleFactor < 1) {
                        dX *= scaleFactor;
                    }
                }
            }

            //create shape for text using transformation to make correct size
            at = new AffineTransform(dX * Trm[0][0], dX * Trm[0][1], dY * Trm[1][0], dY * Trm[1][1], 0, y);

            if (transformedGlyph2 != null) {
                transformedGlyph2.transform(at);
            }

            //save so we can reuse if it occurs again in this TJ command
            setCachedShape(rawInt, transformedGlyph2, at);
        }

        return transformedGlyph2;
    }

    /**
     * returns a generic glyph using inbuilt fonts
     */
    public Area getGlyph(final String displayValue, final float currentWidth) {

        boolean fontMatched = true;

        /*use default if cannot be displayed*/
        GlyphVector gv1 = null;

        //remap font if needed
        String xx = displayValue;

        //if cannot display return to Lucida
        if (!getUnscaledFont().canDisplay(xx.charAt(0))) {
            xx = displayValue;
            fontMatched = false;
        }

        if (this.isCIDFont && isFontEmbedded && fontMatched) {
            gv1 = null;
        } else if (fontMatched) {
            gv1 = getUnscaledFont().createGlyphVector(frc, xx);
        } else {
            Font tempFont = new Font(defaultFont, 0, 1);
            if (!tempFont.canDisplay(xx.charAt(0))) {
                tempFont = new Font("lucida", 0, 1);
            }
            if (tempFont.canDisplay(xx.charAt(0))) {
                gv1 = tempFont.createGlyphVector(frc, xx);
            }
        }

        //gv1 =getUnscaledFont().createGlyphVector(frc, xx);

        Area transformedGlyph2 = null;
        if (gv1 != null) {
            transformedGlyph2 = new Area(gv1.getOutline());

            //put glyph into display position
            double glyphX = gv1.getOutline().getBounds2D().getX();
            //double glyphY=gv1.getOutline().getBounds2D().getY();
            final double width = gv1.getOutline().getBounds2D().getWidth();

            AffineTransform at;

            if (!hasWidths) { //center for looks
                //try standard values which are indexed under NAME of char
                //String charName = StandardFonts.getUnicodeChar(StandardFonts.WIN, rawInt);
                final float leading = (float) (currentWidth - (width + glyphX + glyphX)) / 2;

                if (leading > 0) {
                    at = AffineTransform.getTranslateInstance(leading, 0);
                    transformedGlyph2.transform(at);
                }
            } else {

                if (glyphX < 0) { //ensure inside box
                    glyphX = -glyphX;
                    at = AffineTransform.getTranslateInstance(glyphX, 0);
                    transformedGlyph2.transform(at);
                }

                final double scaleFactor = currentWidth / (transformedGlyph2.getBounds2D().getWidth());
                if (scaleFactor < 1) {
                    at = AffineTransform.getScaleInstance(scaleFactor, 1);
                    transformedGlyph2.transform(at);
                }
            }
        }

        return transformedGlyph2;
    }

    /**
     * Caches the specified shape.
     */
    public final void setCachedShape(final int idx, final Area shape, final AffineTransform at) {
        // using local variable instead of sync'ing
        Area[] cache = cachedShapes;
        AffineTransform[] atCache = cachedAt;

        if (cache == null) {
            cachedShapes = cache = new Area[maxCharCount];
            cachedAt = atCache = new AffineTransform[maxCharCount];
        }

        if (shape == null) {
            cache[idx] = null;
        } else {
            cache[idx] = shape;
        }

        if (shape != null && at != null) {
            atCache[idx] = at;
        }
    }

    /**
     * Returns the specified shape from the cache, or null if the shape
     * is not in the cache.
     */
    public final AffineTransform getCachedTransform(final int idx) {
        // using local variable instead of sync'ing
        final AffineTransform[] cache = cachedAt;

        if (cache == null) {
            return null;
        } else {
            return cache[idx];
        }

    }

    public boolean isValidGIDtoCID(final int value) {
        return false;
    }

    /**
     * Returns the specified shape from the cache, or null if the shape
     * is not in the cache.
     */
    public final Area getCachedShape(final int idx) {
        // using local variable instead of sync'ing
        final Area[] cache = cachedShapes;

        if (cache == null) {
            return null;
        } else {
            final Area currentShape = cache[idx];

            if (currentShape == null) {
                return null;
            } else {
                return currentShape;
            }
        }
        //return cache == null ? null : (Area)cache[idx].clone();
        //return cache == null ? null : cache[idx];
    }

    public void init(final int maxCharCount, final boolean isCIDFont) {
        this.maxCharCount = maxCharCount;
        this.isCIDFont = isCIDFont;
    }

    /**
     * set the font being used or try to approximate
     */
    public final void setFont(String name, final int size) {

        this.size = size;

        //allow user to totally over-ride
        //passing in this allows user to reset any global variables
        //set in this method as well.
        //Helper is a static instance of the inteface JPedalHelper
        if (DecoderOptions.Helper != null) {
            final Font f = DecoderOptions.Helper.setFont(this, StringUtils.convertHexChars(name), size);
            //if you want to implement JPedalHelper but not
            //use this function, just return null
            if (f != null) {
                this.style = f.getStyle();
                this.font_family_name = f.getFamily();
                this.unscaledFont = f;
                return;
            }

        }

        name = StandardFonts.expandName(name);

        //set defaults
        this.font_family_name = name;
        this.style = Font.PLAIN;

        String mappedName = null;

        if (font_family_name == null) {
            font_family_name = this.fontName;
        }

        testFont = font_family_name;
        if (font_family_name != null) {
            testFont = font_family_name.toLowerCase();
        }

        //pick up any weight in type 3 font or - standard font mapped to Java
        int pointer = font_family_name.indexOf(',');
        if ((pointer == -1))//&&(StandardFonts.javaFontList.get(font_family_name)!=null))
        {
            pointer = font_family_name.indexOf('-');
        }

        if (pointer != -1) {

            //see if present with ,
            mappedName = FontMappings.fontSubstitutionAliasTable.get(testFont);


            weight = testFont.substring(pointer + 1, testFont.length());

            style = getWeight(weight);

            font_family_name = font_family_name.substring(0, pointer).toLowerCase();

            testFont = font_family_name;

            if (testFont.endsWith("mt")) {
                testFont = testFont.substring(0, testFont.length() - 2);
            }

        }

        //remap if not type 3 match
        if (mappedName == null) {
            mappedName = FontMappings.fontSubstitutionAliasTable.get(testFont);
        }

        if ((mappedName != null) && (mappedName.equals("arialbd"))) {
            mappedName = "arial-bold";
        }

        if (mappedName != null) {

            font_family_name = mappedName;

            pointer = font_family_name.indexOf('-');
            if (pointer != -1) {

                font_family_name = font_family_name.toLowerCase();

                weight = font_family_name.substring(pointer + 1, font_family_name.length());

                style = getWeight(weight);

                font_family_name = font_family_name.substring(0, pointer);
            }

            testFont = font_family_name.toLowerCase();

            if (testFont.endsWith("mt")) {
                testFont = testFont.substring(0, testFont.length() - 2);
            }

        }


    }

    /**
     * work out style (ITALIC, BOLD)
     */
    private static int getWeight(String weight) {

        int style = Font.PLAIN;

        if (weight.endsWith("mt")) {
            weight = weight.substring(0, weight.length() - 2);
        }

        if (weight.contains("heavy")) {
            style = Font.BOLD;
        } else if (weight.contains("bold")) {
            style = Font.BOLD;
        } else if (weight.contains("roman")) {
            style = Font.ROMAN_BASELINE;
        }

        if (weight.contains("italic")) {
            style += Font.ITALIC;
        } else if (weight.contains("oblique")) {
            style += Font.ITALIC;
        }

        return style;
    }


    /**
     * Returns the unscaled font, initializing it first if it hasn't been used before.
     */
    public final Font getUnscaledFont() {

        if (unscaledFont == null && font_family_name != null) {
            //Recheck
            if (fontList == null) {

                //Make sure lowercase
                fontList = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
                for (int i = 0; i < fontList.length; i++) {
                    fontList[i] = fontList[i].toLowerCase();
                }
            }

            //see if installed
            if (fontList != null) {

                //check exact first
                boolean isFound = false;
                int count = fontList.length;
                for (int i = 0; i < count; i++) {
                    if ((fontList[i].equals(testFont)) || ((weight == null) && (testFont.startsWith(fontList[i])))) {
                        isFontInstalled = true;
                        font_family_name = fontList[i];
                        i = count;
                        isFound = true;
                    }
                }

                if (!isFound) {
                    count = fontList.length;
                    for (int i = 0; i < count; i++) {
                        if ((fontList[i].equals(testFont)) || ((weight == null) && (testFont.startsWith(fontList[i])))) {
                            isFontInstalled = true;
                            font_family_name = fontList[i];
                            i = count;
                        }
                    }
                }

                //hack for windows as some odd things going on
                if (isFontInstalled && font_family_name.equals("arial")) {
                    isArialInstalledLocally = true;
                }
            }

                /*approximate display if not installed*/
            if (!isFontInstalled) {

                //try to approximate font
                if (weight == null) {

                    //pick up any weight
                    final String test = font_family_name.toLowerCase();
                    style = getWeight(test);

                }

                font_family_name = defaultFont;
            }

            unscaledFont = new Font(font_family_name, style, size);
        }

            /*commenting out  this broke originaldoc.pdf*/
        if (unscaledFont == null) {
            unscaledFont = new Font(defaultFont, Font.PLAIN, 1);
        }

        return unscaledFont;
    }

    protected PdfGlyph[] cachedEmbeddedShapes;

    protected int localBias, globalBias;

    /**
     * Caches the specified shape.
     */
    public final void setEmbeddedCachedShape(final int idx, final PdfGlyph shape) {
        // using local variable instead of sync'ing
        PdfGlyph[] cache = cachedEmbeddedShapes;
        if (cache == null) {
            cachedEmbeddedShapes = cache = new PdfGlyph[maxCharCount];
        }

        if (idx < cache.length) {
            cache[idx] = shape;
        }
    }

    /**
     * Returns the specified shape from the cache, or null if the shape
     * is not in the cache.
     */
    public final PdfGlyph getEmbeddedCachedShape(final int idx) {
        // using local variable instead of sync'ing
        final PdfGlyph[] cache = cachedEmbeddedShapes;

        if (cache == null) {
            return null;
        } else if (idx < cache.length) {
            final PdfGlyph currentShape = cache[idx];

            if (currentShape == null) {
                return null;
            } else {
                return currentShape;
            }
        } else {
            return null;
        }
        //return cache == null ? null : (Area)cache[idx].clone();
        //return cache == null ? null : cache[idx];
    }

    /**
     * template used by t1/t3/tt fonts
     */
    @Override
    public PdfGlyph getEmbeddedGlyph(final GlyphFactory factory, final String glyph, final float[][] trm, final int rawInt, final String displayValue, final float currentWidth, final String key) {
        return null;
    }

    public void setGIDtoCID(final int[] cidToGIDMap) {
    }

    public void setEncodingToUse(final boolean hasEncoding, final int fontEncoding, final boolean isCIDFont) {

    }


    public int readEmbeddedFont(final boolean TTstreamisCID, final byte[] fontDataAsArray, final FontData fontData) {

        throw new RuntimeException("readEmbeddedFont in PdfJavaGlyphs should not be called");

    }

    public void setIsSubsetted(final boolean b) {
        isSubsetted = b;
    }

    public void setT3Glyph(final int key, final int altKey, final PdfGlyph glyph) {

    }


    public void setCharString(final String s, final byte[] bytes, final int glyphNo) {
    }

    public boolean is1C() {
        return false;
    }

    public void setis1C(final boolean b) {
    }


    public void setValuesForGlyph(final int rawInt, final String charGlyph, final String displayValue, final String embeddedChar) {
        final Integer key = rawInt;
        chars.put(key, charGlyph);
        displayValues.put(key, displayValue);
        embeddedChars.put(key, embeddedChar);
    }

    @Override
    public String getDisplayValue(final Integer key) {
        return displayValues.get(key);
    }

    @Override
    public String getCharGlyph(final Integer key) {
        return chars.get(key);
    }

    @Override
    public String getEmbeddedEnc(final Integer key) {

        return embeddedChars.get(key);
    }

    public Map getDisplayValues() {
        return Collections.unmodifiableMap(displayValues);
    }

    public Map getCharGlyphs() {
        return Collections.unmodifiableMap(chars);
    }

    public Map getEmbeddedEncs() {

        return Collections.unmodifiableMap(embeddedChars);
    }

    public void setDisplayValues(final Map displayValues) {
        this.displayValues = displayValues;
    }

    public void setCharGlyphs(final Map chars) {
        this.chars = chars;
    }

    public void setEmbeddedEncs(final Map embeddedChars) {

        this.embeddedChars = embeddedChars;
    }

    public void setLocalBias(final int i) {
        localBias = i;

    }

    public void setGlobalBias(final int i) {
        globalBias = i;

    }

    @SuppressWarnings("UnusedParameters")
    public float getTTWidth(final String charGlyph, final int rawInt, final String displayValue, final boolean b) {

        throw new RuntimeException("getTTWidth should not be called");

    }

    @SuppressWarnings({"UnusedParameters", "UnusedDeclaration"})
    public static String getPostName(final int rawInt) {
        return "notdef";
    }

    /**
     * should never be called - just to allow TTGlyphs to extend
     *
     * @param rawInt
     */
    public int getConvertedGlyph(final int rawInt) {
        return -1;
    }

    /**
     * flag for CID TT fonts
     *
     * @param isIdentity
     */
    public void setIsIdentity(final boolean isIdentity) {
        this.isIdentity = isIdentity;
    }

    /**
     * flag to show if CID TT fonts have identity matrix
     *
     * @return
     */
    public boolean isIdentity() {
        return isIdentity;
    }

    @SuppressWarnings("UnusedDeclaration")
    public float[] getFontBoundingBox() {
        return new float[]{0f, 0f, 1000f, 1000f};
    }

    public void setFontEmbedded(final boolean isSet) {
        isFontEmbedded = isSet;
    }

    public int getType() {
        return 0;
    }

    public void setHasWidths(final boolean hasWidths) {
        this.hasWidths = hasWidths;
    }

    /**
     * return value or -1
     */
    public int getCMAPValue(final int rawInt) {
        if (CMAP_Translate == null) {
            return -1;
        } else {
            //System.out.println(rawInt+" becomes "+CMAP_Translate[rawInt]);
            return (CMAP_Translate[rawInt]);
        }
    }


    public boolean isCorrupted() {
        return false;
    }

    public void setCorrupted(final boolean corrupt) {

    }

    //used by PS to OTF convertor
    public void setIndexForCharString(final int jj, final String glyphName) {
    }

    //used by PS to OTF convertor
    public String getIndexForCharString(final int jj) {
        return null;
    }

    //used by PS to OTF converter
    public Map getCharStrings() {
        return null;
    }

    public void setGlyphCount(final int nGlyphs) {
        glyphCount = nGlyphs;
    }

    public int getGlyphCount() {
        return glyphCount;
    }

    public void setRenderer(final DynamicVectorRenderer current) {
    }

    public Table getTable(final int id) {
        return null;
    }

    public boolean hasGIDtoCID() {
        return hasGIDtoCID;
    }

    public void setObjID(final int objID) {
        this.objID = objID;
    }

    public void setRemappedCFFFont(final boolean remappedCFFFont) {
        this.remappedCFFFont = remappedCFFFont;
    }

    public boolean isRemappedCFFFont() {
        return remappedCFFFont;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy