001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.pdf.pd;
031:
032: import java.util.HashSet;
033: import java.util.Iterator;
034: import java.util.Set;
035:
036: import de.intarsys.pdf.content.CSContent;
037: import de.intarsys.pdf.content.CSDeviceBasedInterpreter;
038: import de.intarsys.pdf.content.CSOperation;
039: import de.intarsys.pdf.content.CSOperators;
040: import de.intarsys.pdf.content.CSVirtualDevice;
041: import de.intarsys.pdf.cos.COSDictionary;
042: import de.intarsys.pdf.cos.COSFixed;
043: import de.intarsys.pdf.cos.COSInteger;
044: import de.intarsys.pdf.cos.COSName;
045: import de.intarsys.pdf.font.PDFont;
046: import de.intarsys.pdf.font.PDFontTools;
047:
048: /**
049: * A internal representation of the parsed {@link CSContent} for the default
050: * appearance content stream fragment in a {@link PDAcroFormField}.
051: *
052: */
053: public class DefaultAppearance {
054: public class DefaultAppearanceDevice extends CSVirtualDevice {
055: public void textSetFont(COSName name, PDFont paramFont,
056: float size) {
057: fontName = name;
058: fontSize = size;
059: }
060:
061: public void setNonStrokeColorSpace(COSName name,
062: PDColorSpace colorSpace) {
063: nonStrokeColorSpaceName = name;
064: }
065:
066: public void setNonStrokeColorValues(float[] values) {
067: nonStrokeColorValues = values;
068: }
069:
070: public void setStrokeColorSpace(COSName name,
071: PDColorSpace colorSpace) {
072: strokeColorSpaceName = name;
073: }
074:
075: public void setStrokeColorValues(float[] values) {
076: strokeColorValues = values;
077: }
078: }
079:
080: private PDAcroFormNode node;
081:
082: protected COSName fontName;
083:
084: protected float fontSize;
085:
086: protected float[] nonStrokeColorValues;
087:
088: protected COSName nonStrokeColorSpaceName;
089:
090: protected float[] strokeColorValues;
091:
092: protected COSName strokeColorSpaceName;
093:
094: public DefaultAppearance(PDAcroFormNode node) {
095: super ();
096: this .node = node;
097: CSDeviceBasedInterpreter interpreter;
098: interpreter = new CSDeviceBasedInterpreter(null,
099: new DefaultAppearanceDevice());
100: interpreter.process(node.getDefaultAppearanceContent(), node
101: .getAcroForm().getDefaultResources());
102: }
103:
104: protected void addFontResource(PDAcroForm form, PDFont font,
105: COSName pFontName) {
106: PDResources resources = form.getDefaultResources();
107: if (resources == null) {
108: resources = (PDResources) PDResources.META.createNew();
109: form.setDefaultResources(resources);
110: }
111:
112: // add font to default resource dict (if it's not already there)
113: PDFont containedFont = resources.getFontResource(pFontName);
114: if (containedFont == null) {
115: // add font
116: resources.addFontResource(pFontName, font);
117: }
118: }
119:
120: protected void addFontResource(PDFont font, COSName pFontName) {
121: addFontResource(node.getAcroForm(), font, pFontName);
122: }
123:
124: protected void cleanupFontResources(PDAcroForm form) {
125: // collect used font keys
126: Set referencedKeys = getReferencedFontKeys(form);
127:
128: // determine overhead
129: Set overhead = new HashSet();
130: COSDictionary fontDict = form.getDefaultResources()
131: .cosGetResources(PDResources.CN_RT_Font);
132: for (Iterator i = fontDict.keySet().iterator(); i.hasNext();) {
133: COSName key = (COSName) i.next();
134: if (!referencedKeys.contains(key)) {
135: overhead.add(key);
136: }
137: }
138:
139: // remove overhead
140: for (Iterator i = overhead.iterator(); i.hasNext();) {
141: COSName key = (COSName) i.next();
142: fontDict.remove(key);
143: }
144: }
145:
146: protected void collectDefaultAppearanceFonts(PDAcroFormNode pNode,
147: Set keys) {
148: // check usage in default appearance
149: COSName fontKey = pNode.getDefaultAppearanceFontName();
150: if (fontKey != null) {
151: keys.add(fontKey);
152: }
153:
154: //
155: if (pNode.getGenericChildren() == null) {
156: return;
157: }
158: for (Iterator i = pNode.getGenericChildren().iterator(); i
159: .hasNext();) {
160: PDAcroFormNode child = (PDAcroFormNode) i.next();
161: collectDefaultAppearanceFonts(child, keys);
162: }
163: }
164:
165: protected void contentReplaceColor(float[] color) {
166: CSContent appearance = node.getDefaultAppearanceContent();
167: CSContent content = CSContent.createNew();
168: CSOperation op = null;
169: switch (color.length) {
170: case 1: {
171: op = new CSOperation(CSOperators.CSO_g);
172: break;
173: }
174: case 3: {
175: op = new CSOperation(CSOperators.CSO_rg);
176: break;
177: }
178: case 4: {
179: op = new CSOperation(CSOperators.CSO_k);
180: break;
181: }
182: }
183: for (int index = 0; index < color.length; index++) {
184: op.addOperand(COSFixed.create(color[index]));
185: }
186: if (appearance == null) {
187: content.addOperation(op);
188: } else {
189: // copy / modify stream
190: int len = appearance.size();
191: boolean replaced = false;
192: for (int i = 0; i < len; i++) {
193: CSOperation operation = appearance.getOperation(i);
194: if (operation.matchesOperator(CSOperators.CSO_g)
195: || operation
196: .matchesOperator(CSOperators.CSO_rg)
197: || operation.matchesOperator(CSOperators.CSO_k)) {
198: content.addOperation(op);
199: replaced = true;
200: } else {
201: content.addOperation(operation);
202: }
203: }
204: if (!replaced) {
205: content.addOperation(op);
206: }
207: }
208: node.setDefaultAppearanceContent(content);
209: }
210:
211: protected void contentReplaceFont(COSName pFontName) {
212: CSContent appearance = node.getDefaultAppearanceContent();
213: CSContent content = CSContent.createNew();
214: CSOperation op = new CSOperation(CSOperators.CSO_Tf);
215: op.addOperand(pFontName);
216: op.addOperand(COSInteger.create(0));
217: if (appearance == null) {
218: content.addOperation(op);
219: } else {
220: // copy / modify stream
221: int len = appearance.size();
222: boolean replaced = false;
223: for (int i = 0; i < len; i++) {
224: CSOperation operation = appearance.getOperation(i);
225: if (operation.matchesOperator(CSOperators.CSO_Tf)) {
226: if (operation.operandSize() >= 2) {
227: op.setOperand(1, operation.getOperand(1));
228: }
229: content.addOperation(op);
230: replaced = true;
231: } else {
232: content.addOperation(operation);
233: }
234: }
235: if (!replaced) {
236: content.addOperation(op);
237: }
238: }
239: node.setDefaultAppearanceContent(content);
240: }
241:
242: protected void contentReplaceSize(float pFontSize) {
243: CSContent appearance = node.getDefaultAppearanceContent();
244: CSContent content = CSContent.createNew();
245: CSOperation op = new CSOperation(CSOperators.CSO_Tf);
246: op.addOperand(COSName.create("Helv")); //$NON-NLS-1$
247: op.addOperand(COSFixed.create(pFontSize));
248: if (appearance == null) {
249: content.addOperation(op);
250: } else {
251: // copy / modify stream
252: int len = appearance.size();
253: boolean replaced = false;
254: for (int i = 0; i < len; i++) {
255: CSOperation operation = appearance.getOperation(i);
256: if (operation.matchesOperator(CSOperators.CSO_Tf)) {
257: if (operation.operandSize() >= 1) {
258: op.setOperand(0, operation.getOperand(0));
259: }
260: content.addOperation(op);
261: replaced = true;
262: } else {
263: content.addOperation(operation);
264: }
265: }
266: if (!replaced) {
267: content.addOperation(op);
268: }
269: }
270: node.setDefaultAppearanceContent(content);
271: }
272:
273: public PDFont getFont() {
274: PDResources resources = node.getAcroForm()
275: .getDefaultResources();
276: if (resources == null) {
277: return null;
278: }
279: return PDFontTools.getFont(resources, getFontName());
280: }
281:
282: /**
283: * parse the requested font color from the default appearance string
284: *
285: * @return the font color used in the default appearance
286: *
287: * @throws IllegalStateException
288: */
289: public float[] getFontColorValues() {
290: return nonStrokeColorValues;
291: }
292:
293: public COSName getFontName() {
294: return fontName;
295: }
296:
297: public float getFontSize() {
298: return fontSize;
299: }
300:
301: public PDAcroFormNode getNode() {
302: return node;
303: }
304:
305: protected Set getReferencedFontKeys(PDAcroForm form) {
306: Set referencedKeys = new HashSet();
307: collectDefaultAppearanceFonts(form, referencedKeys);
308: return referencedKeys;
309: }
310:
311: public void setFont(PDFont font) {
312: COSName cosFontName = COSName.create(font.getFontName());
313: //
314: addFontResource(font, cosFontName);
315: setFontName(cosFontName);
316: //
317: cleanupFontResources(node.getAcroForm());
318: }
319:
320: public void setFontColorValues(float[] color) {
321: contentReplaceColor(color);
322: nonStrokeColorValues = color;
323: }
324:
325: public void setFontName(COSName pFontName) {
326: contentReplaceFont(pFontName);
327: fontName = pFontName;
328: }
329:
330: public void setFontSize(float pFontSize) {
331: contentReplaceSize(pFontSize);
332: fontSize = pFontSize;
333: }
334: }
|