Java tutorial
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% */ //~--- non-JDK imports -------------------------------------------------------- import com.itextpdf.text.Anchor; import com.itextpdf.text.BadElementException; import com.itextpdf.text.Chapter; import com.itextpdf.text.ChapterAutoNumber; import com.itextpdf.text.Chunk; import com.itextpdf.text.Element; import com.itextpdf.text.Image; import com.itextpdf.text.ListItem; import com.itextpdf.text.Paragraph; import com.itextpdf.text.Phrase; import com.itextpdf.text.Section; import com.itextpdf.text.TextElementArray; import com.itextpdf.text.io.RandomAccessSourceFactory; import com.itextpdf.text.pdf.ColumnText; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfLayer; import com.itextpdf.text.pdf.PdfPCell; import com.itextpdf.text.pdf.PdfPTable; import com.itextpdf.text.pdf.PdfReader; import com.itextpdf.text.pdf.PdfWriter; import com.itextpdf.text.pdf.RandomAccessFileOrArray; import com.itextpdf.text.pdf.codec.TiffImage; import com.vectorprint.IOHelper; import com.vectorprint.VectorPrintException; import com.vectorprint.VectorPrintRuntimeException; import com.vectorprint.configuration.EnhancedMap; import com.vectorprint.configuration.annotation.Setting; import com.vectorprint.configuration.annotation.SettingsField; import com.vectorprint.report.ReportConstants; import com.vectorprint.report.data.types.Formatter; import com.vectorprint.report.data.types.ReportValue; import com.vectorprint.report.itext.debug.DebuggablePdfPCell; import com.vectorprint.report.itext.style.BaseStyler; import com.vectorprint.report.itext.style.StyleHelper; import static com.vectorprint.report.itext.style.StyleHelper.toCollection; import com.vectorprint.report.itext.style.StylerFactory; import com.vectorprint.report.itext.style.StylerFactoryHelper; import com.vectorprint.report.itext.style.stylers.Advanced; import com.vectorprint.report.itext.style.stylers.Link; import com.vectorprint.report.itext.style.stylers.NoWrap; import com.vectorprint.report.itext.style.stylers.SimpleColumns; import java.awt.AlphaComposite; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.Key; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.ImageIO; //~--- JDK imports ------------------------------------------------------------ /** * Responsible for creating and styling parts of the report, for formatting data and adding data to the report part. For * styling {@link BaseStyler}s are used that can be found using a {@link StylerFactory}, Formatting data is done using a * {@link Formatter}. * * @author Eduard Drenth at VectorPrint.nl */ public class DefaultElementProducer implements ElementProducer { private static final Logger log = Logger.getLogger(DefaultElementProducer.class.getName()); /** * the suffix used when * {@link #startLayerInGroup(java.lang.String, com.itextpdf.text.pdf.PdfContentByte) starting a layer}. */ public static final String CHILD_LAYERSUFFIX = "_child"; /** * prefix for generic tags used for {@link Advanced advanced stylers}. * * @see EventHelper#addDelayedStyler(java.lang.String, java.util.Collection, com.itextpdf.text.Chunk) */ public static final String ADV = "adv"; @Setting(keys = ReportConstants.DEBUG) private boolean debug = false; private int genericTag = -1; private final Formatter formatter; @SettingsField private EnhancedMap settings; private EventHelper ph; private StyleHelper styleHelper; public DefaultElementProducer() { this(new Formatter()); } public DefaultElementProducer(Formatter f) { this.formatter = f; } /** * leaves object creation to the first styler in the list * * @param <E> * @param stylers * @param data * @param clazz * @return * @throws VectorPrintException */ public <E extends Element> E createElementByStyler(Collection<? extends BaseStyler> stylers, Object data, Class<E> clazz) throws VectorPrintException { // pdfptable, Section and others do not have a default constructor, a styler creates it E e = null; return styleHelper.style(e, data, stylers); } /** * Creates and styles a cell with data in it. If the data is an instance of Element it is added as is to the cell, * otherwise a {@link #createPhrase(java.lang.Object, java.util.Collection) phrase} is created from the data. * * @param data * @param stylers * @return * @throws VectorPrintException */ public PdfPCell createCell(Object data, Collection<? extends BaseStyler> stylers) throws VectorPrintException { DebuggablePdfPCell cell; /* * only when creating an instance of PdfPCell with its content (Phrase/Image/Chunk) in the constructor * alignment will work! */ if (null != data) { if (data instanceof Phrase) { cell = new DebuggablePdfPCell((Phrase) data); } else if (data instanceof Chunk) { cell = new DebuggablePdfPCell(new Phrase((Chunk) data)); } else if (data instanceof Image) { cell = new DebuggablePdfPCell((Image) data); } else if (data instanceof Element) { if (data instanceof PdfPTable) { cell = new DebuggablePdfPCell((PdfPTable) data); } else { throw new VectorPrintException( String.format("%s not supported for a cell", data.getClass().getName())); } } else { cell = new DebuggablePdfPCell(createPhrase(data, stylers)); } } else { cell = new DebuggablePdfPCell(); } StylerFactoryHelper.SETTINGS_ANNOTATION_PROCESSOR.initSettings(cell, settings); return styleHelper.style(cell, data, stylers); } /** * Create a piece of text (part of a Phrase), style it and ad the data. * * @param data * @param stylers * @return * @throws VectorPrintException */ public Chunk createChunk(Object data, Collection<? extends BaseStyler> stylers) throws VectorPrintException { Chunk c = styleHelper.style(new Chunk(), data, stylers); if (data != null) { c.append(formatValue(data)); } if (notDelayedStyle(c, ADV + (++advancedTag), stylers) && debug) { c.setGenericTag(String.valueOf(++genericTag)); } return c; } /** * Create a Phrase, style it and add the data * * @param data * @param stylers * @return * @throws VectorPrintException */ public Phrase createPhrase(Object data, Collection<? extends BaseStyler> stylers) throws VectorPrintException { return initTextElementArray(styleHelper.style(new Phrase(Float.NaN), data, stylers), data, stylers); } /** * Create a Paragraph, style it and add the data * * @param data * @param stylers * @return * @throws VectorPrintException */ public Paragraph createParagraph(Object data, Collection<? extends BaseStyler> stylers) throws VectorPrintException { return initTextElementArray(styleHelper.style(new Paragraph(Float.NaN), data, stylers), data, stylers); } /** * Create a Anchor, style it and add the data * * @param data * @param stylers * @return * @throws VectorPrintException */ public Anchor createAnchor(Object data, Collection<? extends BaseStyler> stylers) throws VectorPrintException { return initTextElementArray(styleHelper.style(new Anchor(Float.NaN), data, stylers), data, stylers); } /** * Create a ListItem, style it and add the data * * @param data * @param stylers * @return * @throws VectorPrintException */ public ListItem createListItem(Object data, Collection<? extends BaseStyler> stylers) throws VectorPrintException { return initTextElementArray(styleHelper.style(new ListItem(Float.NaN), data, stylers), data, stylers); } private <P extends TextElementArray> P initTextElementArray(P text, Object data, Collection<? extends BaseStyler> stylers) throws VectorPrintException { if (data != null) { text.add(data instanceof Element ? (Element) data : new Chunk(formatValue(data))); } boolean first = true; for (Chunk c : (List<Chunk>) text.getChunks()) { styleLink(stylers, first, c, data); if (first) { first = false; } if (notDelayedStyle(c, ADV + (++advancedTag), stylers) && debug) { c.setGenericTag(String.valueOf(++genericTag)); } } return text; } private void styleLink(Collection<? extends BaseStyler> stylers, boolean first, Chunk c, Object data) { if (stylers != null && !stylers.isEmpty()) { try { Collection<Link> l = StyleHelper.getStylers(stylers, Link.class); for (Link link : l) { if (first && link.isParameterSet(Link.ANCHOR)) { link.style(c, data); } else if (link.isParameterSet(Link.GOTO) || link.isParameterSet(com.vectorprint.report.itext.style.stylers.Image.URLPARAM)) { link.style(c, data); } } } catch (VectorPrintException ex) { log.log(Level.WARNING, null, ex); } } } private int advancedTag = -1; int getAdvancedTag() { return advancedTag; } /** * register advanced stylers together with data(part) with the EventHelper to do the styling later * * @param c * @param data * @param stylers * @return * @see PageHelper#onGenericTag(com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document, * com.itextpdf.text.Rectangle, java.lang.String) */ private boolean notDelayedStyle(Chunk c, String gt, Collection<? extends BaseStyler> stylers) { if (stylers == null) { return true; } try { Collection<Advanced> a = new ArrayList<>(stylers.size()); for (Advanced adv : StyleHelper.getStylers(stylers, Advanced.class)) { Advanced.EVENTMODE mode = adv.getEventmode(); if (Advanced.EVENTMODE.ALL.equals(mode) || Advanced.EVENTMODE.TEXT.equals(mode)) { // remember data and chunk adv.addDelayedData(gt, c); a.add(adv); } } if (a.size() > 0) { styleHelper.delayedStyle(c, gt, a, ph); return false; } } catch (VectorPrintException ex) { log.log(Level.SEVERE, null, ex); } return true; } /** * Calls {@link #createTableCell(java.lang.Object, java.util.Collection, boolean) } * * @param val * @param style * @param noWrap * @return * @throws com.vectorprint.VectorPrintException */ public PdfPCell createTableCell(Object val, BaseStyler style, boolean noWrap) throws VectorPrintException { return createTableCell(val, toCollection(style), noWrap); } /** * When noWrap is true a {@link NoWrap} is added to the stylers. Calls * {@link #createCell(java.lang.Object, java.util.Collection) } * * @param val * @param stylers * @param noWrap * @return * @throws com.vectorprint.VectorPrintException */ public PdfPCell createTableCell(Object val, Collection<BaseStyler> stylers, boolean noWrap) throws VectorPrintException { if (noWrap) { stylers.add(new NoWrap()); } return createCell(val, stylers); } /** * When data is an instance of {@link ReportValue}, {@link Formatter#formatValue(com.vectorprint.report.data.types.ReportValue) * } * is called, otherwise String.valueOf is used. * * @param data * @return */ @Override public String formatValue(Object data) { if (data instanceof ReportValue) { return formatter.formatValue((ReportValue) data); } else { return String.valueOf(data); } } @Override public void loadPdf(URL pdf, PdfWriter writer, byte[] password, ImageProcessor imageProcessor, int... pages) throws VectorPrintException { try { if (log.isLoggable(Level.FINE)) { log.fine(String.format("loading pdf from %s", String.valueOf(pdf))); } if ("file".equals(pdf.getProtocol())) { loadPdf(new File(pdf.getFile()), writer, password, imageProcessor, pages); } else { loadPdf(pdf.openStream(), writer, password, imageProcessor, pages); } } catch (IOException ex) { throw new VectorPrintException(String.format("unable to load image %s", pdf.toString()), ex); } } /** * loads an image using {@link ImageIO } * * @param image * @param opacity the value of opacity * @throws VectorPrintException * @return the com.itextpdf.text.Image */ @Override public Image loadImage(URL image, float opacity) throws VectorPrintException { try { if (log.isLoggable(Level.FINE)) { log.fine(String.format("loading image from %s", String.valueOf(image))); } BufferedImage awtim = null; if ("file".equals(image.getProtocol())) { awtim = makeImageTranslucent(ImageIO.read(new File(image.getFile())), opacity); } else { awtim = makeImageTranslucent(ImageIO.read(image), opacity); } return Image.getInstance(awtim, null); } catch (BadElementException | IOException ex) { throw new VectorPrintException(String.format("unable to load image %s", image.toString()), ex); } } /** * returns a transparent image when opacity < 1 * * @param source * @param opacity * @return */ public static BufferedImage makeImageTranslucent(BufferedImage source, float opacity) { if (opacity == 1) { return source; } BufferedImage translucent = new BufferedImage(source.getWidth(), source.getHeight(), BufferedImage.TRANSLUCENT); Graphics2D g = translucent.createGraphics(); g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity)); g.drawImage(source, null, 0, 0); g.dispose(); return translucent; } @Override public void loadPdf(InputStream pdf, PdfWriter writer, byte[] password, ImageProcessor imageProcessor, int... pages) throws VectorPrintException { File f = null; try { f = File.createTempFile("pdf.", "pdf"); f.deleteOnExit(); IOHelper.load(pdf, new FileOutputStream(f), ReportConstants.DEFAULTBUFFERSIZE, true); loadPdf(f, writer, password, imageProcessor, pages); } catch (IOException ex) { throw new VectorPrintException(String.format("unable to load pdf"), ex); } finally { if (f != null) { f.delete(); } } } @Override public void loadPdf(File pdf, PdfWriter writer, byte[] password, ImageProcessor imageProcessor, int... pages) throws VectorPrintException { RandomAccessFileOrArray ra = null; try { RandomAccessSourceFactory rasf = new RandomAccessSourceFactory(); ra = new RandomAccessFileOrArray(rasf.createBestSource(pdf.getPath())); PdfReader reader = new PdfReader(ra, password); if (pages == null) { for (int i = 0; i < reader.getNumberOfPages();) { imageProcessor.processImage(Image.getInstance(writer.getImportedPage(reader, ++i))); writer.freeReader(reader); } } else { for (int i : pages) { imageProcessor.processImage(Image.getInstance(writer.getImportedPage(reader, i))); writer.freeReader(reader); } } } catch (BadElementException | IOException ex) { throw new VectorPrintException(String.format("unable to load image %s", pdf.toString()), ex); } finally { if (ra != null) { try { ra.close(); } catch (IOException ex) { } } } } @Override public void loadPdf(InputStream pdf, PdfWriter writer, Certificate certificate, Key key, String securityProvider, ImageProcessor imageProcessor, int... pages) throws VectorPrintException { // first download, then load File f = null; try { f = File.createTempFile("pdf.", "pdf"); f.deleteOnExit(); IOHelper.load(pdf, new FileOutputStream(f), ReportConstants.DEFAULTBUFFERSIZE, true); PdfReader reader = new PdfReader(f.getPath(), certificate, key, securityProvider); if (pages == null) { for (int i = 0; i < reader.getNumberOfPages();) { imageProcessor.processImage(Image.getInstance(writer.getImportedPage(reader, ++i))); writer.freeReader(reader); } } else { for (int i : pages) { imageProcessor.processImage(Image.getInstance(writer.getImportedPage(reader, i))); writer.freeReader(reader); } } } catch (BadElementException | IOException ex) { throw new VectorPrintException(String.format("unable to load image %s", pdf.toString()), ex); } finally { if (f != null) { f.delete(); } } } /** * * @param image the value of image * @param opacity the value of opacity * @throws VectorPrintException */ @Override public Image loadImage(InputStream image, float opacity) throws VectorPrintException { try { BufferedImage awtim = makeImageTranslucent(ImageIO.read(image), opacity); Image img = Image.getInstance(awtim, null); return img; } catch (BadElementException | IOException ex) { throw new VectorPrintException(ex); } } /** * * @param image the value of image * @param opacity the value of opacity * @throws VectorPrintException */ @Override public Image loadImage(File image, float opacity) throws VectorPrintException { try { BufferedImage awtim = makeImageTranslucent(ImageIO.read(image), opacity); Image img = Image.getInstance(awtim, null); return img; } catch (BadElementException | IOException ex) { throw new VectorPrintException(ex); } } @Override public <E extends Element> E createElement(Object data, Class<E> elementClass, List<? extends BaseStyler> stylers) throws VectorPrintException, InstantiationException, IllegalAccessException { if (PdfPCell.class.equals(elementClass)) { return (E) createCell(data, stylers); } else if (Chunk.class.equals(elementClass)) { return (E) createChunk(data, stylers); } else if (Phrase.class.equals(elementClass)) { return (E) createPhrase(data, stylers); } else if (Paragraph.class.equals(elementClass)) { return (E) createParagraph(data, stylers); } else if (Anchor.class.equals(elementClass)) { return (E) createAnchor(data, stylers); } else if (ListItem.class.equals(elementClass)) { return (E) createListItem(data, stylers); } else if (PdfPTable.class.equals(elementClass)) { return createElementByStyler(stylers, data, elementClass); } else if (Image.class.equals(elementClass)) { return createElementByStyler(stylers, data, elementClass); } else if (ChapterAutoNumber.class.equals(elementClass)) { ChapterAutoNumber can = new ChapterAutoNumber(formatValue(data)); return (E) styleHelper.style(can, data, stylers); } return styleHelper.style(elementClass.newInstance(), data, stylers); } private final Map<Integer, List<Section>> sections = new HashMap<>(10); public void clearSections() { sections.clear(); } /** * create the Section, style the title, style the section and return the styled section. * * @param title * @param nesting * @param stylers * @return * @throws VectorPrintException * @throws InstantiationException * @throws IllegalAccessException */ @Override public Section getIndex(String title, int nesting, List<? extends BaseStyler> stylers) throws VectorPrintException, InstantiationException, IllegalAccessException { if (nesting < 1) { throw new VectorPrintException("chapter numbering starts with 1, wrong number: " + nesting); } if (sections.get(nesting) == null) { sections.put(nesting, new ArrayList<>(10)); } Section current; if (nesting == 1) { List<Section> chapters = sections.get(1); current = new Chapter(createElement(title, Paragraph.class, stylers), chapters.size() + 1); chapters.add(current); } else { List<Section> parents = sections.get(nesting - 1); Section parent = parents.get(parents.size() - 1); current = parent.addSection(createParagraph(title, stylers)); sections.get(nesting).add(current); } return styleHelper.style(current, null, stylers); } void setPh(EventHelper ph) { this.ph = ph; } private final Map<String, PdfLayer> layerGroups = new HashMap<>(2); @Override public PdfLayer startLayerInGroup(String groupId, PdfContentByte canvas) { PdfLayer pl; try { pl = new PdfLayer(groupId + CHILD_LAYERSUFFIX, canvas.getPdfWriter()); } catch (IOException ex) { throw new VectorPrintRuntimeException(ex); } initLayerGroup(groupId, canvas).addChild(pl); canvas.beginLayer(pl); return pl; } @Override public PdfLayer initLayerGroup(String layerId, PdfContentByte canvas) { if (!layerGroups.containsKey(layerId)) { PdfLayer parent; try { parent = new PdfLayer(layerId, canvas.getPdfWriter()); } catch (IOException ex) { throw new VectorPrintRuntimeException(ex); } layerGroups.put(layerId, parent); canvas.beginLayer(parent); canvas.endLayer(); } return layerGroups.get(layerId); } public void setStyleHelper(StyleHelper styleHelper) { this.styleHelper = styleHelper; } @Override public void loadTiff(File tiff, ImageProcessor imageProcessor, int... pages) throws VectorPrintException { RandomAccessFileOrArray ra = null; try { RandomAccessSourceFactory rasf = new RandomAccessSourceFactory(); ra = new RandomAccessFileOrArray(rasf.createBestSource(tiff.getPath())); if (pages == null) { for (int i = 0; i < TiffImage.getNumberOfPages(ra);) { imageProcessor.processImage(TiffImage.getTiffImage(ra, ++i)); } } else { for (int i : pages) { imageProcessor.processImage(TiffImage.getTiffImage(ra, i)); } } } catch (IOException ex) { throw new VectorPrintException(String.format("unable to load tiff %s", tiff.toString()), ex); } finally { if (ra != null) { try { ra.close(); } catch (IOException ex) { } } } } /** * Creates tempfile and calls {@link loadTiff(File, ImageProcessor, int...) } * * @param tiff * @param imageProcessor * @param pages * @throws VectorPrintException */ @Override public void loadTiff(InputStream tiff, ImageProcessor imageProcessor, int... pages) throws VectorPrintException { File f = null; try { f = File.createTempFile("tiff.", "tiff"); f.deleteOnExit(); IOHelper.load(tiff, new FileOutputStream(f), ReportConstants.DEFAULTBUFFERSIZE, true); loadTiff(f, imageProcessor, pages); } catch (IOException ex) { throw new VectorPrintException(String.format("unable to load tiff"), ex); } finally { if (f != null) { f.delete(); } } } /** * Calls {@link loadTiff(InputStream, ImageProcessor, int... ) } * * @param tiff * @param imageProcessor * @param pages * @throws VectorPrintException */ @Override public void loadTiff(URL tiff, ImageProcessor imageProcessor, int... pages) throws VectorPrintException { try { if (log.isLoggable(Level.FINE)) { log.fine(String.format("loading tiff from %s", String.valueOf(tiff))); } if ("file".equals(tiff.getProtocol())) { loadTiff(new File(tiff.getFile()), imageProcessor, pages); } else { loadTiff(tiff.openStream(), imageProcessor, pages); } } catch (IOException ex) { throw new VectorPrintException(String.format("unable to load image %s", tiff.toString()), ex); } } @Override public Formatter getFormatter() { return formatter; } @Override public StyleHelper getStyleHelper() { return styleHelper; } /** * Creates a ColumnText, adds the data using addText and returns the {@link SimpleColumns} that can be used to * {@link SimpleColumns#write() write out} or to * {@link SimpleColumns#addContent(java.lang.Object, java.lang.String...) add more data} to the document. * * @param stylers * @return * @throws VectorPrintException */ @Override public SimpleColumns createColumns(List<? extends BaseStyler> stylers) throws VectorPrintException { ColumnText mct = null; mct = styleHelper.style(mct, null, stylers); SimpleColumns sc = StyleHelper.getStylers(stylers, SimpleColumns.class).get(0); return sc; } public void setSettings(EnhancedMap settings) { this.settings = settings; StylerFactoryHelper.SETTINGS_ANNOTATION_PROCESSOR.initSettings(formatter, settings); } }