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 java.awt.Color; 20 import java.awt.color.ColorSpace; 21 import java.awt.color.ICC_ColorSpace; 22 import java.io.IOException; 23 import java.util.Arrays; 24 25 import org.apache.commons.logging.Log; 26 import org.apache.commons.logging.LogFactory; 27 import org.apache.pdfbox.cos.COSArray; 28 29 /** 30 * This class represents a color space and the color value for that colorspace. 31 * 32 * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a> 33 * @version $Revision: 1.7 $ 34 */ 35 public class PDColorState implements Cloneable 36 { 37 38 /** 39 * Log instance. 40 */ 41 private static final Log log = LogFactory.getLog(PDColorState.class); 42 43 /** 44 * The default color that can be set to replace all colors in 45 * {@link ICC_ColorSpace ICC color spaces}. 46 * 47 * @see #setIccOverrideColor(Color) 48 */ 49 private static volatile Color iccOverrideColor = 50 Color.getColor("org.apache.pdfbox.ICC_override_color"); 51 52 /** 53 * Sets the default color to replace all colors in 54 * {@link ICC_ColorSpace ICC color spaces}. This will work around 55 * a potential JVM crash caused by broken native ICC color manipulation 56 * code in the Sun class libraries. 57 * <p> 58 * The default override can be specified by setting the color code in 59 * <code>org.apache.pdfbox.ICC_override_color</code> system property 60 * (see {@link Color#getColor(String)}. If this system property is not 61 * specified, then the override is not enabled unless this method is 62 * explicitly called. 63 * 64 * @param color ICC override color, 65 * or <code>null</code> to disable the override 66 * @see <a href="https://issues.apache.org/jira/browse/PDFBOX-511">PDFBOX-511</a> 67 * @since Apache PDFBox 0.8.1 68 */ 69 public static void setIccOverrideColor(Color color) { 70 iccOverrideColor = color; 71 } 72 73 private PDColorSpace colorSpace = new PDDeviceGray(); 74 private COSArray colorSpaceValue = new COSArray(); 75 76 /** 77 * Cached Java AWT color based on the current color space and value. 78 * The value is cleared whenever the color space or value is set. 79 * 80 * @see #getJavaColor() 81 */ 82 private Color color = null; 83 84 /** 85 * Default constructor. 86 * 87 */ 88 public PDColorState() 89 { 90 setColorSpaceValue( new float[] {0}); 91 } 92 93 /** 94 * {@inheritDoc} 95 */ 96 public Object clone() 97 { 98 PDColorState retval = new PDColorState(); 99 retval.colorSpace = this.colorSpace; 100 retval.colorSpaceValue.clear(); 101 retval.colorSpaceValue.addAll( this.colorSpaceValue ); 102 return retval; 103 } 104 105 /** 106 * Returns the Java AWT color based on the current color space and value. 107 * 108 * @return current Java AWT color 109 * @throws IOException if the current color can not be created 110 */ 111 public Color getJavaColor() throws IOException { 112 if (color == null) { 113 color = createColor(); 114 } 115 return color; 116 } 117 118 /** 119 * Create the current color from the colorspace and values. 120 * @return The current awt color. 121 * @throws IOException If there is an error creating the color. 122 */ 123 private Color createColor() throws IOException 124 { 125 float[] components = colorSpaceValue.toFloatArray(); 126 try 127 { 128 if( colorSpace.getName().equals(PDDeviceRGB.NAME) && components.length == 3 ) 129 { 130 //for some reason, when using RGB and the RGB colorspace 131 //the new Color doesn't maintain exactly the same values 132 //I think some color conversion needs to take place first 133 //for now we will just make rgb a special case. 134 return new Color( components[0], components[1], components[2] ); 135 } 136 else 137 { 138 Color override = iccOverrideColor; 139 ColorSpace cs = colorSpace.getJavaColorSpace(); 140 if (colorSpace.getName().equals(PDSeparation.NAME) && components.length == 1) 141 { 142 //Use that component as a single-integer RGB value 143 return new Color((int)components[0]); 144 } 145 else if (cs instanceof ICC_ColorSpace && override != null) 146 { 147 log.warn( 148 "Using an ICC override color to avoid a potential" 149 + " JVM crash (see PDFBOX-511)"); 150 return override; 151 } 152 else 153 { 154 return new Color( cs, components, 1f ); 155 } 156 } 157 } 158 // Catch IOExceptions from PDColorSpace.getJavaColorSpace(), but 159 // possibly also IllegalArgumentExceptions or other RuntimeExceptions 160 // from the potentially complex color management code. 161 catch (Exception e) 162 { 163 log.warn("Unable to create the color instance " 164 + Arrays.toString(components) + " in color space " 165 + colorSpace + "; using black instead", e); 166 return Color.BLACK; 167 } 168 } 169 170 /** 171 * Constructor with an existing color set. Default colorspace is PDDeviceGray. 172 * 173 * @param csValues The color space values. 174 */ 175 public PDColorState( COSArray csValues ) 176 { 177 colorSpaceValue = csValues; 178 } 179 180 181 /** 182 * This will get the current colorspace. 183 * 184 * @return The current colorspace. 185 */ 186 public PDColorSpace getColorSpace() 187 { 188 return colorSpace; 189 } 190 191 /** 192 * This will set the current colorspace. 193 * 194 * @param value The new colorspace. 195 */ 196 public void setColorSpace(PDColorSpace value) 197 { 198 colorSpace = value; 199 // Clear color cache 200 color = null; 201 } 202 203 /** 204 * This will get the color space values. Either 1 for gray or 3 for RGB. 205 * 206 * @return The colorspace values. 207 */ 208 public float[] getColorSpaceValue() 209 { 210 return colorSpaceValue.toFloatArray(); 211 } 212 213 /** 214 * This will get the color space values. Either 1 for gray or 3 for RGB. 215 * 216 * @return The colorspace values. 217 */ 218 public COSArray getCOSColorSpaceValue() 219 { 220 return colorSpaceValue; 221 } 222 223 /** 224 * This will update the colorspace values. 225 * 226 * @param value The new colorspace values. 227 */ 228 public void setColorSpaceValue(float[] value) 229 { 230 colorSpaceValue.setFloatArray( value ); 231 // Clear color cache 232 color = null; 233 } 234 }