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 
18 /* $Id: Trait.java 1790795 2017-04-10 10:03:35Z cbowditch $ */
19 
20 package org.apache.fop.area;
21 
22 import java.awt.Color;
23 import java.io.Serializable;
24 
25 import org.apache.xmlgraphics.image.loader.ImageInfo;
26 
27 import org.apache.fop.fonts.FontTriplet;
28 import org.apache.fop.traits.BorderProps;
29 import org.apache.fop.traits.Direction;
30 import org.apache.fop.traits.Visibility;
31 import org.apache.fop.traits.WritingMode;
32 import org.apache.fop.util.ColorUtil;
33 
34 import static org.apache.fop.fo.Constants.EN_NOREPEAT;
35 import static org.apache.fop.fo.Constants.EN_REPEAT;
36 import static org.apache.fop.fo.Constants.EN_REPEATX;
37 import static org.apache.fop.fo.Constants.EN_REPEATY;
38 
39 // properties should be serialized by the holder
40 /**
41  * Area traits used for rendering.
42  * This class represents an area trait that specifies a value for rendering.
43  */
44 public final class Trait implements Serializable {
45 
46     private static final long serialVersionUID = 3234280285391611437L;
47 
Trait()48     private Trait() {
49     }
50 
51     /** Id reference line, not resolved. (not sure if this is needed.) */
52     //public static final Integer ID_LINK = Integer.valueOf(0);
53 
54     /**
55      * Internal link trait.
56      * Contains the PageViewport key and the PROD_ID of the target area
57      */
58     public static final Integer INTERNAL_LINK = 1;
59 
60     /** * External link. A URL link to an external resource. */
61     public static final Integer EXTERNAL_LINK = 2;
62 
63     /** The font triplet for the current font. */
64     public static final Integer FONT = 3;
65 
66     /** Font size for the current font. */
67     public static final Integer FONT_SIZE = 4;
68 
69     /** The current color. */
70     public static final Integer COLOR = 7;
71 
72     /** The ID of the FO that produced an area. */
73     public static final Integer PROD_ID = 8;
74 
75     /** Background trait for an area. */
76     public static final Integer BACKGROUND = 9;
77 
78     /** Underline trait used when rendering inline parent. */
79     public static final Integer UNDERLINE = 10;
80 
81     /** Overline trait used when rendering inline parent. */
82     public static final Integer OVERLINE = 11;
83 
84     /** Linethrough trait used when rendering inline parent. */
85     public static final Integer LINETHROUGH = 12;
86 
87     /** Shadow offset. */
88     //public static final Integer OFFSET = Integer.valueOf(13);
89 
90     /** The shadow for text. */
91     //public static final Integer SHADOW = Integer.valueOf(14);
92 
93     /** The border start. */
94     public static final Integer BORDER_START = 15;
95 
96     /** The border end. */
97     public static final Integer BORDER_END = 16;
98 
99     /** The border before. */
100     public static final Integer BORDER_BEFORE = 17;
101 
102     /** The border after. */
103     public static final Integer BORDER_AFTER = 18;
104 
105     /** The padding start. */
106     public static final Integer PADDING_START = 19;
107 
108     /** The padding end. */
109     public static final Integer PADDING_END = 20;
110 
111     /** The padding before. */
112     public static final Integer PADDING_BEFORE = 21;
113 
114     /** The padding after. */
115     public static final Integer PADDING_AFTER = 22;
116 
117     /** The space start. */
118     public static final Integer SPACE_START = 23;
119 
120     /** The space end. */
121     public static final Integer SPACE_END  = 24;
122 
123     /** break before */
124     //public static final Integer BREAK_BEFORE = Integer.valueOf(25);
125 
126     /** break after */
127     //public static final Integer BREAK_AFTER = Integer.valueOf(26);
128 
129     /** The start-indent trait. */
130     public static final Integer START_INDENT = 27;
131 
132     /** The end-indent trait. */
133     public static final Integer END_INDENT  = 28;
134 
135     /** The space-before trait. */
136     public static final Integer SPACE_BEFORE  = 29;
137 
138     /** The space-after trait. */
139     public static final Integer SPACE_AFTER  = 30;
140 
141     /** The is-reference-area trait. */
142     public static final Integer IS_REFERENCE_AREA = 31;
143 
144     /** The is-viewport-area trait. */
145     public static final Integer IS_VIEWPORT_AREA = 32;
146 
147     /** Blinking trait used when rendering inline parent. */
148     public static final Integer BLINK = 33;
149 
150     /** Trait for color of underline decorations when rendering inline parent. */
151     public static final Integer UNDERLINE_COLOR = 34;
152 
153     /** Trait for color of overline decorations when rendering inline parent. */
154     public static final Integer OVERLINE_COLOR = 35;
155 
156     /** Trait for color of linethrough decorations when rendering inline parent. */
157     public static final Integer LINETHROUGH_COLOR = 36;
158 
159     /** For navigation in the document structure. */
160     public static final Integer STRUCTURE_TREE_ELEMENT = 37;
161 
162     /** writing mode trait */
163     public static final Integer WRITING_MODE = 38;
164     /** inline progression direction trait */
165     public static final Integer INLINE_PROGRESSION_DIRECTION = 39;
166     /** block progression direction trait */
167     public static final Integer BLOCK_PROGRESSION_DIRECTION = 40;
168     /** column progression direction trait */
169     public static final Integer COLUMN_PROGRESSION_DIRECTION = 41;
170     /** shift direction trait */
171     public static final Integer SHIFT_DIRECTION = 42;
172 
173     /** For optional content groups. */
174     public static final Integer LAYER = 43;
175 
176     /** Used to disable the rendering of a Block http://www.w3.org/TR/xsl/#rend-vis */
177     public static final Integer VISIBILITY = 44;
178 
179     /** Maximum value used by trait keys */
180     public static final int MAX_TRAIT_KEY = 44;
181 
182     private static final TraitInfo[] TRAIT_INFO = new TraitInfo[MAX_TRAIT_KEY + 1];
183 
184     private static class TraitInfo {
185         private String name;
186         private Class clazz; // Class of trait data
187 
TraitInfo(String name, Class clazz)188         public TraitInfo(String name, Class clazz) {
189             this.name = name;
190             this.clazz = clazz;
191         }
192 
getName()193         public String getName() {
194             return this.name;
195         }
196 
getClazz()197         public Class getClazz() {
198             return this.clazz;
199         }
200     }
201 
put(Integer key, TraitInfo info)202     private static void put(Integer key, TraitInfo info) {
203         TRAIT_INFO[key] = info;
204     }
205 
206     static {
207         // Create a hashmap mapping trait code to name for external representation
208         //put(ID_LINK, new TraitInfo("id-link", String.class));
put(STRUCTURE_TREE_ELEMENT, new TraitInfo(R, String.class))209         put(STRUCTURE_TREE_ELEMENT, new TraitInfo("structure-tree-element", String.class));
put(INTERNAL_LINK, new TraitInfo(R, InternalLink.class))210         put(INTERNAL_LINK,  new TraitInfo("internal-link", InternalLink.class));
put(EXTERNAL_LINK, new TraitInfo(R, ExternalLink.class))211         put(EXTERNAL_LINK,  new TraitInfo("external-link", ExternalLink.class));
put(FONT, new TraitInfo(R, FontTriplet.class))212         put(FONT,           new TraitInfo("font", FontTriplet.class));
put(FONT_SIZE, new TraitInfo(R, Integer.class))213         put(FONT_SIZE,      new TraitInfo("font-size", Integer.class));
put(COLOR, new TraitInfo(R, Color.class))214         put(COLOR,          new TraitInfo("color", Color.class));
put(PROD_ID, new TraitInfo(R, String.class))215         put(PROD_ID,        new TraitInfo("prod-id", String.class));
put(BACKGROUND, new TraitInfo(R, Background.class))216         put(BACKGROUND,     new TraitInfo("background", Background.class));
put(UNDERLINE, new TraitInfo(R, Boolean.class))217         put(UNDERLINE,      new TraitInfo("underline-score", Boolean.class));
put(UNDERLINE_COLOR, new TraitInfo(R, Color.class))218         put(UNDERLINE_COLOR, new TraitInfo("underline-score-color", Color.class));
put(OVERLINE, new TraitInfo(R, Boolean.class))219         put(OVERLINE,       new TraitInfo("overline-score", Boolean.class));
put(OVERLINE_COLOR, new TraitInfo(R, Color.class))220         put(OVERLINE_COLOR, new TraitInfo("overline-score-color", Color.class));
put(LINETHROUGH, new TraitInfo(R, Boolean.class))221         put(LINETHROUGH,    new TraitInfo("through-score", Boolean.class));
put(LINETHROUGH_COLOR, new TraitInfo(R, Color.class))222         put(LINETHROUGH_COLOR, new TraitInfo("through-score-color", Color.class));
put(BLINK, new TraitInfo(R, Boolean.class))223         put(BLINK,          new TraitInfo("blink", Boolean.class));
224         //put(OFFSET,       new TraitInfo("offset", Integer.class));
225         //put(SHADOW,       new TraitInfo("shadow", Integer.class));
put(BORDER_START, new TraitInfo(R, BorderProps.class))226         put(BORDER_START,   new TraitInfo("border-start", BorderProps.class));
put(BORDER_END, new TraitInfo(R, BorderProps.class))227         put(BORDER_END,     new TraitInfo("border-end", BorderProps.class));
put(BORDER_BEFORE, new TraitInfo(R, BorderProps.class))228         put(BORDER_BEFORE,  new TraitInfo("border-before", BorderProps.class));
put(BORDER_AFTER, new TraitInfo(R, BorderProps.class))229         put(BORDER_AFTER,   new TraitInfo("border-after", BorderProps.class));
put(PADDING_START, new TraitInfo(R, Integer.class))230         put(PADDING_START,  new TraitInfo("padding-start", Integer.class));
put(PADDING_END, new TraitInfo(R, Integer.class))231         put(PADDING_END,    new TraitInfo("padding-end", Integer.class));
put(PADDING_BEFORE, new TraitInfo(R, Integer.class))232         put(PADDING_BEFORE, new TraitInfo("padding-before", Integer.class));
put(PADDING_AFTER, new TraitInfo(R, Integer.class))233         put(PADDING_AFTER,  new TraitInfo("padding-after", Integer.class));
put(SPACE_START, new TraitInfo(R, Integer.class))234         put(SPACE_START,    new TraitInfo("space-start", Integer.class));
put(SPACE_END, new TraitInfo(R, Integer.class))235         put(SPACE_END,      new TraitInfo("space-end", Integer.class));
236         //put(BREAK_BEFORE, new TraitInfo("break-before", Integer.class));
237         //put(BREAK_AFTER,  new TraitInfo("break-after", Integer.class));
put(START_INDENT, new TraitInfo(R, Integer.class))238         put(START_INDENT,   new TraitInfo("start-indent", Integer.class));
put(END_INDENT, new TraitInfo(R, Integer.class))239         put(END_INDENT,     new TraitInfo("end-indent", Integer.class));
put(SPACE_BEFORE, new TraitInfo(R, Integer.class))240         put(SPACE_BEFORE,   new TraitInfo("space-before", Integer.class));
put(SPACE_AFTER, new TraitInfo(R, Integer.class))241         put(SPACE_AFTER,    new TraitInfo("space-after", Integer.class));
put(IS_REFERENCE_AREA, new TraitInfo(R, Boolean.class))242         put(IS_REFERENCE_AREA,  new TraitInfo("is-reference-area", Boolean.class));
put(IS_VIEWPORT_AREA, new TraitInfo(R, Boolean.class))243         put(IS_VIEWPORT_AREA,   new TraitInfo("is-viewport-area", Boolean.class));
put(WRITING_MODE, new TraitInfo(R, WritingMode.class))244         put(WRITING_MODE,
245                 new TraitInfo("writing-mode", WritingMode.class));
put(INLINE_PROGRESSION_DIRECTION, new TraitInfo(R, Direction.class))246         put(INLINE_PROGRESSION_DIRECTION,
247                 new TraitInfo("inline-progression-direction", Direction.class));
put(BLOCK_PROGRESSION_DIRECTION, new TraitInfo(R, Direction.class))248         put(BLOCK_PROGRESSION_DIRECTION,
249                 new TraitInfo("block-progression-direction", Direction.class));
put(SHIFT_DIRECTION, new TraitInfo(R, Direction.class))250         put(SHIFT_DIRECTION,
251                 new TraitInfo("shift-direction", Direction.class));
put(LAYER, new TraitInfo(R, String.class))252         put(LAYER, new TraitInfo("layer", String.class));
put(VISIBILITY, new TraitInfo(R, Visibility.class))253         put(VISIBILITY, new TraitInfo("visibility", Visibility.class));
254     }
255 
256     /**
257      * Get the trait name for a trait code.
258      *
259      * @param traitCode the trait code to get the name for
260      * @return the trait name
261      */
getTraitName(Object traitCode)262     public static String getTraitName(Object traitCode) {
263         return TRAIT_INFO[(Integer)traitCode].getName();
264     }
265 
266     /**
267      * Get the data storage class for the trait.
268      *
269      * @param traitCode the trait code to lookup
270      * @return the class type for the trait
271      */
getTraitClass(Object traitCode)272     public static Class getTraitClass(Object traitCode) {
273         return TRAIT_INFO[(Integer)traitCode].getClazz();
274     }
275 
276     /**
277      * Class for internal link traits.
278      * Stores PageViewport key and producer ID
279      */
280     public static class InternalLink implements Serializable {
281 
282         private static final long serialVersionUID = -8993505060996723039L;
283 
284         /** The unique key of the PageViewport. */
285         private String pvKey;
286 
287         /** The PROD_ID of the link target */
288         private String idRef;
289 
290         /**
291          * Create an InternalLink to the given PageViewport and target ID
292          *
293          * @param pvKey the PageViewport key
294          * @param idRef the target ID
295          */
InternalLink(String pvKey, String idRef)296         public InternalLink(String pvKey, String idRef) {
297             setPVKey(pvKey);
298             setIDRef(idRef);
299         }
300 
301         /**
302          * Create an InternalLink based on the given XML attribute value.
303          * This is typically called when data are read from an XML area tree.
304          *
305          * @param attrValue attribute value to be parsed by InternalLink.parseXMLAttribute
306          */
InternalLink(String attrValue)307         public InternalLink(String attrValue) {
308             String[] values = parseXMLAttribute(attrValue);
309             setPVKey(values[0]);
310             setIDRef(values[1]);
311         }
312 
313         /**
314          * Sets the key of the targeted PageViewport.
315          *
316          * @param pvKey the PageViewport key
317          */
setPVKey(String pvKey)318         public void setPVKey(String pvKey) {
319             this.pvKey = pvKey;
320         }
321 
322         /**
323          * Returns the key of the targeted PageViewport.
324          *
325          * @return the PageViewport key
326          */
getPVKey()327         public String getPVKey() {
328             return pvKey;
329         }
330 
331         /**
332          * Sets the target ID.
333          *
334          * @param idRef the target ID
335          */
setIDRef(String idRef)336         public void setIDRef(String idRef) {
337             this.idRef = idRef;
338         }
339 
340         /**
341          * Returns the target ID.
342          *
343          * @return the target ID
344          */
getIDRef()345         public String getIDRef() {
346             return idRef;
347         }
348 
349        /**
350         * Returns the attribute value for this object as
351         * used in the area tree XML.
352         *
353         * @return a string of the type "(thisPVKey,thisIDRef)"
354         */
xmlAttribute()355        public String xmlAttribute() {
356            return makeXMLAttribute(pvKey, idRef);
357        }
358 
359        /**
360         * Returns the XML attribute value for the given PV key and ID ref.
361         * This value is used in the area tree XML.
362         *
363         * @param pvKey the PageViewport key of the link target
364         * @param idRef the ID of the link target
365         * @return a string of the type "(thisPVKey,thisIDRef)"
366         */
makeXMLAttribute(String pvKey, String idRef)367        public static String makeXMLAttribute(String pvKey, String idRef) {
368            return "(" + (pvKey == null ? "" : pvKey) + ","
369                       + (idRef == null ? "" : idRef) + ")";
370        }
371 
372        /**
373         * Parses XML attribute value from the area tree into
374         * PageViewport key + IDRef strings. If the attribute value is
375         * formatted like "(s1,s2)", then s1 and s2 are considered to be
376         * the PV key and the IDRef, respectively.
377         * Otherwise, the entire string is the PV key and the IDRef is null.
378         *
379         * @param attrValue the atribute value (PV key and possibly IDRef)
380         * @return a 2-String array containing the PV key and the IDRef.
381         * Both may be null.
382         */
parseXMLAttribute(String attrValue)383        public static String[] parseXMLAttribute(String attrValue) {
384            String[] result = {null, null};
385            if (attrValue != null) {
386               int len = attrValue.length();
387               if (len >= 2 && attrValue.charAt(0) == '(' && attrValue.charAt(len - 1) == ')'
388                       && attrValue.indexOf(',') != -1) {
389                   String value = attrValue.substring(1, len - 1); // remove brackets
390                   int delimIndex = value.indexOf(',');
391                   result[0] = value.substring(0, delimIndex).trim(); // PV key
392                   result[1] = value.substring(delimIndex + 1, value.length()).trim(); // IDRef
393               } else {
394                   // PV key only, e.g. from old area tree XML:
395                   result[0] = attrValue;
396               }
397            }
398            return result;
399        }
400 
401         /**
402          * Return the human-friendly string for debugging.
403          * {@inheritDoc}
404          */
405         @Override
toString()406         public String toString() {
407             StringBuffer sb = new StringBuffer();
408             sb.append("pvKey=").append(pvKey);
409             sb.append(",idRef=").append(idRef);
410             return sb.toString();
411         }
412     }
413 
414     /**
415      * External Link trait structure
416      */
417     public static class ExternalLink implements Serializable {
418 
419         private static final long serialVersionUID = -3720707599232620946L;
420 
421         private String destination;
422         private boolean newWindow;
423 
424         /**
425          * Constructs an ExternalLink object with the given destination
426          *
427          * @param destination   target of the link
428          * @param newWindow     true if the target should be opened in a new window
429          */
ExternalLink(String destination, boolean newWindow)430         public ExternalLink(String destination, boolean newWindow) {
431             this.destination = destination;
432             this.newWindow = newWindow;
433         }
434 
435         /**
436          * Create an <code>ExternalLink</code> from a trait value/attribute value in the
437          * area tree
438          * @param traitValue    the value to use (should match the result of {@link #toString()}
439          * @return an <code>ExternalLink</code> instance corresponding to the given value
440          */
makeFromTraitValue(String traitValue)441         protected static ExternalLink makeFromTraitValue(String traitValue) {
442             String dest = null;
443             boolean newWindow = false;
444             String[] values = traitValue.split(",");
445             for (String v : values) {
446                 if (v.startsWith("dest=")) {
447                     dest = v.substring(5);
448                 } else if (v.startsWith("newWindow=")) {
449                     newWindow = Boolean.valueOf(v.substring(10));
450                 } else {
451                     throw new IllegalArgumentException(
452                             "Malformed trait value for Trait.ExternalLink: " + traitValue);
453                 }
454             }
455             return new ExternalLink(dest, newWindow);
456         }
457 
458         /**
459          * Get the target/destination of the link
460          * @return  the destination of the link
461          */
getDestination()462         public String getDestination() {
463             return this.destination;
464         }
465 
466         /**
467          * Check if the target has to be displayed in a new window
468          * @return  <code>true</code> if the target has to be displayed in a new window
469          */
newWindow()470         public boolean newWindow() {
471             return this.newWindow;
472         }
473 
474         /**
475          * Return a String representation of the object.
476          * @return  a <code>String</code> of the form
477          *          "org.apache.fop.area.Trait.ExternalLink[dest=someURL,newWindow=false]"
478          */
479         @Override
toString()480         public String toString() {
481             StringBuffer sb = new StringBuffer(64);
482             sb.append("newWindow=").append(newWindow);
483             sb.append(",dest=").append(this.destination);
484             return sb.toString();
485         }
486     }
487 
488     /**
489      * Background trait structure.
490      * Used for storing back trait information which are related.
491      */
492     public static class Background implements Serializable {
493 
494         private static final long serialVersionUID = 8452078676273242870L;
495 
496         /** The background color if any. */
497         private Color color;
498 
499         /** The background image url if any. */
500         private String url;
501 
502         /** The background image if any. */
503         private ImageInfo imageInfo;
504 
505         /** Background repeat enum for images. */
506         private int repeat;
507 
508         /** Background horizontal offset for images. */
509         private int horiz;
510 
511         /** Background vertical offset for images. */
512         private int vertical;
513 
514         private int imageTargetWidth;
515 
516         private int imageTargetHeight;
517 
518         /**
519          * Returns the background color.
520          * @return background color, null if n/a
521          */
getColor()522         public Color getColor() {
523             return color;
524         }
525 
526         /**
527          * Returns the horizontal offset for images.
528          * @return the horizontal offset
529          */
getHoriz()530         public int getHoriz() {
531             return horiz;
532         }
533 
534         /**
535          * Returns the image repetition behaviour for images.
536          * @return the image repetition behaviour
537          */
getRepeat()538         public int getRepeat() {
539             return repeat;
540         }
541 
542         /**
543          * Returns the URL to the background image
544          * @return URL to the background image, null if n/a
545          */
getURL()546         public String getURL() {
547             return url;
548         }
549 
550         /**
551          * Returns the ImageInfo object representing the background image
552          * @return the background image, null if n/a
553          */
getImageInfo()554         public ImageInfo getImageInfo() {
555             return imageInfo;
556         }
557 
558         /**
559          * Returns the vertical offset for images.
560          * @return the vertical offset
561          */
getVertical()562         public int getVertical() {
563             return vertical;
564         }
565 
566         /**
567          * Sets the color.
568          * @param color The color to set
569          */
setColor(Color color)570         public void setColor(Color color) {
571             this.color = color;
572         }
573 
574         /**
575          * Sets the horizontal offset.
576          * @param horiz The horizontal offset to set
577          */
setHoriz(int horiz)578         public void setHoriz(int horiz) {
579             this.horiz = horiz;
580         }
581 
582         /**
583          * Sets the image repetition behaviour for images.
584          * @param repeat The image repetition behaviour to set
585          */
setRepeat(int repeat)586         public void setRepeat(int repeat) {
587             this.repeat = repeat;
588         }
589 
590         /**
591          * Sets the image repetition behaviour for images.
592          * @param repeat The image repetition behaviour to set
593          */
setRepeat(String repeat)594         public void setRepeat(String repeat) {
595             setRepeat(getConstantForRepeat(repeat));
596         }
597 
598         /**
599          * Sets the URL to the background image.
600          * @param url The URL to set
601          */
setURL(String url)602         public void setURL(String url) {
603             this.url = url;
604         }
605 
606         /**
607          * Sets the ImageInfo of the image to use as the background image.
608          * @param info The background image's info object
609          */
setImageInfo(ImageInfo info)610         public void setImageInfo(ImageInfo info) {
611             this.imageInfo = info;
612         }
613 
614         /**
615          * Sets the vertical offset for images.
616          * @param vertical The vertical offset to set
617          */
setVertical(int vertical)618         public void setVertical(int vertical) {
619             this.vertical = vertical;
620         }
621 
getRepeatString()622         private String getRepeatString() {
623             switch (getRepeat()) {
624             case EN_REPEAT: return "repeat";
625             case EN_REPEATX: return "repeat-x";
626             case EN_REPEATY: return "repeat-y";
627             case EN_NOREPEAT: return "no-repeat";
628             default: throw new IllegalStateException("Illegal repeat style: " + getRepeat());
629             }
630         }
631 
getConstantForRepeat(String repeat)632         private static int getConstantForRepeat(String repeat) {
633             if ("repeat".equalsIgnoreCase(repeat)) {
634                 return EN_REPEAT;
635             } else if ("repeat-x".equalsIgnoreCase(repeat)) {
636                 return EN_REPEATX;
637             } else if ("repeat-y".equalsIgnoreCase(repeat)) {
638                 return EN_REPEATY;
639             } else if ("no-repeat".equalsIgnoreCase(repeat)) {
640                 return EN_NOREPEAT;
641             } else {
642                 throw new IllegalStateException("Illegal repeat style: " + repeat);
643             }
644         }
645 
646         /**
647          * Return the string for debugging.
648          * {@inheritDoc}
649          */
650         @Override
toString()651         public String toString() {
652             StringBuffer sb = new StringBuffer();
653             if (color != null) {
654                 sb.append("color=").append(ColorUtil.colorToString(color));
655             }
656             if (url != null) {
657                 if (color != null) {
658                     sb.append(",");
659                 }
660                 sb.append("url=").append(url);
661                 sb.append(",repeat=").append(getRepeatString());
662                 sb.append(",horiz=").append(horiz);
663                 sb.append(",vertical=").append(vertical);
664             }
665             if (imageTargetWidth != 0) {
666                 sb.append(",target-width=").append(Integer.toString(imageTargetWidth));
667             }
668             if (imageTargetHeight != 0) {
669                 sb.append(",target-height=").append(Integer.toString(imageTargetHeight));
670             }
671             return sb.toString();
672         }
673 
setImageTargetWidth(int value)674         public void setImageTargetWidth(int value) {
675             imageTargetWidth = value;
676         }
677 
getImageTargetWidth()678         public int getImageTargetWidth() {
679             return imageTargetWidth;
680         }
681 
setImageTargetHeight(int value)682         public void setImageTargetHeight(int value) {
683             imageTargetHeight = value;
684         }
685 
getImageTargetHeight()686         public int getImageTargetHeight() {
687             return imageTargetHeight;
688         }
689 
690     }
691 }
692 
693