/* * Copyright 2006-2017 ICEsoft Technologies Canada Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.icepdf.core.pobjects.graphics; import org.icepdf.core.pobjects.Page; import org.icepdf.core.pobjects.graphics.commands.DrawCmd; import org.icepdf.core.pobjects.graphics.commands.FormDrawCmd; import org.icepdf.core.pobjects.graphics.commands.ImageDrawCmd; import org.icepdf.core.pobjects.graphics.commands.ShapesDrawCmd; import org.icepdf.core.pobjects.graphics.text.PageText; import org.icepdf.core.util.Defs; import java.awt.*; import java.awt.geom.AffineTransform; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; /** * <p>The Shapes class hold all object that are parsed from a Page's content * streams. These contained object make up a pages graphics stack which can * be iterated through to paint a page's content.<p> * <br> * <p>This class is generally only used by the Content parser during content * parsing. The class also stores points to the images found in the content * as well as the text that is encoded on a page.</p> * * @since 1.0 */ public class Shapes { private static final Logger logger = Logger.getLogger(Shapes.class.toString()); private static int shapesInitialCapacity = 5000; // disables alpha painting. protected boolean paintAlpha = !Defs.sysPropertyBoolean("org.icepdf.core.paint.disableAlpha", false); static { shapesInitialCapacity = Defs.sysPropertyInt( "org.icepdf.core.shapes.initialCapacity", shapesInitialCapacity); } // cache of common draw state, we try to avoid adding new operands if the // stack already has the needed state, more ops take longer to paint. private int rule; private float alpha; private boolean interrupted; // Graphics stack for a page's content. protected ArrayList<DrawCmd> shapes = new ArrayList<DrawCmd>(shapesInitialCapacity); // stores the state of the currently visible optional content. protected OptionalContentState optionalContentState = new OptionalContentState(); // the collection of objects listening for page paint events private Page parentPage; // text extraction data structure private PageText pageText = new PageText(); public PageText getPageText() { return pageText; } /** * Gets the number of shapes on the shapes stack. * * @return number of shapes on the stack */ public int getShapesCount() { if (shapes != null) { return shapes.size(); } else { return 0; } } public ArrayList<DrawCmd> getShapes() { return shapes; } public void add(ArrayList<DrawCmd> shapes) { shapes.addAll(shapes); } public void setPageParent(Page parent) { parentPage = parent; } public void add(DrawCmd drawCmd){ if (!(drawCmd instanceof FormDrawCmd)){ shapes.add(drawCmd); }else{ shapes.add(drawCmd); } } public boolean isPaintAlpha() { return paintAlpha; } public void setPaintAlpha(boolean paintAlpha) { this.paintAlpha = paintAlpha; } /** * Paint the graphics stack to the graphics context * * @param g graphics context to paint to. */ public void paint(Graphics2D g) throws InterruptedException{ try { interrupted = false; AffineTransform base = new AffineTransform(g.getTransform()); Shape clip = g.getClip(); PaintTimer paintTimer = new PaintTimer(); Shape previousShape = null; DrawCmd nextShape; // for loops actually faster in this case. for (int i = 0, max = shapes.size(); i < max; i++) { // try and minimize interrupted checks, costly. if (interrupted || (i % 1000 == 0 && Thread.currentThread().isInterrupted())) { interrupted = false; throw new InterruptedException("Page painting thread interrupted"); } nextShape = shapes.get(i); previousShape = nextShape.paintOperand(g, parentPage, previousShape, clip, base, optionalContentState, paintAlpha, paintTimer); } } catch (InterruptedException e){ throw new InterruptedException(e.getMessage()); } catch (Exception e) { logger.log(Level.FINE, "Error painting shapes.", e); } } /** * @deprecated use Thread.interrupt() instead. */ public void interruptPaint() { interrupted = true; } /** * @deprecated use Thread.interrupt() instead. */ public boolean isInterrupted() { return interrupted; } /** * Iterates over the Shapes objects extracting all Image objects. * * @return all images in a page's content, if any. */ public ArrayList<Image> getImages() throws InterruptedException { ArrayList<Image> images = new ArrayList<Image>(); for (Object object : shapes) { if (object instanceof ImageDrawCmd) { images.add(((ImageDrawCmd) object).getImage()); } else if (object instanceof ShapesDrawCmd) { images.addAll(((ShapesDrawCmd) object).getShapes().getImages()); } } return images; } /** * Contracts the shapes ArrayList to the actual size of the elements * it contains. */ public void contract() { if (shapes != null) { shapes.trimToSize(); } } public int getRule() { return rule; } public void setRule(int rule) { this.rule = rule; } public float getAlpha() { return alpha; } public void setAlpha(float alpha) { this.alpha = alpha; } }