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

com.itextpdf.kernel.pdf.canvas.parser.PdfCanvasProcessor Maven / Gradle / Ivy

There is a newer version: 9.0.0
Show newest version
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2024 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    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 Affero 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 .
 */
package com.itextpdf.kernel.pdf.canvas.parser;

import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.logs.IoLogMessageConstant;
import com.itextpdf.io.source.PdfTokenizer;
import com.itextpdf.io.source.RandomAccessFileOrArray;
import com.itextpdf.io.source.RandomAccessSourceFactory;
import com.itextpdf.commons.utils.MessageFormatUtil;
import com.itextpdf.kernel.logs.KernelLogMessageConstant;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.colors.CalGray;
import com.itextpdf.kernel.colors.CalRgb;
import com.itextpdf.kernel.colors.Color;
import com.itextpdf.kernel.colors.DeviceCmyk;
import com.itextpdf.kernel.colors.DeviceGray;
import com.itextpdf.kernel.colors.DeviceN;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.colors.IccBased;
import com.itextpdf.kernel.colors.Indexed;
import com.itextpdf.kernel.colors.Lab;
import com.itextpdf.kernel.colors.PatternColor;
import com.itextpdf.kernel.colors.Separation;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Matrix;
import com.itextpdf.kernel.geom.NoninvertibleTransformException;
import com.itextpdf.kernel.geom.Path;
import com.itextpdf.kernel.pdf.MemoryLimitsAwareHandler;
import com.itextpdf.kernel.pdf.PdfArray;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfIndirectReference;
import com.itextpdf.kernel.pdf.PdfLiteral;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfNumber;
import com.itextpdf.kernel.pdf.PdfObject;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfResources;
import com.itextpdf.kernel.pdf.PdfStream;
import com.itextpdf.kernel.pdf.PdfString;
import com.itextpdf.kernel.pdf.canvas.CanvasTag;
import com.itextpdf.kernel.pdf.canvas.PdfCanvasConstants;
import com.itextpdf.kernel.pdf.canvas.PdfCanvasConstants.FillingRule;
import com.itextpdf.kernel.pdf.canvas.parser.data.AbstractRenderInfo;
import com.itextpdf.kernel.pdf.canvas.parser.data.ClippingPathInfo;
import com.itextpdf.kernel.pdf.canvas.parser.data.IEventData;
import com.itextpdf.kernel.pdf.canvas.parser.data.ImageRenderInfo;
import com.itextpdf.kernel.pdf.canvas.parser.data.PathRenderInfo;
import com.itextpdf.kernel.pdf.canvas.parser.data.TextRenderInfo;
import com.itextpdf.kernel.pdf.canvas.parser.listener.IEventListener;
import com.itextpdf.kernel.pdf.canvas.parser.util.PdfCanvasParser;
import com.itextpdf.kernel.pdf.colorspace.PdfCieBasedCs;
import com.itextpdf.kernel.pdf.colorspace.PdfColorSpace;
import com.itextpdf.kernel.pdf.colorspace.PdfPattern;
import com.itextpdf.kernel.pdf.colorspace.PdfSpecialCs;
import com.itextpdf.kernel.pdf.extgstate.PdfExtGState;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.*;

/**
 * Processor for a PDF content stream.
 */
public class PdfCanvasProcessor {
    public static final String DEFAULT_OPERATOR = "DefaultOperator";

    /**
     * Listener that will be notified of render events
     */
    protected final IEventListener eventListener;

    /**
     * Cache supported events in case the user's {@link IEventListener#getSupportedEvents()} method is not very efficient
     */
    protected final Set supportedEvents;

    protected Path currentPath = new Path();

    /**
     * Indicates whether the current clipping path should be modified by
     * intersecting it with the current path.
     */
    protected boolean isClip;

    /**
     * Specifies the filling rule which should be applied while calculating
     * new clipping path.
     */
    protected int clippingRule;

    /**
     * A map with all supported operators (PDF syntax).
     */
    private Map operators;

    /**
     * Resources for the content stream.
     * Current resources are always at the top of the stack.
     * Stack is needed in case if some "inner" content stream with it's own resources
     * is encountered (like Form XObject).
     */
    private List resourcesStack;

    /**
     * Stack keeping track of the graphics state.
     */
    private final Stack gsStack = new Stack<>();

    private Matrix textMatrix;
    private Matrix textLineMatrix;

    /**
     * A map with all supported XObject handlers
     */
    private Map xobjectDoHandlers;

    /**
     * The font cache
     */
    private Map> cachedFonts = new HashMap<>();

    /**
     * A stack containing marked content info.
     */
    private Stack markedContentStack = new Stack<>();

    /**
     * A memory limits handler.
     */
    private MemoryLimitsAwareHandler memoryLimitsHandler = null;

    /**
     * Page size in bytes.
     */
    private long pageSize = 0;

    /**
     * Creates a new PDF Content Stream Processor that will send its output to the
     * designated render listener.
     *
     * @param eventListener the {@link IEventListener} that will receive rendering notifications
     */
    public PdfCanvasProcessor(IEventListener eventListener) {
        this.eventListener = eventListener;
        this.supportedEvents = eventListener.getSupportedEvents();
        operators = new HashMap<>();
        populateOperators();
        xobjectDoHandlers = new HashMap<>();
        populateXObjectDoHandlers();
        reset();
    }

    /**
     * Creates a new PDF Content Stream Processor that will send its output to the
     * designated render listener.
     * Also allows registration of custom IContentOperators that can influence
     * how (and whether or not) the PDF instructions will be parsed.
     *
     * @param eventListener              the {@link IEventListener} that will receive rendering notifications
     * @param additionalContentOperators an optional map of custom {@link IContentOperator}s for rendering instructions
     */
    public PdfCanvasProcessor(IEventListener eventListener, Map additionalContentOperators) {
        this(eventListener);
        for (Map.Entry entry : additionalContentOperators.entrySet()) {
            registerContentOperator(entry.getKey(), entry.getValue());
        }
    }

