From 6025b6016517c6d898d8957d1d7e03ba71431912 Mon Sep 17 00:00:00 2001 From: tknall Date: Fri, 1 Dec 2006 12:20:24 +0000 Subject: Initial import of release 2.2. git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@4 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../pdfbox/pdmodel/edit/PDPageContentStream.java | 648 +++++++++++++++++++++ src/main/java/org/pdfbox/pdmodel/edit/package.html | 9 + 2 files changed, 657 insertions(+) create mode 100644 src/main/java/org/pdfbox/pdmodel/edit/PDPageContentStream.java create mode 100644 src/main/java/org/pdfbox/pdmodel/edit/package.html (limited to 'src/main/java/org/pdfbox/pdmodel/edit') diff --git a/src/main/java/org/pdfbox/pdmodel/edit/PDPageContentStream.java b/src/main/java/org/pdfbox/pdmodel/edit/PDPageContentStream.java new file mode 100644 index 0000000..1e3b25f --- /dev/null +++ b/src/main/java/org/pdfbox/pdmodel/edit/PDPageContentStream.java @@ -0,0 +1,648 @@ +/** + * Copyright (c) 2004, www.pdfbox.org + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of pdfbox; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://www.pdfbox.org + * + */ +package org.pdfbox.pdmodel.edit; + +import java.awt.Color; +import java.awt.color.ColorSpace; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import java.text.NumberFormat; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.HashMap; + +import org.pdfbox.pdmodel.PDDocument; +import org.pdfbox.pdmodel.PDPage; +import org.pdfbox.pdmodel.PDResources; + +import org.pdfbox.pdmodel.common.COSStreamArray; +import org.pdfbox.pdmodel.common.PDStream; + +import org.pdfbox.pdmodel.font.PDFont; +import org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; + +import org.pdfbox.cos.COSArray; +import org.pdfbox.cos.COSName; +import org.pdfbox.cos.COSString; + + +/** + * This class will is a convenience for creating page content streams. You MUST + * call close() when you are finished with this object. + * + * @author Ben Litchfield (ben@csh.rit.edu) + * @version $Revision: 1.14 $ + */ +public class PDPageContentStream +{ + private PDPage page; + private OutputStream output; + private boolean inTextMode = false; + private Map fontMappings = new HashMap(); + private Map imageMappings = new HashMap(); + private int fontNumber = 1; + private int imageNumber = 1; + private PDResources resources; + + //cached storage component for getting color values + private float[] colorComponents = new float[4]; + + private NumberFormat formatDecimal = NumberFormat.getNumberInstance( Locale.US ); + + private static final String BEGIN_TEXT = "BT\n"; + private static final String END_TEXT = "ET\n"; + private static final String SET_FONT = "Tf\n"; + private static final String MOVE_TEXT_POSITION = "Td\n"; + private static final String SHOW_TEXT = "Tj\n"; + + private static final String SAVE_GRAPHICS_STATE = "q\n"; + private static final String RESTORE_GRAPHICS_STATE = "Q\n"; + private static final String CONCATENATE_MATRIX = "cm\n"; + private static final String XOBJECT_DO = "Do\n"; + private static final String RG_STROKING = "RG\n"; + private static final String RG_NON_STROKING = "rg\n"; + private static final String K_STROKING = "K\n"; + private static final String K_NON_STROKING = "k\n"; + private static final String G_STROKING = "G\n"; + private static final String G_NON_STROKING = "g\n"; + private static final String APPEND_RECTANGLE = "re\n"; + private static final String FILL = "f\n"; + + + private static final int SPACE = 32; + + + /** + * Create a new PDPage content stream. + * + * @param document The document the page is part of. + * @param sourcePage The page to write the contents to. + * @throws IOException If there is an error writing to the page contents. + */ + public PDPageContentStream( PDDocument document, PDPage sourcePage ) throws IOException + { + this(document,sourcePage,false,true); + } + + /** + * Create a new PDPage content stream. + * + * @param document The document the page is part of. + * @param sourcePage The page to write the contents to. + * @param appendContent Indicates whether content will be overwritten. If false all previous content is deleted. + * @param compress Tell if the content stream should compress the page contents. + * @throws IOException If there is an error writing to the page contents. + */ + public PDPageContentStream( PDDocument document, PDPage sourcePage, boolean appendContent, boolean compress ) + throws IOException + { + page = sourcePage; + resources = page.getResources(); + if( resources == null ) + { + resources = new PDResources(); + page.setResources( resources ); + } + // If request specifies the need to append to the document + if(appendContent) + { + // Get the pdstream from the source page instead of creating a new one + PDStream contents = sourcePage.getContents(); + + // Create a pdstream to append new content + PDStream contentsToAppend = new PDStream( document ); + + // This will be the resulting COSStreamArray after existing and new streams are merged + COSStreamArray compoundStream = null; + + // If contents is already an array, a new stream is simply appended to it + if(contents.getStream() instanceof COSStreamArray) + { + compoundStream = (COSStreamArray)contents.getStream(); + compoundStream.appendStream( contentsToAppend.getStream()); + } + else + { + // Creates the COSStreamArray and adds the current stream plus a new one to it + COSArray newArray = new COSArray(); + newArray.add(contents.getCOSObject()); + newArray.add(contentsToAppend.getCOSObject()); + compoundStream = new COSStreamArray(newArray); + } + + if( compress ) + { + List filters = new ArrayList(); + filters.add( COSName.FLATE_DECODE ); + contentsToAppend.setFilters( filters ); + } + + // Sets the compoundStream as page contents + sourcePage.setContents( new PDStream(compoundStream) ); + output = contentsToAppend.createOutputStream(); + } + else + { + PDStream contents = new PDStream( document ); + if( compress ) + { + List filters = new ArrayList(); + filters.add( COSName.FLATE_DECODE ); + contents.setFilters( filters ); + } + sourcePage.setContents( contents ); + output = contents.createOutputStream(); + } + formatDecimal.setMaximumFractionDigits( 10 ); + 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. + */ + public void beginText() throws IOException + { + if( inTextMode ) + { + throw new IOException( "Error: Nested beginText() calls are not allowed." ); + } + appendRawCommands( BEGIN_TEXT ); + 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. + */ + public void endText() throws IOException + { + if( !inTextMode ) + { + throw new IOException( "Error: You must call beginText() before calling endText." ); + } + appendRawCommands( END_TEXT ); + inTextMode = false; + } + + /** + * Set the font 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 + { + String fontMapping = (String)fontMappings.get( font ); + if( fontMapping == null ) + { + fontMapping = "F" + fontNumber++; + fontMappings.put( font, fontMapping ); + resources.getFonts().put( fontMapping, font ); + } + appendRawCommands( "/"); + appendRawCommands( fontMapping ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( fontSize ) ); + appendRawCommands( SPACE ); + appendRawCommands( SET_FONT ); + } + + /** + * 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( PDXObjectImage image, int x, int y ) throws IOException + { + drawImage( image, x, y, image.getWidth(), image.getHeight() ); + } + + /** + * Draw an image at the x,y coordinates and a certain width and height. + * + * @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 of the image to draw. + * @param height The height of the image to draw. + * + * @throws IOException If there is an error writing to the stream. + */ + public void drawImage( PDXObjectImage image, int x, int y, int width, int height ) throws IOException + { + String imageMapping = (String)imageMappings.get( image ); + if( imageMapping == null ) + { + imageMapping = "Im" + imageNumber++; + imageMappings.put( image, imageMapping ); + resources.getImages().put( imageMapping, image ); + } + appendRawCommands( SAVE_GRAPHICS_STATE ); + appendRawCommands( formatDecimal.format( width ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( 0 ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( 0 ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( height ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( x ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y ) ); + appendRawCommands( SPACE ); + appendRawCommands( CONCATENATE_MATRIX ); + appendRawCommands( SPACE ); + appendRawCommands( "/" ); + appendRawCommands( imageMapping ); + appendRawCommands( SPACE ); + appendRawCommands( XOBJECT_DO ); + appendRawCommands( SPACE ); + appendRawCommands( RESTORE_GRAPHICS_STATE ); + } + + /** + * The Td operator. + * @param x The x coordinate. + * @param y The y coordinate. + * @throws IOException If there is an error writing to the stream. + */ + public void moveTextPositionByAmount( float x, float y ) throws IOException + { + if( !inTextMode ) + { + throw new IOException( "Error: must call beginText() before moveTextPositionByAmount"); + } + appendRawCommands( formatDecimal.format( x ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y ) ); + appendRawCommands( SPACE ); + appendRawCommands( MOVE_TEXT_POSITION ); + } + + /** + * This will draw a string at the current location on the screen. + * + * @param text The text to draw. + * @throws IOException If an io exception occurs. + */ + public void drawString( String text ) throws IOException + { + if( !inTextMode ) + { + throw new IOException( "Error: must call beginText() before drawString"); + } + COSString string = new COSString( text ); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + string.writePDF( buffer ); + appendRawCommands( new String( buffer.toByteArray(), "ISO-8859-1")); + appendRawCommands( SPACE ); + appendRawCommands( SHOW_TEXT ); + } + + /** + * Set the stroking color, specified as RGB. + * + * @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 + { + ColorSpace colorSpace = color.getColorSpace(); + if( colorSpace.getType() == ColorSpace.TYPE_RGB ) + { + setStrokingColor( color.getRed(), color.getGreen(), color.getBlue() ); + } + else if( colorSpace.getType() == ColorSpace.TYPE_GRAY ) + { + color.getColorComponents( colorComponents ); + setStrokingColor( colorComponents[0] ); + } + else if( colorSpace.getType() == ColorSpace.TYPE_CMYK ) + { + color.getColorComponents( colorComponents ); + setStrokingColor( colorComponents[0], colorComponents[2], colorComponents[2], colorComponents[3] ); + } + else + { + throw new IOException( "Error: unknown colorspace:" + colorSpace ); + } + } + + /** + * Set the non stroking color, specified as RGB. + * + * @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 + { + ColorSpace colorSpace = color.getColorSpace(); + if( colorSpace.getType() == ColorSpace.TYPE_RGB ) + { + setNonStrokingColor( color.getRed(), color.getGreen(), color.getBlue() ); + } + else if( colorSpace.getType() == ColorSpace.TYPE_GRAY ) + { + color.getColorComponents( colorComponents ); + setNonStrokingColor( colorComponents[0] ); + } + else if( colorSpace.getType() == ColorSpace.TYPE_CMYK ) + { + color.getColorComponents( colorComponents ); + setNonStrokingColor( colorComponents[0], colorComponents[2], colorComponents[2], colorComponents[3] ); + } + else + { + throw new IOException( "Error: unknown colorspace:" + colorSpace ); + } + } + + /** + * Set the stroking color, specified as RGB, 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. + */ + public void setStrokingColor( int r, int g, int b ) throws IOException + { + appendRawCommands( formatDecimal.format( r/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( g/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( b/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( RG_STROKING ); + } + + /** + * Set the stroking color, specified as CMYK, 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. + */ + public void setStrokingColor( int c, int m, int y, int k) throws IOException + { + appendRawCommands( formatDecimal.format( c/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( m/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( k/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( K_STROKING ); + } + + /** + * Set the stroking color, specified as CMYK, 0.0-1.0. + * + * @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 setStrokingColor( double c, double m, double y, double k) throws IOException + { + appendRawCommands( formatDecimal.format( c ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( m ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( k ) ); + appendRawCommands( SPACE ); + appendRawCommands( K_STROKING ); + } + + /** + * Set the stroking color, specified as grayscale, 0-255. + * + * @param g The gray value. + * @throws IOException If an IO error occurs while writing to the stream. + */ + public void setStrokingColor( int g ) throws IOException + { + appendRawCommands( formatDecimal.format( g/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( G_STROKING ); + } + + /** + * Set the stroking color, specified as Grayscale 0.0-1.0. + * + * @param g The gray value. + * @throws IOException If an IO error occurs while writing to the stream. + */ + public void setStrokingColor( double g ) throws IOException + { + appendRawCommands( formatDecimal.format( g ) ); + appendRawCommands( SPACE ); + appendRawCommands( G_STROKING ); + } + + /** + * Set the non stroking color, specified as RGB, 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. + */ + public void setNonStrokingColor( int r, int g, int b ) throws IOException + { + appendRawCommands( formatDecimal.format( r/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( g/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( b/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( RG_NON_STROKING ); + } + + /** + * Set the non stroking color, specified as CMYK, 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. + */ + public void setNonStrokingColor( int c, int m, int y, int k) throws IOException + { + appendRawCommands( formatDecimal.format( c/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( m/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( k/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( K_NON_STROKING ); + } + + /** + * Set the non stroking color, specified as CMYK, 0.0-1.0. + * + * @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 + { + appendRawCommands( formatDecimal.format( c ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( m ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( k ) ); + appendRawCommands( SPACE ); + appendRawCommands( K_NON_STROKING ); + } + + /** + * Set the non stroking color, specified as grayscale, 0-255. + * + * @param g The gray value. + * @throws IOException If an IO error occurs while writing to the stream. + */ + public void setNonStrokingColor( int g ) throws IOException + { + appendRawCommands( formatDecimal.format( g/255d ) ); + appendRawCommands( SPACE ); + appendRawCommands( G_NON_STROKING ); + } + + /** + * Set the non stroking color, specified as Grayscale 0.0-1.0. + * + * @param g The gray value. + * @throws IOException If an IO error occurs while writing to the stream. + */ + public void setNonStrokingColor( double g ) throws IOException + { + appendRawCommands( formatDecimal.format( g ) ); + appendRawCommands( SPACE ); + appendRawCommands( G_NON_STROKING ); + } + + /** + * Draw a rectangle on the page using the current non stroking color. + * + * @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 there is an error while drawing on the screen. + */ + public void fillRect( float x, float y, float width, float height ) throws IOException + { + appendRawCommands( formatDecimal.format( x ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( y ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( width ) ); + appendRawCommands( SPACE ); + appendRawCommands( formatDecimal.format( height ) ); + appendRawCommands( SPACE ); + appendRawCommands( APPEND_RECTANGLE ); + appendRawCommands( FILL ); + } + + + /** + * This will append raw commands to the content stream. + * + * @param commands The commands to append to the stream. + * @throws IOException If an error occurs while writing to the stream. + */ + public void appendRawCommands( String commands ) throws IOException + { + appendRawCommands( commands.getBytes( "ISO-8859-1" ) ); + } + + /** + * This will append raw commands to the content stream. + * + * @param commands The commands to append to the stream. + * @throws IOException If an error occurs while writing to the stream. + */ + public void appendRawCommands( byte[] commands ) throws IOException + { + output.write( commands ); + } + + /** + * This will append raw commands to the content stream. + * + * @param data Append a raw byte to the stream. + * + * @throws IOException If an error occurs while writing to the stream. + */ + public void appendRawCommands( int data ) throws IOException + { + output.write( data ); + } + + /** + * 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. + */ + public void close() throws IOException + { + output.close(); + } +} \ No newline at end of file diff --git a/src/main/java/org/pdfbox/pdmodel/edit/package.html b/src/main/java/org/pdfbox/pdmodel/edit/package.html new file mode 100644 index 0000000..f6b0ffd --- /dev/null +++ b/src/main/java/org/pdfbox/pdmodel/edit/package.html @@ -0,0 +1,9 @@ + + + + + + +The PDModel edit package will be used to store classes for creating page content. + + -- cgit v1.2.3