001/*
002 * $Id: Document.java 4905 2011-06-08 12:26:03Z 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;
045
046import java.text.SimpleDateFormat;
047import java.util.ArrayList;
048import java.util.Date;
049
050import com.itextpdf.text.error_messages.MessageLocalization;
051
052/**
053 * A generic Document class.
054 * <P>
055 * All kinds of Text-elements can be added to a <CODE>HTMLDocument</CODE>.
056 * The <CODE>Document</CODE> signals all the listeners when an element has
057 * been added.
058 * <P>
059 * Remark:
060 * <OL>
061 *     <LI>Once a document is created you can add some meta information.
062 *     <LI>You can also set the headers/footers.
063 *     <LI>You have to open the document before you can write content.
064 * <LI>You can only write content (no more meta-formation!) once a document is
065 * opened.
066 * <LI>When you change the header/footer on a certain page, this will be
067 * effective starting on the next page.
068 * <LI>After closing the document, every listener (as well as its <CODE>
069 * OutputStream</CODE>) is closed too.
070 * </OL>
071 * Example: <BLOCKQUOTE>
072 *
073 * <PRE>// creation of the document with a certain size and certain margins
074 * <STRONG>Document document = new Document(PageSize.A4, 50, 50, 50, 50);
075 * </STRONG> try {
076 *   // creation of the different writers
077 *   HtmlWriter.getInstance(<STRONG>document </STRONG>, System.out);
078 *   PdfWriter.getInstance(<STRONG>document </STRONG>, new FileOutputStream("text.pdf"));
079 *   // we add some meta information to the document
080 *   <STRONG>document.addAuthor("Bruno Lowagie"); </STRONG>
081 *   <STRONG>document.addSubject("This is the result of a Test."); </STRONG>
082 *   // we open the document for writing
083 *   <STRONG>document.open(); </STRONG>
084 *   <STRONG>document.add(new Paragraph("Hello world"));</STRONG>
085 *  } catch(DocumentException de) {
086 *   System.err.println(de.getMessage());
087 *  }
088 *  <STRONG>document.close();</STRONG>
089 * </PRE>
090 *
091 * </BLOCKQUOTE>
092 */
093
094public class Document implements DocListener {
095
096    // membervariables
097    /**
098     * This constant contains the name of the product.
099     * iText is a registered trademark.
100     * Please don't change this constant.
101     * @since   2.1.6
102     */
103        private static final String ITEXT = "iText\u00ae";
104    /**
105     * This constant contains the version number of this iText release.
106     * For debugging purposes, we request you NOT to change this constant.
107     * @since   2.1.6
108     */
109        private static final String RELEASE = "5.1.2-SNAPSHOT";
110        /**
111         * This constant the iText version as shown in the producer line.
112         * iText is a product developed by 1T3XT BVBA.
113         * 1T3XT requests that you retain the iText producer line
114         * in every PDF that is created or manipulated using iText.
115         */
116        private static final String ITEXT_VERSION = ITEXT + " " + RELEASE + " \u00a92000-2011 1T3XT BVBA";
117
118        /**
119         * Allows the pdf documents to be produced without compression for debugging
120         * purposes.
121         */
122    public static boolean compress = true;
123
124        /**
125         * When true the file access is not done through a memory mapped file. Use it if the file
126     * is too big to be mapped in your address space.
127         */
128    public static boolean plainRandomAccess = false;
129
130    /** Scales the WMF font size. The default value is 0.86. */
131    public static float wmfFontCorrection = 0.86f;
132
133        /**
134         * The DocListener.
135         * @since iText 5.1.0 changed from private to protected
136         */
137    protected ArrayList<DocListener> listeners = new ArrayList<DocListener>();
138
139        /** Is the document open or not? */
140    protected boolean open;
141
142        /** Has the document already been closed? */
143    protected boolean close;
144
145    // membervariables concerning the layout
146
147        /** The size of the page. */
148    protected Rectangle pageSize;
149
150        /** margin in x direction starting from the left */
151    protected float marginLeft = 0;
152
153        /** margin in x direction starting from the right */
154    protected float marginRight = 0;
155
156        /** margin in y direction starting from the top */
157    protected float marginTop = 0;
158
159        /** margin in y direction starting from the bottom */
160    protected float marginBottom = 0;
161
162    /** mirroring of the left/right margins */
163    protected boolean marginMirroring = false;
164
165    /**
166     * mirroring of the top/bottom margins
167     * @since   2.1.6
168     */
169    protected boolean marginMirroringTopBottom = false;
170
171        /** Content of JavaScript onLoad function */
172    protected String javaScript_onLoad = null;
173
174        /** Content of JavaScript onUnLoad function */
175    protected String javaScript_onUnLoad = null;
176
177        /** Style class in HTML body tag */
178    protected String htmlStyleClass = null;
179
180    // headers, footers
181
182        /** Current pagenumber */
183    protected int pageN = 0;
184
185    /** This is a chapter number in case ChapterAutoNumber is used. */
186    protected int chapternumber = 0;
187
188    // constructor
189
190        /**
191         * Constructs a new <CODE>Document</CODE> -object.
192 */
193
194    public Document() {
195        this(PageSize.A4);
196    }
197
198        /**
199         * Constructs a new <CODE>Document</CODE> -object.
200 *
201         * @param pageSize
202         *            the pageSize
203 */
204
205    public Document(Rectangle pageSize) {
206        this(pageSize, 36, 36, 36, 36);
207    }
208
209        /**
210         * Constructs a new <CODE>Document</CODE> -object.
211 *
212         * @param pageSize
213         *            the pageSize
214         * @param marginLeft
215         *            the margin on the left
216         * @param marginRight
217         *            the margin on the right
218         * @param marginTop
219         *            the margin on the top
220         * @param marginBottom
221         *            the margin on the bottom
222 */
223
224        public Document(Rectangle pageSize, float marginLeft, float marginRight,
225                        float marginTop, float marginBottom) {
226        this.pageSize = pageSize;
227        this.marginLeft = marginLeft;
228        this.marginRight = marginRight;
229        this.marginTop = marginTop;
230        this.marginBottom = marginBottom;
231    }
232
233    // listener methods
234
235        /**
236 * Adds a <CODE>DocListener</CODE> to the <CODE>Document</CODE>.
237 *
238         * @param listener
239         *            the new DocListener.
240 */
241
242    public void addDocListener(DocListener listener) {
243        listeners.add(listener);
244    }
245
246        /**
247 * Removes a <CODE>DocListener</CODE> from the <CODE>Document</CODE>.
248 *
249         * @param listener
250         *            the DocListener that has to be removed.
251 */
252
253    public void removeDocListener(DocListener listener) {
254        listeners.remove(listener);
255    }
256
257    // methods implementing the DocListener interface
258
259        /**
260         * Adds an <CODE>Element</CODE> to the <CODE>Document</CODE>.
261 *
262         * @param element
263         *            the <CODE>Element</CODE> to add
264         * @return <CODE>true</CODE> if the element was added, <CODE>false
265         *         </CODE> if not
266         * @throws DocumentException
267         *             when a document isn't open yet, or has been closed
268 */
269
270    public boolean add(Element element) throws DocumentException {
271        if (close) {
272                        throw new DocumentException(MessageLocalization.getComposedMessage("the.document.has.been.closed.you.can.t.add.any.elements"));
273        }
274                if (!open && element.isContent()) {
275                        throw new DocumentException(MessageLocalization.getComposedMessage("the.document.is.not.open.yet.you.can.only.add.meta.information"));
276        }
277        boolean success = false;
278        if (element instanceof ChapterAutoNumber) {
279                chapternumber = ((ChapterAutoNumber)element).setAutomaticNumber(chapternumber);
280        }
281        for (DocListener listener : listeners) {
282            success |= listener.add(element);
283        }
284                if (element instanceof LargeElement) {
285                        LargeElement e = (LargeElement)element;
286                        if (!e.isComplete())
287                                e.flushContent();
288                }
289        return success;
290    }
291
292        /**
293 * Opens the document.
294 * <P>
295         * Once the document is opened, you can't write any Header- or
296         * Meta-information anymore. You have to open the document before you can
297         * begin to add content to the body of the document.
298 */
299
300    public void open() {
301                if (!close) {
302            open = true;
303        }
304                for (DocListener listener : listeners) {
305            listener.setPageSize(pageSize);
306                        listener.setMargins(marginLeft, marginRight, marginTop,
307                                        marginBottom);
308            listener.open();
309        }
310    }
311
312        /**
313 * Sets the pagesize.
314 *
315         * @param pageSize
316         *            the new pagesize
317 * @return      a <CODE>boolean</CODE>
318 */
319
320    public boolean setPageSize(Rectangle pageSize) {
321        this.pageSize = pageSize;
322                for (DocListener listener : listeners) {
323            listener.setPageSize(pageSize);
324        }
325        return true;
326    }
327
328        /**
329 * Sets the margins.
330 *
331         * @param marginLeft
332         *            the margin on the left
333         * @param marginRight
334         *            the margin on the right
335         * @param marginTop
336         *            the margin on the top
337         * @param marginBottom
338         *            the margin on the bottom
339 * @return      a <CODE>boolean</CODE>
340 */
341
342        public boolean setMargins(float marginLeft, float marginRight,
343                        float marginTop, float marginBottom) {
344        this.marginLeft = marginLeft;
345        this.marginRight = marginRight;
346        this.marginTop = marginTop;
347        this.marginBottom = marginBottom;
348                for (DocListener listener : listeners) {
349                        listener.setMargins(marginLeft, marginRight, marginTop,
350                                        marginBottom);
351        }
352        return true;
353    }
354
355        /**
356 * Signals that an new page has to be started.
357 *
358         * @return <CODE>true</CODE> if the page was added, <CODE>false</CODE>
359         *         if not.
360 */
361
362    public boolean newPage() {
363        if (!open || close) {
364            return false;
365        }
366        for (DocListener listener : listeners) {
367            listener.newPage();
368        }
369        return true;
370    }
371
372        /**
373 * Sets the page number to 0.
374 */
375
376    public void resetPageCount() {
377        pageN = 0;
378        for (DocListener listener: listeners) {
379            listener.resetPageCount();
380        }
381    }
382
383        /**
384 * Sets the page number.
385 *
386         * @param pageN
387         *            the new page number
388 */
389
390    public void setPageCount(int pageN) {
391        this.pageN = pageN;
392        for (DocListener listener: listeners) {
393            listener.setPageCount(pageN);
394        }
395    }
396
397        /**
398 * Returns the current page number.
399 *
400 * @return the current page number
401 */
402
403    public int getPageNumber() {
404        return this.pageN;
405    }
406
407        /**
408 * Closes the document.
409 * <P>
410         * Once all the content has been written in the body, you have to close the
411         * body. After that nothing can be written to the body anymore.
412 */
413
414    public void close() {
415                if (!close) {
416            open = false;
417            close = true;
418        }
419                for (DocListener listener : listeners) {
420            listener.close();
421        }
422    }
423
424    // methods concerning the header or some meta information
425
426        /**
427 * Adds a user defined header to the document.
428 *
429         * @param name
430         *            the name of the header
431         * @param content
432         *            the content of the header
433 * @return      <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
434 */
435
436    public boolean addHeader(String name, String content) {
437        try {
438            return add(new Header(name, content));
439                } catch (DocumentException de) {
440            throw new ExceptionConverter(de);
441        }
442    }
443
444        /**
445 * Adds the title to a Document.
446 *
447         * @param title
448         *            the title
449 * @return      <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
450 */
451
452    public boolean addTitle(String title) {
453        try {
454            return add(new Meta(Element.TITLE, title));
455                } catch (DocumentException de) {
456            throw new ExceptionConverter(de);
457        }
458    }
459
460        /**
461 * Adds the subject to a Document.
462 *
463         * @param subject
464         *            the subject
465 * @return      <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
466 */
467
468    public boolean addSubject(String subject) {
469        try {
470            return add(new Meta(Element.SUBJECT, subject));
471                } catch (DocumentException de) {
472            throw new ExceptionConverter(de);
473        }
474    }
475
476        /**
477 * Adds the keywords to a Document.
478 *
479         * @param keywords
480         *            adds the keywords to the document
481 * @return <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
482 */
483
484    public boolean addKeywords(String keywords) {
485        try {
486            return add(new Meta(Element.KEYWORDS, keywords));
487                } catch (DocumentException de) {
488            throw new ExceptionConverter(de);
489        }
490    }
491
492        /**
493 * Adds the author to a Document.
494 *
495         * @param author
496         *            the name of the author
497 * @return      <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
498 */
499
500    public boolean addAuthor(String author) {
501        try {
502            return add(new Meta(Element.AUTHOR, author));
503                } catch (DocumentException de) {
504            throw new ExceptionConverter(de);
505        }
506    }
507
508        /**
509 * Adds the creator to a Document.
510 *
511         * @param creator
512         *            the name of the creator
513 * @return      <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
514 */
515
516    public boolean addCreator(String creator) {
517        try {
518            return add(new Meta(Element.CREATOR, creator));
519                } catch (DocumentException de) {
520            throw new ExceptionConverter(de);
521        }
522    }
523
524        /**
525 * Adds the producer to a Document.
526 *
527 * @return      <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
528 */
529
530    public boolean addProducer() {
531        try {
532            return add(new Meta(Element.PRODUCER, getVersion()));
533                } catch (DocumentException de) {
534            throw new ExceptionConverter(de);
535        }
536    }
537
538        /**
539 * Adds the current date and time to a Document.
540 *
541 * @return      <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
542 */
543
544    public boolean addCreationDate() {
545        try {
546                        /* bugfix by 'taqua' (Thomas) */
547                        final SimpleDateFormat sdf = new SimpleDateFormat(
548                                        "EEE MMM dd HH:mm:ss zzz yyyy");
549                        return add(new Meta(Element.CREATIONDATE, sdf.format(new Date())));
550                } catch (DocumentException de) {
551            throw new ExceptionConverter(de);
552        }
553    }
554
555    // methods to get the layout of the document.
556
557        /**
558 * Returns the left margin.
559 *
560 * @return      the left margin
561 */
562
563    public float leftMargin() {
564        return marginLeft;
565    }
566
567        /**
568 * Return the right margin.
569 *
570 * @return      the right margin
571 */
572
573    public float rightMargin() {
574        return marginRight;
575    }
576
577        /**
578 * Returns the top margin.
579 *
580 * @return      the top margin
581 */
582
583    public float topMargin() {
584        return marginTop;
585    }
586
587        /**
588 * Returns the bottom margin.
589 *
590 * @return      the bottom margin
591 */
592
593    public float bottomMargin() {
594        return marginBottom;
595    }
596
597        /**
598 * Returns the lower left x-coordinate.
599 *
600 * @return      the lower left x-coordinate
601 */
602
603    public float left() {
604        return pageSize.getLeft(marginLeft);
605    }
606
607        /**
608 * Returns the upper right x-coordinate.
609 *
610 * @return      the upper right x-coordinate
611 */
612
613    public float right() {
614        return pageSize.getRight(marginRight);
615    }
616
617        /**
618 * Returns the upper right y-coordinate.
619 *
620 * @return      the upper right y-coordinate
621 */
622
623    public float top() {
624        return pageSize.getTop(marginTop);
625    }
626
627        /**
628 * Returns the lower left y-coordinate.
629 *
630 * @return      the lower left y-coordinate
631 */
632
633    public float bottom() {
634        return pageSize.getBottom(marginBottom);
635    }
636
637        /**
638 * Returns the lower left x-coordinate considering a given margin.
639 *
640         * @param margin
641         *            a margin
642 * @return      the lower left x-coordinate
643 */
644
645    public float left(float margin) {
646        return pageSize.getLeft(marginLeft + margin);
647    }
648
649        /**
650 * Returns the upper right x-coordinate, considering a given margin.
651 *
652         * @param margin
653         *            a margin
654 * @return      the upper right x-coordinate
655 */
656
657    public float right(float margin) {
658        return pageSize.getRight(marginRight + margin);
659    }
660
661        /**
662 * Returns the upper right y-coordinate, considering a given margin.
663 *
664         * @param margin
665         *            a margin
666 * @return      the upper right y-coordinate
667 */
668
669    public float top(float margin) {
670        return pageSize.getTop(marginTop + margin);
671    }
672
673        /**
674 * Returns the lower left y-coordinate, considering a given margin.
675 *
676         * @param margin
677         *            a margin
678 * @return      the lower left y-coordinate
679 */
680
681    public float bottom(float margin) {
682        return pageSize.getBottom(marginBottom + margin);
683    }
684
685        /**
686 * Gets the pagesize.
687         *
688 * @return the page size
689 */
690
691        public Rectangle getPageSize() {
692        return this.pageSize;
693    }
694
695        /**
696         * Checks if the document is open.
697         *
698     * @return <CODE>true</CODE> if the document is open
699     */
700    public boolean isOpen() {
701        return open;
702    }
703
704        /**
705         * Gets the product name.
706         * This method may only be changed by Paulo Soares and/or Bruno Lowagie.
707     * @return the product name
708     * @since   2.1.6
709     */
710    public static final String getProduct() {
711        return ITEXT;
712    }
713
714        /**
715         * Gets the release number.
716         * This method may only be changed by Paulo Soares and/or Bruno Lowagie.
717     * @return the product name
718     * @since   2.1.6
719     */
720    public static final String getRelease() {
721        return RELEASE;
722    }
723
724        /**
725         * Returns the iText version as shown in the producer line.
726         * iText is a product developed by 1T3XT BVBA.
727         * 1T3XT requests that you retain the iText producer line
728         * in every PDF that is created or manipulated using iText.
729     * @return iText version
730     */
731    public static final String getVersion() {
732        return ITEXT_VERSION;
733    }
734
735        /**
736 * Adds a JavaScript onLoad function to the HTML body tag
737 *
738         * @param code
739         *            the JavaScript code to be executed on load of the HTML page
740 */
741
742    public void setJavaScript_onLoad(String code) {
743        this.javaScript_onLoad = code;
744    }
745
746        /**
747 * Gets the JavaScript onLoad command.
748         *
749 * @return the JavaScript onLoad command
750 */
751
752    public String getJavaScript_onLoad() {
753        return this.javaScript_onLoad;
754    }
755
756        /**
757 * Adds a JavaScript onUnLoad function to the HTML body tag
758 *
759         * @param code
760         *            the JavaScript code to be executed on unload of the HTML page
761 */
762
763    public void setJavaScript_onUnLoad(String code) {
764        this.javaScript_onUnLoad = code;
765    }
766
767        /**
768 * Gets the JavaScript onUnLoad command.
769         *
770 * @return the JavaScript onUnLoad command
771 */
772
773    public String getJavaScript_onUnLoad() {
774        return this.javaScript_onUnLoad;
775    }
776
777        /**
778 * Adds a style class to the HTML body tag
779 *
780         * @param htmlStyleClass
781         *            the style class for the HTML body tag
782 */
783
784    public void setHtmlStyleClass(String htmlStyleClass) {
785        this.htmlStyleClass = htmlStyleClass;
786    }
787
788        /**
789 * Gets the style class of the HTML body tag
790 *
791 * @return              the style class of the HTML body tag
792 */
793
794    public String getHtmlStyleClass() {
795        return this.htmlStyleClass;
796    }
797
798    /**
799     * Set the margin mirroring. It will mirror right/left margins for odd/even pages.
800         *
801         * @param marginMirroring
802         *            <CODE>true</CODE> to mirror the margins
803     * @return always <CODE>true</CODE>
804     */
805    public boolean setMarginMirroring(boolean marginMirroring) {
806        this.marginMirroring = marginMirroring;
807        DocListener listener;
808                for (Object element : listeners) {
809            listener = (DocListener) element;
810            listener.setMarginMirroring(marginMirroring);
811        }
812        return true;
813    }
814
815    /**
816     * Set the margin mirroring. It will mirror top/bottom margins for odd/even pages.
817         *
818         * @param marginMirroringTopBottom
819         *            <CODE>true</CODE> to mirror the margins
820     * @return always <CODE>true</CODE>
821     * @since   2.1.6
822     */
823    public boolean setMarginMirroringTopBottom(boolean marginMirroringTopBottom) {
824        this.marginMirroringTopBottom = marginMirroringTopBottom;
825        DocListener listener;
826                for (Object element : listeners) {
827            listener = (DocListener) element;
828            listener.setMarginMirroringTopBottom(marginMirroringTopBottom);
829        }
830        return true;
831    }
832
833    /**
834     * Gets the margin mirroring flag.
835         *
836     * @return the margin mirroring flag
837     */
838    public boolean isMarginMirroring() {
839        return marginMirroring;
840    }
841}