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

org.jpedal.objects.raw.FormStream Maven / Gradle / Ivy

There is a newer version: 7.15.25
Show newest version
/*
 * ===========================================
 * 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
 *
 @LICENSE@
 *
 * ---------------
 * FormStream.java
 * ---------------
 */
package org.jpedal.objects.raw;

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.StringTokenizer;

import org.jpedal.external.ExternalHandlers;
import org.jpedal.fonts.FontMappings;
import org.jpedal.fonts.PdfFont;
import org.jpedal.fonts.StandardFonts;
import org.jpedal.io.ObjectStore;
import org.jpedal.io.PdfFileReader;
import org.jpedal.io.PdfObjectFactory;
import org.jpedal.io.PdfObjectReader;
import org.jpedal.parser.PdfStreamDecoder;
import org.jpedal.parser.ValueTypes;
import org.jpedal.render.T3Display;
import org.jpedal.render.T3Renderer;
import org.jpedal.utils.LogWriter;

/**
 * Scan object and create images, set values in Appearances
 */
public class FormStream {

    public static final boolean debugUnimplemented = false; //to show unimplemented parts*/
    public static final boolean debug = false; //print info to screen

    //only display once
    private static boolean showFontMessage;

    /**
     * exit when an unimplemented feature or error has occured in form/annot
     * code
     */
    public static final boolean exitOnError = false;

    public static Object[] getRolloverKeyValues(final PdfObject form, final PdfFileReader pdfFileReader) {

        String key = null;
        PdfObject val = null;
        PdfObject rollOffDic = null;

        final PdfKeyPairsIterator APkeys = form.getKeyPairsIterator();

        if (APkeys != null && APkeys.getTokenCount() > 0) {

            while (APkeys.hasMorePairs()) {
                final String glyphKey = APkeys.getNextKeyAsString();
                final byte[] data = APkeys.getNextValueAsBytes();

                if (data != null) {
                    if (glyphKey.equals("Off")) {
                        rollOffDic = PdfObjectFactory.getPDFObjectObjectFromRefOrDirect(new FormObject(glyphKey), pdfFileReader, data, PdfDictionary.AP);
                    } else {
                        key = glyphKey;
                        val = PdfObjectFactory.getPDFObjectObjectFromRefOrDirect(new FormObject(glyphKey), pdfFileReader, data, PdfDictionary.AP);
                    }
                }
                APkeys.nextPair();
            }
        } else {
            if (form.getDictionary(PdfDictionary.Off) != null) {
                rollOffDic = form.getDictionary(PdfDictionary.Off);
            } else if (form.getDecodedStream() != null) {
                rollOffDic = form;
            }

            //if we have a root stream then it is the off value
            if (form.getDictionary(PdfDictionary.On) != null) {
                key = "On";
                val = form.getDictionary(PdfDictionary.On);
            }
        }
        return new Object[]{key, val, rollOffDic};
    }

    public static Object[] getNormalKeyValues(final PdfObject form, final PdfFileReader pdfFileReader) {

        final PdfObject APobjN = form.getDictionary(PdfDictionary.AP).getDictionary(PdfDictionary.N);

        String key = null;
        PdfObject val = null;
        PdfObject normalOffDic = null;

        final PdfKeyPairsIterator APkeys = APobjN.getKeyPairsIterator();

        if (APkeys != null && APkeys.getTokenCount() > 0) {

            while (APkeys.hasMorePairs()) {

                final String glyphKey = APkeys.getNextKeyAsString();
                final byte[] data = APkeys.getNextValueAsBytes();

                if (data != null) {
                    if (glyphKey.equals("Off")) {
                        normalOffDic = PdfObjectFactory.getPDFObjectObjectFromRefOrDirect(new FormObject(glyphKey), pdfFileReader, data, PdfDictionary.AP);
                    } else {
                        key = glyphKey;
                        val = PdfObjectFactory.getPDFObjectObjectFromRefOrDirect(new FormObject(glyphKey), pdfFileReader, data, PdfDictionary.AP);
                    }
                }

                APkeys.nextPair();
            }
        } else {

            //if we have a root stream then it is the off value
            //check in order of N Off, MK I, then N
            //as N Off overrides others and MK I is in preference to N
            if (APobjN.getDictionary(PdfDictionary.Off) != null) {
                normalOffDic = APobjN.getDictionary(PdfDictionary.Off);
            } else if (form.getDictionary(PdfDictionary.MK).getDictionary(PdfDictionary.I) != null
                    && form.getDictionary(PdfDictionary.MK).getDictionary(PdfDictionary.IF) == null) {
                //look here for MK IF
                //if we have an IF inside the MK then use the MK I as some files shown that this value is there
                //only when the MK I value is not as important as the AP N.
                normalOffDic = form.getDictionary(PdfDictionary.MK).getDictionary(PdfDictionary.I);
            } else if (APobjN.getDecodedStream() != null) {
                normalOffDic = APobjN;
            }

            if (APobjN.getDictionary(PdfDictionary.On) != null) {
                val = APobjN.getDictionary(PdfDictionary.On);
                key = "On";
            }
        }

        return new Object[]{key, val, normalOffDic};
    }

