1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.pdfbox.pdmodel.graphics.color; 18 19 import org.apache.pdfbox.cos.COSArray; 20 import org.apache.pdfbox.cos.COSBase; 21 import org.apache.pdfbox.cos.COSInteger; 22 import org.apache.pdfbox.cos.COSName; 23 import org.apache.pdfbox.cos.COSNumber; 24 import org.apache.pdfbox.cos.COSStream; 25 import org.apache.pdfbox.cos.COSString; 26 27 import java.awt.color.ColorSpace; 28 import java.awt.image.ColorModel; 29 import java.awt.image.DataBuffer; 30 import java.awt.image.IndexColorModel; 31 32 import java.io.InputStream; 33 import java.io.ByteArrayOutputStream; 34 import java.io.IOException; 35 36 /** 37 * This class represents an Indexed color space. 38 * 39 * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a> 40 * @version $Revision: 1.4 $ 41 */ 42 public class PDIndexed extends PDColorSpace 43 { 44 45 /** 46 * The name of this color space. 47 */ 48 public static final String NAME = "Indexed"; 49 50 /** 51 * The abbreviated name of this color space. 52 */ 53 public static final String ABBREVIATED_NAME = "I"; 54 55 private COSArray array; 56 57 /** 58 * Constructor, default DeviceRGB, hival 255. 59 */ 60 public PDIndexed() 61 { 62 array = new COSArray(); 63 array.add( COSName.getPDFName( NAME ) ); 64 array.add( COSName.getPDFName( PDDeviceRGB.NAME ) ); 65 array.add( COSInteger.get( 255 ) ); 66 array.add( org.apache.pdfbox.cos.COSNull.NULL ); 67 } 68 69 /** 70 * Constructor. 71 * 72 * @param indexedArray The array containing the indexed parameters 73 */ 74 public PDIndexed( COSArray indexedArray ) 75 { 76 array = indexedArray; 77 } 78 79 /** 80 * This will return the number of color components. This will return the 81 * number of color components in the base color. 82 * 83 * @return The number of components in this color space. 84 * 85 * @throws IOException If there is an error getting the number of color components. 86 */ 87 public int getNumberOfComponents() throws IOException 88 { 89 return getBaseColorSpace().getNumberOfComponents(); 90 } 91 92 /** 93 * This will return the name of the color space. 94 * 95 * @return The name of the color space. 96 */ 97 public String getName() 98 { 99 return NAME; 100 } 101 102 /** 103 * Create a Java colorspace for this colorspace. 104 * 105 * @return A color space that can be used for Java AWT operations. 106 * 107 * @throws IOException If there is an error creating the color space. 108 */ 109 protected ColorSpace createColorSpace() throws IOException 110 { 111 throw new IOException( "Not implemented" ); 112 } 113 114 /** 115 * Create a Java color model for this colorspace. 116 * 117 * @param bpc The number of bits per component. 118 * 119 * @return A color model that can be used for Java AWT operations. 120 * 121 * @throws IOException If there is an error creating the color model. 122 */ 123 public ColorModel createColorModel( int bpc ) throws IOException 124 { 125 int size = getHighValue(); 126 byte[] index = getLookupData(); 127 PDColorSpace baseColorSpace = getBaseColorSpace(); 128 ColorModel cm = null; 129 if( baseColorSpace instanceof PDDeviceRGB ) 130 { 131 cm = new IndexColorModel(bpc, size+1, index,0,false); 132 } 133 else 134 { 135 ColorModel baseColorModel = baseColorSpace.createColorModel(bpc); 136 if( baseColorModel.getTransferType() != DataBuffer.TYPE_BYTE ) 137 { 138 throw new IOException( "Not implemented" ); 139 } 140 byte[] r = new byte[size+1]; 141 byte[] g = new byte[size+1]; 142 byte[] b = new byte[size+1]; 143 byte[] a = baseColorModel.hasAlpha() ? new byte[size+1] : null; 144 byte[] inData = new byte[baseColorModel.getNumComponents()]; 145 for( int i = 0; i <= size; i++ ) 146 { 147 System.arraycopy(index, i * inData.length, inData, 0, inData.length); 148 r[i] = (byte)baseColorModel.getRed(inData); 149 g[i] = (byte)baseColorModel.getGreen(inData); 150 b[i] = (byte)baseColorModel.getBlue(inData); 151 if(a != null) 152 { 153 a[i] = (byte)baseColorModel.getAlpha(inData); 154 } 155 } 156 cm = a == null ? new IndexColorModel(bpc, size+1, r, g, b) : new IndexColorModel(bpc, size+1, r, g, b, a); 157 } 158 return cm; 159 } 160 161 /** 162 * This will get the color space that acts as the index for this color space. 163 * 164 * @return The base color space. 165 * 166 * @throws IOException If there is error creating the base color space. 167 */ 168 public PDColorSpace getBaseColorSpace() throws IOException 169 { 170 COSBase base = array.getObject( 1 ); 171 return PDColorSpaceFactory.createColorSpace( base ); 172 } 173 174 /** 175 * This will set the base color space. 176 * 177 * @param base The base color space to use as the index. 178 */ 179 public void setBaseColorSpace( PDColorSpace base ) 180 { 181 array.set( 1, base.getCOSObject() ); 182 } 183 184 /** 185 * Get the highest value for the lookup. 186 * 187 * @return The hival entry. 188 */ 189 public int getHighValue() 190 { 191 return ((COSNumber)array.getObject( 2 )).intValue(); 192 } 193 194 /** 195 * This will set the highest value that is allowed. This cannot be higher 196 * than 255. 197 * 198 * @param high The highest value for the lookup table. 199 */ 200 public void setHighValue( int high ) 201 { 202 array.set( 2, high ); 203 } 204 205 /** 206 * This will perform a lookup into the color lookup table. 207 * 208 * @param lookupIndex The zero-based index into the table, should not exceed the high value. 209 * @param componentNumber The component number, probably 1,2,3,3. 210 * 211 * @return The value that was from the lookup table. 212 * 213 * @throws IOException If there is an error looking up the color. 214 */ 215 public int lookupColor( int lookupIndex, int componentNumber ) throws IOException 216 { 217 PDColorSpace baseColor = getBaseColorSpace(); 218 byte[] data = getLookupData(); 219 int numberOfComponents = baseColor.getNumberOfComponents(); 220 return (data[lookupIndex*numberOfComponents + componentNumber]+256)%256; 221 } 222 223 private byte[] getLookupData() throws IOException 224 { 225 COSBase lookupTable = array.getObject( 3 ); 226 byte[] data = null; 227 if( lookupTable instanceof COSString ) 228 { 229 data = ((COSString)lookupTable).getBytes(); 230 } 231 else if( lookupTable instanceof COSStream ) 232 { 233 //Data will be small so just load the whole thing into memory for 234 //easier processing 235 COSStream lookupStream = (COSStream)lookupTable; 236 InputStream input = lookupStream.getUnfilteredStream(); 237 ByteArrayOutputStream output = new ByteArrayOutputStream(1024); 238 byte[] buffer = new byte[ 1024 ]; 239 int amountRead; 240 while( (amountRead = input.read(buffer, 0, buffer.length)) != -1 ) 241 { 242 output.write( buffer, 0, amountRead ); 243 } 244 data = output.toByteArray(); 245 } 246 else if( lookupTable == null ) 247 { 248 data = new byte[0]; 249 } 250 else 251 { 252 throw new IOException( "Error: Unknown type for lookup table " + lookupTable ); 253 } 254 return data; 255 } 256 257 /** 258 * This will set a color in the color lookup table. 259 * 260 * @param lookupIndex The zero-based index into the table, should not exceed the high value. 261 * @param componentNumber The component number, probably 1,2,3,3. 262 * @param color The color that will go into the table. 263 * 264 * @throws IOException If there is an error looking up the color. 265 */ 266 public void setLookupColor( int lookupIndex, int componentNumber, int color ) throws IOException 267 { 268 PDColorSpace baseColor = getBaseColorSpace(); 269 int numberOfComponents = baseColor.getNumberOfComponents(); 270 byte[] data = getLookupData(); 271 data[lookupIndex*numberOfComponents + componentNumber] = (byte)color; 272 COSString string = new COSString( data ); 273 array.set( 3, string ); 274 } 275 }