001/*
002 * $Id: PdfShading.java 4784 2011-03-15 08:33:00Z blowagie $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044package com.itextpdf.text.pdf;
045
046import java.io.IOException;
047import com.itextpdf.text.error_messages.MessageLocalization;
048import com.itextpdf.text.BaseColor;
049/** Implements the shading dictionary (or stream).
050 *
051 * @author Paulo Soares
052 */
053public class PdfShading {
054
055    protected PdfDictionary shading;
056    
057    protected PdfWriter writer;
058    
059    protected int shadingType;
060    
061    protected ColorDetails colorDetails;
062    
063    protected PdfName shadingName;
064    
065    protected PdfIndirectReference shadingReference;
066    
067    private BaseColor cspace;
068    
069    /** Holds value of property bBox. */
070    protected float[] bBox;
071    
072    /** Holds value of property antiAlias. */
073    protected boolean antiAlias = false;
074    
075    /** Creates new PdfShading */
076    protected PdfShading(PdfWriter writer) {
077        this.writer = writer;
078    }
079    
080    protected void setColorSpace(BaseColor color) {
081        cspace = color;
082        int type = ExtendedColor.getType(color);
083        PdfObject colorSpace = null;
084        switch (type) {
085            case ExtendedColor.TYPE_GRAY: {
086                colorSpace = PdfName.DEVICEGRAY;
087                break;
088            }
089            case ExtendedColor.TYPE_CMYK: {
090                colorSpace = PdfName.DEVICECMYK;
091                break;
092            }
093            case ExtendedColor.TYPE_SEPARATION: {
094                SpotColor spot = (SpotColor)color;
095                colorDetails = writer.addSimple(spot.getPdfSpotColor());
096                colorSpace = colorDetails.getIndirectReference();
097                break;
098            }
099            case ExtendedColor.TYPE_PATTERN:
100            case ExtendedColor.TYPE_SHADING: {
101                throwColorSpaceError();
102            }
103            default:
104                colorSpace = PdfName.DEVICERGB;
105                break;
106        }
107        shading.put(PdfName.COLORSPACE, colorSpace);
108    }
109    
110    public BaseColor getColorSpace() {
111        return cspace;
112    }
113    
114    public static void throwColorSpaceError() {
115        throw new IllegalArgumentException(MessageLocalization.getComposedMessage("a.tiling.or.shading.pattern.cannot.be.used.as.a.color.space.in.a.shading.pattern"));
116    }
117    
118    public static void checkCompatibleColors(BaseColor c1, BaseColor c2) {
119        int type1 = ExtendedColor.getType(c1);
120        int type2 = ExtendedColor.getType(c2);
121        if (type1 != type2)
122            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("both.colors.must.be.of.the.same.type"));
123        if (type1 == ExtendedColor.TYPE_SEPARATION && ((SpotColor)c1).getPdfSpotColor() != ((SpotColor)c2).getPdfSpotColor())
124            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("the.spot.color.must.be.the.same.only.the.tint.can.vary"));
125        if (type1 == ExtendedColor.TYPE_PATTERN || type1 == ExtendedColor.TYPE_SHADING)
126            throwColorSpaceError();
127    }
128    
129    public static float[] getColorArray(BaseColor color) {
130        int type = ExtendedColor.getType(color);
131        switch (type) {
132            case ExtendedColor.TYPE_GRAY: {
133                return new float[]{((GrayColor)color).getGray()};
134            }
135            case ExtendedColor.TYPE_CMYK: {
136                CMYKColor cmyk = (CMYKColor)color;
137                return new float[]{cmyk.getCyan(), cmyk.getMagenta(), cmyk.getYellow(), cmyk.getBlack()};
138            }
139            case ExtendedColor.TYPE_SEPARATION: {
140                return new float[]{((SpotColor)color).getTint()};
141            }
142            case ExtendedColor.TYPE_RGB: {
143                return new float[]{color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f};
144            }
145        }
146        throwColorSpaceError();
147        return null;
148    }
149
150    public static PdfShading type1(PdfWriter writer, BaseColor colorSpace, float domain[], float tMatrix[], PdfFunction function) {
151        PdfShading sp = new PdfShading(writer);
152        sp.shading = new PdfDictionary();
153        sp.shadingType = 1;
154        sp.shading.put(PdfName.SHADINGTYPE, new PdfNumber(sp.shadingType));
155        sp.setColorSpace(colorSpace);
156        if (domain != null)
157            sp.shading.put(PdfName.DOMAIN, new PdfArray(domain));
158        if (tMatrix != null)
159            sp.shading.put(PdfName.MATRIX, new PdfArray(tMatrix));
160        sp.shading.put(PdfName.FUNCTION, function.getReference());
161        return sp;
162    }
163    
164    public static PdfShading type2(PdfWriter writer, BaseColor colorSpace, float coords[], float domain[], PdfFunction function, boolean extend[]) {
165        PdfShading sp = new PdfShading(writer);
166        sp.shading = new PdfDictionary();
167        sp.shadingType = 2;
168        sp.shading.put(PdfName.SHADINGTYPE, new PdfNumber(sp.shadingType));
169        sp.setColorSpace(colorSpace);
170        sp.shading.put(PdfName.COORDS, new PdfArray(coords));
171        if (domain != null)
172            sp.shading.put(PdfName.DOMAIN, new PdfArray(domain));
173        sp.shading.put(PdfName.FUNCTION, function.getReference());
174        if (extend != null && (extend[0] || extend[1])) {
175            PdfArray array = new PdfArray(extend[0] ? PdfBoolean.PDFTRUE : PdfBoolean.PDFFALSE);
176            array.add(extend[1] ? PdfBoolean.PDFTRUE : PdfBoolean.PDFFALSE);
177            sp.shading.put(PdfName.EXTEND, array);
178        }
179        return sp;
180    }
181
182    public static PdfShading type3(PdfWriter writer, BaseColor colorSpace, float coords[], float domain[], PdfFunction function, boolean extend[]) {
183        PdfShading sp = type2(writer, colorSpace, coords, domain, function, extend);
184        sp.shadingType = 3;
185        sp.shading.put(PdfName.SHADINGTYPE, new PdfNumber(sp.shadingType));
186        return sp;
187    }
188    
189    public static PdfShading simpleAxial(PdfWriter writer, float x0, float y0, float x1, float y1, BaseColor startColor, BaseColor endColor, boolean extendStart, boolean extendEnd) {
190        checkCompatibleColors(startColor, endColor);
191        PdfFunction function = PdfFunction.type2(writer, new float[]{0, 1}, null, getColorArray(startColor),
192            getColorArray(endColor), 1);
193        return type2(writer, startColor, new float[]{x0, y0, x1, y1}, null, function, new boolean[]{extendStart, extendEnd});
194    }
195    
196    public static PdfShading simpleAxial(PdfWriter writer, float x0, float y0, float x1, float y1, BaseColor startColor, BaseColor endColor) {
197        return simpleAxial(writer, x0, y0, x1, y1, startColor, endColor, true, true);
198    }
199    
200    public static PdfShading simpleRadial(PdfWriter writer, float x0, float y0, float r0, float x1, float y1, float r1, BaseColor startColor, BaseColor endColor, boolean extendStart, boolean extendEnd) {
201        checkCompatibleColors(startColor, endColor);
202        PdfFunction function = PdfFunction.type2(writer, new float[]{0, 1}, null, getColorArray(startColor),
203            getColorArray(endColor), 1);
204        return type3(writer, startColor, new float[]{x0, y0, r0, x1, y1, r1}, null, function, new boolean[]{extendStart, extendEnd});
205    }
206
207    public static PdfShading simpleRadial(PdfWriter writer, float x0, float y0, float r0, float x1, float y1, float r1, BaseColor startColor, BaseColor endColor) {
208        return simpleRadial(writer, x0, y0, r0, x1, y1, r1, startColor, endColor, true, true);
209    }
210
211    PdfName getShadingName() {
212        return shadingName;
213    }
214    
215    PdfIndirectReference getShadingReference() {
216        if (shadingReference == null)
217            shadingReference = writer.getPdfIndirectReference();
218        return shadingReference;
219    }
220    
221    void setName(int number) {
222        shadingName = new PdfName("Sh" + number);
223    }
224    
225    void addToBody() throws IOException {
226        if (bBox != null)
227            shading.put(PdfName.BBOX, new PdfArray(bBox));
228        if (antiAlias)
229            shading.put(PdfName.ANTIALIAS, PdfBoolean.PDFTRUE);
230        writer.addToBody(shading, getShadingReference());
231    }
232    
233    PdfWriter getWriter() {
234        return writer;
235    }
236    
237    ColorDetails getColorDetails() {
238        return colorDetails;
239    }
240    
241    public float[] getBBox() {
242        return bBox;
243    }
244    
245    public void setBBox(float[] bBox) {
246        if (bBox.length != 4)
247            throw new IllegalArgumentException(MessageLocalization.getComposedMessage("bbox.must.be.a.4.element.array"));
248        this.bBox = bBox;
249    }
250    
251    public boolean isAntiAlias() {
252        return antiAlias;
253    }
254    
255    public void setAntiAlias(boolean antiAlias) {
256        this.antiAlias = antiAlias;
257    }
258    
259}