    public static Object[] getDownKeyValues(final PdfObject form, final PdfFileReader pdfFileReader) {

        String key = null;
        PdfObject val = null;
        PdfObject downOffDic = null;

        final PdfKeyPairsIterator APkeys = form.getKeyPairsIterator();

        if (APkeys != null && APkeys.getTokenCount() > 0) {

            while (APkeys.hasMorePairs()) {

                final String glyphKey = APkeys.getNextKeyAsString();
                final byte[] data = APkeys.getNextValueAsBytes();

                if (data != null) {
                    if (glyphKey.equals("Off")) {
                        downOffDic = PdfObjectFactory.getPDFObjectObjectFromRefOrDirect(new FormObject(glyphKey), pdfFileReader, data, PdfDictionary.AP);
                    } else {
                        key = glyphKey;
                        val = PdfObjectFactory.getPDFObjectObjectFromRefOrDirect(new FormObject(glyphKey), pdfFileReader, data, PdfDictionary.AP);
                    }
                }

                APkeys.nextPair();

            }
        } else {

            //down on
            if (form.getDictionary(PdfDictionary.On) != null) {
                key = "On";
                val = form.getDictionary(PdfDictionary.On);
            }

            //down off
            //if we have a root stream then it is the off value
            if (form.getDecodedStream() != null) {
                downOffDic = form;
            } else if (form.getDictionary(PdfDictionary.Off) != null) {
                downOffDic = form.getDictionary(PdfDictionary.Off);
            }

        }

        return new Object[]{key, val, downOffDic};

    }

    /**
     * handle of file reader for form streams
     */
    protected PdfObjectReader currentPdfFile;

    /**
     * flag to show if XFA (will be disabled if XFA from version which is not
     * pure XFA)
     */
    public boolean isXFA;

    public FormStream() {
    }

    public static final int[] id = {PdfDictionary.A, PdfDictionary.C2, PdfDictionary.Bl,
            PdfDictionary.E, PdfDictionary.X, PdfDictionary.D, PdfDictionary.U, PdfDictionary.Fo,
            PdfDictionary.PO, PdfDictionary.PC, PdfDictionary.PV,
            PdfDictionary.PI, PdfDictionary.O, PdfDictionary.C1, PdfDictionary.K,
            PdfDictionary.F, PdfDictionary.V, PdfDictionary.C2, PdfDictionary.DC,
            PdfDictionary.WS, PdfDictionary.DS, PdfDictionary.WP, PdfDictionary.DP};

    /**
     * takes in a FormObject already populated with values for the child to
     * overwrite
     */
    public void createAppearanceString(final FormObject formObj, final PdfObjectReader inCurrentPdfFile) {

        currentPdfFile = inCurrentPdfFile;

        init(formObj);

    }

