/*
* Copyright 2006-2017 ICEsoft Technologies Canada Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS
* IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.icepdf.core.pobjects.graphics;
import org.icepdf.core.pobjects.Dictionary;
import org.icepdf.core.pobjects.Name;
import org.icepdf.core.util.Library;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <p>This class represents an External Graphics State (ExtGState) object. An
* ExtGState is an extension of the individual operators which sets a graphics
* state, q, Q, cm, w, J, j, M, d, ri, i and gs. The gs operand points to a
* named ExtGState resource which contains a dictionary whose contents specify
* the values of one or more graphics state parameters. All the entries in this
* dictionary are summarized in the following table. An ExtGSate dictionary
* does not need to specify all entries and the results of gs operations are
* cumulative.</p>
* <br>
* <table border="1" summary="">
* <tr>
* <td><b> Key </b></td>
* <td><b> Type</b></td>
* <td><b> Desription</b></td>
* </tr>
* <tr>
* <td valign="top" >Type</td>
* <td valign="top" >name</td>
* <td>(Optional) The type of PDF object that this dictionary describes;
* must be ExtGState for a graphics state parameter dictionary.</td>
* </tr>
* <tr>
* <td valign="top" >LW</td>
* <td valign="top" >number</td>
* <td>(Optional; PDF 1.3) The line width</td>
* </tr>
* <tr>
* <td valign="top" >LC</td>
* <td valign="top" >integer</td>
* <td>(Optional; PDF 1.3) The line cap style.</td>
* </tr>
* <tr>
* <td valign="top" >LJ</td>
* <td valign="top" >integer</td>
* <td>(Optional; PDF 1.3) The line join styl.</td>
* </tr>
* <tr>
* <td valign="top" >ML</td>
* <td valign="top" >number</td>
* <td>(Optional; PDF 1.3) The miter limit.</td>
* </tr>
* <tr>
* <td valign="top" >D</td>
* <td valign="top" >array</td>
* <td>(Optional; PDF 1.3) The line dash pattern, expressed as an array of
* the form [dashArray dashPhase], where dashArray is itself an array
* and dashPhase is an integer</td>
* </tr>
* <tr>
* <td valign="top" >RI</td>
* <td valign="top" >name</td>
* <td>(Optional; PDF 1.3) The name of the rendering intent.</td>
* </tr>
* <tr>
* <td valign="top" >OP</td>
* <td valign="top" >boolean</td>
* <td>(Optional) A flag specifying whether to apply overprint. In PDF 1.2
* and earlier, there is a single overprint parameter that applies to
* all painting operations. Beginning with PDF 1.3, there are two
* separate overprint parameters: one for stroking and one for all other
* painting operations. Specifying an OP entry sets both parameters
* unless there is also an op entry in the same graphics state parameter
* dictionary, in which case the OP entry sets only the overprint
* parameter for stroking.</td>
* </tr>
* <tr>
* <td valign="top" >op</td>
* <td valign="top" >boolean</td>
* <td>(Optional; PDF 1.3) A flag specifying whether to apply overprint for
* painting operations other than stroking. If this entry is absent,
* the OP entry, if any, sets this parameter.</td>
* </tr>
* <tr>
* <td valign="top" >OPM</td>
* <td valign="top" >integer</td>
* <td>(Optional; PDF 1.3) The overprint mode</td>
* </tr>
* <tr>
* <td valign="top" >Font</td>
* <td valign="top" >array</td>
* <td>(Optional; PDF 1.3) An array of the form [font size], where font is
* an indirect reference to a font dictionary and size is a number
* expressed in text space units. These two objects correspond to the
* operands of the Tf operator; however, the first operand is an
* indirect object reference instead of a resource name.</td>
* </tr>
* <tr>
* <td valign="top" >BG</td>
* <td valign="top" >function</td>
* <td>(Optional) The black-generation function, which maps the interval
* [0.0 1.0] to the interval [0.0 1.0]</td>
* </tr>
* <tr>
* <td valign="top" >BG2</td>
* <td valign="top" >function or name</td>
* <td>(Optional; PDF 1.3) Same as BG except that the value may also be the
* name Default, denoting the black-generation function that was in effect at the
* start of the page. If both BG and BG2 are present in the same graphics state
* parameter dictionary, BG2 takes precedence.</td>
* </tr>
* <tr>
* <td valign="top" >UCR</td>
* <td valign="top" >function</td>
* <td>(Optional) The undercolor-removal function, which maps the interval
* [0.0 1.0] to the interval [?1.0 1.0] (see Section 6.2.3, "Conversion
* from DeviceRGB to DeviceCMYK").</td>
* </tr>
* <tr>
* <td valign="top" >UCR2</td>
* <td valign="top" >function or name</td>
* <td>(Optional; PDF 1.3) Same as UCR except that the value may also be
* the name Default, denoting the undercolor-removal function that was
* in effect at the start of the page. If both UCR and UCR2 are present
* in the same graphics state parameter dictionary, UCR2 takes precedence.</td>
* </tr>
* <tr>
* <td valign="top" >TR</td>
* <td valign="top" >function, array, or name</td>
* <td>(Optional) The transfer function, which maps the interval [0.0 1.0]
* to the interval [0.0 1.0] (see Section 6.3, "Transfer Functions").
* The value is either a single function (which applies to all process
* colorants) or an array of four functions (which apply to the process
* colorants individually). The name Identity may be used to represent
* the identity function.</td>
* </tr>
* <tr>
* <td valign="top" >TR2</td>
* <td valign="top" >function, array, or name</td>
* <td>(Optional; PDF 1.3) Same as TR except that the value may also be the name
* Default, denoting the transfer function that was in effect at the start of the
* page. If both TR and TR2 are present in the same graphics state parameter dictionary,
* TR2 takes precedence.</td>
* </tr>
* <tr>
* <td valign="top" >HT</td>
* <td valign="top" >dictionary, stream, or name</td>
* <td>(Optional) The halftone dictionary or stream (see Section 6.4, "Halftones") or
* the name Default, denoting the halftone that was in effect at the start of the
* page.</td>
* </tr>
* <tr>
* <td valign="top" >FL</td>
* <td valign="top" >number</td>
* <td>(Optional; PDF 1.3) The flatness tolerance</td>
* </tr>
* <tr>
* <td valign="top" >SM</td>
* <td valign="top" >number</td>
* <td>(Optional; PDF 1.3) The smoothness tolerance</td>
* </tr>
* <tr>
* <td valign="top" >SA</td>
* <td valign="top" >boolean</td>
* <td>(Optional) A flag specifying whether to apply automatic stroke adjustment
* (see Section 6.5.4, "Automatic Stroke Adjustment").</td>
* </tr>
* <tr>
* <td valign="top" >BM</td>
* <td valign="top" >name or array</td>
* <td>(Optional; PDF 1.4) The current blend mode to be used in the
* transparent imaging model.</td>
* </tr>
* <tr>
* <td valign="top" >SMask</td>
* <td valign="top" >dictionary or name</td>
* <td>(Optional; PDF 1.4) The current soft mask, specifying the mask shape
* or mask opacity values to be used in the transparent imaging model</td>
* </tr>
* <tr>
* <td valign="top" >CA </td>
* <td valign="top" >number</td>
* <td>(Optional; PDF 1.4) The current stroking alpha constant,
* specifying the constant shape or constant opacity value to be used for
* stroking operations in the transparent imaging model.</td>
* </tr>
* <tr>
* <td valign="top" >ca</td>
* <td valign="top" >number</td>
* <td>(Optional; PDF 1.4) Same as CA, but for nonstroking operations.</td>
* </tr>
* </table>
* <br>
* <p>An <code>ExtGState</code> object is is referenced by a named resource of
* the type ExtGSate. The Resources class method getExtGState() will try and
* return an <code>ExtGState</code> object of the specified name. If successful
* the <code>ExtGState</code> object should be concatenated with the current
* <code>GraphicsState</code> object.</p>
* <br>
* <p><b>Note: </b>many of the external graphics state parameters have not
* yet been been implemented in the context of the content parser.
* The <code>GraphicsSate</code> object and other relevant rendering pipeline
* classes can be updated as needed.</p>
*
* @since 1.4
*/
public class ExtGState extends Dictionary {
private static final Logger logger =
Logger.getLogger(ExtGState.class.toString());
public static final Name SMASK_KEY = new Name("SMask");
public static final Name LW_KEY = new Name("LW");
public static final Name LC_KEY = new Name("LC");
public static final Name LJ_KEY = new Name("LJ");
public static final Name ML_KEY = new Name("ML");
public static final Name CA_KEY = new Name("CA");
public static final Name ca_KEY = new Name("ca");
public static final Name BM_KEY = new Name("BM");
public static final Name OP_KEY = new Name("OP");
public static final Name op_KEY = new Name("op");
public static final Name OPM_KEY = new Name("OPM");
public static final Name D_KEY = new Name("D");
public static final Name AIS_KEY = new Name("AIS");
public static final Name HT_KEY = new Name("HT");
public static final Name BG2_KEY = new Name("BG2");
// (Optional) A flag specifying whether to apply automatic stroke adjustment
// (see 10.6.5, "Automatic Stroke Adjustment").
public static final Name SA_KEY = new Name("SA");
/**
* Creates a a new Graphics State object.
*
* @param library document object library
* @param graphicsState dictionary containing entries from teh graphcis
* state parameters dictionary.
*/
public ExtGState(Library library, HashMap graphicsState) {
super(library, graphicsState);
}
/**
* Gets the line width specified by the external graphics state.
*
* @return the line width with Number value. If the line width was not
* specified in the dictionary null is returned.
*/
public Number getLineWidth() {
return getNumber(LW_KEY);
}
/**
* Gets the line cap style specified by the external graphics state.
*
* @return the line cap style Number value. If the cap style was not
* specified in the dictionary null is returned.
*/
public Number getLineCapStyle() {
return getNumber(LC_KEY);
}
/**
* Gets the blending mode assigned to the GS.
*
* @return
*/
public Name getBlendingMode() {
Object tmp = library.getObject(entries, BM_KEY);
if (tmp instanceof Name) {
return (Name) tmp;
} else if (tmp instanceof List) {
List list = (List) tmp;
return (Name) list.get(0);
}
return null;
}
/**
* Gets the line join style specified by the external graphics state.
*
* @return the line join style Number value. If the join style was not
* specified in the dictionary null is returned.
*/
public Number getLineJoinStyle() {
return getNumber(LJ_KEY);
}
/**
* Gets the miter limit specified by the external graphics state.
*
* @return the miter limit Number value. If the miter limit was not
* specified in the dictionary null is returned.
*/
Number getMiterLimit() {
return getNumber(ML_KEY);
}
/**
* Gets the line dash pattern specified by the external graphics state.
*
* @return the line dash array [dashArray dashPhase]. If the dash pattern
* is not specified the dictionary null is returned.
*/
public List getLineDashPattern() {
List<Object> dashPattern = null;
Number dashPhase;
float[] dashArray = null;
if (entries.containsKey(D_KEY)) {
try {
List dashData = (List) entries.get(D_KEY);
// pop dashPhase off the stack
dashPhase = (Number) dashData.get(1);
// pop the dashVector of the stack
List dashVector = (List) dashData.get(0);
// if the dash vector size is zero we have a default none dashed
// line and thus we skip out
if (dashVector.size() > 0) {
// conver dash vector to a array of floats
final int sz = dashVector.size();
dashArray = new float[sz];
for (int i = 0; i < sz; i++) {
dashArray[i] = ((Number) dashVector.get(i)).floatValue();
}
}
// default to standard black line
else {
dashPhase = 0f;
dashArray = null;
}
dashPattern = new ArrayList<Object>(2);
dashPattern.add(dashArray);
dashPattern.add(dashPhase);
} catch (ClassCastException e) {
logger.log(Level.FINE, "Dash pattern syntax error: ", e);
}
}
return dashPattern;
}
/**
* Gets the stroking alpha constant specified by the external graphics state.
*
* @return the stroking alpha constant value. If the stroking alpha constant
* was not specified in the dictionary null is returned.
*/
public float getStrokingAlphConstant() {
if (getNumber(CA_KEY) != null)
return getFloat(CA_KEY);
else {
return -1;
}
}
/**
* Gets the non-stroking alpha constant specified by the external graphics state.
*
* @return the non stroking alpha constant value. If the non-stroking alpha constant
* was not specified in the dictionary null is returned.
*/
public float getNonStrokingAlphConstant() {
if (getNumber(ca_KEY) != null)
return getFloat(ca_KEY);
else {
return -1;
}
}
/**
* An optional flag specifying whether to apply overprint. In PDF 1.2
* and earlier, there is a single overprint parameter that applies to
* all painting operations. Beginning with PDF 1.3, there are two
* separate overprint parameters: one for stroking and one for all other
* painting operations. Specifying an OP entry sets both parameters
* unless there is also an op entry in the same graphics state parameter
* dictionary, in which case the OP entry sets only the overprint
* parameter for stroking.
*
* @return true if OP is enabled.
*/
public Boolean getOverprint() {
Object o = getObject(OP_KEY);
if (o instanceof String)
return Boolean.valueOf((String) o);
else if (o instanceof Boolean) {
return (Boolean) o;
}
return null;
}
public Boolean isAlphaAShape() {
Object o = getObject(AIS_KEY);
if (o instanceof String)
return Boolean.valueOf((String) o);
else if (o instanceof Boolean) {
return (Boolean) o;
}
return false;
}
/**
* An optional flag specifying whether to apply overprint for
* painting operations other than stroking. If this entry is absent,
* the OP entry, if any, sets this parameter.
*
* @return true if enabled, false otherwise.
*/
public Boolean getOverprintFill() {
Object o = getObject(op_KEY);
if (o instanceof String)
return Boolean.valueOf((String) o);
else if (o instanceof Boolean) {
return (Boolean) o;
}
return null;
}
/**
* The overprint mode
*
* @return
*/
public int getOverprintMode() {
return getInt(OPM_KEY);
}
public boolean hasOverPrintMode() {
return library.getObject(entries, OPM_KEY) != null;
}
public boolean hasAlphaIsShape() {
return library.getObject(entries, AIS_KEY) != null;
}
public boolean hasHalfTone() {
return library.getObject(entries, HT_KEY) != null;
}
public boolean hasBG2Function() {
return library.getObject(entries, BG2_KEY) != null;
}
public SoftMask getSMask() {
Object tmp = library.getObject(entries, SMASK_KEY);
if (tmp != null && tmp instanceof HashMap) {
// create a new SMask dictionary
SoftMask softMask = new SoftMask(library, (HashMap) tmp);
softMask.setPObjectReference(library.getReference(entries, SMASK_KEY));
return softMask;
}
return null;
}
}