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

org.jpedal.io.annotation.utils.AnnotationUtils 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
 *
 @LICENSE@
 *
 * ---------------
 * AnnotationUtils.java
 * ---------------
 */
package org.jpedal.io.annotation.utils;

import java.awt.*;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Rectangle2D;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.List;

import org.jpedal.PdfDecoderInt;
import org.jpedal.fonts.StandardFonts;
import org.jpedal.io.ObjectDecoder;
import org.jpedal.io.PdfObjectReader;
import org.jpedal.objects.acroforms.AcroRenderer;
import org.jpedal.objects.acroforms.GUIData;
import org.jpedal.objects.acroforms.creation.AnnotationFactory;
import org.jpedal.objects.raw.FormObject;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.objects.raw.ResourcesObject;
import org.jpedal.utils.LogWriter;

/**
 * AnnotationUtils contains basic utility methods that are required
 * by the annotation creation and editing functionality.
 */
public class AnnotationUtils {

    /**
     * Checks if the given annotation has editable properties
     *
     * @param form object representing an annotation
     * @return boolean
     */
    public static boolean isEditableAnnotation(final FormObject form) {
        final int annotType = form.getParameterConstant(PdfDictionary.Subtype);
        return annotType == PdfDictionary.Circle
                || annotType == PdfDictionary.Square
                || annotType == PdfDictionary.Line
                || annotType == PdfDictionary.Ink
                || annotType == PdfDictionary.Link
                || annotType == PdfDictionary.Polygon
                || annotType == PdfDictionary.PolyLine
                || annotType == PdfDictionary.Highlight
                || annotType == PdfDictionary.FreeText
                || annotType == PdfDictionary.Text;
    }

    /**
     * Checks if the given annotation can be moved on the page
     *
     * @param form object representing an annotation
     * @return boolean
     */
    public static boolean isDraggableAnnotation(final FormObject form) {
        final int annotType = form.getParameterConstant(PdfDictionary.Subtype);
        return annotType == PdfDictionary.Circle
                || annotType == PdfDictionary.Square
                || annotType == PdfDictionary.Line
                || annotType == PdfDictionary.Ink
                || annotType == PdfDictionary.Polygon
                || annotType == PdfDictionary.PolyLine
                || annotType == PdfDictionary.FreeText
                || annotType == PdfDictionary.Text;
    }

    /**
     * Checks if the given annotation can be resized
     *
     * @param form object representing an annotation
     * @return boolean
     */
    public static boolean isResizableAnnotation(final FormObject form) {
        final int annotType = form.getParameterConstant(PdfDictionary.Subtype);
        return annotType == PdfDictionary.Circle
                || annotType == PdfDictionary.Square
                || annotType == PdfDictionary.Line
                || annotType == PdfDictionary.Ink
                || annotType == PdfDictionary.FreeText
                || annotType == PdfDictionary.Polygon
                || annotType == PdfDictionary.PolyLine;
    }

