001/*
002 * $Id: Chunk.java 4847 2011-05-05 19:46:13Z redlab_b $
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;
045
046import java.net.URL;
047import java.util.List;
048import java.util.ArrayList;
049import java.util.HashMap;
050
051import com.itextpdf.text.error_messages.MessageLocalization;
052import com.itextpdf.text.pdf.HyphenationEvent;
053import com.itextpdf.text.pdf.PdfAction;
054import com.itextpdf.text.pdf.PdfAnnotation;
055import com.itextpdf.text.pdf.PdfContentByte;
056import com.itextpdf.text.pdf.draw.DrawInterface;
057
058/**
059 * This is the smallest significant part of text that can be added to a
060 * document.
061 * <P>
062 * Most elements can be divided in one or more <CODE>Chunk</CODE>s. A chunk
063 * is a <CODE>String</CODE> with a certain <CODE>Font</CODE>. All other
064 * layout parameters should be defined in the object to which this chunk of text
065 * is added.
066 * <P>
067 * Example: <BLOCKQUOTE>
068 *
069 * <PRE>
070 *
071 * <STRONG>Chunk chunk = new Chunk("Hello world",
072 * FontFactory.getFont(FontFactory.COURIER, 20, Font.ITALIC, new BaseColor(255, 0,
073 * 0))); </STRONG> document.add(chunk);
074 *
075 * </PRE>
076 *
077 * </BLOCKQUOTE>
078 */
079
080public class Chunk implements Element {
081
082        // public static membervariables
083
084        /** The character stand in for an image or a separator. */
085        public static final String OBJECT_REPLACEMENT_CHARACTER = "\ufffc";
086
087        /** This is a Chunk containing a newline. */
088        public static final Chunk NEWLINE = new Chunk("\n");
089
090        /** This is a Chunk containing a newpage. */
091        public static final Chunk NEXTPAGE = new Chunk("");
092        static {
093                NEXTPAGE.setNewPage();
094        }
095
096        // member variables
097
098        /** This is the content of this chunk of text. */
099        protected StringBuffer content = null;
100
101        /** This is the <CODE>Font</CODE> of this chunk of text. */
102        protected Font font = null;
103
104        /** Contains some of the attributes for this Chunk. */
105        protected HashMap<String, Object> attributes = null;
106
107        // constructors
108
109        /**
110         * Empty constructor.
111         */
112        public Chunk() {
113                this.content = new StringBuffer();
114                this.font = new Font();
115        }
116
117    /**
118     * A <CODE>Chunk</CODE> copy constructor.
119     * @param ck the <CODE>Chunk</CODE> to be copied
120     */
121    public Chunk(final Chunk ck) {
122        if (ck.content != null) {
123            content = new StringBuffer(ck.content.toString());
124        }
125        if (ck.font != null) {
126            font = new Font(ck.font);
127        }
128        if (ck.attributes != null) {
129            attributes = new HashMap<String, Object>(ck.attributes);
130        }
131    }
132
133        /**
134         * Constructs a chunk of text with a certain content and a certain <CODE>
135         * Font</CODE>.
136         *
137         * @param content
138         *            the content
139         * @param font
140         *            the font
141         */
142        public Chunk(final String content, final Font font) {
143                this.content = new StringBuffer(content);
144                this.font = font;
145        }
146
147        /**
148         * Constructs a chunk of text with a certain content, without specifying a
149         * <CODE>Font</CODE>.
150         *
151         * @param content
152         *            the content
153         */
154        public Chunk(final String content) {
155                this(content, new Font());
156        }
157
158        /**
159         * Constructs a chunk of text with a char and a certain <CODE>Font</CODE>.
160         *
161         * @param c
162         *            the content
163         * @param font
164         *            the font
165         */
166        public Chunk(final char c, final Font font) {
167                this.content = new StringBuffer();
168                this.content.append(c);
169                this.font = font;
170        }
171
172        /**
173         * Constructs a chunk of text with a char, without specifying a <CODE>Font
174         * </CODE>.
175         *
176         * @param c
177         *            the content
178         */
179        public Chunk(final char c) {
180                this(c, new Font());
181        }
182
183        /**
184         * Constructs a chunk containing an <CODE>Image</CODE>.
185         *
186         * @param image
187         *            the image
188         * @param offsetX
189         *            the image offset in the x direction
190         * @param offsetY
191         *            the image offset in the y direction
192         */
193        public Chunk(final Image image, final float offsetX, final float offsetY) {
194                this(OBJECT_REPLACEMENT_CHARACTER, new Font());
195                Image copyImage = Image.getInstance(image);
196                copyImage.setAbsolutePosition(Float.NaN, Float.NaN);
197                setAttribute(IMAGE, new Object[] { copyImage, new Float(offsetX),
198                                new Float(offsetY), Boolean.FALSE });
199        }
200
201        /**
202         * Key for drawInterface of the Separator.
203         * @since       2.1.2
204         */
205        public static final String SEPARATOR = "SEPARATOR";
206
207        /**
208         * Creates a separator Chunk.
209     * Note that separator chunks can't be used in combination with tab chunks!
210         * @param       separator       the drawInterface to use to draw the separator.
211         * @since       2.1.2
212         */
213        public Chunk(final DrawInterface separator) {
214                this(separator, false);
215        }
216
217        /**
218         * Creates a separator Chunk.
219     * Note that separator chunks can't be used in combination with tab chunks!
220         * @param       separator       the drawInterface to use to draw the separator.
221         * @param       vertical        true if this is a vertical separator
222         * @since       2.1.2
223         */
224        public Chunk(final DrawInterface separator, final boolean vertical) {
225                this(OBJECT_REPLACEMENT_CHARACTER, new Font());
226                setAttribute(SEPARATOR, new Object[] {separator, Boolean.valueOf(vertical)});
227        }
228
229        /**
230         * Key for drawInterface of the tab.
231         * @since       2.1.2
232         */
233        public static final String TAB = "TAB";
234
235        /**
236         * Creates a tab Chunk.
237     * Note that separator chunks can't be used in combination with tab chunks!
238         * @param       separator       the drawInterface to use to draw the tab.
239         * @param       tabPosition     an X coordinate that will be used as start position for the next Chunk.
240         * @since       2.1.2
241         */
242        public Chunk(final DrawInterface separator, final float tabPosition) {
243                this(separator, tabPosition, false);
244        }
245
246        /**
247         * Creates a tab Chunk.
248     * Note that separator chunks can't be used in combination with tab chunks!
249         * @param       separator       the drawInterface to use to draw the tab.
250         * @param       tabPosition     an X coordinate that will be used as start position for the next Chunk.
251         * @param       newline         if true, a newline will be added if the tabPosition has already been reached.
252         * @since       2.1.2
253         */
254        public Chunk(final DrawInterface separator, final float tabPosition, final boolean newline) {
255                this(OBJECT_REPLACEMENT_CHARACTER, new Font());
256                if (tabPosition < 0) {
257                        throw new IllegalArgumentException(MessageLocalization.getComposedMessage("a.tab.position.may.not.be.lower.than.0.yours.is.1", String.valueOf(tabPosition)));
258                }
259                setAttribute(TAB, new Object[] {separator, new Float(tabPosition), Boolean.valueOf(newline), new Float(0)});
260        }
261
262        /**
263         * Constructs a chunk containing an <CODE>Image</CODE>.
264         *
265         * @param image
266         *            the image
267         * @param offsetX
268         *            the image offset in the x direction
269         * @param offsetY
270         *            the image offset in the y direction
271         * @param changeLeading
272         *            true if the leading has to be adapted to the image
273         */
274        public Chunk(final Image image, final float offsetX, final float offsetY,
275                        final boolean changeLeading) {
276                this(OBJECT_REPLACEMENT_CHARACTER, new Font());
277                setAttribute(IMAGE, new Object[] { image, new Float(offsetX),
278                                new Float(offsetY), Boolean.valueOf(changeLeading) });
279        }
280
281        // implementation of the Element-methods
282
283        /**
284         * Processes the element by adding it (or the different parts) to an <CODE>
285         * ElementListener</CODE>.
286         *
287         * @param listener
288         *            an <CODE>ElementListener</CODE>
289         * @return <CODE>true</CODE> if the element was processed successfully
290         */
291        public boolean process(final ElementListener listener) {
292                try {
293                        return listener.add(this);
294                } catch (DocumentException de) {
295                        return false;
296                }
297        }
298
299        /**
300         * Gets the type of the text element.
301         *
302         * @return a type
303         */
304        public int type() {
305                return Element.CHUNK;
306        }
307
308        /**
309         * Gets all the chunks in this element.
310         *
311         * @return an <CODE>ArrayList</CODE>
312         */
313        public List<Chunk> getChunks() {
314                List<Chunk> tmp = new ArrayList<Chunk>();
315                tmp.add(this);
316                return tmp;
317        }
318
319        // methods that change the member variables
320
321        /**
322         * appends some text to this <CODE>Chunk</CODE>.
323         *
324         * @param string
325         *            <CODE>String</CODE>
326         * @return a <CODE>StringBuffer</CODE>
327         */
328        public StringBuffer append(final String string) {
329                return content.append(string);
330        }
331
332        /**
333         * Sets the font of this <CODE>Chunk</CODE>.
334         *
335         * @param font
336         *            a <CODE>Font</CODE>
337         */
338        public void setFont(final Font font) {
339                this.font = font;
340        }
341
342        // methods to retrieve information
343
344        /**
345         * Gets the font of this <CODE>Chunk</CODE>.
346         *
347         * @return a <CODE>Font</CODE>
348         */
349        public Font getFont() {
350                return font;
351        }
352
353        /**
354         * Returns the content of this <CODE>Chunk</CODE>.
355         *
356         * @return a <CODE>String</CODE>
357         */
358        public String getContent() {
359                return content.toString();
360        }
361
362        /**
363         * Returns the content of this <CODE>Chunk</CODE>.
364         *
365         * @return a <CODE>String</CODE>
366         */
367        @Override
368    public String toString() {
369                return getContent();
370        }
371
372        /**
373         * Checks is this <CODE>Chunk</CODE> is empty.
374         *
375         * @return <CODE>false</CODE> if the Chunk contains other characters than
376         *         space.
377         */
378        public boolean isEmpty() {
379                return content.toString().trim().length() == 0
380                                && content.toString().indexOf("\n") == -1
381                                && attributes == null;
382        }
383
384        /**
385         * Gets the width of the Chunk in points.
386         *
387         * @return a width in points
388         */
389        public float getWidthPoint() {
390                if (getImage() != null) {
391                        return getImage().getScaledWidth();
392                }
393                return font.getCalculatedBaseFont(true).getWidthPoint(getContent(),
394                                font.getCalculatedSize())
395                                * getHorizontalScaling();
396        }
397
398        // attributes
399
400        /**
401         * Checks the attributes of this <CODE>Chunk</CODE>.
402         *
403         * @return false if there aren't any.
404         */
405
406        public boolean hasAttributes() {
407                return attributes != null;
408        }
409
410        /**
411         * Gets the attributes for this <CODE>Chunk</CODE>.
412         * <P>
413         * It may be null.
414         *
415         * @return the attributes for this <CODE>Chunk</CODE>
416         */
417
418        public HashMap<String, Object> getAttributes() {
419                return attributes;
420        }
421
422        /**
423         * Sets the attributes all at once.
424         * @param       attributes      the attributes of a Chunk
425         */
426        public void setAttributes(final HashMap<String, Object> attributes) {
427                this.attributes = attributes;
428        }
429
430        /**
431         * Sets an arbitrary attribute.
432         *
433         * @param name
434         *            the key for the attribute
435         * @param obj
436         *            the value of the attribute
437         * @return this <CODE>Chunk</CODE>
438         */
439
440        private Chunk setAttribute(final String name, final Object obj) {
441                if (attributes == null)
442                        attributes = new HashMap<String, Object>();
443                attributes.put(name, obj);
444                return this;
445        }
446
447        // the attributes are ordered as they appear in the book 'iText in Action'
448
449        /** Key for text horizontal scaling. */
450        public static final String HSCALE = "HSCALE";
451
452        /**
453         * Sets the text horizontal scaling. A value of 1 is normal and a value of
454         * 0.5f shrinks the text to half it's width.
455         *
456         * @param scale
457         *            the horizontal scaling factor
458         * @return this <CODE>Chunk</CODE>
459         */
460        public Chunk setHorizontalScaling(final float scale) {
461                return setAttribute(HSCALE, new Float(scale));
462        }
463
464        /**
465         * Gets the horizontal scaling.
466         *
467         * @return a percentage in float
468         */
469        public float getHorizontalScaling() {
470                if (attributes == null)
471                        return 1f;
472                Float f = (Float) attributes.get(HSCALE);
473                if (f == null)
474                        return 1f;
475                return f.floatValue();
476        }
477
478        /** Key for underline. */
479        public static final String UNDERLINE = "UNDERLINE";
480
481        /**
482         * Sets an horizontal line that can be an underline or a strikethrough.
483         * Actually, the line can be anywhere vertically and has always the <CODE>
484         * Chunk</CODE> width. Multiple call to this method will produce multiple
485         * lines.
486         *
487         * @param thickness
488         *            the absolute thickness of the line
489         * @param yPosition
490         *            the absolute y position relative to the baseline
491         * @return this <CODE>Chunk</CODE>
492         */
493        public Chunk setUnderline(final float thickness, final float yPosition) {
494                return setUnderline(null, thickness, 0f, yPosition, 0f,
495                                PdfContentByte.LINE_CAP_BUTT);
496        }
497
498        /**
499         * Sets an horizontal line that can be an underline or a strikethrough.
500         * Actually, the line can be anywhere vertically and has always the <CODE>
501         * Chunk</CODE> width. Multiple call to this method will produce multiple
502         * lines.
503         *
504         * @param color
505         *            the color of the line or <CODE>null</CODE> to follow the
506         *            text color
507         * @param thickness
508         *            the absolute thickness of the line
509         * @param thicknessMul
510         *            the thickness multiplication factor with the font size
511         * @param yPosition
512         *            the absolute y position relative to the baseline
513         * @param yPositionMul
514         *            the position multiplication factor with the font size
515         * @param cap
516         *            the end line cap. Allowed values are
517         *            PdfContentByte.LINE_CAP_BUTT, PdfContentByte.LINE_CAP_ROUND
518         *            and PdfContentByte.LINE_CAP_PROJECTING_SQUARE
519         * @return this <CODE>Chunk</CODE>
520         */
521        public Chunk setUnderline(final BaseColor color, final float thickness, final float thicknessMul,
522                        final float yPosition, final float yPositionMul, final int cap) {
523                if (attributes == null)
524                        attributes = new HashMap<String, Object>();
525                Object obj[] = {
526                                color,
527                                new float[] { thickness, thicknessMul, yPosition, yPositionMul, cap } };
528                Object unders[][] = Utilities.addToArray((Object[][]) attributes.get(UNDERLINE),
529                                obj);
530                return setAttribute(UNDERLINE, unders);
531        }
532
533        /** Key for sub/superscript. */
534        public static final String SUBSUPSCRIPT = "SUBSUPSCRIPT";
535
536        /**
537         * Sets the text displacement relative to the baseline. Positive values rise
538         * the text, negative values lower the text.
539         * <P>
540         * It can be used to implement sub/superscript.
541         *
542         * @param rise
543         *            the displacement in points
544         * @return this <CODE>Chunk</CODE>
545         */
546
547        public Chunk setTextRise(final float rise) {
548                return setAttribute(SUBSUPSCRIPT, new Float(rise));
549        }
550
551        /**
552         * Gets the text displacement relative to the baseline.
553         *
554         * @return a displacement in points
555         */
556        public float getTextRise() {
557                if (attributes != null && attributes.containsKey(SUBSUPSCRIPT)) {
558                        Float f = (Float) attributes.get(SUBSUPSCRIPT);
559                        return f.floatValue();
560                }
561                return 0.0f;
562        }
563
564        /** Key for text skewing. */
565        public static final String SKEW = "SKEW";
566
567        /**
568         * Skews the text to simulate italic and other effects. Try <CODE>alpha=0
569         * </CODE> and <CODE>beta=12</CODE>.
570         *
571         * @param alpha
572         *            the first angle in degrees
573         * @param beta
574         *            the second angle in degrees
575         * @return this <CODE>Chunk</CODE>
576         */
577        public Chunk setSkew(float alpha, float beta) {
578                alpha = (float) Math.tan(alpha * Math.PI / 180);
579                beta = (float) Math.tan(beta * Math.PI / 180);
580                return setAttribute(SKEW, new float[] { alpha, beta });
581        }
582
583        /** Key for background. */
584        public static final String BACKGROUND = "BACKGROUND";
585
586        /**
587         * Sets the color of the background <CODE>Chunk</CODE>.
588         *
589         * @param color
590         *            the color of the background
591         * @return this <CODE>Chunk</CODE>
592         */
593        public Chunk setBackground(final BaseColor color) {
594                return setBackground(color, 0, 0, 0, 0);
595        }
596
597        /**
598         * Sets the color and the size of the background <CODE>Chunk</CODE>.
599         *
600         * @param color
601         *            the color of the background
602         * @param extraLeft
603         *            increase the size of the rectangle in the left
604         * @param extraBottom
605         *            increase the size of the rectangle in the bottom
606         * @param extraRight
607         *            increase the size of the rectangle in the right
608         * @param extraTop
609         *            increase the size of the rectangle in the top
610         * @return this <CODE>Chunk</CODE>
611         */
612        public Chunk setBackground(final BaseColor color, final float extraLeft, final float extraBottom,
613                        final float extraRight, final float extraTop) {
614                return setAttribute(BACKGROUND, new Object[] { color,
615                                new float[] { extraLeft, extraBottom, extraRight, extraTop } });
616        }
617
618        /** Key for text rendering mode. */
619        public static final String TEXTRENDERMODE = "TEXTRENDERMODE";
620
621        /**
622         * Sets the text rendering mode. It can outline text, simulate bold and make
623         * text invisible.
624         *
625         * @param mode
626         *            the text rendering mode. It can be <CODE>
627         *            PdfContentByte.TEXT_RENDER_MODE_FILL</CODE>,<CODE>
628         *            PdfContentByte.TEXT_RENDER_MODE_STROKE</CODE>,<CODE>
629         *            PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE</CODE> and <CODE>
630         *            PdfContentByte.TEXT_RENDER_MODE_INVISIBLE</CODE>.
631         * @param strokeWidth
632         *            the stroke line width for the modes <CODE>
633         *            PdfContentByte.TEXT_RENDER_MODE_STROKE</CODE> and <CODE>
634         *            PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE</CODE>.
635         * @param strokeColor
636         *            the stroke color or <CODE>null</CODE> to follow the text
637         *            color
638         * @return this <CODE>Chunk</CODE>
639         */
640        public Chunk setTextRenderMode(final int mode, final float strokeWidth,
641                        final BaseColor strokeColor) {
642                return setAttribute(TEXTRENDERMODE, new Object[] { Integer.valueOf(mode),
643                                new Float(strokeWidth), strokeColor });
644        }
645
646        /** Key for split character. */
647        public static final String SPLITCHARACTER = "SPLITCHARACTER";
648
649        /**
650         * Sets the split characters.
651         *
652         * @param splitCharacter
653         *            the <CODE>SplitCharacter</CODE> interface
654         * @return this <CODE>Chunk</CODE>
655         */
656
657        public Chunk setSplitCharacter(final SplitCharacter splitCharacter) {
658                return setAttribute(SPLITCHARACTER, splitCharacter);
659        }
660
661        /** Key for hyphenation. */
662        public static final String HYPHENATION = "HYPHENATION";
663
664        /**
665         * sets the hyphenation engine to this <CODE>Chunk</CODE>.
666         *
667         * @param hyphenation
668         *            the hyphenation engine
669         * @return this <CODE>Chunk</CODE>
670         */
671        public Chunk setHyphenation(final HyphenationEvent hyphenation) {
672                return setAttribute(HYPHENATION, hyphenation);
673        }
674
675        /** Key for remote goto. */
676        public static final String REMOTEGOTO = "REMOTEGOTO";
677
678        /**
679         * Sets a goto for a remote destination for this <CODE>Chunk</CODE>.
680         *
681         * @param filename
682         *            the file name of the destination document
683         * @param name
684         *            the name of the destination to go to
685         * @return this <CODE>Chunk</CODE>
686         */
687
688        public Chunk setRemoteGoto(final String filename, final String name) {
689                return setAttribute(REMOTEGOTO, new Object[] { filename, name });
690        }
691
692        /**
693         * Sets a goto for a remote destination for this <CODE>Chunk</CODE>.
694         *
695         * @param filename
696         *            the file name of the destination document
697         * @param page
698         *            the page of the destination to go to. First page is 1
699         * @return this <CODE>Chunk</CODE>
700         */
701
702        public Chunk setRemoteGoto(final String filename, final int page) {
703                return setAttribute(REMOTEGOTO, new Object[] { filename,
704                                Integer.valueOf(page) });
705        }
706
707        /** Key for local goto. */
708        public static final String LOCALGOTO = "LOCALGOTO";
709
710        /**
711         * Sets a local goto for this <CODE>Chunk</CODE>.
712         * <P>
713         * There must be a local destination matching the name.
714         *
715         * @param name
716         *            the name of the destination to go to
717         * @return this <CODE>Chunk</CODE>
718         */
719
720        public Chunk setLocalGoto(final String name) {
721                return setAttribute(LOCALGOTO, name);
722        }
723
724        /** Key for local destination. */
725        public static final String LOCALDESTINATION = "LOCALDESTINATION";
726
727        /**
728         * Sets a local destination for this <CODE>Chunk</CODE>.
729         *
730         * @param name
731         *            the name for this destination
732         * @return this <CODE>Chunk</CODE>
733         */
734        public Chunk setLocalDestination(final String name) {
735                return setAttribute(LOCALDESTINATION, name);
736        }
737
738        /** Key for generic tag. */
739        public static final String GENERICTAG = "GENERICTAG";
740
741        /**
742         * Sets the generic tag <CODE>Chunk</CODE>.
743         * <P>
744         * The text for this tag can be retrieved with <CODE>PdfPageEvent</CODE>.
745         *
746         * @param text
747         *            the text for the tag
748         * @return this <CODE>Chunk</CODE>
749         */
750
751        public Chunk setGenericTag(final String text) {
752                return setAttribute(GENERICTAG, text);
753        }
754
755        /** Key for image. */
756        public static final String IMAGE = "IMAGE";
757
758        /**
759         * Returns the image.
760         *
761         * @return the image
762         */
763
764        public Image getImage() {
765                if (attributes == null)
766                        return null;
767                Object obj[] = (Object[]) attributes.get(Chunk.IMAGE);
768                if (obj == null)
769                        return null;
770                else {
771                        return (Image) obj[0];
772                }
773        }
774
775        /** Key for Action. */
776        public static final String ACTION = "ACTION";
777
778        /**
779         * Sets an action for this <CODE>Chunk</CODE>.
780         *
781         * @param action
782         *            the action
783         * @return this <CODE>Chunk</CODE>
784         */
785
786        public Chunk setAction(final PdfAction action) {
787                return setAttribute(ACTION, action);
788        }
789
790        /**
791         * Sets an anchor for this <CODE>Chunk</CODE>.
792         *
793         * @param url
794         *            the <CODE>URL</CODE> to link to
795         * @return this <CODE>Chunk</CODE>
796         */
797
798        public Chunk setAnchor(final URL url) {
799                return setAttribute(ACTION, new PdfAction(url.toExternalForm()));
800        }
801
802        /**
803         * Sets an anchor for this <CODE>Chunk</CODE>.
804         *
805         * @param url
806         *            the url to link to
807         * @return this <CODE>Chunk</CODE>
808         */
809
810        public Chunk setAnchor(final String url) {
811                return setAttribute(ACTION, new PdfAction(url));
812        }
813
814        /** Key for newpage. */
815        public static final String NEWPAGE = "NEWPAGE";
816
817        /**
818         * Sets a new page tag..
819         *
820         * @return this <CODE>Chunk</CODE>
821         */
822
823        public Chunk setNewPage() {
824                return setAttribute(NEWPAGE, null);
825        }
826
827        /** Key for annotation. */
828        public static final String PDFANNOTATION = "PDFANNOTATION";
829
830        /**
831         * Sets a generic annotation to this <CODE>Chunk</CODE>.
832         *
833         * @param annotation
834         *            the annotation
835         * @return this <CODE>Chunk</CODE>
836         */
837        public Chunk setAnnotation(final PdfAnnotation annotation) {
838                return setAttribute(PDFANNOTATION, annotation);
839        }
840
841        /**
842         * @see com.itextpdf.text.Element#isContent()
843         * @since       iText 2.0.8
844         */
845        public boolean isContent() {
846                return true;
847        }
848
849        /**
850         * @see com.itextpdf.text.Element#isNestable()
851         * @since       iText 2.0.8
852         */
853        public boolean isNestable() {
854                return true;
855        }
856
857        /**
858     * Returns the hyphenation (if present).
859         * @return the HypenationEvent of this Chunk
860     * @since   2.1.2
861         */
862    public HyphenationEvent getHyphenation() {
863        if (attributes == null) return null;
864        return (HyphenationEvent) attributes.get(Chunk.HYPHENATION);
865        }
866
867        // keys used in PdfChunk
868
869        /** Key for color. */
870        public static final String COLOR = "COLOR";
871
872        /** Key for encoding. */
873        public static final String ENCODING = "ENCODING";
874
875        /**
876         * Key for character spacing.
877         */
878        public static final String CHAR_SPACING = "CHAR_SPACING";
879
880        /**
881         * Sets the character spacing.
882         *
883         * @param charSpace the character spacing value
884         * @return this <CODE>Chunk</CODE>
885         */
886        public Chunk setCharacterSpacing(final float charSpace) {
887                return setAttribute(CHAR_SPACING, new Float(charSpace));
888        }
889
890        /**
891         * Gets the character spacing.
892         *
893         * @return a value in float
894         */
895        public float getCharacterSpacing() {
896                if (attributes != null && attributes.containsKey(CHAR_SPACING)) {
897                        Float f = (Float) attributes.get(CHAR_SPACING);
898                        return f.floatValue();
899                }
900                return 0.0f;
901        }
902}