    private void init(final FormObject formObject) {

        final boolean debug = false; //formObject.getPDFRef().equals("68 0 R");

        if (debug) {
            System.out.println("------------------------------setValues-------------------------------" + formObject + ' ' + formObject.getObjectRefAsString());
        }

        //set Ff flags
        final int Ff = formObject.getInt(PdfDictionary.Ff);
        if (Ff != PdfDictionary.Unknown) {
            formObject.commandFf(Ff);
        }

        //set Javascript
        resolveAdditionalAction(formObject);

        if (debug) {
            System.out.println("AP=" + formObject.getDictionary(PdfDictionary.AP));
        }

        //at the moment only handles static
        // (and not dynamic which are created at Runtime if
        // formObject.getBoolean(PdfDictionary.NeedAppearances) is true
        setupAPimages(formObject, currentPdfFile.getObjectReader());

        //set H
        final int key = formObject.getNameAsConstant(PdfDictionary.H);
        if (key != PdfDictionary.Unknown) {
            /*
             * highlighting mode
             * done when the mouse is pressed or held down inside the fields active area
             * N nothing
             * I invert the contents
             * O invert the border
             * P display down appearance stream, or if non available offset the normal to look down
             * T same as P
             *
             * this overides the down appearance
             * Default value = I
             */
            if (key == PdfDictionary.T || key == PdfDictionary.P) {
                if (!formObject.hasDownImage()) {
                    formObject.setOffsetDownApp();
                }

            } else if (key == PdfDictionary.N) {
                //do nothing on press
                formObject.setNoDownIcon();

            } else if (key == PdfDictionary.I) {
                //invert the contents colors
                formObject.setInvertForDownIcon();

            }
        }

        //set Fonts
        final String textStream = formObject.getTextStreamValue(PdfDictionary.DA);

        if (textStream != null) {
            decodeFontCommandObj(textStream, formObject);
        }
    }

    /**
     * set correct flags for AP images
     */
    private static void setupAPimages(final FormObject formObject, final PdfFileReader pdfFileReader) {

        final PdfObject APobjN = formObject.getDictionary(PdfDictionary.AP).getDictionary(PdfDictionary.N);
        //if valid AP, setup flags to show we use images
        if (APobjN != null) {

            final String ASvalue = formObject.getName(PdfDictionary.AS);

            formObject.setAppreancesUsed(true);

            final String key = (String) getNormalKeyValues(formObject, pdfFileReader)[0];

            if (key != null) {
                formObject.setNormalOnState(key);

                if (ASvalue != null && ASvalue.equals(key)) {
                    formObject.setSelected(true);
                }
            }
        }
    }

    /**
     * defines actions to be executed on events 'Trigger Events'
     *
     * @Action This is where the raw data is parsed and put into the FormObject
     */
    private void resolveAdditionalAction(final FormObject formObject) {

        /*
         * entries NP, PP, FP, LP never used
         * A action when pressed in active area ?some others should now be ignored?
         * E action when cursor enters active area
         * X action when cursor exits active area
         * D action when cursor button pressed inside active area
         * U action when cursor button released inside active area
         * Fo action on input focus
         * BI action when input focus lost
         * PO action when page containing is opened,
         * 	actions O of pages AA dic, and OpenAction in document catalog should be done first
         * PC action when page is closed, action C from pages AA dic follows this
         * PV action on viewing containing page
         * PI action when no longer visible in viewer
         * K action on - [javascript]
         * 	keystroke in textfield or combobox
         * 	modifys the list box selection
         * 	(can access the keystroke for validity and reject or modify)
         * F the display formatting of the field (e.g 2 decimal places) [javascript]
         * V action when fields value is changed [javascript]
         * C action when another field changes (recalculate this field) [javascript]
         */
        int idValue;

        for (final int anId : id) {

            //store most actions in lookup table to make code shorter/faster
            idValue = anId;

            currentPdfFile.setJavascriptForObject(formObject, PdfDictionary.AA, idValue);
            currentPdfFile.setJavascriptForObject(formObject, PdfDictionary.A, idValue);
        }
    }

    /**
     * decode appearance stream and convert into VectorRenderObject we can
     * redraw if width and height are 0 we define the size hear offsetImage - 0=
     * no change, 1= offset, 2= invert image pScaling used by HTML - set to 1
     * otherwise
     */
    public static BufferedImage decode(final PdfObject formObj, final PdfObjectReader currentPdfFile, final PdfObject XObject, final int subtype,
                                       int width, int height, final int offsetImage, final float pageScaling) {

        //handle XFA differently
        if (XObject.getObjectType() == PdfDictionary.XFA_APPEARANCE) {

            return ExternalHandlers.decode(formObj, currentPdfFile, XObject, subtype, width, height, offsetImage, pageScaling);
        }

        currentPdfFile.checkResolved(XObject);
        try {

            //create renderer object
            final org.jpedal.fonts.glyph.T3Glyph form = decodeStream(currentPdfFile, XObject);

            final float[] matrix = XObject.getFloatArray(PdfDictionary.Matrix);
            final float[] BBox = XObject.getFloatArray(PdfDictionary.BBox);

            final float scaling;

            float rectX1 = 0, rectY1 = 0;
            if (BBox != null) {

                for (int ii = 0; ii < 4; ii++) {
                    BBox[ii] *= pageScaling;
                }

                rectX1 = (BBox[0]);
                rectY1 = (BBox[1]);

                //Some files have fractions of a pixel in their size In some cases 
                //this needs to be ignored, other times it should be expanded to a full pixel
                //At the moment this is not required, this note is left as a reminder.
                int boxWidth = (int) (((BBox[2] + 0.5f) - BBox[0]));
                if (boxWidth < 0) {
                    boxWidth = -boxWidth;
                }
                int boxHeight = (int) (((BBox[3] + 0.5f) - BBox[1]));
                if (boxHeight < 0) {
                    boxHeight = -boxHeight;
                }

                if (boxWidth == 0 && boxHeight > 0) {
                    boxWidth = 1;
                }
                if (boxWidth > 0 && boxHeight == 0) {
                    boxHeight = 1;
                }

                //if the width and height scaling are miles apart then the width and height
                //are probably the wrong way round so swap them. and recalc the scalings.
                float ws = width / ((float) boxWidth);
                float hs = height / ((float) boxHeight);

                //check if dimensions are correct and alter if not
                final float diff = ws - hs;
                final int diffInt = (int) diff;
                if (diffInt != 0) {
                    //NOTE width and height sent in need to be rotated
                    //as they are not as the image is drawn
                    final int tmpI = width;
                    width = height;
                    height = tmpI;

                    ws = width / ((float) boxWidth);
                    hs = height / ((float) boxHeight);
                }
                //NOTE now we re set the width and height to scaled
                //value of Bounding box to keep the orientation

                //if scaling less than 1 use 1
                if (ws < 1 || hs < 1) {
                    scaling = 1;
                    width = boxWidth;
                    height = boxHeight;
                } else {
                    //use larger scaling as will produce better image
                    if (ws > hs) {
                        scaling = ws;
                        height = (int) (boxHeight * scaling);
                    } else {
                        scaling = hs;
                        width = (int) (boxWidth * scaling);
                    }

                    //make sure image position is scaled
                    rectX1 *= scaling;
                    rectY1 *= scaling;
                }

            } else {

                final float defaultSize = 20;
                if (height < defaultSize) {
                    height = (int) defaultSize;
                }
                if (width < defaultSize) {
                    width = (int) defaultSize;
                }

                final float ws = width / defaultSize;
                final float hs = height / defaultSize;
                if (ws > hs) {
                    scaling = ws;
                    height = (int) (defaultSize * scaling);
                } else {
                    scaling = hs;
                    width = (int) (defaultSize * scaling);
                }

                //make sure image position is scaled
                rectX1 *= scaling;
                rectY1 *= scaling;
            }

            if (width == 0 || height == 0) {
                return null;
            }

            //if offset
            if (offsetImage == 1) {
                width += 2;
                height += 2;
            }

            final BufferedImage aa;

            if (matrix != null && matrix[2] != 0) {
                aa = new BufferedImage(height, width, BufferedImage.TYPE_INT_ARGB);
            } else {
                aa = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            }

            final Graphics2D g2 = createGraphics(aa, formObj, matrix, BBox, pageScaling, scaling, rectX1, rectY1, width, height);

            if (offsetImage == 2) { //invert
                g2.scale(-1, -1);
            } else if (offsetImage == 1) { //offset
                g2.translate(1, 1);
            }

            //carry the sclaing through to the render method
            form.render(0, g2, scaling * pageScaling, true);

            g2.dispose();

            if (subtype == PdfDictionary.Highlight) {
                final BufferedImage i = new BufferedImage(aa.getWidth(), aa.getHeight(), aa.getType());
                final Graphics2D gg = i.createGraphics();
                gg.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
                gg.drawImage(aa, 0, 0, null);
                gg.dispose();
                return i;
            }

            return aa;

        } catch (final Exception e) {
            LogWriter.writeLog("Exception: " + e.getMessage());

            return null;
        } catch (final Error e) {
            LogWriter.writeLog("Error: " + e.getMessage());

            if (ExternalHandlers.throwMissingCIDError && e.getMessage() != null && e.getMessage().contains("kochi")) {
                throw e;
            }

            return null;
        }
    }