    /**
     * Checks if there are any form objects at the given point
     *
     * @param decode_pdf PdfDecoderInt object used to access forms data
     * @param x          int object representing the x coordinate
     * @param y          int object representing the y coordinate
     * @return the first FormObject found at the given point, else null
     */
    public static FormObject isOverForm(final PdfDecoderInt decode_pdf, final int x, final int y) {

        if (decode_pdf.isOpen()) {

            final AcroRenderer formRenderer = decode_pdf.getFormRenderer();
            if (formRenderer != null) {

                final GUIData compData = formRenderer.getCompData();
                if (compData != null) {

                    final List[] formList = compData.getFormList(true);
                    if (formList != null && decode_pdf.getPageNumber() < formList.length) {

                        final List forms = formList[decode_pdf.getPageNumber()];
                        if (forms != null) {
                            for (final FormObject form : forms) {

                                //Turn off if statement for now
                                if (form != null && form.getBoundingRectangle().contains(x, y)) {
                                    return form;
                                }
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * Checks if the given coordinates are within the bounds of any editable annotations
     * on the current pages
     *
     * @param decode_pdf PdfDecoderInt object used to access forms data
     * @param x          int object representing the x coordinate
     * @param y          int object representing the y coordinate
     * @return boolean
     */
    public static boolean isOverAnnotation(final PdfDecoderInt decode_pdf, final int x, final int y) {
        if (decode_pdf.isOpen() && decode_pdf.getFormRenderer() != null) {
            final GUIData compData = decode_pdf.getFormRenderer().getCompData();
            if (compData != null) {

                final List[] formList = compData.getFormList(true);
                if (formList != null && decode_pdf.getPageNumber() < formList.length) {

                    final List forms = formList[decode_pdf.getPageNumber()];

                    if (forms != null) {
                        for (final FormObject form : forms) {
                            if (form != null && isEditableAnnotation(form)) {
                                if (form.getBoundingRectangle().contains(x, y)) {
                                    return true;
                                }
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    /**
     * Return the provided coordinates as an array in the order smallest x,
     * smallest y, largest x, largest y
     *
     * @param sx float value representing one point on the x axis
     * @param sy float value representing one point on the y axis
     * @param ex float value representing one point on the x axis
     * @param ey float value representing one point on the y axis
     * @return float array representing the input order as alternating x / y
     * coordinates, the first two being the smallest and the final two being the
     * largest
     */
    public static float[] validateCoordinates(final float sx, final float sy, final float ex, final float ey) {

        final float[] values = new float[4];

        if (sx < ex) {
            values[0] = sx;
            values[2] = ex;
        } else {

            values[0] = ex;
            values[2] = sx;
        }

        if (sy < ey) {
            values[1] = sy;
            values[3] = ey;
        } else {

            values[1] = ey;
            values[3] = sy;
        }
        return values;
    }

    /**
     * Takes a FormObject and returns the font name from the the DS value
     * contained within. If there is no value for DS set it will retrieve the
     * values return by FormObject.getFontName().
     *
     * @param form FormObject to retrieve the font name from
     * @return String value representing the font name for this form
     */
    public static String getFontNameFromFormDS(final FormObject form) {
        final String DSString = form.getTextStreamValue(PdfDictionary.DS);
        if (DSString != null) {
            final int startOfFont = DSString.indexOf("font:");
            if (startOfFont > -1) {
                final String fontString = DSString.substring(startOfFont, DSString.indexOf(';', startOfFont + 1));
                final int firstSpace = fontString.indexOf(' ');
                final int secondSpace = fontString.lastIndexOf(' ');
                return fontString.substring(firstSpace + 1, secondSpace);
            }
        }
        return form.getFontName();
    }

    /**
     * Takes a FormObject and returns the font colour from the the DS value
     * contained within. If there is no value for DS set it will return a value
     * for black.
     *
     * @param form FormObject to retrieve the font colour from
     * @return String value representing the font colour for this form
     */
    public static String getFontHexColorFromFormDS(final FormObject form) {
        final String DSString = form.getTextStreamValue(PdfDictionary.DS);
        if (DSString != null) {
            final int startOfFont = DSString.indexOf("color:");
            if (startOfFont > -1) {
                int end = DSString.indexOf(';', startOfFont + 1);
                if (end < 0) {
                    end = DSString.length();
                }
                final String fontString = DSString.substring(startOfFont, end);
                final int firstSpace = fontString.indexOf(':');
                int last = fontString.length();
                if (fontString.endsWith(" ")) {
                    last--;
                }
                return fontString.substring(firstSpace + 1, last);
            }
        }
        return "#000000";
    }

    /**
     * Takes a FormObject and returns the font size from the the DS value
     * contained within. If there is no value for DS set it will retrieve the
     * values return by FormObject.getFontSize().
     *
     * @param form FormObject to retrieve the font size from
     * @return float value representing the font size for this form
     */
    public static float getFontSizeFromFormDS(final FormObject form) {
        final String DSString = form.getTextStreamValue(PdfDictionary.DS);
        if (DSString != null) {
            final int startOfFont = DSString.indexOf("font:");
            if (startOfFont > -1) {
                final String fontString = DSString.substring(startOfFont, DSString.indexOf(';', startOfFont + 1));
                final int sizeStart = fontString.lastIndexOf(' ');
                final String size = fontString.substring(sizeStart + 1, fontString.indexOf('p', sizeStart + 1));
                return Float.parseFloat(size);
            }
        }
        return form.getFontSize();
    }

    public static void createAPObjectForFormObject(final FormObject form, final PdfObjectReader currentPdfFile) {

        switch (form.getParameterConstant(PdfDictionary.Subtype)) {
            case PdfDictionary.FreeText:
                createFreeTextAPObject(form, currentPdfFile);
                break;
            case PdfDictionary.Line:
                createBasicAPObject(form);
                break;
            case PdfDictionary.Square:
                createBasicAPObject(form);
                break;
            case PdfDictionary.Text:
                createTextAPObject(form, currentPdfFile);
                break;
            case PdfDictionary.Polygon:
                createBasicAPObject(form);
                break;
            case PdfDictionary.PolyLine:
                createBasicAPObject(form);
                break;
            case PdfDictionary.Ink:
                createInkAPObject(form);
                break;
            case PdfDictionary.Circle:
                createBasicAPObject(form);
                break;
            case PdfDictionary.Highlight:
                createHighlightAPObject(form, currentPdfFile);
                break;
        }
    }

    /**
     * Create a basic AP object with all common values set
     *
     * @param form FormObject holding the values for an annotation
     *
     * @return Null if unable to create AP Object for form. Otherwise String for the AP stream so specific code can
     * check contents and update objects specific to this form type.
     */
    private static String createBasicAPObject(final FormObject form) {

        final String APString = createAPStream(form);

        if (APString != null) {
            boolean newAP = false;
            PdfObject AP = form.getDictionary(PdfDictionary.AP);
            PdfObject N;

            if (AP != null) {
                N = AP.getDictionary(PdfDictionary.N);
                if (N == null) {
                    N = new FormObject(PdfDictionary.N);
                    newAP = true;
                }
            } else {
                AP = new FormObject(PdfDictionary.AP);
                N = new FormObject(PdfDictionary.N);
                newAP = true;
            }

            final float[] rect = form.getFloatArray(PdfDictionary.Rect);

            final byte[] bytes = APString.getBytes();

            N.setDecodedStream(bytes);
            N.setIntNumber(PdfDictionary.Length, bytes.length);

            N.setFloatArray(PdfDictionary.BBox, new float[]{0, 0, rect[2] - rect[0], rect[3] - rect[1]});

            if (newAP) {
                N.setConstant(PdfDictionary.Subtype, PdfDictionary.Form);
                N.setConstant(PdfDictionary.Type, PdfDictionary.XObject);
                N.setIntNumber(PdfDictionary.FormType, 1);

                AP.setDictionary(PdfDictionary.N, N);
            }

            //Disable values that may be set and not needed
            N.setName(PdfDictionary.Filter, new byte[0]);
            N.setFloatArray(PdfDictionary.Matrix, null);

            form.setDictionary(PdfDictionary.AP, AP);
        }
        return APString;
    }

    /**
     * Create a basic AP object then add additional values for the Ink FormObject provided
     * and add it to the FormObject
     *
     * @param form           FormObject holding the values for a Ink annotation
     */
    private static void createInkAPObject(final FormObject form) {

        createBasicAPObject(form);

        final PdfObject AP = form.getDictionary(PdfDictionary.AP);
        final PdfObject N = AP.getDictionary(PdfDictionary.N);
        final float[] rect = form.getFloatArray(PdfDictionary.Rect);
        N.setFloatArray(PdfDictionary.BBox, new float[]{rect[0], rect[1], rect[2], rect[3]});
        N.setFloatArray(PdfDictionary.Matrix, new float[]{1, 0, 0, 1, -rect[0], -rect[1]});
    }

    /**
     * Create a basic AP object then add additional values for the FreeText FormObject provided
     * and add it to the FormObject
     *
     * @param form           FormObject holding the values for a FreeText annotation
     * @param currentPdfFile PdfObjectReader to be used to create any resources
     */
    private static void createFreeTextAPObject(final FormObject form, final PdfObjectReader currentPdfFile) {

        createBasicAPObject(form);

        final PdfObject AP = form.getDictionary(PdfDictionary.AP);
        final PdfObject N = AP.getDictionary(PdfDictionary.N);
        final String fontName = StandardFonts.expandName(AnnotationUtils.getFontNameFromFormDS(form));

        final String sb = "<> >> >>";

        final PdfObject resources = getResObjectFromDirect(currentPdfFile, sb.getBytes());
        N.setDictionary(PdfDictionary.Resources, resources);
    }

    /**
     * Create a basic AP object then add additional values for the Text FormObject provided
     * and add it to the FormObject
     *
     * @param form           FormObject holding the values for a Text annotation
     */
    private static void createTextAPObject(final FormObject form, final PdfObjectReader currentPdfFile) {

        final String apStream = createBasicAPObject(form);

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

        //Set size base on icon to be used
        final float[] apSize = getTextIconDimensions(form.getName(PdfDictionary.Name));
        N.setFloatArray(PdfDictionary.BBox, new float[]{0, 0, apSize[0], apSize[1]});

        if (apStream.contains("GS0")) {
            final String sb = "<> >> >>";

            final PdfObject resources = getResObjectFromDirect(currentPdfFile, sb.getBytes());
            N.setDictionary(PdfDictionary.Resources, resources);
        }
    }


    /**
     * Create a basic AP object then add additional values for the Text FormObject provided
     * and add it to the FormObject
     *
     * @param form           FormObject holding the values for a Text annotation
     */
    private static void createHighlightAPObject(final FormObject form, final PdfObjectReader currentPdfFile) {

        final String apStream = createBasicAPObject(form);

        final PdfObject AP = form.getDictionary(PdfDictionary.AP);
        final PdfObject N = AP.getDictionary(PdfDictionary.N);
        final float[] rect = form.getFloatArray(PdfDictionary.Rect);
        N.setFloatArray(PdfDictionary.BBox, new float[]{rect[0], rect[1], rect[2], rect[3]});
        N.setFloatArray(PdfDictionary.Matrix, new float[]{1, 0, 0, 1, -rect[0], -rect[1]});

        if (apStream.contains("GS0")) {
            final String sb = "<> >> >>";

            final PdfObject resources = getResObjectFromDirect(currentPdfFile, sb.getBytes());
            N.setDictionary(PdfDictionary.Resources, resources);
        }
    }

    public static String createAPStream(final FormObject form) {

        switch (form.getParameterConstant(PdfDictionary.Subtype)) {
            case PdfDictionary.FreeText:
                return createFreeTextAPStream(form);
            case PdfDictionary.Square:
                return createSquareAPStream(form);
            case PdfDictionary.Line:
                return createLineAPStream(form);
            case PdfDictionary.Text:
                return createTextAPStream(form);
            case PdfDictionary.Polygon:
                return createPolygonAPStream(form, true);
            case PdfDictionary.PolyLine:
                return createPolygonAPStream(form, false);
            case PdfDictionary.Ink:
                return createInkAPStream(form);
            case PdfDictionary.Circle:
                return createCircleAPStream(form);
            case PdfDictionary.Highlight:
                return createHighlightAPStream(form);
        }

        return null;
    }

    private static String createCircleAPStream(final FormObject form) {


        final StringBuilder stream = new StringBuilder();

        String drawCommand = null;
        final float[] color = form.getFloatArray(PdfDictionary.C);
        if (color != null && color.length == 3) {
            stream.append((color[0]));
            stream.append(' ');
            stream.append((color[1]));
            stream.append(' ');
            stream.append((color[2]));
            stream.append(" RG\n");

            drawCommand = "S";
        }

        final float[] internalColor = form.getFloatArray(PdfDictionary.IC);
        if (internalColor != null && internalColor.length == 3) {
            stream.append((internalColor[0]));
            stream.append(' ');
            stream.append((internalColor[1]));
            stream.append(' ');
            stream.append((internalColor[2]));
            stream.append(" rg\n");

            if (drawCommand != null) {
                drawCommand = "B";
            } else {
                drawCommand = "F";
            }
        }

        int w = 1;
        final PdfObject bs = form.getDictionary(PdfDictionary.BS);
        if (bs != null) {
            final int width = bs.getInt(PdfDictionary.W);
            if (width != -1) {
                w = width;
                stream.append(w);
                stream.append(" w\n");
            }
        }

        final float[] rect = form.getFloatArray(PdfDictionary.Rect);

        if (rect[0] == rect[2] &&
                rect[1] == rect[3]) {

            stream.append(rect[0]);
            stream.append(' ');
            stream.append(rect[1]);
            stream.append(" m\n");


            stream.append(rect[0]);
            stream.append(' ');
            stream.append(rect[1]);
            stream.append(" l\n");

            stream.append(" S\n");

        } else {

            final float width = ((rect[2] - rect[0])) - (w * 2);
            final float height = ((rect[3] - rect[1])) - (w * 2);

            final float longRatio = 0.777f;
            final float shortRatio = 0.222f;

            //Left Middle
            stream.append(w);
            stream.append(' ');
            stream.append(w + (height / 2));
            stream.append(" m\n");


            //Control points to top middle
            stream.append(w);
            stream.append(' ');
            stream.append(w + (height * longRatio));
            stream.append(' ');
            stream.append(w + (width * shortRatio));
            stream.append(' ');
            stream.append(w + height);
            stream.append(' ');
            stream.append(w + (width / 2));
            stream.append(' ');
            stream.append(w + height);
            stream.append(" c\n");


            //Control points to right middle
            stream.append(w + (width * longRatio));
            stream.append(' ');
            stream.append(w + height);
            stream.append(' ');
            stream.append(w + width);
            stream.append(' ');
            stream.append(w + (height * longRatio));
            stream.append(' ');
            stream.append(w + width);
            stream.append(' ');
            stream.append(w + (height / 2));
            stream.append(" c\n");


            //Control points to bottom middle
            stream.append(w + width);
            stream.append(' ');
            stream.append(w + (height * shortRatio));
            stream.append(' ');
            stream.append(w + (width * longRatio));
            stream.append(' ');
            stream.append(w);
            stream.append(' ');
            stream.append(w + (width / 2));
            stream.append(' ');
            stream.append(w);
            stream.append(" c\n");


            //Control points to left middle
            stream.append(w + (width * shortRatio));
            stream.append(' ');
            stream.append(w);
            stream.append(' ');
            stream.append(w);
            stream.append(' ');
            stream.append(w + (height * shortRatio));
            stream.append(' ');
            stream.append(w);
            stream.append(' ');
            stream.append(w + (height / 2));
            stream.append(" c\n");

            stream.append("h\n");
            stream.append(drawCommand);

        }
        return stream.toString();
    }

    private static String createSquareAPStream(final FormObject form) {


        final StringBuilder stream = new StringBuilder();

        String drawCommand = null;
        final float[] color = form.getFloatArray(PdfDictionary.C);
        if (color != null && color.length == 3) {
            stream.append((color[0]));
            stream.append(' ');
            stream.append((color[1]));
            stream.append(' ');
            stream.append((color[2]));
            stream.append(" RG\n");

            drawCommand = "S";
        }

        final float[] internalColor = form.getFloatArray(PdfDictionary.IC);
        if (internalColor != null && internalColor.length == 3) {
            stream.append((internalColor[0]));
            stream.append(' ');
            stream.append((internalColor[1]));
            stream.append(' ');
            stream.append((internalColor[2]));
            stream.append(" rg\n");

            if (drawCommand != null) {
                drawCommand = "B";
            } else {
                drawCommand = "F";
            }
        }

        int w = 1;
        final PdfObject bs = form.getDictionary(PdfDictionary.BS);
        if (bs != null) {
            final int width = bs.getInt(PdfDictionary.W);
            if (width != -1) {
                w = width;
                stream.append(w);
                stream.append(" w\n");
            }
        }

        final float[] rect = form.getFloatArray(PdfDictionary.Rect);
        stream.append((float) (w / 2));
        stream.append(' ');
        stream.append((float) (w));
        stream.append(' ');
        stream.append((rect[2] - rect[0]) - (w));
        stream.append(' ');
        stream.append((rect[3] - rect[1]) - (w * 2));
        stream.append(" re\n");

        stream.append(drawCommand);

        return stream.toString();
    }

    private static String createHighlightAPStream(final FormObject form) {


        final StringBuilder stream = new StringBuilder();

        stream.append("/GS0 gs\n");
        final float[] color = form.getFloatArray(PdfDictionary.C);
        if (color != null && color.length == 3) {
            stream.append(color[0]);
            stream.append(' ');
            stream.append(color[1]);
            stream.append(' ');
            stream.append(color[2]);
            stream.append(" rg\n");

        }

        final float[] quads = form.getFloatArray(PdfDictionary.QuadPoints);

        final float[] rect = form.getFloatArray(PdfDictionary.Rect);
        final float[] modRect = new float[4];
        System.arraycopy(rect, 0, modRect, 0, 4);

        for (int i = 0; i < quads.length; i += 8) {

            final float controlPointDistance = ((quads[i + 1] - rect[1]) - (quads[i + 7] - rect[1])) / 4.24f;

            if (modRect[0] > quads[i] - controlPointDistance) {
                modRect[0] = quads[i] - controlPointDistance;
            }
            if (modRect[2] < quads[i + 2] + controlPointDistance) {
                modRect[2] = quads[i + 2] + controlPointDistance;
            }

            stream.append((int) quads[i + 4]);
            stream.append(' ');
            stream.append((int) quads[i + 5]);
            stream.append(" m\n");

            stream.append((int)  quads[i + 4] - controlPointDistance);
            stream.append(' ');
            stream.append((int) quads[i + 5] + controlPointDistance);
            stream.append(' ');
            stream.append((int) quads[i] - controlPointDistance);
            stream.append(' ');
            stream.append((int) quads[i + 1] - controlPointDistance);
            stream.append(' ');
            stream.append((int) quads[i]);
            stream.append(' ');
            stream.append((int) quads[i + 1]);
            stream.append(" c\n");

            stream.append((int) quads[i + 2]);
            stream.append(' ');
            stream.append((int) quads[i + 3]);
            stream.append(" l\n");

            stream.append((int) quads[i + 2] + controlPointDistance);
            stream.append(' ');
            stream.append((int) quads[i + 3] - controlPointDistance);
            stream.append(' ');
            stream.append((int) quads[i + 6] + controlPointDistance);
            stream.append(' ');
            stream.append((int) quads[i + 7] + controlPointDistance);
            stream.append(' ');
            stream.append((int) quads[i + 6]);
            stream.append(' ');
            stream.append((int) quads[i + 7]);
            stream.append(" c\n");

            stream.append("f\n");

        }

        form.setFloatArray(PdfDictionary.Rect, modRect);

        return stream.toString();
    }

    private static String createTextAPStream(final FormObject form) {

        String name = form.getName(PdfDictionary.Name);

        if (name == null || name.isEmpty()) {
            name = "Note";
        }

        return loadApTemplate(form, name);
    }


    private static String loadApTemplate(final FormObject form, final String name) {
        final StringBuilder stream = new StringBuilder();

        //Load file here
        final String iconFile = "/org/jpedal/objects/acroforms/res/" + name + ".txt";

        final URL apFile = AnnotationUtils.class.getResource(iconFile);
        try {
            final BufferedReader in = new BufferedReader(new InputStreamReader(apFile.openStream()));
            String line;
            while ((line = in.readLine()) != null) {
                stream.append(line);
                if (!line.endsWith(" ")) {
                    stream.append(' ');
                }
            }
            in.close();
        } catch (final IOException e) {
            LogWriter.writeLog("Exception: " + e.getMessage());
        }

        String apStream = stream.toString();
        final float[] color = form.getFloatArray(PdfDictionary.C);
        if (color != null && color.length == 3) {
            final String colorString = color[0] + " " + color[1] + " " + color[2] + " rg";

            //Replace 0 0 0 rg with this string
            apStream = apStream.replace("1 0.819611 0 rg", colorString);
        }

        return apStream;
    }

    private static String createFreeTextAPStream(final FormObject form) {

        final float[] rect = form.getFloatArray(PdfDictionary.Rect);
        final String bounds = 0 + " " + 0 + " " + (rect[2] - rect[0]) + " " + (rect[3] - rect[1]) + " re\n";
        final String fontName = StandardFonts.expandName(AnnotationUtils.getFontNameFromFormDS(form));
        final float fontSize = AnnotationUtils.getFontSizeFromFormDS(form);
        final String fontString = "/" + fontName + " " + fontSize + " Tf\n";
        final Color c = Color.decode(AnnotationUtils.getFontHexColorFromFormDS(form));
        final String colorString = (c.getRed() / 255f) + " " + (c.getGreen() / 255f) + " " + (c.getBlue() / 255f) + " rg\n";
        final String content = form.getTextStreamValue(PdfDictionary.Contents);
        final String[] contentLines = content.split("\\r\\n|\\n|\\r");
        final StringBuilder stream = new StringBuilder();

        stream.append("0 w\n");
        stream.append(bounds);
        stream.append("n\n");
        stream.append("q\n");
        stream.append("1 0 0 1 0 0 cm\n");
        stream.append(bounds);
        stream.append("W\n");
        stream.append("n\n");
        stream.append("0 g\n");
        stream.append("1 w\n");
        stream.append("BT\n");
        stream.append(fontString);
        stream.append(colorString);

        stream.append((0.01));
        stream.append(' ');
        stream.append(((rect[3] - rect[1]) - fontSize));
        stream.append(" Td\n");

//        FontRenderContext fontContext = new FontRenderContext(null, true, true);
//        Font font = new Font(fontName, Font.PLAIN, (int)fontSize);
        for (int i = 0; i != contentLines.length; i++) {

            final int lineCurrentPoint = 0;
//
//            StringTokenizer tokens = new StringTokenizer(contentLines[i], ""+((char)9));
//
//            if(tokens.countTokens()==1){

            stream.append('(');
            stream.append(contentLines[i]);
            stream.append(") Tj\n");

//            }else{
//                while(tokens.hasMoreTokens()){
//                    String tab = tokens.nextToken();
//                    stream.append("(");
//                    stream.append(tab);
//                    stream.append(") Tj\n");
//                    int textWidth = (int)(font.getStringBounds(tab, fontContext).getWidth());
//
//                    if(tokens.hasMoreTokens()){
//                        int nextTab = lineCurrentPoint + textWidth;
//                        nextTab += (36 - (nextTab%36));
//                        System.out.println(nextTab+" - "+lineCurrentPoint+" = "+(nextTab - lineCurrentPoint));
//                        stream.append(nextTab-lineCurrentPoint);
//                        stream.append(' ');
//                        stream.append(0);
//                        stream.append(" Td\n");
//                        lineCurrentPoint += (nextTab-lineCurrentPoint);
//                    }
//                }
//            }
            //Drop down to nw line
            if (i != (contentLines.length - 1)) {
                stream.append(-lineCurrentPoint);
                stream.append(' ');
                stream.append((-fontSize * 1.3f));
                stream.append(" Td\n");
            }
        }
        stream.append("ET\n");
        stream.append('Q');

        return stream.toString();
    }

    private static String createPolygonAPStream(final FormObject form, final boolean closeShape) {

        final float[] color = form.getFloatArray(PdfDictionary.C);
        final float[] icolor = form.getFloatArray(PdfDictionary.IC);
        final float coords[] = form.getFloatArray(PdfDictionary.Vertices);
        final float rect[] = form.getFloatArray(PdfDictionary.Rect);

        //line width
        int w = 1;
        final PdfObject bs = form.getDictionary(PdfDictionary.BS);
        if (bs != null) {
            final int width = bs.getInt(PdfDictionary.W);
            if (width != -1) {
                w = width;
            }
        }

        //Stringbuilder used to create stream
        final StringBuilder stream = new StringBuilder();
        String drawCommand = null;
        stream.append(w);
        stream.append(" w\n");

        if (color != null && color.length == 3) {
            stream.append(color[0]);
            stream.append(' ');
            stream.append(color[1]);
            stream.append(' ');
            stream.append(color[2]);
            stream.append(' ');
            stream.append(" RG\n");
            drawCommand = "s";
        }

        if (icolor != null && icolor.length == 3) {
            stream.append(icolor[0]);
            stream.append(' ');
            stream.append(icolor[1]);
            stream.append(' ');
            stream.append(icolor[2]);
            stream.append(' ');
            stream.append(" rg\n");
            if (drawCommand != null) {
                drawCommand = "b";
            } else {
                drawCommand = "f";

            }
        }

        stream.append(coords[0] - rect[0]);
        stream.append(' ');
        stream.append(coords[1] - rect[1]);
        stream.append(" m\n");

        for (int i = 2; i != coords.length; i += 2) {
            stream.append(coords[i] - rect[0]);
            stream.append(' ');
            stream.append(coords[i + 1] - rect[1]);
            stream.append(" l\n");
        }

        if (closeShape) {
            stream.append("h\n");
            stream.append(drawCommand);
        } else {
            stream.append(drawCommand.toUpperCase());
        }
        return stream.toString();
    }

    private static String createInkAPStream(final FormObject form) {

        final float[] color = form.getFloatArray(PdfDictionary.C);
        final float[][] inkList = form.getFloat2DArray(PdfDictionary.InkList);

        //line width
        int w = 1;
        final PdfObject bs = form.getDictionary(PdfDictionary.BS);
        if (bs != null) {
            final int width = bs.getInt(PdfDictionary.W);
            if (width != -1) {
                w = width;
            }
        }

        //Stringbuilder used to create stream
        final StringBuilder stream = new StringBuilder();
        stream.append(w);
        stream.append(" w\n");

        if (color != null && color.length == 3) {
            stream.append(color[0]);
            stream.append(' ');
            stream.append(color[1]);
            stream.append(' ');
            stream.append(color[2]);
            stream.append(" RG\n");
        }

        //Curve can extend beyond bounds so resize bounds to compensate for this.
        final float[] rect = form.getFloatArray(PdfDictionary.Rect);
        Rectangle2D bounds = new Rectangle2D.Float(rect[0], rect[1], rect[2] - rect[0], rect[3] - rect[1]);

        for (float[] coords : inkList) {

            stream.append(coords[0]);
            stream.append(' ');
            stream.append(coords[1]);
            stream.append(" m\n");

            if (coords.length > 4) {
                coords = AnnotationFactory.createInkCurve(coords);

                final CubicCurve2D curve = new CubicCurve2D.Float();

                for (int i = 2; i < coords.length - 5; i += 6) {
                    stream.append(coords[i]);
                    stream.append(' ');
                    stream.append(coords[i + 1]);
                    stream.append(' ');
                    stream.append(coords[i + 2]);
                    stream.append(' ');
                    stream.append(coords[i + 3]);
                    stream.append(' ');
                    stream.append(coords[i + 4]);
                    stream.append(' ');
                    stream.append(coords[i + 5]);
                    stream.append(" c\n");

                    curve.setCurve(coords[i - 2], coords[i - 1], coords[i], coords[i + 1], coords[i + 2], coords[i + 3], coords[i + 4], coords[i + 5]);
                    bounds = bounds.createUnion(curve.getBounds2D());
                }

            } else {
                if (coords.length == 4) {
                    stream.append(coords[2]);
                    stream.append(' ');
                    stream.append(coords[3]);
                    stream.append(" l");
                } else {
                    stream.append(coords[0]);
                    stream.append(' ');
                    stream.append(coords[1]);
                    stream.append(" l");
                }

            }

        }
        stream.append("S\n");

        form.setFloatArray(PdfDictionary.Rect, AnnotationUtils.validateCoordinates((float) bounds.getMinX(), (float) bounds.getMinY(), (float) bounds.getMaxX(), (float) bounds.getMaxY()));

        return stream.toString();
    }

    private static String createLineAPStream(final FormObject form) {
        //Stringbuilder used to create stream
        final StringBuilder stream = new StringBuilder();

        //line color
        final float[] color = form.getFloatArray(PdfDictionary.C);
        final String colorString = color[0] + " " + color[1] + " " + color[2] + " RG\n";

        //line coords
        final float coords[] = form.getFloatArray(PdfDictionary.L);
        final float[] rect = form.getFloatArray(PdfDictionary.Rect);
        final String coordString = (coords[0] - rect[0]) + " " + (coords[1] - rect[1]) + " m\n" + (coords[2] - rect[0]) + " " + (coords[3] - rect[1]) + " l\n";

        //line width
        int w = 1;
        final PdfObject bs = form.getDictionary(PdfDictionary.BS);
        if (bs != null) {
            final int width = bs.getInt(PdfDictionary.W);
            if (width != -1) {
                w = width;
            }
        }
        final String widthString = w + " w\n";

        //build stream
        stream.append(widthString);
        stream.append(colorString);
        stream.append(coordString);
        stream.append('S');

        return stream.toString();
    }

    private static ResourcesObject getResObjectFromDirect(final PdfObjectReader currentPdfFile, final byte[] data) {

        final ResourcesObject resObj = new ResourcesObject(new String(data));

        resObj.setStatus(PdfObject.UNDECODED_DIRECT);

        resObj.setUnresolvedData(data, PdfDictionary.Resources);

        final ObjectDecoder objectDecoder = new ObjectDecoder(currentPdfFile.getObjectReader());
        objectDecoder.checkResolved(resObj);

        return resObj;
    }

    public static float[] getTextIconDimensions(String name) {

        if (name == null || name.isEmpty()) {
            name = "Note";
        }

        if (name.equals("Comment")) {
            return new float[]{18f, 18f};
        } else if (name.equals("Check")) {
            return new float[]{19f, 19f};
        } else if (name.equals("Checkmark")) {
            return new float[]{20f, 20f};
        } else if (name.equals("Circle")) {
            return new float[]{20f, 20f};
        } else if (name.equals("Cross")) {
            return new float[]{19f, 19f};
        } else if (name.equals("CrossHairs")) {
            return new float[]{20f, 20f};
        } else if (name.equals("Help")) {
            return new float[]{20f, 20f};
        } else if (name.equals("Insert")) {
            return new float[]{17f, 20f};
        } else if (name.equals("Key")) {
            return new float[]{13f, 18f};
        } else if (name.equals("NewParagraph")) {
            return new float[]{13f, 20f};
        } else if (name.equals("Paragraph")) {
            return new float[]{20f, 20f};
        } else if (name.equals("RightArrow")) {
            return new float[]{20f, 20f};
        } else if (name.equals("RightPointer")) {
            return new float[]{20f, 17f};
        } else if (name.equals("Star")) {
            return new float[]{20f, 19f};
        } else if (name.equals("UpLeftArrow")) {
            return new float[]{17f, 17f};
        } else if (name.equals("UpArrow")) {
            return new float[]{17f, 20f};
        } else { //Default option. Name = Note
            return new float[]{18f, 20f};
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy