com.vectorprint.report.itext.VectorPrintDocument.java Source code

Java tutorial

Introduction

Here is the source code for com.vectorprint.report.itext.VectorPrintDocument.java

Source

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.vectorprint.report.itext;

/*
 * #%L
 * VectorPrintReport4.0
 * %%
 * Copyright (C) 2012 - 2013 VectorPrint
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Image;
import com.itextpdf.text.Section;
import com.itextpdf.text.pdf.PdfWriter;
import com.vectorprint.VectorPrintException;
import com.vectorprint.VectorPrintRuntimeException;
import com.vectorprint.configuration.EnhancedMap;
import com.vectorprint.report.ReportConstants;
import com.vectorprint.report.itext.VectorPrintDocument.AddElementHook.INTENTION;
import com.vectorprint.report.itext.style.BaseStyler;
import com.vectorprint.report.itext.style.StyleHelper;
import com.vectorprint.report.itext.style.StylerFactory;
import com.vectorprint.report.itext.style.stylers.Advanced;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * This subclass of document gathers information for table of contents and enables you to work with
 * {@link VectorPrintDocument.AddElementHook hooks}. Hooks are added to this document before elements are added and
 * enable for example drawing near content.
 *
 * @author Eduard Drenth at VectorPrint.nl
 */
public class VectorPrintDocument extends Document {

    private static final Logger log = Logger.getLogger(VectorPrintDocument.class.getName());

    /**
     * prefix for generic tags used for debugging images.
     */
    public static final String IMG_DEBUG = "img_debug_";
    public static final String DRAWNEAR = "draw_near_";
    public static final String DRAWSHADOW = "draw_shadow_";

    private final EventHelper eventHelper;
    private final StylerFactory factory;
    private PdfWriter writer;
    private StyleHelper styleHelper;
    private Queue<AddElementHook> hooks = new ArrayDeque<>(20);
    private Map<Integer, List<Section>> toc = new TreeMap<>();

    /**
     *
     * @param eventHelper the value of eventHelper
     * @param factory the value of factory
     * @param styleHelper the value of styleHelper
     */
    public VectorPrintDocument(EventHelper eventHelper, StylerFactory factory, StyleHelper styleHelper) {
        this.eventHelper = eventHelper;
        this.factory = factory;
        this.styleHelper = styleHelper;
    }

    /**
     * If there is a hook for the element it will be processed, For all hooks except {@link INTENTION stylelater}
     * {@link StyleHelper#delayedStyle(com.itextpdf.text.Chunk, java.lang.String, java.util.Collection, com.vectorprint.report.itext.EventHelper, com.itextpdf.text.Image)
     * }
     * (variant without image when there is none) will be called. When element is a Section the pagenumber and the
     * Section are remembered, see {@link #getToc() }.
     *
     * @param element
     * @return
     * @throws DocumentException
     * @see EventHelper#onGenericTag(com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document,
     * com.itextpdf.text.Rectangle, java.lang.String)
     */
    @Override
    public boolean add(Element element) throws DocumentException {
        boolean rv = false;
        AddElementHook hook = null;
        if (element instanceof Image) {
            Image image = (Image) element;
            // see if we should style after adding
            hook = find(element, AddElementHook.INTENTION.PRINTIMAGESHADOW);
            /*
             we want to draw a shadow, write a chunk to get the position of the image later
             */
            if (hook != null) {
                String gt = DRAWSHADOW + hook.styleClass;
                Chunk chunk = positionChunk();
                styleHelper.delayedStyle(chunk, gt, StyleHelper.toCollection((Advanced) hook.bs), eventHelper,
                        image);
                super.add(chunk);
            }

            hook = find(element, AddElementHook.INTENTION.DEBUGIMAGE);
            if (hook != null
                    && ((EnhancedMap) factory.getSettings()).getBooleanProperty(false, ReportConstants.DEBUG)) {
                rv = tracePosition(image, hook, IMG_DEBUG, true);
            }
            hook = find(element, AddElementHook.INTENTION.DRAWNEARIMAGE);
            if (hook != null) {
                if (rv) {
                    tracePosition(image, hook, DRAWNEAR, false);
                } else {
                    rv = tracePosition(image, hook, DRAWNEAR, true);
                }
            }
            if (!rv) {
                rv = super.add(element);
            }

        } else if (element instanceof Section) {
            rv = super.add(element);
            if (!toc.containsKey(writer.getCurrentPageNumber())) {
                toc.put(writer.getCurrentPageNumber(), new ArrayList<>(3));
            }
            toc.get(writer.getCurrentPageNumber()).add((Section) element);
        } else {
            rv = super.add(element);
        }
        // see if we should style after adding
        Iterator<AddElementHook> it = hooks.iterator();
        while (it.hasNext() && (hook = it.next()) != null) {
            if (hook.intention == AddElementHook.INTENTION.STYLELATER && hook.e.type() == element.type()
                    && hook.e.equals(element) && hook.bs.canStyle(element) && hook.bs.shouldStyle(null, element)) {
                it.remove();
                try {
                    hook.bs.style(element, null);
                } catch (VectorPrintException ex) {
                    throw new VectorPrintRuntimeException(ex);
                }
            }
        }
        return rv;
    }

    /**
     *
     * @param image
     * @param hook
     * @param prefix
     * @param wrap
     * @return true when the image was added to the document
     * @throws DocumentException
     */
    private boolean tracePosition(Image image, AddElementHook hook, String prefix, boolean wrap)
            throws DocumentException {
        String gt = prefix + hook.styleClass;
        if (wrap && image.hasAbsoluteX() && image.hasAbsoluteY()) {
            // tracing position when an image is absolutely positioned by wrapping it in a chunk
            Chunk wrapper = new Chunk(image, (Float.NaN == image.getAbsoluteX()) ? 0 : image.getAbsoluteX(),
                    (Float.NaN == image.getAbsoluteY()) ? 0 : image.getAbsoluteY(), true);
            try {
                styleHelper.delayedStyle(wrapper, gt,
                        StyleHelper.getStylers(factory.getStylers(gt.replaceFirst(prefix, "")), Advanced.class),
                        eventHelper);
            } catch (VectorPrintException ex) {
                throw new DocumentException(ex);
            }
            return super.add(wrapper);
        } else {
            // the chunk will provide feedback on the actual x and y of the image when onGenericTag is fired,
            // the width and height of the image are passed to the eventhelper
            Chunk chunk = positionChunk();
            try {
                styleHelper.delayedStyle(chunk, gt,
                        StyleHelper.getStylers(factory.getStylers(gt.replaceFirst(prefix, "")), Advanced.class),
                        eventHelper, image);
            } catch (VectorPrintException ex) {
                throw new DocumentException(ex);
            }
            super.add(chunk);
            return false;
        }
    }

    private Chunk positionChunk() {
        return new Chunk(" ");
    }

    private AddElementHook find(Element element, AddElementHook.INTENTION intention) {
        AddElementHook hook = null;
        Iterator<AddElementHook> it = hooks.iterator();
        while (it.hasNext() && (hook = it.next()) != null) {
            if (hook.intention == intention && hook.e.type() == element.type() && hook.e.equals(element)) {
                it.remove();
                if (log.isLoggable(Level.FINE)) {
                    log.log(Level.FINE, "found {0}", new Object[] { hook });
                }
                return hook;
            }
        }
        return null;
    }

    public void setWriter(PdfWriter writer) {
        this.writer = writer;
    }

    /**
     *
     * @param hook the value of hook
     */
    public void addHook(AddElementHook hook) {
        hooks.add(hook);
    }

    /**
     * Before or after adding elements to the document we can perform actions. Supported are:
     * <ul>
     * <li>print debugging info near the element</li>
     * <li>print a shadow</li>
     * <li>draw near an image</li>
     * <li>style an element after adding it to the document</li>
     * </ul>
     */
    public static class AddElementHook {

        public enum INTENTION {

            DEBUGIMAGE, PRINTIMAGESHADOW, STYLELATER, DRAWNEARIMAGE;
        }

        private final INTENTION intention;
        private final Element e;
        private final BaseStyler bs;
        private final String styleClass;

        public AddElementHook(INTENTION intention, Element e, BaseStyler bs, String styleClass) {
            this.intention = intention;
            this.e = e;
            this.bs = bs;
            this.styleClass = styleClass;
        }

        @Override
        public String toString() {
            return "AddHook{" + "intention=" + intention + ", e=" + e + ", bs=" + bs + ", styleClass=" + styleClass
                    + '}';
        }

    }

    public Map<Integer, List<Section>> getToc() {
        return toc;
    }

}