    /**
     * Registers a Do handler that will be called when Do for the provided XObject subtype is encountered during content processing.
     * 
* If you register a handler, it is a very good idea to pass the call on to the existing registered handler (returned by this call), otherwise you * may inadvertently change the internal behavior of the processor. * * @param xobjectSubType the XObject subtype this handler will process, or PdfName.DEFAULT for a catch-all handler * @param handler the handler that will receive notification when the Do operator for the specified subtype is encountered * @return the existing registered handler, if any */ public IXObjectDoHandler registerXObjectDoHandler(PdfName xobjectSubType, IXObjectDoHandler handler) { return xobjectDoHandlers.put(xobjectSubType, handler); } /** * Registers a content operator that will be called when the specified operator string is encountered during content processing. *
* If you register an operator, it is a very good idea to pass the call on to the existing registered operator (returned by this call), otherwise you * may inadvertently change the internal behavior of the processor. * * @param operatorString the operator id, or DEFAULT_OPERATOR for a catch-all operator * @param operator the operator that will receive notification when the operator is encountered * @return the existing registered operator, if any */ public IContentOperator registerContentOperator(String operatorString, IContentOperator operator) { return operators.put(operatorString, operator); } /** * Gets the {@link java.util.Collection} containing all the registered operators strings. * * @return {@link java.util.Collection} containing all the registered operators strings. */ public Collection getRegisteredOperatorStrings() { return new ArrayList(operators.keySet()); } /** * Resets the graphics state stack, matrices and resources. */ public void reset() { memoryLimitsHandler = null; pageSize = 0; gsStack.removeAllElements(); gsStack.push(new ParserGraphicsState()); textMatrix = null; textLineMatrix = null; resourcesStack = new ArrayList<>(); isClip = false; currentPath = new Path(); } /** * Gets the current {@link ParserGraphicsState} * * @return the current {@link ParserGraphicsState} */ public ParserGraphicsState getGraphicsState() { return gsStack.peek(); } /** * Processes PDF syntax. * Note: If you re-use a given {@link PdfCanvasProcessor}, you must call {@link PdfCanvasProcessor#reset()} * * @param contentBytes the bytes of a content stream * @param resources the resources of the content stream. Must not be null. */ public void processContent(byte[] contentBytes, PdfResources resources) { if (resources == null) { throw new PdfException(KernelExceptionMessageConstant.RESOURCES_CANNOT_BE_NULL); } if (memoryLimitsHandler != null) { pageSize += (long)contentBytes.length; memoryLimitsHandler.checkIfPageSizeExceedsTheLimit(this.pageSize); } this.resourcesStack.add(resources); PdfTokenizer tokeniser = new PdfTokenizer(new RandomAccessFileOrArray(new RandomAccessSourceFactory().createSource(contentBytes))); PdfCanvasParser ps = new PdfCanvasParser(tokeniser, resources); List operands = new ArrayList<>(); try { while (ps.parse(operands).size() > 0) { PdfLiteral operator = (PdfLiteral) operands.get(operands.size() - 1); invokeOperator(operator, operands); } } catch (IOException e) { throw new PdfException(KernelExceptionMessageConstant.CANNOT_PARSE_CONTENT_STREAM, e); } this.resourcesStack.remove(resourcesStack.size() - 1); } /** * Processes PDF syntax. * * Note: If you re-use a given {@link PdfCanvasProcessor}, you must call {@link PdfCanvasProcessor#reset()} * * @param page the page to process */ public void processPageContent(PdfPage page) { this.memoryLimitsHandler = page.getDocument().getMemoryLimitsAwareHandler(); initClippingPath(page); ParserGraphicsState gs = getGraphicsState(); eventOccurred(new ClippingPathInfo(gs, gs.getClippingPath(), gs.getCtm()), EventType.CLIP_PATH_CHANGED); processContent(page.getContentBytes(), page.getResources()); } /** * Accessor method for the {@link IEventListener} object maintained in this class. * Necessary for implementing custom ContentOperator implementations. * * @return the renderListener */ public IEventListener getEventListener() { return eventListener; } /** * Loads all the supported graphics and text state operators in a map. */ protected void populateOperators() { registerContentOperator(DEFAULT_OPERATOR, new IgnoreOperator()); registerContentOperator("q", new PushGraphicsStateOperator()); registerContentOperator("Q", new PopGraphicsStateOperator()); registerContentOperator("cm", new ModifyCurrentTransformationMatrixOperator()); registerContentOperator("Do", new DoOperator()); registerContentOperator("BMC", new BeginMarkedContentOperator()); registerContentOperator("BDC", new BeginMarkedContentDictionaryOperator()); registerContentOperator("EMC", new EndMarkedContentOperator()); if (supportedEvents == null || supportedEvents.contains(EventType.RENDER_TEXT) || supportedEvents.contains(EventType.RENDER_PATH) || supportedEvents.contains(EventType.CLIP_PATH_CHANGED)) { registerContentOperator("g", new SetGrayFillOperator()); registerContentOperator("G", new SetGrayStrokeOperator()); registerContentOperator("rg", new SetRGBFillOperator()); registerContentOperator("RG", new SetRGBStrokeOperator()); registerContentOperator("k", new SetCMYKFillOperator()); registerContentOperator("K", new SetCMYKStrokeOperator()); registerContentOperator("cs", new SetColorSpaceFillOperator()); registerContentOperator("CS", new SetColorSpaceStrokeOperator()); registerContentOperator("sc", new SetColorFillOperator()); registerContentOperator("SC", new SetColorStrokeOperator()); registerContentOperator("scn", new SetColorFillOperator()); registerContentOperator("SCN", new SetColorStrokeOperator()); registerContentOperator("gs", new ProcessGraphicsStateResourceOperator()); } if (supportedEvents == null || supportedEvents.contains(EventType.RENDER_IMAGE)) { registerContentOperator("EI", new EndImageOperator()); } if (supportedEvents == null || supportedEvents.contains(EventType.RENDER_TEXT) || supportedEvents.contains(EventType.BEGIN_TEXT) || supportedEvents.contains(EventType.END_TEXT)) { registerContentOperator("BT", new BeginTextOperator()); registerContentOperator("ET", new EndTextOperator()); } if (supportedEvents == null || supportedEvents.contains(EventType.RENDER_TEXT)) { SetTextCharacterSpacingOperator tcOperator = new SetTextCharacterSpacingOperator(); registerContentOperator("Tc", tcOperator); SetTextWordSpacingOperator twOperator = new SetTextWordSpacingOperator(); registerContentOperator("Tw", twOperator); registerContentOperator("Tz", new SetTextHorizontalScalingOperator()); SetTextLeadingOperator tlOperator = new SetTextLeadingOperator(); registerContentOperator("TL", tlOperator); registerContentOperator("Tf", new SetTextFontOperator()); registerContentOperator("Tr", new SetTextRenderModeOperator()); registerContentOperator("Ts", new SetTextRiseOperator()); TextMoveStartNextLineOperator tdOperator = new TextMoveStartNextLineOperator(); registerContentOperator("Td", tdOperator); registerContentOperator("TD", new TextMoveStartNextLineWithLeadingOperator(tdOperator, tlOperator)); registerContentOperator("Tm", new TextSetTextMatrixOperator()); TextMoveNextLineOperator tstarOperator = new TextMoveNextLineOperator(tdOperator); registerContentOperator("T*", tstarOperator); ShowTextOperator tjOperator = new ShowTextOperator(); registerContentOperator("Tj", tjOperator); MoveNextLineAndShowTextOperator tickOperator = new MoveNextLineAndShowTextOperator(tstarOperator, tjOperator); registerContentOperator("'", tickOperator); registerContentOperator("\"", new MoveNextLineAndShowTextWithSpacingOperator(twOperator, tcOperator, tickOperator)); registerContentOperator("TJ", new ShowTextArrayOperator()); } if (supportedEvents == null || supportedEvents.contains(EventType.CLIP_PATH_CHANGED) || supportedEvents.contains(EventType.RENDER_PATH)) { registerContentOperator("w", new SetLineWidthOperator()); registerContentOperator("J", new SetLineCapOperator()); registerContentOperator("j", new SetLineJoinOperator()); registerContentOperator("M", new SetMiterLimitOperator()); registerContentOperator("d", new SetLineDashPatternOperator()); int fillStroke = PathRenderInfo.FILL | PathRenderInfo.STROKE; registerContentOperator("m", new MoveToOperator()); registerContentOperator("l", new LineToOperator()); registerContentOperator("c", new CurveOperator()); registerContentOperator("v", new CurveFirstPointDuplicatedOperator()); registerContentOperator("y", new CurveFourhPointDuplicatedOperator()); registerContentOperator("h", new CloseSubpathOperator()); registerContentOperator("re", new RectangleOperator()); registerContentOperator("S", new PaintPathOperator(PathRenderInfo.STROKE, -1, false)); registerContentOperator("s", new PaintPathOperator(PathRenderInfo.STROKE, -1, true)); registerContentOperator("f", new PaintPathOperator(PathRenderInfo.FILL, PdfCanvasConstants.FillingRule.NONZERO_WINDING, false)); registerContentOperator("F", new PaintPathOperator(PathRenderInfo.FILL, PdfCanvasConstants.FillingRule.NONZERO_WINDING, false)); registerContentOperator("f*", new PaintPathOperator(PathRenderInfo.FILL, PdfCanvasConstants.FillingRule.EVEN_ODD, false)); registerContentOperator("B", new PaintPathOperator(fillStroke, PdfCanvasConstants.FillingRule.NONZERO_WINDING, false)); registerContentOperator("B*", new PaintPathOperator(fillStroke, PdfCanvasConstants.FillingRule.EVEN_ODD, false)); registerContentOperator("b", new PaintPathOperator(fillStroke, PdfCanvasConstants.FillingRule.NONZERO_WINDING, true)); registerContentOperator("b*", new PaintPathOperator(fillStroke, PdfCanvasConstants.FillingRule.EVEN_ODD, true)); registerContentOperator("n", new PaintPathOperator(PathRenderInfo.NO_OP, -1, false)); registerContentOperator("W", new ClipPathOperator(PdfCanvasConstants.FillingRule.NONZERO_WINDING)); registerContentOperator("W*", new ClipPathOperator(PdfCanvasConstants.FillingRule.EVEN_ODD)); } } /** * Displays the current path. * * @param operation One of the possible combinations of {@link PathRenderInfo#STROKE} * and {@link PathRenderInfo#FILL} values or * {@link PathRenderInfo#NO_OP} * @param rule Either {@link FillingRule#NONZERO_WINDING} or {@link FillingRule#EVEN_ODD} * In case it isn't applicable pass any byte value. */ protected void paintPath(int operation, int rule) { ParserGraphicsState gs = getGraphicsState(); PathRenderInfo renderInfo = new PathRenderInfo(this.markedContentStack, gs, currentPath, operation, rule, isClip, clippingRule); eventOccurred(renderInfo, EventType.RENDER_PATH); if (isClip) { isClip = false; gs.clip(currentPath, clippingRule); eventOccurred(new ClippingPathInfo(gs, gs.getClippingPath(), gs.getCtm()), EventType.CLIP_PATH_CHANGED); } currentPath = new Path(); } /** * Invokes an operator. * * @param operator the PDF Syntax of the operator * @param operands a list with operands */ protected void invokeOperator(PdfLiteral operator, List operands) { IContentOperator op = operators.get(operator.toString()); if (op == null) { op = operators.get(DEFAULT_OPERATOR); } op.invoke(this, operator, operands); } protected PdfStream getXObjectStream(PdfName xobjectName) { PdfDictionary xobjects = getResources().getResource(PdfName.XObject); return xobjects.getAsStream(xobjectName); } protected PdfResources getResources() { return resourcesStack.get(resourcesStack.size() - 1); } protected void populateXObjectDoHandlers() { registerXObjectDoHandler(PdfName.Default, new IgnoreXObjectDoHandler()); registerXObjectDoHandler(PdfName.Form, new FormXObjectDoHandler()); if (supportedEvents == null || supportedEvents.contains(EventType.RENDER_IMAGE)) { registerXObjectDoHandler(PdfName.Image, new ImageXObjectDoHandler()); } } /** * Creates a {@link PdfFont} object by a font dictionary. The font may have been cached in case * it is an indirect object. * * @param fontDict the {@link PdfDictionary font dictionary} to create the font from * @return the created font */ protected PdfFont getFont(PdfDictionary fontDict) { if (fontDict.getIndirectReference() == null) { return PdfFontFactory.createFont(fontDict); } else { int n = fontDict.getIndirectReference().getObjNumber(); WeakReference fontRef = cachedFonts.get(n); PdfFont font = (PdfFont) (fontRef == null ? null : fontRef.get()); if (font == null) { font = PdfFontFactory.createFont(fontDict); cachedFonts.put(n, new WeakReference<>(font)); } return font; } } /** * Add to the marked content stack * * @param tag the tag of the marked content * @param dict the PdfDictionary associated with the marked content */ protected void beginMarkedContent(PdfName tag, PdfDictionary dict) { markedContentStack.push(new CanvasTag(tag).setProperties(dict)); } /** * Remove the latest marked content from the stack. Keeps track of the BMC, BDC and EMC operators. */ protected void endMarkedContent() { markedContentStack.pop(); } /** * Used to trigger beginTextBlock on the renderListener */ private void beginText() { eventOccurred(null, EventType.BEGIN_TEXT); } /** * Used to trigger endTextBlock on the renderListener */ private void endText() { eventOccurred(null, EventType.END_TEXT); } /** * This is a proxy to pass only those events to the event listener which are supported by it. * * @param data event data * @param type event type */ protected void eventOccurred(IEventData data, EventType type) { if (supportedEvents == null || supportedEvents.contains(type)) { eventListener.eventOccurred(data, type); } if (data instanceof AbstractRenderInfo) { ((AbstractRenderInfo) data).releaseGraphicsState(); } } /** * Displays text. * * @param string the text to display */ private void displayPdfString(PdfString string) { TextRenderInfo renderInfo = new TextRenderInfo(string, getGraphicsState(), textMatrix, markedContentStack); textMatrix = new Matrix(renderInfo.getUnscaledWidth(), 0).multiply(textMatrix); eventOccurred(renderInfo, EventType.RENDER_TEXT); } /** * Displays an XObject using the registered handler for this XObject's subtype * * @param resourceName the name of the XObject to retrieve from the resource dictionary */ private void displayXObject(PdfName resourceName) { PdfStream xobjectStream = getXObjectStream(resourceName); PdfName subType = xobjectStream.getAsName(PdfName.Subtype); IXObjectDoHandler handler = xobjectDoHandlers.get(subType); if (handler == null) { handler = xobjectDoHandlers.get(PdfName.Default); } handler.handleXObject(this, this.markedContentStack, xobjectStream, resourceName); } private void displayImage(Stack canvasTagHierarchy, PdfStream imageStream, PdfName resourceName, boolean isInline) { PdfDictionary colorSpaceDic = getResources().getResource(PdfName.ColorSpace); ImageRenderInfo renderInfo = new ImageRenderInfo(canvasTagHierarchy, getGraphicsState(), getGraphicsState().getCtm(), imageStream, resourceName, colorSpaceDic, isInline); eventOccurred(renderInfo, EventType.RENDER_IMAGE); } /** * Adjusts the text matrix for the specified adjustment value (see TJ operator in the PDF spec for information) * * @param tj the text adjustment */ private void applyTextAdjust(float tj) { final float adjustBy = FontProgram.convertTextSpaceToGlyphSpace(-tj) * getGraphicsState().getFontSize() * ( getGraphicsState().getHorizontalScaling() / 100F); textMatrix = new Matrix(adjustBy, 0).multiply(textMatrix); } private void initClippingPath(PdfPage page) { Path clippingPath = new Path(); clippingPath.rectangle(page.getCropBox()); getGraphicsState().setClippingPath(clippingPath); } /** * A handler that implements operator (unregistered). */ private static class IgnoreOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { // ignore the operator } } /** * A handler that implements operator (TJ). For more information see Table 51 ISO-32000-1 */ private static class ShowTextArrayOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfArray array = (PdfArray) operands.get(0); float tj = 0; for (PdfObject entryObj : array) { if (entryObj instanceof PdfString) { processor.displayPdfString((PdfString) entryObj); tj = 0; } else { tj = ((PdfNumber) entryObj).floatValue(); processor.applyTextAdjust(tj); } } } } /** * A handler that implements operator ("). For more information see Table 51 ISO-32000-1 */ private static class MoveNextLineAndShowTextWithSpacingOperator implements IContentOperator { private final SetTextWordSpacingOperator setTextWordSpacing; private final SetTextCharacterSpacingOperator setTextCharacterSpacing; private final MoveNextLineAndShowTextOperator moveNextLineAndShowText; /** * Create new instance of this handler. * * @param setTextWordSpacing the handler for Tw operator * @param setTextCharacterSpacing the handler for Tc operator * @param moveNextLineAndShowText the handler for ' operator */ public MoveNextLineAndShowTextWithSpacingOperator(SetTextWordSpacingOperator setTextWordSpacing, SetTextCharacterSpacingOperator setTextCharacterSpacing, MoveNextLineAndShowTextOperator moveNextLineAndShowText) { this.setTextWordSpacing = setTextWordSpacing; this.setTextCharacterSpacing = setTextCharacterSpacing; this.moveNextLineAndShowText = moveNextLineAndShowText; } /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfNumber aw = (PdfNumber) operands.get(0); PdfNumber ac = (PdfNumber) operands.get(1); PdfString string = (PdfString) operands.get(2); List twOperands = new ArrayList(1); twOperands.add(0, aw); setTextWordSpacing.invoke(processor, null, twOperands); List tcOperands = new ArrayList(1); tcOperands.add(0, ac); setTextCharacterSpacing.invoke(processor, null, tcOperands); List tickOperands = new ArrayList(1); tickOperands.add(0, string); moveNextLineAndShowText.invoke(processor, null, tickOperands); } } /** * A handler that implements operator ('). For more information see Table 51 ISO-32000-1 */ private static class MoveNextLineAndShowTextOperator implements IContentOperator { private final TextMoveNextLineOperator textMoveNextLine; private final ShowTextOperator showText; /** * Creates the new instance of this handler * * @param textMoveNextLine the handler for T* operator * @param showText the handler for Tj operator */ public MoveNextLineAndShowTextOperator(TextMoveNextLineOperator textMoveNextLine, ShowTextOperator showText) { this.textMoveNextLine = textMoveNextLine; this.showText = showText; } /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { textMoveNextLine.invoke(processor, null, new ArrayList(0)); showText.invoke(processor, null, operands); } } /** * A handler that implements operator (Tj). For more information see Table 51 ISO-32000-1 */ private static class ShowTextOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfString string = (PdfString) operands.get(0); processor.displayPdfString(string); } } /** * A handler that implements operator (T*). For more information see Table 51 ISO-32000-1 */ private static class TextMoveNextLineOperator implements IContentOperator { private final TextMoveStartNextLineOperator moveStartNextLine; public TextMoveNextLineOperator(TextMoveStartNextLineOperator moveStartNextLine) { this.moveStartNextLine = moveStartNextLine; } /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { List tdoperands = new ArrayList(2); tdoperands.add(0, new PdfNumber(0)); tdoperands.add(1, new PdfNumber(-processor.getGraphicsState().getLeading())); moveStartNextLine.invoke(processor, null, tdoperands); } } /** * A handler that implements operator (Tm). For more information see Table 51 ISO-32000-1 */ private static class TextSetTextMatrixOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { float a = ((PdfNumber) operands.get(0)).floatValue(); float b = ((PdfNumber) operands.get(1)).floatValue(); float c = ((PdfNumber) operands.get(2)).floatValue(); float d = ((PdfNumber) operands.get(3)).floatValue(); float e = ((PdfNumber) operands.get(4)).floatValue(); float f = ((PdfNumber) operands.get(5)).floatValue(); processor.textLineMatrix = new Matrix(a, b, c, d, e, f); processor.textMatrix = processor.textLineMatrix; } } /** * A handler that implements operator (TD). For more information see Table 51 ISO-32000-1 */ private static class TextMoveStartNextLineWithLeadingOperator implements IContentOperator { private final TextMoveStartNextLineOperator moveStartNextLine; private final SetTextLeadingOperator setTextLeading; public TextMoveStartNextLineWithLeadingOperator(TextMoveStartNextLineOperator moveStartNextLine, SetTextLeadingOperator setTextLeading) { this.moveStartNextLine = moveStartNextLine; this.setTextLeading = setTextLeading; } /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { float ty = ((PdfNumber) operands.get(1)).floatValue(); List tlOperands = new ArrayList(1); tlOperands.add(0, new PdfNumber(-ty)); setTextLeading.invoke(processor, null, tlOperands); moveStartNextLine.invoke(processor, null, operands); } } /** * A handler that implements operator (Td). For more information see Table 51 ISO-32000-1 */ private static class TextMoveStartNextLineOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { float tx = ((PdfNumber) operands.get(0)).floatValue(); float ty = ((PdfNumber) operands.get(1)).floatValue(); Matrix translationMatrix = new Matrix(tx, ty); processor.textMatrix = translationMatrix.multiply(processor.textLineMatrix); processor.textLineMatrix = processor.textMatrix; } } /** * A handler that implements operator (Tf). For more information see Table 51 ISO-32000-1 */ private static class SetTextFontOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfName fontResourceName = (PdfName) operands.get(0); float size = ((PdfNumber) operands.get(1)).floatValue(); PdfDictionary fontsDictionary = processor.getResources().getResource(PdfName.Font); PdfDictionary fontDict = fontsDictionary.getAsDictionary(fontResourceName); PdfFont font = null; font = processor.getFont(fontDict); processor.getGraphicsState().setFont(font); processor.getGraphicsState().setFontSize(size); } } /** * A handler that implements operator (Tr). For more information see Table 51 ISO-32000-1 */ private static class SetTextRenderModeOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfNumber render = (PdfNumber) operands.get(0); processor.getGraphicsState().setTextRenderingMode(render.intValue()); } } /** * A handler that implements operator (Ts). For more information see Table 51 ISO-32000-1 */ private static class SetTextRiseOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfNumber rise = (PdfNumber) operands.get(0); processor.getGraphicsState().setTextRise(rise.floatValue()); } } /** * A handler that implements operator (TL). For more information see Table 51 ISO-32000-1 */ private static class SetTextLeadingOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfNumber leading = (PdfNumber) operands.get(0); processor.getGraphicsState().setLeading(leading.floatValue()); } } /** * A handler that implements operator (Tz). For more information see Table 51 ISO-32000-1 */ private static class SetTextHorizontalScalingOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfNumber scale = (PdfNumber) operands.get(0); processor.getGraphicsState().setHorizontalScaling(scale.floatValue()); } } /** * A handler that implements operator (Tc). For more information see Table 51 ISO-32000-1 */ private static class SetTextCharacterSpacingOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfNumber charSpace = (PdfNumber) operands.get(0); processor.getGraphicsState().setCharSpacing(charSpace.floatValue()); } } /** * A handler that implements operator (Tw). For more information see Table 51 ISO-32000-1 */ private static class SetTextWordSpacingOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfNumber wordSpace = (PdfNumber) operands.get(0); processor.getGraphicsState().setWordSpacing(wordSpace.floatValue()); } } /** * A handler that implements operator (gs). For more information see Table 51 ISO-32000-1 */ private static class ProcessGraphicsStateResourceOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfName dictionaryName = (PdfName) operands.get(0); PdfDictionary extGState = processor.getResources().getResource(PdfName.ExtGState); if (extGState == null) throw new PdfException( KernelExceptionMessageConstant.RESOURCES_DO_NOT_CONTAIN_EXTGSTATE_ENTRY_UNABLE_TO_PROCESS_THIS_OPERATOR ).setMessageParams(operator); PdfDictionary gsDic = extGState.getAsDictionary(dictionaryName); if (gsDic == null) { gsDic = extGState.getAsStream(dictionaryName); if (gsDic == null) throw new PdfException(KernelExceptionMessageConstant.UNKNOWN_GRAPHICS_STATE_DICTIONARY) .setMessageParams(dictionaryName); } PdfArray fontParameter = gsDic.getAsArray(PdfName.Font); if (fontParameter != null) { PdfFont font = processor.getFont(fontParameter.getAsDictionary(0)); float size = fontParameter.getAsNumber(1).floatValue(); processor.getGraphicsState().setFont(font); processor.getGraphicsState().setFontSize(size); } PdfExtGState pdfExtGState = new PdfExtGState(gsDic.clone(Collections.singletonList(PdfName.Font))); processor.getGraphicsState().updateFromExtGState(pdfExtGState); } } /** * A handler that implements operator (q). For more information see Table 51 ISO-32000-1 */ private static class PushGraphicsStateOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { ParserGraphicsState gs = processor.gsStack.peek(); ParserGraphicsState copy = new ParserGraphicsState(gs); processor.gsStack.push(copy); } } /** * A handler that implements operator (cm). For more information see Table 51 ISO-32000-1 */ private static class ModifyCurrentTransformationMatrixOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { float a = ((PdfNumber) operands.get(0)).floatValue(); float b = ((PdfNumber) operands.get(1)).floatValue(); float c = ((PdfNumber) operands.get(2)).floatValue(); float d = ((PdfNumber) operands.get(3)).floatValue(); float e = ((PdfNumber) operands.get(4)).floatValue(); float f = ((PdfNumber) operands.get(5)).floatValue(); Matrix matrix = new Matrix(a, b, c, d, e, f); try { processor.getGraphicsState().updateCtm(matrix); } catch (PdfException exception) { if (!(exception.getCause() instanceof NoninvertibleTransformException)) { throw exception; } else { Logger logger = LoggerFactory.getLogger(PdfCanvasProcessor.class); logger.error(MessageFormatUtil.format(IoLogMessageConstant.FAILED_TO_PROCESS_A_TRANSFORMATION_MATRIX)); } } } } /** * Gets a color based on a list of operands and Color space. */ private static Color getColor(PdfColorSpace pdfColorSpace, List operands, PdfResources resources) { PdfObject pdfObject; if (pdfColorSpace.getPdfObject().isIndirectReference()) { pdfObject = ((PdfIndirectReference) pdfColorSpace.getPdfObject()).getRefersTo(); } else { pdfObject = pdfColorSpace.getPdfObject(); } if (pdfObject.isName()) { if (PdfName.DeviceGray.equals(pdfObject)) { return new DeviceGray(getColorants(operands)[0]); } else if (PdfName.Pattern.equals(pdfObject)) { if (operands.get(0) instanceof PdfName) { PdfPattern pattern = resources.getPattern((PdfName) operands.get(0)); if (pattern != null) { return new PatternColor(pattern); } } } if (PdfName.DeviceRGB.equals(pdfObject)) { float[] c = getColorants(operands); return new DeviceRgb(c[0], c[1], c[2]); } else if (PdfName.DeviceCMYK.equals(pdfObject)) { float[] c = getColorants(operands); return new DeviceCmyk(c[0], c[1], c[2], c[3]); } } else if (pdfObject.isArray()) { PdfArray array = (PdfArray) pdfObject; PdfName csType = array.getAsName(0); if (PdfName.CalGray.equals(csType)) return new CalGray((PdfCieBasedCs.CalGray) pdfColorSpace, getColorants(operands)[0]); else if (PdfName.CalRGB.equals(csType)) return new CalRgb((PdfCieBasedCs.CalRgb) pdfColorSpace, getColorants(operands)); else if (PdfName.Lab.equals(csType)) return new Lab((PdfCieBasedCs.Lab) pdfColorSpace, getColorants(operands)); else if (PdfName.ICCBased.equals(csType)) return new IccBased((PdfCieBasedCs.IccBased) pdfColorSpace, getColorants(operands)); else if (PdfName.Indexed.equals(csType)) return new Indexed(pdfColorSpace, (int) getColorants(operands)[0]); else if (PdfName.Separation.equals(csType)) return new Separation((PdfSpecialCs.Separation) pdfColorSpace, getColorants(operands)[0]); else if (PdfName.DeviceN.equals(csType)) return new DeviceN((PdfSpecialCs.DeviceN) pdfColorSpace, getColorants(operands)); else if (PdfName.Pattern.equals(csType)) { List underlyingOperands = new ArrayList<>(operands); PdfObject patternName = underlyingOperands.remove(operands.size() - 2); PdfColorSpace underlyingCs = ((PdfSpecialCs.UncoloredTilingPattern) pdfColorSpace).getUnderlyingColorSpace(); if (patternName instanceof PdfName) { PdfPattern pattern = resources.getPattern((PdfName) patternName); if (pattern instanceof PdfPattern.Tiling && !((PdfPattern.Tiling) pattern).isColored()) { return new PatternColor((PdfPattern.Tiling) pattern, underlyingCs, getColorants(underlyingOperands)); } } } } Logger logger = LoggerFactory.getLogger(PdfCanvasProcessor.class); logger.warn(MessageFormatUtil.format(KernelLogMessageConstant.UNABLE_TO_PARSE_COLOR_WITHIN_COLORSPACE, Arrays.toString((Object[])operands.toArray()), pdfColorSpace.getPdfObject())); return null; } /** * Gets a color based on a list of operands. */ private static Color getColor(int nOperands, List operands) { float[] c = new float[nOperands]; for (int i = 0; i < nOperands; i++) { c[i] = ((PdfNumber) operands.get(i)).floatValue(); } switch (nOperands) { case 1: return new DeviceGray(c[0]); case 3: return new DeviceRgb(c[0], c[1], c[2]); case 4: return new DeviceCmyk(c[0], c[1], c[2], c[3]); } return null; } private static float[] getColorants(List operands) { float[] c = new float[operands.size() - 1]; for (int i = 0; i < operands.size() - 1; i++) { c[i] = ((PdfNumber) operands.get(i)).floatValue(); } return c; } /** * A handler that implements operator (Q). For more information see Table 51 ISO-32000-1 */ protected static class PopGraphicsStateOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.gsStack.pop(); ParserGraphicsState gs = processor.getGraphicsState(); processor.eventOccurred(new ClippingPathInfo(gs, gs.getClippingPath(), gs.getCtm()), EventType.CLIP_PATH_CHANGED); } } /** * A handler that implements operator (g). For more information see Table 51 ISO-32000-1 */ private static class SetGrayFillOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.getGraphicsState().setFillColor(getColor(1, operands)); } } /** * A handler that implements operator (G). For more information see Table 51 ISO-32000-1 */ private static class SetGrayStrokeOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.getGraphicsState().setStrokeColor(getColor(1, operands)); } } /** * A handler that implements operator (rg). For more information see Table 51 ISO-32000-1 */ private static class SetRGBFillOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.getGraphicsState().setFillColor(getColor(3, operands)); } } /** * A handler that implements operator (RG). For more information see Table 51 ISO-32000-1 */ private static class SetRGBStrokeOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.getGraphicsState().setStrokeColor(getColor(3, operands)); } } /** * A handler that implements operator (k). For more information see Table 51 ISO-32000-1 */ private static class SetCMYKFillOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.getGraphicsState().setFillColor(getColor(4, operands)); } } /** * A handler that implements operator (K). For more information see Table 51 ISO-32000-1 */ private static class SetCMYKStrokeOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.getGraphicsState().setStrokeColor(getColor(4, operands)); } } /** * A handler that implements operator (CS). For more information see Table 51 ISO-32000-1 */ private static class SetColorSpaceFillOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfColorSpace pdfColorSpace = determineColorSpace((PdfName) operands.get(0), processor); processor.getGraphicsState().setFillColor(Color.makeColor(pdfColorSpace)); } static PdfColorSpace determineColorSpace(PdfName colorSpace, PdfCanvasProcessor processor) { PdfColorSpace pdfColorSpace; if (PdfColorSpace.DIRECT_COLOR_SPACES.contains(colorSpace)) { pdfColorSpace = PdfColorSpace.makeColorSpace(colorSpace); } else { PdfResources pdfResources = processor.getResources(); PdfDictionary resourceColorSpace = pdfResources.getPdfObject().getAsDictionary(PdfName.ColorSpace); pdfColorSpace = PdfColorSpace.makeColorSpace(resourceColorSpace.get(colorSpace)); } return pdfColorSpace; } } /** * A handler that implements operator (cs). For more information see Table 51 ISO-32000-1 */ private static class SetColorSpaceStrokeOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfColorSpace pdfColorSpace = SetColorSpaceFillOperator.determineColorSpace((PdfName) operands.get(0), processor); processor.getGraphicsState().setStrokeColor(Color.makeColor(pdfColorSpace)); } } /** * A handler that implements operator (sc / scn). For more information see Table 51 ISO-32000-1 */ private static class SetColorFillOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.getGraphicsState().setFillColor(getColor(processor.getGraphicsState().getFillColor().getColorSpace(), operands, processor.getResources())); } } /** * A handler that implements operator (SC / SCN). For more information see Table 51 ISO-32000-1 */ private static class SetColorStrokeOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.getGraphicsState().setStrokeColor(getColor(processor.getGraphicsState().getStrokeColor().getColorSpace(), operands, processor.getResources())); } } /** * A handler that implements operator (BT). For more information see Table 51 ISO-32000-1 */ private static class BeginTextOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.textMatrix = new Matrix(); processor.textLineMatrix = processor.textMatrix; processor.beginText(); } } /** * A handler that implements operator (ET). For more information see Table 51 ISO-32000-1 */ private static class EndTextOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.textMatrix = null; processor.textLineMatrix = null; processor.endText(); } } /** * A handler that implements operator (BMC). For more information see Table 51 ISO-32000-1 */ private static class BeginMarkedContentOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.beginMarkedContent((PdfName) operands.get(0), null); } } /** * A handler that implements operator (BDC). For more information see Table 51 ISO-32000-1 */ private static class BeginMarkedContentDictionaryOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfObject properties = operands.get(1); processor.beginMarkedContent((PdfName) operands.get(0), getPropertiesDictionary(properties, processor.getResources())); } PdfDictionary getPropertiesDictionary(PdfObject operand1, PdfResources resources) { if (operand1.isDictionary()) return (PdfDictionary) operand1; PdfName dictionaryName = ((PdfName) operand1); PdfDictionary properties = resources.getResource(PdfName.Properties); if (null == properties) { Logger logger = LoggerFactory.getLogger(PdfCanvasProcessor.class); logger.warn( MessageFormatUtil.format(IoLogMessageConstant.PDF_REFERS_TO_NOT_EXISTING_PROPERTY_DICTIONARY, PdfName.Properties)); return null; } PdfDictionary propertiesDictionary = properties.getAsDictionary(dictionaryName); if (null == propertiesDictionary) { Logger logger = LoggerFactory.getLogger(PdfCanvasProcessor.class); logger.warn( MessageFormatUtil.format(IoLogMessageConstant.PDF_REFERS_TO_NOT_EXISTING_PROPERTY_DICTIONARY, dictionaryName)); return null; } return properties.getAsDictionary(dictionaryName); } } /** * A handler that implements operator (EMC). For more information see Table 51 ISO-32000-1 */ private static class EndMarkedContentOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.endMarkedContent(); } } /** * A handler that implements operator (Do). For more information see Table 51 ISO-32000-1 */ private static class DoOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfName resourceName = (PdfName) operands.get(0); processor.displayXObject(resourceName); } } /** * A handler that implements operator (EI). For more information see Table 51 ISO-32000-1 * BI and ID operators are parsed along with this operator. * This not a usual operator, it will have a single operand, which will be a PdfStream object which * encapsulates inline image dictionary and bytes */ private static class EndImageOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { PdfStream imageStream = (PdfStream) operands.get(0); processor.displayImage(processor.markedContentStack, imageStream, null, true); } } /** * A handler that implements operator (w). For more information see Table 51 ISO-32000-1 */ private static class SetLineWidthOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral oper, List operands) { float lineWidth = ((PdfNumber) operands.get(0)).floatValue(); processor.getGraphicsState().setLineWidth(lineWidth); } } /** * A handler that implements operator (J). For more information see Table 51 ISO-32000-1 */ private static class SetLineCapOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral oper, List operands) { int lineCap = ((PdfNumber) operands.get(0)).intValue(); processor.getGraphicsState().setLineCapStyle(lineCap); } } /** * A handler that implements operator (j). For more information see Table 51 ISO-32000-1 */ private static class SetLineJoinOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral oper, List operands) { int lineJoin = ((PdfNumber) operands.get(0)).intValue(); processor.getGraphicsState().setLineJoinStyle(lineJoin); } } /** * A handler that implements operator (M). For more information see Table 51 ISO-32000-1 */ private static class SetMiterLimitOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral oper, List operands) { float miterLimit = ((PdfNumber) operands.get(0)).floatValue(); processor.getGraphicsState().setMiterLimit(miterLimit); } } /** * A handler that implements operator (d). For more information see Table 51 ISO-32000-1 */ private static class SetLineDashPatternOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral oper, List operands) { processor.getGraphicsState().setDashPattern(new PdfArray(Arrays.asList(operands.get(0), operands.get(1)))); } } /** * An XObject subtype handler for FORM */ private static class FormXObjectDoHandler implements IXObjectDoHandler { public void handleXObject(PdfCanvasProcessor processor, Stack canvasTagHierarchy, PdfStream xObjectStream, PdfName xObjectName) { PdfDictionary resourcesDic = xObjectStream.getAsDictionary(PdfName.Resources); PdfResources resources; if (resourcesDic == null) { resources = processor.getResources(); } else { resources = new PdfResources(resourcesDic); } // we read the content bytes up here so if it fails we don't leave the graphics state stack corrupted // this is probably not necessary (if we fail on this, probably the entire content stream processing // operation should be rejected byte[] contentBytes; contentBytes = xObjectStream.getBytes(); final PdfArray matrix = xObjectStream.getAsArray(PdfName.Matrix); new PushGraphicsStateOperator().invoke(processor, null, null); if (matrix != null) { float a = matrix.getAsNumber(0).floatValue(); float b = matrix.getAsNumber(1).floatValue(); float c = matrix.getAsNumber(2).floatValue(); float d = matrix.getAsNumber(3).floatValue(); float e = matrix.getAsNumber(4).floatValue(); float f = matrix.getAsNumber(5).floatValue(); Matrix formMatrix = new Matrix(a, b, c, d, e, f); processor.getGraphicsState().updateCtm(formMatrix); } processor.processContent(contentBytes, resources); new PopGraphicsStateOperator().invoke(processor, null, null); } } /** * An XObject subtype handler for IMAGE */ private static class ImageXObjectDoHandler implements IXObjectDoHandler { public void handleXObject(PdfCanvasProcessor processor, Stack canvasTagHierarchy, PdfStream xObjectStream, PdfName resourceName) { processor.displayImage(canvasTagHierarchy, xObjectStream, resourceName,false); } } /** * An XObject subtype handler that does nothing */ private static class IgnoreXObjectDoHandler implements IXObjectDoHandler { public void handleXObject(PdfCanvasProcessor processor, Stack canvasTagHierarchy, PdfStream xObjectStream, PdfName xObjectName) { // ignore XObject subtype } } /** * A handler that implements operator (m). For more information see Table 51 ISO-32000-1 */ private static class MoveToOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { float x = ((PdfNumber) operands.get(0)).floatValue(); float y = ((PdfNumber) operands.get(1)).floatValue(); processor.currentPath.moveTo(x, y); } } /** * A handler that implements operator (l). For more information see Table 51 ISO-32000-1 */ private static class LineToOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { float x = ((PdfNumber) operands.get(0)).floatValue(); float y = ((PdfNumber) operands.get(1)).floatValue(); processor.currentPath.lineTo(x, y); } } /** * A handler that implements operator (c). For more information see Table 51 ISO-32000-1 */ private static class CurveOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { float x1 = ((PdfNumber) operands.get(0)).floatValue(); float y1 = ((PdfNumber) operands.get(1)).floatValue(); float x2 = ((PdfNumber) operands.get(2)).floatValue(); float y2 = ((PdfNumber) operands.get(3)).floatValue(); float x3 = ((PdfNumber) operands.get(4)).floatValue(); float y3 = ((PdfNumber) operands.get(5)).floatValue(); processor.currentPath.curveTo(x1, y1, x2, y2, x3, y3); } } /** * A handler that implements operator (v). For more information see Table 51 ISO-32000-1 */ private static class CurveFirstPointDuplicatedOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { float x2 = ((PdfNumber) operands.get(0)).floatValue(); float y2 = ((PdfNumber) operands.get(1)).floatValue(); float x3 = ((PdfNumber) operands.get(2)).floatValue(); float y3 = ((PdfNumber) operands.get(3)).floatValue(); processor.currentPath.curveTo(x2, y2, x3, y3); } } /** * A handler that implements operator (y). For more information see Table 51 ISO-32000-1 */ private static class CurveFourhPointDuplicatedOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { float x1 = ((PdfNumber) operands.get(0)).floatValue(); float y1 = ((PdfNumber) operands.get(1)).floatValue(); float x3 = ((PdfNumber) operands.get(2)).floatValue(); float y3 = ((PdfNumber) operands.get(3)).floatValue(); processor.currentPath.curveFromTo(x1, y1, x3, y3); } } /** * A handler that implements operator (h). For more information see Table 51 ISO-32000-1 */ private static class CloseSubpathOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.currentPath.closeSubpath(); } } /** * A handler that implements operator (re). For more information see Table 51 ISO-32000-1 */ private static class RectangleOperator implements IContentOperator { /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { float x = ((PdfNumber) operands.get(0)).floatValue(); float y = ((PdfNumber) operands.get(1)).floatValue(); float w = ((PdfNumber) operands.get(2)).floatValue(); float h = ((PdfNumber) operands.get(3)).floatValue(); processor.currentPath.rectangle(x, y, w, h); } } /** * A handler that implements operator (S, s, f, F, f*, B, B*, b, b*). For more information see Table 51 ISO-32000-1 */ private static class PaintPathOperator implements IContentOperator { private int operation; private int rule; private boolean close; /** * Constructs PainPath object. * * @param operation One of the possible combinations of {@link PathRenderInfo#STROKE} * and {@link PathRenderInfo#FILL} values or * {@link PathRenderInfo#NO_OP} * @param rule Either {@link FillingRule#NONZERO_WINDING} or {@link FillingRule#EVEN_ODD} * In case it isn't applicable pass any value. * @param close Indicates whether the path should be closed or not. */ public PaintPathOperator(int operation, int rule, boolean close) { this.operation = operation; this.rule = rule; this.close = close; } /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { if (close) { processor.currentPath.closeSubpath(); } processor.paintPath(operation, rule); } } /** * A handler that implements operator (W, W*). For more information see Table 51 ISO-32000-1 */ private static class ClipPathOperator implements IContentOperator { private int rule; public ClipPathOperator(int rule) { this.rule = rule; } /** * {@inheritDoc} */ public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List operands) { processor.isClip = true; processor.clippingRule = rule; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy