Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 3D387200AC9 for ; Sun, 22 May 2016 17:04:49 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 3BAA7160A28; Sun, 22 May 2016 15:04:49 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 6E62B1609AF for ; Sun, 22 May 2016 17:04:47 +0200 (CEST) Received: (qmail 47702 invoked by uid 500); 22 May 2016 15:04:46 -0000 Mailing-List: contact commits-help@pdfbox.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@pdfbox.apache.org Delivered-To: mailing list commits@pdfbox.apache.org Received: (qmail 47693 invoked by uid 99); 22 May 2016 15:04:46 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 22 May 2016 15:04:46 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id F3884C234E for ; Sun, 22 May 2016 15:04:45 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -0.426 X-Spam-Level: X-Spam-Status: No, score=-0.426 tagged_above=-999 required=6.31 tests=[KAM_LAZY_DOMAIN_SECURITY=1, RP_MATCHES_RCVD=-1.426] autolearn=disabled Received: from mx2-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id bIqNAgQ7WVZC for ; Sun, 22 May 2016 15:04:42 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx2-lw-eu.apache.org (ASF Mail Server at mx2-lw-eu.apache.org) with ESMTP id 85A205F263 for ; Sun, 22 May 2016 15:04:41 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id 2984FE071D for ; Sun, 22 May 2016 15:04:40 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 4A09B3A0E9F for ; Sun, 22 May 2016 15:04:39 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1745066 [3/3] - in /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox: contentstream/PDAbstractContentStream.java pdmodel/PDPageContentStream.java pdmodel/interactive/annotation/PDAppearanceContentStream.java Date: Sun, 22 May 2016 15:04:38 -0000 To: commits@pdfbox.apache.org From: msahyoun@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20160522150439.4A09B3A0E9F@svn01-us-west.apache.org> archived-at: Sun, 22 May 2016 15:04:49 -0000 Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceContentStream.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceContentStream.java?rev=1745066&r1=1745065&r2=1745066&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceContentStream.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/PDAppearanceContentStream.java Sun May 22 15:04:38 2016 @@ -16,45 +16,21 @@ */ package org.apache.pdfbox.pdmodel.interactive.annotation; -import java.awt.Color; -import java.awt.geom.AffineTransform; import java.io.Closeable; import java.io.IOException; import java.io.OutputStream; import java.text.NumberFormat; import java.util.Locale; -import java.util.Stack; -import org.apache.pdfbox.cos.COSBase; -import org.apache.pdfbox.cos.COSName; -import org.apache.pdfbox.cos.COSNumber; -import org.apache.pdfbox.pdfwriter.COSWriter; -import org.apache.pdfbox.pdmodel.PDResources; -import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDPropertyList; -import org.apache.pdfbox.pdmodel.font.PDFont; -import org.apache.pdfbox.pdmodel.graphics.color.PDColor; -import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; -import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceCMYK; -import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray; -import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceN; -import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB; -import org.apache.pdfbox.pdmodel.graphics.color.PDICCBased; -import org.apache.pdfbox.pdmodel.graphics.color.PDPattern; -import org.apache.pdfbox.pdmodel.graphics.color.PDSeparation; -import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; -import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; -import org.apache.pdfbox.pdmodel.graphics.image.PDInlineImage; -import org.apache.pdfbox.pdmodel.graphics.shading.PDShading; -import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; + +import org.apache.pdfbox.contentstream.PDAbstractContentStream; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream; -import org.apache.pdfbox.util.Charsets; -import org.apache.pdfbox.util.Matrix; /** * Provides the ability to write to a page content stream. * * @author Ben Litchfield */ -public final class PDAppearanceContentStream implements Closeable +public final class PDAppearanceContentStream extends PDAbstractContentStream implements Closeable { /** @@ -86,14 +62,6 @@ public final class PDAppearanceContentSt } } - private OutputStream output; - private PDResources resources; - - private boolean inTextMode = false; - private final Stack fontStack = new Stack(); - - private final Stack nonStrokingColorSpaceStack = new Stack(); - private final Stack strokingColorSpaceStack = new Stack(); // number format private final NumberFormat formatDecimal = NumberFormat.getNumberInstance(Locale.US); @@ -121,412 +89,13 @@ public final class PDAppearanceContentSt public PDAppearanceContentStream(PDAppearanceStream appearance, OutputStream outputStream) throws IOException { - output = outputStream; - this.resources = appearance.getResources(); + super(appearance, outputStream); + setResources(appearance.getResources()); formatDecimal.setMaximumFractionDigits(4); formatDecimal.setGroupingUsed(false); } - /** - * Begin some text operations. - * - * @throws IOException If there is an error writing to the stream or if you attempt to - * nest beginText calls. - * @throws IllegalStateException If the method was not allowed to be called at this time. - */ - public void beginText() throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: Nested beginText() calls are not allowed."); - } - writeOperator("BT"); - inTextMode = true; - } - - /** - * End some text operations. - * - * @throws IOException If there is an error writing to the stream or if you attempt to - * nest endText calls. - * @throws IllegalStateException If the method was not allowed to be called at this time. - */ - public void endText() throws IOException - { - if (!inTextMode) - { - throw new IllegalStateException("Error: You must call beginText() before calling endText."); - } - writeOperator("ET"); - inTextMode = false; - } - - /** - * Set the font and font size to draw text with. - * - * @param font The font to use. - * @param fontSize The font size to draw the text. - * @throws IOException If there is an error writing the font information. - */ - public void setFont(PDFont font, float fontSize) throws IOException - { - if (fontStack.isEmpty()) - { - fontStack.add(font); - } - else - { - fontStack.setElementAt(font, fontStack.size() - 1); - } - - writeOperand(resources.add(font)); - writeOperand(fontSize); - writeOperator("Tf"); - } - - /** - * Shows the given text at the location specified by the current text matrix. - * - * @param text The Unicode text to show. - * @throws IOException If an io exception occurs. - */ - public void showText(String text) throws IOException - { - if (!inTextMode) - { - throw new IllegalStateException("Must call beginText() before showText()"); - } - - if (fontStack.isEmpty()) - { - throw new IllegalStateException("Must call setFont() before showText()"); - } - - PDFont font = fontStack.peek(); - - // Unicode code points to keep when subsetting - if (font.willBeSubset()) - { - for (int offset = 0; offset < text.length(); ) - { - int codePoint = text.codePointAt(offset); - font.addToSubset(codePoint); - offset += Character.charCount(codePoint); - } - } - - COSWriter.writeString(font.encode(text), output); - write(" "); - - writeOperator("Tj"); - } - - /** - * Sets the text leading. - * - * @param leading The leading in unscaled text units. - * @throws IOException If there is an error writing to the stream. - */ - public void setLeading(double leading) throws IOException - { - writeOperand((float) leading); - writeOperator("TL"); - } - - /** - * Move to the start of the next line of text. Requires the leading (see {@link #setLeading}) - * to have been set. - * - * @throws IOException If there is an error writing to the stream. - */ - public void newLine() throws IOException - { - if (!inTextMode) - { - throw new IllegalStateException("Must call beginText() before newLine()"); - } - writeOperator("T*"); - } - - /** - * The Td operator. - * Move to the start of the next line, offset from the start of the current line by (tx, ty). - * - * @param tx The x translation. - * @param ty The y translation. - * @throws IOException If there is an error writing to the stream. - * @throws IllegalStateException If the method was not allowed to be called at this time. - */ - public void newLineAtOffset(float tx, float ty) throws IOException - { - if (!inTextMode) - { - throw new IllegalStateException("Error: must call beginText() before newLineAtOffset()"); - } - writeOperand(tx); - writeOperand(ty); - writeOperator("Td"); - } - - /** - * The Tm operator. Sets the text matrix to the given values. - * A current text matrix will be replaced with the new one. - * - * @param matrix the transformation matrix - * @throws IOException If there is an error writing to the stream. - * @throws IllegalStateException If the method was not allowed to be called at this time. - */ - public void setTextMatrix(Matrix matrix) throws IOException - { - if (!inTextMode) - { - throw new IllegalStateException("Error: must call beginText() before setTextMatrix"); - } - writeAffineTransform(matrix.createAffineTransform()); - writeOperator("Tm"); - } - - /** - * Draw an image at the x,y coordinates, with the default size of the image. - * - * @param image The image to draw. - * @param x The x-coordinate to draw the image. - * @param y The y-coordinate to draw the image. - * - * @throws IOException If there is an error writing to the stream. - */ - public void drawImage(PDImageXObject image, float x, float y) throws IOException - { - drawImage(image, x, y, image.getWidth(), image.getHeight()); - } - - /** - * Draw an image at the x,y coordinates, with the given size. - * - * @param image The image to draw. - * @param x The x-coordinate to draw the image. - * @param y The y-coordinate to draw the image. - * @param width The width to draw the image. - * @param height The height to draw the image. - * - * @throws IOException If there is an error writing to the stream. - * @throws IllegalStateException If the method was called within a text block. - */ - public void drawImage(PDImageXObject image, float x, float y, float width, float height) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: drawImage is not allowed within a text block."); - } - - saveGraphicsState(); - - AffineTransform transform = new AffineTransform(width, 0, 0, height, x, y); - transform(new Matrix(transform)); - - writeOperand(resources.add(image)); - writeOperator("Do"); - - restoreGraphicsState(); - } - - /** - * Draw an image at the origin with the given transformation matrix. - * - * @param image The image to draw. - * @param matrix The transformation matrix to apply to the image. - * - * @throws IOException If there is an error writing to the stream. - * @throws IllegalStateException If the method was called within a text block. - */ - public void drawImage(PDImageXObject image, Matrix matrix) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: drawImage is not allowed within a text block."); - } - - saveGraphicsState(); - - AffineTransform transform = matrix.createAffineTransform(); - transform(new Matrix(transform)); - - writeOperand(resources.add(image)); - writeOperator("Do"); - - restoreGraphicsState(); - } - - /** - * Draw an inline image at the x,y coordinates, with the default size of the image. - * - * @param inlineImage The inline image to draw. - * @param x The x-coordinate to draw the inline image. - * @param y The y-coordinate to draw the inline image. - * - * @throws IOException If there is an error writing to the stream. - */ - public void drawImage(PDInlineImage inlineImage, float x, float y) throws IOException - { - drawImage(inlineImage, x, y, inlineImage.getWidth(), inlineImage.getHeight()); - } - - /** - * Draw an inline image at the x,y coordinates and a certain width and height. - * - * @param inlineImage The inline image to draw. - * @param x The x-coordinate to draw the inline image. - * @param y The y-coordinate to draw the inline image. - * @param width The width of the inline image to draw. - * @param height The height of the inline image to draw. - * - * @throws IOException If there is an error writing to the stream. - * @throws IllegalStateException If the method was called within a text block. - */ - public void drawImage(PDInlineImage inlineImage, float x, float y, float width, float height) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: drawImage is not allowed within a text block."); - } - - saveGraphicsState(); - transform(new Matrix(width, 0, 0, height, x, y)); - - // create the image dictionary - StringBuilder sb = new StringBuilder(); - sb.append("BI"); - - sb.append("\n /W "); - sb.append(inlineImage.getWidth()); - - sb.append("\n /H "); - sb.append(inlineImage.getHeight()); - - sb.append("\n /CS "); - sb.append("/"); - sb.append(inlineImage.getColorSpace().getName()); - - if (inlineImage.getDecode() != null && inlineImage.getDecode().size() > 0) - { - sb.append("\n /D "); - sb.append("["); - for (COSBase base : inlineImage.getDecode()) - { - sb.append(((COSNumber) base).intValue()); - sb.append(" "); - } - sb.append("]"); - } - - if (inlineImage.isStencil()) - { - sb.append("\n /IM true"); - } - - sb.append("\n /BPC "); - sb.append(inlineImage.getBitsPerComponent()); - - // image dictionary - write(sb.toString()); - writeLine(); - - // binary data - writeOperator("ID"); - writeBytes(inlineImage.getData()); - writeLine(); - writeOperator("EI"); - - restoreGraphicsState(); - } - - /** - * Draws the given Form XObject at the current location. - * - * @param form Form XObject - * @throws IOException if the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void drawForm(PDFormXObject form) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: drawForm is not allowed within a text block."); - } - - writeOperand(resources.add(form)); - writeOperator("Do"); - } - - /** - * The cm operator. Concatenates the given matrix with the CTM. - * - * @param matrix the transformation matrix - * @throws IOException If there is an error writing to the stream. - */ - public void transform(Matrix matrix) throws IOException - { - writeAffineTransform(matrix.createAffineTransform()); - writeOperator("cm"); - } - - /** - * q operator. Saves the current graphics state. - * @throws IOException If an error occurs while writing to the stream. - */ - public void saveGraphicsState() throws IOException - { - if (!fontStack.isEmpty()) - { - fontStack.push(fontStack.peek()); - } - if (!strokingColorSpaceStack.isEmpty()) - { - strokingColorSpaceStack.push(strokingColorSpaceStack.peek()); - } - if (!nonStrokingColorSpaceStack.isEmpty()) - { - nonStrokingColorSpaceStack.push(nonStrokingColorSpaceStack.peek()); - } - writeOperator("q"); - } - - /** - * Q operator. Restores the current graphics state. - * @throws IOException If an error occurs while writing to the stream. - */ - public void restoreGraphicsState() throws IOException - { - if (!fontStack.isEmpty()) - { - fontStack.pop(); - } - if (!strokingColorSpaceStack.isEmpty()) - { - strokingColorSpaceStack.pop(); - } - if (!nonStrokingColorSpaceStack.isEmpty()) - { - nonStrokingColorSpaceStack.pop(); - } - writeOperator("Q"); - } - - private COSName getName(PDColorSpace colorSpace) throws IOException - { - if (colorSpace instanceof PDDeviceGray || - colorSpace instanceof PDDeviceRGB || - colorSpace instanceof PDDeviceCMYK) - { - return COSName.getPDFName(colorSpace.getName()); - } - else - { - return resources.add(colorSpace); - } - } - public void setStrokingColor(float[] components) throws IOException { for (float value : components) @@ -547,888 +116,4 @@ public final class PDAppearanceContentSt writeOperator("K"); } } - - /** - * Sets the stroking color and, if necessary, the stroking color space. - * - * @param color Color in a specific color space. - * @throws IOException If an IO error occurs while writing to the stream. - */ - public void setStrokingColor(PDColor color) throws IOException - { - if (strokingColorSpaceStack.isEmpty() || - strokingColorSpaceStack.peek() != color.getColorSpace()) - { - writeOperand(getName(color.getColorSpace())); - writeOperator("CS"); - setStrokingColorSpaceStack(color.getColorSpace()); - } - - for (float value : color.getComponents()) - { - writeOperand(value); - } - - if (color.getColorSpace() instanceof PDPattern) - { - writeOperand(color.getPatternName()); - } - - if (color.getColorSpace() instanceof PDPattern || - color.getColorSpace() instanceof PDSeparation || - color.getColorSpace() instanceof PDDeviceN || - color.getColorSpace() instanceof PDICCBased) - { - writeOperator("SCN"); - } - else - { - writeOperator("SC"); - } - } - - /** - * Set the stroking color using an AWT color. Conversion uses the default sRGB color space. - * - * @param color The color to set. - * @throws IOException If an IO error occurs while writing to the stream. - */ - public void setStrokingColor(Color color) throws IOException - { - float[] components = new float[] { - color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f }; - PDColor pdColor = new PDColor(components, PDDeviceRGB.INSTANCE); - setStrokingColor(pdColor); - } - - /** - * Set the stroking color in the DeviceRGB color space. Range is 0..255. - * - * @param r The red value - * @param g The green value. - * @param b The blue value. - * @throws IOException If an IO error occurs while writing to the stream. - * @throws IllegalArgumentException If the parameters are invalid. - */ - public void setStrokingColor(int r, int g, int b) throws IOException - { - if (isOutside255Interval(r) || isOutside255Interval(g) || isOutside255Interval(b)) - { - throw new IllegalArgumentException("Parameters must be within 0..255, but are " - + String.format("(%d,%d,%d)", r, g, b)); - } - writeOperand(r / 255f); - writeOperand(g / 255f); - writeOperand(b / 255f); - writeOperator("RG"); - setStrokingColorSpaceStack(PDDeviceRGB.INSTANCE); - } - - /** - * Set the stroking color in the DeviceCMYK color space. Range is 0..1 - * - * @param c The cyan value. - * @param m The magenta value. - * @param y The yellow value. - * @param k The black value. - * @throws IOException If an IO error occurs while writing to the stream. - * @throws IllegalArgumentException If the parameters are invalid. - */ - public void setStrokingColor(float c, float m, float y, float k) throws IOException - { - if (isOutsideOneInterval(c) || isOutsideOneInterval(m) || isOutsideOneInterval(y) || isOutsideOneInterval(k)) - { - throw new IllegalArgumentException("Parameters must be within 0..1, but are " - + String.format("(%.2f,%.2f,%.2f,%.2f)", c, m, y, k)); - } - writeOperand(c); - writeOperand(m); - writeOperand(y); - writeOperand(k); - writeOperator("K"); - setStrokingColorSpaceStack(PDDeviceCMYK.INSTANCE); - } - - /** - * Set the stroking color in the DeviceGray color space. Range is 0..1. - * - * @param g The gray value. - * @throws IOException If an IO error occurs while writing to the stream. - * @throws IllegalArgumentException If the parameter is invalid. - */ - public void setStrokingColor(double g) throws IOException - { - if (isOutsideOneInterval(g)) - { - throw new IllegalArgumentException("Parameter must be within 0..1, but is " + g); - } - writeOperand((float) g); - writeOperator("G"); - setStrokingColorSpaceStack(PDDeviceGray.INSTANCE); - } - - /** - * Sets the non-stroking color and, if necessary, the non-stroking color space. - * - * @param color Color in a specific color space. - * @throws IOException If an IO error occurs while writing to the stream. - */ - public void setNonStrokingColor(PDColor color) throws IOException - { - if (nonStrokingColorSpaceStack.isEmpty() || - nonStrokingColorSpaceStack.peek() != color.getColorSpace()) - { - writeOperand(getName(color.getColorSpace())); - writeOperator("cs"); - setNonStrokingColorSpaceStack(color.getColorSpace()); - } - - for (float value : color.getComponents()) - { - writeOperand(value); - } - - if (color.getColorSpace() instanceof PDPattern) - { - writeOperand(color.getPatternName()); - } - - if (color.getColorSpace() instanceof PDPattern || - color.getColorSpace() instanceof PDSeparation || - color.getColorSpace() instanceof PDDeviceN || - color.getColorSpace() instanceof PDICCBased) - { - writeOperator("scn"); - } - else - { - writeOperator("sc"); - } - } - - /** - * Set the non-stroking color using an AWT color. Conversion uses the default sRGB color space. - * - * @param color The color to set. - * @throws IOException If an IO error occurs while writing to the stream. - */ - public void setNonStrokingColor(Color color) throws IOException - { - float[] components = new float[] { - color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f }; - PDColor pdColor = new PDColor(components, PDDeviceRGB.INSTANCE); - setNonStrokingColor(pdColor); - } - - /** - * Set the non-stroking color in the DeviceRGB color space. Range is 0..255. - * - * @param r The red value. - * @param g The green value. - * @param b The blue value. - * @throws IOException If an IO error occurs while writing to the stream. - * @throws IllegalArgumentException If the parameters are invalid. - */ - public void setNonStrokingColor(int r, int g, int b) throws IOException - { - if (isOutside255Interval(r) || isOutside255Interval(g) || isOutside255Interval(b)) - { - throw new IllegalArgumentException("Parameters must be within 0..255, but are " - + String.format("(%d,%d,%d)", r, g, b)); - } - writeOperand(r / 255f); - writeOperand(g / 255f); - writeOperand(b / 255f); - writeOperator("rg"); - setNonStrokingColorSpaceStack(PDDeviceRGB.INSTANCE); - } - - /** - * Set the non-stroking color in the DeviceCMYK color space. Range is 0..255. - * - * @param c The cyan value. - * @param m The magenta value. - * @param y The yellow value. - * @param k The black value. - * @throws IOException If an IO error occurs while writing to the stream. - * @throws IllegalArgumentException If the parameters are invalid. - */ - public void setNonStrokingColor(int c, int m, int y, int k) throws IOException - { - if (isOutside255Interval(c) || isOutside255Interval(m) || isOutside255Interval(y) || isOutside255Interval(k)) - { - throw new IllegalArgumentException("Parameters must be within 0..255, but are " - + String.format("(%d,%d,%d,%d)", c, m, y, k)); - } - setNonStrokingColor(c / 255f, m / 255f, y / 255f, k / 255f); - } - - /** - * Set the non-stroking color in the DeviceRGB color space. Range is 0..1. - * - * @param c The cyan value. - * @param m The magenta value. - * @param y The yellow value. - * @param k The black value. - * @throws IOException If an IO error occurs while writing to the stream. - */ - public void setNonStrokingColor(double c, double m, double y, double k) throws IOException - { - if (isOutsideOneInterval(c) || isOutsideOneInterval(m) || isOutsideOneInterval(y) || isOutsideOneInterval(k)) - { - throw new IllegalArgumentException("Parameters must be within 0..1, but are " - + String.format("(%.2f,%.2f,%.2f,%.2f)", c, m, y, k)); - } - writeOperand((float) c); - writeOperand((float) m); - writeOperand((float) y); - writeOperand((float) k); - writeOperator("k"); - setNonStrokingColorSpaceStack(PDDeviceCMYK.INSTANCE); - } - - /** - * Set the non-stroking color in the DeviceGray color space. Range is 0..255. - * - * @param g The gray value. - * @throws IOException If an IO error occurs while writing to the stream. - * @throws IllegalArgumentException If the parameter is invalid. - */ - public void setNonStrokingColor(int g) throws IOException - { - if (isOutside255Interval(g)) - { - throw new IllegalArgumentException("Parameter must be within 0..255, but is " + g); - } - setNonStrokingColor(g / 255f); - } - - /** - * Set the non-stroking color in the DeviceGray color space. Range is 0..1. - * - * @param g The gray value. - * @throws IOException If an IO error occurs while writing to the stream. - * @throws IllegalArgumentException If the parameter is invalid. - */ - public void setNonStrokingColor(double g) throws IOException - { - if (isOutsideOneInterval(g)) - { - throw new IllegalArgumentException("Parameter must be within 0..1, but is " + g); - } - writeOperand((float) g); - writeOperator("g"); - setNonStrokingColorSpaceStack(PDDeviceGray.INSTANCE); - } - - /** - * Add a rectangle to the current path. - * - * @param x The lower left x coordinate. - * @param y The lower left y coordinate. - * @param width The width of the rectangle. - * @param height The height of the rectangle. - * @throws IOException If the content stream could not be written. - * @throws IllegalStateException If the method was called within a text block. - */ - public void addRect(float x, float y, float width, float height) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: addRect is not allowed within a text block."); - } - writeOperand(x); - writeOperand(y); - writeOperand(width); - writeOperand(height); - writeOperator("re"); - } - - /** - * Append a cubic Bézier curve to the current path. The curve extends from the current point to - * the point (x3, y3), using (x1, y1) and (x2, y2) as the Bézier control points. - * - * @param x1 x coordinate of the point 1 - * @param y1 y coordinate of the point 1 - * @param x2 x coordinate of the point 2 - * @param y2 y coordinate of the point 2 - * @param x3 x coordinate of the point 3 - * @param y3 y coordinate of the point 3 - * @throws IOException If the content stream could not be written. - * @throws IllegalStateException If the method was called within a text block. - */ - public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: curveTo is not allowed within a text block."); - } - writeOperand(x1); - writeOperand(y1); - writeOperand(x2); - writeOperand(y2); - writeOperand(x3); - writeOperand(y3); - writeOperator("c"); - } - - /** - * Append a cubic Bézier curve to the current path. The curve extends from the current point to - * the point (x3, y3), using the current point and (x2, y2) as the Bézier control points. - * - * @param x2 x coordinate of the point 2 - * @param y2 y coordinate of the point 2 - * @param x3 x coordinate of the point 3 - * @param y3 y coordinate of the point 3 - * @throws IllegalStateException If the method was called within a text block. - * @throws IOException If the content stream could not be written. - */ - public void curveTo2(float x2, float y2, float x3, float y3) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: curveTo2 is not allowed within a text block."); - } - writeOperand(x2); - writeOperand(y2); - writeOperand(x3); - writeOperand(y3); - writeOperator("v"); - } - - /** - * Append a cubic Bézier curve to the current path. The curve extends from the current point to - * the point (x3, y3), using (x1, y1) and (x3, y3) as the Bézier control points. - * - * @param x1 x coordinate of the point 1 - * @param y1 y coordinate of the point 1 - * @param x3 x coordinate of the point 3 - * @param y3 y coordinate of the point 3 - * @throws IOException If the content stream could not be written. - * @throws IllegalStateException If the method was called within a text block. - */ - public void curveTo1(float x1, float y1, float x3, float y3) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: curveTo1 is not allowed within a text block."); - } - writeOperand(x1); - writeOperand(y1); - writeOperand(x3); - writeOperand(y3); - writeOperator("y"); - } - - /** - * Move the current position to the given coordinates. - * - * @param x The x coordinate. - * @param y The y coordinate. - * @throws IOException If the content stream could not be written. - * @throws IllegalStateException If the method was called within a text block. - */ - public void moveTo(float x, float y) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: moveTo is not allowed within a text block."); - } - writeOperand(x); - writeOperand(y); - writeOperator("m"); - } - - /** - * Draw a line from the current position to the given coordinates. - * - * @param x The x coordinate. - * @param y The y coordinate. - * @throws IOException If the content stream could not be written. - * @throws IllegalStateException If the method was called within a text block. - */ - public void lineTo(float x, float y) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: lineTo is not allowed within a text block."); - } - writeOperand(x); - writeOperand(y); - writeOperator("l"); - } - - /** - * Stroke the path. - * - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void stroke() throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: stroke is not allowed within a text block."); - } - writeOperator("S"); - } - - /** - * Close and stroke the path. - * - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void closeAndStroke() throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: closeAndStroke is not allowed within a text block."); - } - writeOperator("s"); - } - - /** - * Fills the path using the nonzero winding number rule. - * - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void fill() throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: fill is not allowed within a text block."); - } - writeOperator("f"); - } - - /** - * Fills the path using the even-odd winding rule. - * - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void fillEvenOdd() throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: fillEvenOdd is not allowed within a text block."); - } - writeOperator("f*"); - } - - /** - * Fill and then stroke the path, using the nonzero winding number rule to determine the region - * to fill. This shall produce the same result as constructing two identical path objects, - * painting the first with {@link #fill() } and the second with {@link #stroke() }. - * - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void fillAndStroke() throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: fillAndStroke is not allowed within a text block."); - } - writeOperator("B"); - } - - /** - * Fill and then stroke the path, using the even-odd rule to determine the region to - * fill. This shall produce the same result as constructing two identical path objects, painting - * the first with {@link #fillEvenOdd() } and the second with {@link #stroke() }. - * - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void fillAndStrokeEvenOdd() throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: fillAndStrokeEvenOdd is not allowed within a text block."); - } - writeOperator("B*"); - } - - /** - * Close, fill, and then stroke the path, using the nonzero winding number rule to determine the - * region to fill. This shall have the same effect as the sequence {@link #closePath() } - * and then {@link #fillAndStroke() }. - * - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void closeAndFillAndStroke() throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: closeAndFillAndStroke is not allowed within a text block."); - } - writeOperator("b"); - } - - /** - * Close, fill, and then stroke the path, using the even-odd rule to determine the region to - * fill. This shall have the same effect as the sequence {@link #closePath() } - * and then {@link #fillAndStrokeEvenOdd() }. - * - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void closeAndFillAndStrokeEvenOdd() throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: closeAndFillAndStrokeEvenOdd is not allowed within a text block."); - } - writeOperator("b*"); - } - - /** - * Fills the clipping area with the given shading. - * - * @param shading Shading resource - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void shadingFill(PDShading shading) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: shadingFill is not allowed within a text block."); - } - - writeOperand(resources.add(shading)); - writeOperator("sh"); - } - - /** - * Closes the current subpath. - * - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void closePath() throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: closePath is not allowed within a text block."); - } - writeOperator("h"); - } - - /** - * Intersects the current clipping path with the current path, using the nonzero rule. - * - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void clip() throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: clip is not allowed within a text block."); - } - writeOperator("W"); - - // end path without filling or stroking - writeOperator("n"); - } - - /** - * Intersects the current clipping path with the current path, using the even-odd rule. - * - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void clipEvenOdd() throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: clipEvenOdd is not allowed within a text block."); - } - writeOperator("W*"); - - // end path without filling or stroking - writeOperator("n"); - } - - /** - * Set line width to the given value. - * - * @param lineWidth The width which is used for drwaing. - * @throws IOException If the content stream could not be written - * @throws IllegalStateException If the method was called within a text block. - */ - public void setLineWidth(float lineWidth) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: setLineWidth is not allowed within a text block."); - } - writeOperand(lineWidth); - writeOperator("w"); - } - - /** - * Set the line join style. - * - * @param lineJoinStyle 0 for miter join, 1 for round join, and 2 for bevel join. - * @throws IOException If the content stream could not be written. - * @throws IllegalStateException If the method was called within a text block. - * @throws IllegalArgumentException If the parameter is not a valid line join style. - */ - public void setLineJoinStyle(int lineJoinStyle) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: setLineJoinStyle is not allowed within a text block."); - } - if (lineJoinStyle >= 0 && lineJoinStyle <= 2) - { - writeOperand(lineJoinStyle); - writeOperator("j"); - } - else - { - throw new IllegalArgumentException("Error: unknown value for line join style"); - } - } - - /** - * Set the line cap style. - * - * @param lineCapStyle 0 for butt cap, 1 for round cap, and 2 for projecting square cap. - * @throws IOException If the content stream could not be written. - * @throws IllegalStateException If the method was called within a text block. - * @throws IllegalArgumentException If the parameter is not a valid line cap style. - */ - public void setLineCapStyle(int lineCapStyle) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: setLineCapStyle is not allowed within a text block."); - } - if (lineCapStyle >= 0 && lineCapStyle <= 2) - { - writeOperand(lineCapStyle); - writeOperator("J"); - } - else - { - throw new IllegalArgumentException("Error: unknown value for line cap style"); - } - } - - /** - * Set the line dash pattern. - * - * @param pattern The pattern array - * @param phase The phase of the pattern - * @throws IOException If the content stream could not be written. - * @throws IllegalStateException If the method was called within a text block. - */ - public void setLineDashPattern(float[] pattern, float phase) throws IOException - { - if (inTextMode) - { - throw new IllegalStateException("Error: setLineDashPattern is not allowed within a text block."); - } - write("["); - for (float value : pattern) - { - writeOperand(value); - } - write("] "); - writeOperand(phase); - writeOperator("d"); - } - - /** - * Begin a marked content sequence. - * - * @param tag the tag - * @throws IOException If the content stream could not be written - */ - public void beginMarkedContent(COSName tag) throws IOException - { - writeOperand(tag); - writeOperator("BMC"); - } - - /** - * Begin a marked content sequence with a reference to an entry in the page resources' - * Properties dictionary. - * - * @param tag the tag - * @param propertyList property list - * @throws IOException If the content stream could not be written - */ - public void beginMarkedContent(COSName tag, PDPropertyList propertyList) throws IOException - { - writeOperand(tag); - writeOperand(resources.add(propertyList)); - writeOperator("BDC"); - } - - /** - * End a marked content sequence. - * - * @throws IOException If the content stream could not be written - */ - public void endMarkedContent() throws IOException - { - writeOperator("EMC"); - } - - /** - * Set an extended graphics state. - * - * @param state The extended graphics state. - * @throws IOException If the content stream could not be written. - */ - public void setGraphicsStateParameters(PDExtendedGraphicsState state) throws IOException - { - writeOperand(resources.add(state)); - writeOperator("gs"); - } - - /** - * Write a comment line. - * - * @param comment - * @throws IOException If the content stream could not be written. - * @throws IllegalArgumentException If the comment contains a newline. This is not allowed, - * because the next line could be ordinary PDF content. - */ - public void addComment(String comment) throws IOException - { - if (comment.indexOf('\n') >= 0 || comment.indexOf('\r') >= 0) - { - throw new IllegalArgumentException("comment should not include a newline"); - } - output.write('%'); - output.write(comment.getBytes(Charsets.US_ASCII)); - output.write('\n'); - } - - /** - * Writes a real real to the content stream. - */ - private void writeOperand(float real) throws IOException - { - write(formatDecimal.format(real)); - output.write(' '); - } - - /** - * Writes a real number to the content stream. - */ - private void writeOperand(int integer) throws IOException - { - write(formatDecimal.format(integer)); - output.write(' '); - } - - /** - * Writes a COSName to the content stream. - */ - private void writeOperand(COSName name) throws IOException - { - name.writePDF(output); - output.write(' '); - } - - /** - * Writes a string to the content stream as ASCII. - */ - private void writeOperator(String text) throws IOException - { - output.write(text.getBytes(Charsets.US_ASCII)); - output.write('\n'); - } - - /** - * Writes a string to the content stream as ASCII. - */ - private void write(String text) throws IOException - { - output.write(text.getBytes(Charsets.US_ASCII)); - } - - /** - * Writes a string to the content stream as ASCII. - */ - private void writeLine() throws IOException - { - output.write('\n'); - } - - /** - * Writes binary data to the content stream. - */ - private void writeBytes(byte[] data) throws IOException - { - output.write(data); - } - - /** - * Writes an AffineTransform to the content stream as an array. - */ - private void writeAffineTransform(AffineTransform transform) throws IOException - { - double[] values = new double[6]; - transform.getMatrix(values); - for (double v : values) - { - writeOperand((float) v); - } - } - - /** - * Close the content stream. This must be called when you are done with this object. - * - * @throws IOException If the underlying stream has a problem being written to. - */ - @Override - public void close() throws IOException - { - output.close(); - } - - private boolean isOutside255Interval(int val) - { - return val < 0 || val > 255; - } - - private boolean isOutsideOneInterval(double val) - { - return val < 0 || val > 1; - } - - private void setStrokingColorSpaceStack(PDColorSpace colorSpace) - { - if (strokingColorSpaceStack.isEmpty()) - { - strokingColorSpaceStack.add(colorSpace); - } - else - { - strokingColorSpaceStack.setElementAt(colorSpace, strokingColorSpaceStack.size() - 1); - } - } - - private void setNonStrokingColorSpaceStack(PDColorSpace colorSpace) - { - if (nonStrokingColorSpaceStack.isEmpty()) - { - nonStrokingColorSpaceStack.add(colorSpace); - } - else - { - nonStrokingColorSpaceStack.setElementAt(colorSpace, nonStrokingColorSpaceStack.size() - 1); - } - } }