    private static Graphics2D createGraphics(final BufferedImage aa, final PdfObject formObj, float[] matrix, final float[] BBox, final float pageScaling, final float scaling, final float transformOffsetX, final float transformOffsetY, final int width, final int height) {

        final Graphics2D g2;
        int offset = height;

        if (matrix != null) {

            //Added for odd case 22179
            //pageScaling!=1 added to lock out of html as when not 1 html baseline is affected
            if (pageScaling == 1 && matrix[4] > 0 && matrix[5] > 0) {
                matrix = createMatrixFromBoundingBoxes(BBox, formObj.getFloatArray(PdfDictionary.Rect));
            } else {
                //scale so they offset correctly
                matrix[4] = matrix[4] * scaling * pageScaling;
                matrix[5] = matrix[5] * scaling * pageScaling;
            }

            if (matrix[2] != 0) {
                offset = width;
            } else if (matrix[1] >= 0) {
                //rectX1 and rectY1 already have the scaling applied
                if (matrix[4] != 0f) {
                    matrix[4] = -transformOffsetX;
                }
                if (matrix[5] != 0f) {
                    matrix[5] = -transformOffsetY;
                }
            }

            g2 = (Graphics2D) aa.getGraphics();
            final AffineTransform flip = new AffineTransform();
            flip.translate(0, offset);
            flip.scale(1, -1);
            g2.setTransform(flip);

            if (debug) {
                System.out.println(" rectX1 = " + transformOffsetX + " rectY1 = " + transformOffsetY + " width = " + width + " height = " + height);
            }

            final AffineTransform affineTransform = new AffineTransform(matrix);
            g2.transform(affineTransform);
        } else {
            g2 = (Graphics2D) aa.getGraphics();

            final AffineTransform flip = new AffineTransform();
            flip.translate(0, offset);
            flip.scale(1, -1);
            g2.setTransform(flip);
        }

        return g2;
    }

    private static float[] createMatrixFromBoundingBoxes(final float[] BBox, final float[] BBox2) {

        final float[] matrix = new float[6];
        if (BBox2[1] > BBox2[3]) {
            final float t = BBox2[1];
            BBox2[1] = BBox2[3];
            BBox2[3] = t;
        }

        if (BBox2[0] > BBox2[2]) {
            final float t = BBox2[0];
            BBox2[0] = BBox2[2];
            BBox2[2] = t;
        }

        matrix[0] = (BBox2[2] - BBox2[0]) / (BBox[2] - BBox[0]);
        matrix[1] = 0;
        matrix[2] = 0;
        matrix[3] = (BBox2[3] - BBox2[1]) / (BBox[3] - BBox[1]);
        matrix[4] = (BBox2[0] - BBox[0]);
        matrix[5] = (BBox2[1] - BBox[1]);

        return matrix;
    }

    private static org.jpedal.fonts.glyph.T3Glyph decodeStream(final PdfObjectReader currentPdfFile, final PdfObject XObject) {

        //generate local object to decode the stream
        final ObjectStore localStore = new ObjectStore();

        final T3Renderer glyphDisplay = new T3Display(0, false, 20, localStore);

        final PdfStreamDecoder glyphDecoder = new PdfStreamDecoder(currentPdfFile);
        glyphDecoder.setParameters(false, true, 15, 0, false, false);

        glyphDecoder.setStreamType(ValueTypes.FORM);

        glyphDecoder.setObjectValue(ValueTypes.ObjectStore, localStore);

        glyphDecoder.setRenderer(glyphDisplay);

        /*read any resources*/
        try {
            final PdfObject Resources = XObject.getDictionary(PdfDictionary.Resources);

            if (Resources != null) {
                glyphDecoder.readResources(Resources, false);
            }

        } catch (final Exception e) {
            LogWriter.writeLog("Exception: " + e.getMessage());
        }

        /*decode the stream*/
        final byte[] commands = XObject.getDecodedStream();
        if (commands != null) {
            glyphDecoder.decodeStreamIntoObjects(commands, false);
        }

        final boolean ignoreColors = glyphDecoder.ignoreColors;

        localStore.flush();

        return new org.jpedal.fonts.glyph.T3Glyph(glyphDisplay, 0, 0, ignoreColors);

    }

    /**
     * Accepts PdfObjectReader and PdfObject containing an AP stream This reads
     * the AP stream, and returns the display text from stream
     *
     * @param currentPdfFile PdfObjectReader object to read the AP stream
     * @param Xobject        PdfObject containing the AP stream to be read
     * @return String containing display text from AP stream
     */
    public static String decipherTextFromAP(final PdfObjectReader currentPdfFile, final PdfObject Xobject) {
        try {

            final ObjectStore localStore = new ObjectStore();

            /*
             * create renderer object
             */
            final T3Renderer glyphDisplay = new T3Display(0, false, 20, localStore);

            /*
			 * generate local object to decode the stream
             */
            final PdfStreamDecoder glyphDecoder = new PdfStreamDecoder(currentPdfFile, null);
            glyphDecoder.setParameters(false, true, 15, 0, false, false);

            glyphDecoder.setObjectValue(ValueTypes.ObjectStore, localStore);

            glyphDecoder.setRenderer(glyphDisplay);

            /*read any resources*/
            try {

                final PdfObject Resources = Xobject.getDictionary(PdfDictionary.Resources);
                if (Resources != null) {
                    glyphDecoder.readResources(Resources, false);
                }

            } catch (final Exception e) {
                LogWriter.writeLog("Exception: " + e.getMessage());
            }

            /*decode the stream*/
            final byte[] commands = Xobject.getDecodedStream();

            String textString = "";
            if (commands != null) {
                textString = glyphDecoder.decodeStreamIntoObjects(commands, true);
            }
            if (textString == null || textString.isEmpty()) {
                textString = null;
            }

            localStore.flush();

            return textString;
        } catch (final Exception e) {
            LogWriter.writeLog("Exception: " + e.getMessage());

            return null;
        } catch (final Error e) {
            LogWriter.writeLog("Error: " + e.getMessage());

            if (ExternalHandlers.throwMissingCIDError && e.getMessage() != null && e.getMessage().contains("kochi")) {
                throw e;
            }
            return null;
        }
    }

    /**
     * method to rotate an image through a given angle
     *
     * @param src      the source image
     * @param rotation the angle to rotate the image through
     * @return the rotated image
     */
    public static BufferedImage rotate(final BufferedImage src, final int rotation) {
        final BufferedImage dst;

        if (src == null) {
            return null;
        }

        //if angle is 0 we dont need to do anything
        if (rotation == 0) {
            return src;
        }

        final double angle = rotation * Math.PI / 180;

        final int w = src.getWidth();
        final int h = src.getHeight();
        final int newW = (int) (Math.round(h * Math.abs(Math.sin(angle)) + w * Math.abs(Math.cos(angle))));
        final int newH = (int) (Math.round(h * Math.abs(Math.cos(angle)) + w * Math.abs(Math.sin(angle))));
        final AffineTransform at = AffineTransform.getTranslateInstance((newW - w) / 2, (newH - h) / 2);
        at.rotate(angle, w / 2, h / 2);

        dst = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB);
        final Graphics2D g2 = dst.createGraphics();
        g2.drawRenderedImage(src, at);

        g2.dispose();

        return dst;
    }

    public boolean hasXFADataSet() {
        return false;
    }

    /**
     * takes the PDF commands and creates a font
     *
     * @param
     */
    public static void decodeFontCommandObj(final String fontStream, final FormObject formObject) {

        //now parse the stream into a sequence of tokens
        final StringTokenizer tokens = new StringTokenizer(fontStream, "() []");
        final int tokenCount = tokens.countTokens();
        final String[] tokenValues = new String[tokenCount];
        int i = 0;
        while (tokens.hasMoreTokens()) {
            tokenValues[i] = tokens.nextToken();
            i++;
        }

        //now work out what it does and build up info
        for (i = tokenCount - 1; i > -1; i--) {
//			System.out.println(tokenValues[i]+" "+i);

            //look for commands
            if (tokenValues[i].equals("g")) { //set color (takes 1 values
                i--;
                float col = 0;
                try {
                    col = Float.parseFloat(handleComma(tokenValues[i]));
                } catch (final Exception e) {
                    LogWriter.writeLog("Error in generating g value " + tokenValues[i] + ' ' + e);
                }

                formObject.setTextColor(new float[]{col});

            } else if (tokenValues[i].equals("Tf")) { //set font (takes 2 values - size and font
                i--;
                int textSize = 8;
                try {
                    textSize = (int) Float.parseFloat(handleComma(tokenValues[i]));
//					if(textSize==0)
//						textSize = 0; //TODO check for 0 sizes CHANGE size to best fit on 0
                } catch (final Exception e) {
                    LogWriter.writeLog("Error in generating Tf size " + tokenValues[i] + ' ' + e);
                }

                i--; //decriment for font name
                String font = null;
                try {
                    font = tokenValues[i];
                    if (font.startsWith("/")) {
                        font = font.substring(1);
                    }
                } catch (final Exception e) {
                    LogWriter.writeLog("Error in generating Tf font " + tokenValues[i] + "  " + e);
                }

                final PdfFont currentFont = new PdfFont();

                currentFont.setFont(font, textSize);

                String fontName = StandardFonts.expandName(font);

                final String altName = FontMappings.fontSubstitutionAliasTable.get(fontName.toLowerCase());
                if (altName != null) {
                    fontName = altName;
                }

                formObject.setFontName(fontName);
                formObject.setTextFont(currentFont.getGlyphData().getUnscaledFont());

                formObject.setTextSize(textSize);

            } else if (tokenValues[i].equals("rg") || tokenValues[i].equals("r")) {
                i--;
                final float b = Float.parseFloat(handleComma(tokenValues[i]));
                i--;
                final float g = Float.parseFloat(handleComma(tokenValues[i]));
                i--;
                final float r = Float.parseFloat(handleComma(tokenValues[i]));

                formObject.setTextColor(new float[]{r, g, b});

            } else if (tokenValues[i].equals("Sig")) {
                LogWriter.writeFormLog("Sig-  UNIMPLEMENTED=" + fontStream + "< " + i, debugUnimplemented);
            } else if (tokenValues[i].equals("\\n")) {
                //ignore \n
                if (debug) {
                    System.out.println("ignore \\n");
                }
            } else if (!showFontMessage) {
                showFontMessage = true;
                LogWriter.writeFormLog("{stream} Unknown FONT command " + tokenValues[i] + ' ' + i + " string=" + fontStream, debugUnimplemented);
            }
        }
    }

    private static String handleComma(String tokenValue) {

        //if comma used as full stop remove
        final int comma = tokenValue.indexOf(',');
        if (comma != -1) {
            tokenValue = tokenValue.substring(0, comma);
        }

        return tokenValue;
    }

    @SuppressWarnings("UnusedDeclaration")
    /*
     * Unimplemented in base class, intended for use in XFAFormStream
     *
     * @throws RuntimeException if called from this class, it throws an exception
     **/
    public byte[] getXFA(final int xfaTemplate) {
        throw new RuntimeException("getXFA Should never be called in base class");
    }

    public boolean isXFA() {
        return isXFA;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy