1 /*
2 * $Id: Document.java 3550 2008-07-12 11:40:41Z blowagie $
3 *
4 * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * (the "License"); you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the License.
13 *
14 * The Original Code is 'iText, a free JAVA-PDF library'.
15 *
16 * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17 * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18 * All Rights Reserved.
19 * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20 * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21 *
22 * Contributor(s): all the names of the contributors are added in the source code
23 * where applicable.
24 *
25 * Alternatively, the contents of this file may be used under the terms of the
26 * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27 * provisions of LGPL are applicable instead of those above. If you wish to
28 * allow use of your version of this file only under the terms of the LGPL
29 * License and not to allow others to use your version of this file under
30 * the MPL, indicate your decision by deleting the provisions above and
31 * replace them with the notice and other provisions required by the LGPL.
32 * If you do not delete the provisions above, a recipient may use your version
33 * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34 *
35 * This library is free software; you can redistribute it and/or modify it
36 * under the terms of the MPL as stated above or under the terms of the GNU
37 * Library General Public License as published by the Free Software Foundation;
38 * either version 2 of the License, or any later version.
39 *
40 * This library is distributed in the hope that it will be useful, but WITHOUT
41 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42 * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43 * details.
44 *
45 * If you didn't download this code from the following link, you should check if
46 * you aren't using an obsolete version:
47 * http://www.lowagie.com/iText/
48 */
49
50 package com.lowagie.text;
51
52 import java.text.SimpleDateFormat;
53 import java.util.ArrayList;
54 import java.util.Date;
55 import java.util.Iterator;
56
57 /**
58 * A generic Document class.
59 * <P>
60 * All kinds of Text-elements can be added to a <CODE>HTMLDocument</CODE>.
61 * The <CODE>Document</CODE> signals all the listeners when an element has
62 * been added.
63 * <P>
64 * Remark:
65 * <OL>
66 * <LI>Once a document is created you can add some meta information.
67 * <LI>You can also set the headers/footers.
68 * <LI>You have to open the document before you can write content.
69 * <LI>You can only write content (no more meta-formation!) once a document is
70 * opened.
71 * <LI>When you change the header/footer on a certain page, this will be
72 * effective starting on the next page.
73 * <LI>After closing the document, every listener (as well as its <CODE>
74 * OutputStream</CODE>) is closed too.
75 * </OL>
76 * Example: <BLOCKQUOTE>
77 *
78 * <PRE>// creation of the document with a certain size and certain margins
79 * <STRONG>Document document = new Document(PageSize.A4, 50, 50, 50, 50);
80 * </STRONG> try {
81 * // creation of the different writers
82 * HtmlWriter.getInstance(<STRONG>document </STRONG>, System.out);
83 * PdfWriter.getInstance(<STRONG>document </STRONG>, new FileOutputStream("text.pdf"));
84 * // we add some meta information to the document
85 * <STRONG>document.addAuthor("Bruno Lowagie"); </STRONG>
86 * <STRONG>document.addSubject("This is the result of a Test."); </STRONG>
87 * // we open the document for writing
88 * <STRONG>document.open(); </STRONG>
89 * <STRONG>document.add(new Paragraph("Hello world"));</STRONG>
90 * } catch(DocumentException de) {
91 * System.err.println(de.getMessage());
92 * }
93 * <STRONG>document.close();</STRONG>
94 * </PRE>
95 *
96 * </BLOCKQUOTE>
97 */
98
99 public class Document implements DocListener {
100
101 // membervariables
102
103 /** This constant may only be changed by Paulo Soares and/or Bruno Lowagie. */
104 private static final String ITEXT_VERSION = "iText 2.1.3 (by lowagie.com)";
105
106 /**
107 * Allows the pdf documents to be produced without compression for debugging
108 * purposes.
109 */
110 public static boolean compress = true;
111
112 /**
113 * When true the file access is not done through a memory mapped file. Use it if the file
114 * is too big to be mapped in your address space.
115 */
116 public static boolean plainRandomAccess = false;
117
118 /** Scales the WMF font size. The default value is 0.86. */
119 public static float wmfFontCorrection = 0.86f;
120
121 /** The DocListener. */
122 private ArrayList listeners = new ArrayList();
123
124 /** Is the document open or not? */
125 protected boolean open;
126
127 /** Has the document already been closed? */
128 protected boolean close;
129
130 // membervariables concerning the layout
131
132 /** The size of the page. */
133 protected Rectangle pageSize;
134
135 /** margin in x direction starting from the left */
136 protected float marginLeft = 0;
137
138 /** margin in x direction starting from the right */
139 protected float marginRight = 0;
140
141 /** margin in y direction starting from the top */
142 protected float marginTop = 0;
143
144 /** margin in y direction starting from the bottom */
145 protected float marginBottom = 0;
146
147 protected boolean marginMirroring = false;
148
149 /** Content of JavaScript onLoad function */
150 protected String javaScript_onLoad = null;
151
152 /** Content of JavaScript onUnLoad function */
153 protected String javaScript_onUnLoad = null;
154
155 /** Style class in HTML body tag */
156 protected String htmlStyleClass = null;
157
158 // headers, footers
159
160 /** Current pagenumber */
161 protected int pageN = 0;
162
163 /** This is the textual part of a Page; it can contain a header */
164 protected HeaderFooter header = null;
165
166 /** This is the textual part of the footer */
167 protected HeaderFooter footer = null;
168
169 /** This is a chapter number in case ChapterAutoNumber is used. */
170 protected int chapternumber = 0;
171
172 // constructor
173
174 /**
175 * Constructs a new <CODE>Document</CODE> -object.
176 */
177
178 public Document() {
179 this(PageSize.A4);
180 }
181
182 /**
183 * Constructs a new <CODE>Document</CODE> -object.
184 *
185 * @param pageSize
186 * the pageSize
187 */
188
189 public Document(Rectangle pageSize) {
190 this(pageSize, 36, 36, 36, 36);
191 }
192
193 /**
194 * Constructs a new <CODE>Document</CODE> -object.
195 *
196 * @param pageSize
197 * the pageSize
198 * @param marginLeft
199 * the margin on the left
200 * @param marginRight
201 * the margin on the right
202 * @param marginTop
203 * the margin on the top
204 * @param marginBottom
205 * the margin on the bottom
206 */
207
208 public Document(Rectangle pageSize, float marginLeft, float marginRight,
209 float marginTop, float marginBottom) {
210 this.pageSize = pageSize;
211 this.marginLeft = marginLeft;
212 this.marginRight = marginRight;
213 this.marginTop = marginTop;
214 this.marginBottom = marginBottom;
215 }
216
217 // listener methods
218
219 /**
220 * Adds a <CODE>DocListener</CODE> to the <CODE>Document</CODE>.
221 *
222 * @param listener
223 * the new DocListener.
224 */
225
226 public void addDocListener(DocListener listener) {
227 listeners.add(listener);
228 }
229
230 /**
231 * Removes a <CODE>DocListener</CODE> from the <CODE>Document</CODE>.
232 *
233 * @param listener
234 * the DocListener that has to be removed.
235 */
236
237 public void removeDocListener(DocListener listener) {
238 listeners.remove(listener);
239 }
240
241 // methods implementing the DocListener interface
242
243 /**
244 * Adds an <CODE>Element</CODE> to the <CODE>Document</CODE>.
245 *
246 * @param element
247 * the <CODE>Element</CODE> to add
248 * @return <CODE>true</CODE> if the element was added, <CODE>false
249 * </CODE> if not
250 * @throws DocumentException
251 * when a document isn't open yet, or has been closed
252 */
253
254 public boolean add(Element element) throws DocumentException {
255 if (close) {
256 throw new DocumentException(
257 "The document has been closed. You can't add any Elements.");
258 }
259 if (!open && element.isContent()) {
260 throw new DocumentException(
261 "The document is not open yet; you can only add Meta information.");
262 }
263 boolean success = false;
264 DocListener listener;
265 if (element instanceof ChapterAutoNumber) {
266 chapternumber++;
267 ((ChapterAutoNumber)element).setChapterNumber(chapternumber);
268 }
269 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
270 listener = (DocListener) iterator.next();
271 success |= listener.add(element);
272 }
273 if (element instanceof LargeElement) {
274 LargeElement e = (LargeElement)element;
275 if (!e.isComplete())
276 e.flushContent();
277 }
278 return success;
279 }
280
281 /**
282 * Opens the document.
283 * <P>
284 * Once the document is opened, you can't write any Header- or
285 * Meta-information anymore. You have to open the document before you can
286 * begin to add content to the body of the document.
287 */
288
289 public void open() {
290 if (!close) {
291 open = true;
292 }
293 DocListener listener;
294 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
295 listener = (DocListener) iterator.next();
296 listener.setPageSize(pageSize);
297 listener.setMargins(marginLeft, marginRight, marginTop,
298 marginBottom);
299 listener.open();
300 }
301 }
302
303 /**
304 * Sets the pagesize.
305 *
306 * @param pageSize
307 * the new pagesize
308 * @return a <CODE>boolean</CODE>
309 */
310
311 public boolean setPageSize(Rectangle pageSize) {
312 this.pageSize = pageSize;
313 DocListener listener;
314 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
315 listener = (DocListener) iterator.next();
316 listener.setPageSize(pageSize);
317 }
318 return true;
319 }
320
321 /**
322 * Sets the margins.
323 *
324 * @param marginLeft
325 * the margin on the left
326 * @param marginRight
327 * the margin on the right
328 * @param marginTop
329 * the margin on the top
330 * @param marginBottom
331 * the margin on the bottom
332 * @return a <CODE>boolean</CODE>
333 */
334
335 public boolean setMargins(float marginLeft, float marginRight,
336 float marginTop, float marginBottom) {
337 this.marginLeft = marginLeft;
338 this.marginRight = marginRight;
339 this.marginTop = marginTop;
340 this.marginBottom = marginBottom;
341 DocListener listener;
342 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
343 listener = (DocListener) iterator.next();
344 listener.setMargins(marginLeft, marginRight, marginTop,
345 marginBottom);
346 }
347 return true;
348 }
349
350 /**
351 * Signals that an new page has to be started.
352 *
353 * @return <CODE>true</CODE> if the page was added, <CODE>false</CODE>
354 * if not.
355 */
356
357 public boolean newPage() {
358 if (!open || close) {
359 return false;
360 }
361 DocListener listener;
362 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
363 listener = (DocListener) iterator.next();
364 listener.newPage();
365 }
366 return true;
367 }
368
369 /**
370 * Changes the header of this document.
371 *
372 * @param header
373 * the new header
374 */
375
376 public void setHeader(HeaderFooter header) {
377 this.header = header;
378 DocListener listener;
379 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
380 listener = (DocListener) iterator.next();
381 listener.setHeader(header);
382 }
383 }
384
385 /**
386 * Resets the header of this document.
387 */
388
389 public void resetHeader() {
390 this.header = null;
391 DocListener listener;
392 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
393 listener = (DocListener) iterator.next();
394 listener.resetHeader();
395 }
396 }
397
398 /**
399 * Changes the footer of this document.
400 *
401 * @param footer
402 * the new footer
403 */
404
405 public void setFooter(HeaderFooter footer) {
406 this.footer = footer;
407 DocListener listener;
408 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
409 listener = (DocListener) iterator.next();
410 listener.setFooter(footer);
411 }
412 }
413
414 /**
415 * Resets the footer of this document.
416 */
417
418 public void resetFooter() {
419 this.footer = null;
420 DocListener listener;
421 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
422 listener = (DocListener) iterator.next();
423 listener.resetFooter();
424 }
425 }
426
427 /**
428 * Sets the page number to 0.
429 */
430
431 public void resetPageCount() {
432 pageN = 0;
433 DocListener listener;
434 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
435 listener = (DocListener) iterator.next();
436 listener.resetPageCount();
437 }
438 }
439
440 /**
441 * Sets the page number.
442 *
443 * @param pageN
444 * the new page number
445 */
446
447 public void setPageCount(int pageN) {
448 this.pageN = pageN;
449 DocListener listener;
450 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
451 listener = (DocListener) iterator.next();
452 listener.setPageCount(pageN);
453 }
454 }
455
456 /**
457 * Returns the current page number.
458 *
459 * @return the current page number
460 */
461
462 public int getPageNumber() {
463 return this.pageN;
464 }
465
466 /**
467 * Closes the document.
468 * <P>
469 * Once all the content has been written in the body, you have to close the
470 * body. After that nothing can be written to the body anymore.
471 */
472
473 public void close() {
474 if (!close) {
475 open = false;
476 close = true;
477 }
478 DocListener listener;
479 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
480 listener = (DocListener) iterator.next();
481 listener.close();
482 }
483 }
484
485 // methods concerning the header or some meta information
486
487 /**
488 * Adds a user defined header to the document.
489 *
490 * @param name
491 * the name of the header
492 * @param content
493 * the content of the header
494 * @return <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
495 */
496
497 public boolean addHeader(String name, String content) {
498 try {
499 return add(new Header(name, content));
500 } catch (DocumentException de) {
501 throw new ExceptionConverter(de);
502 }
503 }
504
505 /**
506 * Adds the title to a Document.
507 *
508 * @param title
509 * the title
510 * @return <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
511 */
512
513 public boolean addTitle(String title) {
514 try {
515 return add(new Meta(Element.TITLE, title));
516 } catch (DocumentException de) {
517 throw new ExceptionConverter(de);
518 }
519 }
520
521 /**
522 * Adds the subject to a Document.
523 *
524 * @param subject
525 * the subject
526 * @return <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
527 */
528
529 public boolean addSubject(String subject) {
530 try {
531 return add(new Meta(Element.SUBJECT, subject));
532 } catch (DocumentException de) {
533 throw new ExceptionConverter(de);
534 }
535 }
536
537 /**
538 * Adds the keywords to a Document.
539 *
540 * @param keywords
541 * adds the keywords to the document
542 * @return <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
543 */
544
545 public boolean addKeywords(String keywords) {
546 try {
547 return add(new Meta(Element.KEYWORDS, keywords));
548 } catch (DocumentException de) {
549 throw new ExceptionConverter(de);
550 }
551 }
552
553 /**
554 * Adds the author to a Document.
555 *
556 * @param author
557 * the name of the author
558 * @return <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
559 */
560
561 public boolean addAuthor(String author) {
562 try {
563 return add(new Meta(Element.AUTHOR, author));
564 } catch (DocumentException de) {
565 throw new ExceptionConverter(de);
566 }
567 }
568
569 /**
570 * Adds the creator to a Document.
571 *
572 * @param creator
573 * the name of the creator
574 * @return <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
575 */
576
577 public boolean addCreator(String creator) {
578 try {
579 return add(new Meta(Element.CREATOR, creator));
580 } catch (DocumentException de) {
581 throw new ExceptionConverter(de);
582 }
583 }
584
585 /**
586 * Adds the producer to a Document.
587 *
588 * @return <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
589 */
590
591 public boolean addProducer() {
592 try {
593 return add(new Meta(Element.PRODUCER, "iText by lowagie.com"));
594 } catch (DocumentException de) {
595 throw new ExceptionConverter(de);
596 }
597 }
598
599 /**
600 * Adds the current date and time to a Document.
601 *
602 * @return <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise
603 */
604
605 public boolean addCreationDate() {
606 try {
607 /* bugfix by 'taqua' (Thomas) */
608 final SimpleDateFormat sdf = new SimpleDateFormat(
609 "EEE MMM dd HH:mm:ss zzz yyyy");
610 return add(new Meta(Element.CREATIONDATE, sdf.format(new Date())));
611 } catch (DocumentException de) {
612 throw new ExceptionConverter(de);
613 }
614 }
615
616 // methods to get the layout of the document.
617
618 /**
619 * Returns the left margin.
620 *
621 * @return the left margin
622 */
623
624 public float leftMargin() {
625 return marginLeft;
626 }
627
628 /**
629 * Return the right margin.
630 *
631 * @return the right margin
632 */
633
634 public float rightMargin() {
635 return marginRight;
636 }
637
638 /**
639 * Returns the top margin.
640 *
641 * @return the top margin
642 */
643
644 public float topMargin() {
645 return marginTop;
646 }
647
648 /**
649 * Returns the bottom margin.
650 *
651 * @return the bottom margin
652 */
653
654 public float bottomMargin() {
655 return marginBottom;
656 }
657
658 /**
659 * Returns the lower left x-coordinate.
660 *
661 * @return the lower left x-coordinate
662 */
663
664 public float left() {
665 return pageSize.getLeft(marginLeft);
666 }
667
668 /**
669 * Returns the upper right x-coordinate.
670 *
671 * @return the upper right x-coordinate
672 */
673
674 public float right() {
675 return pageSize.getRight(marginRight);
676 }
677
678 /**
679 * Returns the upper right y-coordinate.
680 *
681 * @return the upper right y-coordinate
682 */
683
684 public float top() {
685 return pageSize.getTop(marginTop);
686 }
687
688 /**
689 * Returns the lower left y-coordinate.
690 *
691 * @return the lower left y-coordinate
692 */
693
694 public float bottom() {
695 return pageSize.getBottom(marginBottom);
696 }
697
698 /**
699 * Returns the lower left x-coordinate considering a given margin.
700 *
701 * @param margin
702 * a margin
703 * @return the lower left x-coordinate
704 */
705
706 public float left(float margin) {
707 return pageSize.getLeft(marginLeft + margin);
708 }
709
710 /**
711 * Returns the upper right x-coordinate, considering a given margin.
712 *
713 * @param margin
714 * a margin
715 * @return the upper right x-coordinate
716 */
717
718 public float right(float margin) {
719 return pageSize.getRight(marginRight + margin);
720 }
721
722 /**
723 * Returns the upper right y-coordinate, considering a given margin.
724 *
725 * @param margin
726 * a margin
727 * @return the upper right y-coordinate
728 */
729
730 public float top(float margin) {
731 return pageSize.getTop(marginTop + margin);
732 }
733
734 /**
735 * Returns the lower left y-coordinate, considering a given margin.
736 *
737 * @param margin
738 * a margin
739 * @return the lower left y-coordinate
740 */
741
742 public float bottom(float margin) {
743 return pageSize.getBottom(marginBottom + margin);
744 }
745
746 /**
747 * Gets the pagesize.
748 *
749 * @return the page size
750 */
751
752 public Rectangle getPageSize() {
753 return this.pageSize;
754 }
755
756 /**
757 * Checks if the document is open.
758 *
759 * @return <CODE>true</CODE> if the document is open
760 */
761 public boolean isOpen() {
762 return open;
763 }
764
765 /**
766 * Gets the iText version.
767 * This method may only be changed by Paulo Soares and/or Bruno Lowagie.
768 * @return iText version
769 */
770 public static final String getVersion() {
771 return ITEXT_VERSION;
772 }
773
774 /**
775 * Adds a JavaScript onLoad function to the HTML body tag
776 *
777 * @param code
778 * the JavaScript code to be executed on load of the HTML page
779 */
780
781 public void setJavaScript_onLoad(String code) {
782 this.javaScript_onLoad = code;
783 }
784
785 /**
786 * Gets the JavaScript onLoad command.
787 *
788 * @return the JavaScript onLoad command
789 */
790
791 public String getJavaScript_onLoad() {
792 return this.javaScript_onLoad;
793 }
794
795 /**
796 * Adds a JavaScript onUnLoad function to the HTML body tag
797 *
798 * @param code
799 * the JavaScript code to be executed on unload of the HTML page
800 */
801
802 public void setJavaScript_onUnLoad(String code) {
803 this.javaScript_onUnLoad = code;
804 }
805
806 /**
807 * Gets the JavaScript onUnLoad command.
808 *
809 * @return the JavaScript onUnLoad command
810 */
811
812 public String getJavaScript_onUnLoad() {
813 return this.javaScript_onUnLoad;
814 }
815
816 /**
817 * Adds a style class to the HTML body tag
818 *
819 * @param htmlStyleClass
820 * the style class for the HTML body tag
821 */
822
823 public void setHtmlStyleClass(String htmlStyleClass) {
824 this.htmlStyleClass = htmlStyleClass;
825 }
826
827 /**
828 * Gets the style class of the HTML body tag
829 *
830 * @return the style class of the HTML body tag
831 */
832
833 public String getHtmlStyleClass() {
834 return this.htmlStyleClass;
835 }
836
837 /**
838 * Set the margin mirroring. It will mirror margins for odd/even pages.
839 * <p>
840 * Note: it will not work with {@link Table}.
841 *
842 * @param marginMirroring
843 * <CODE>true</CODE> to mirror the margins
844 * @return always <CODE>true</CODE>
845 */
846 public boolean setMarginMirroring(boolean marginMirroring) {
847 this.marginMirroring = marginMirroring;
848 DocListener listener;
849 for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
850 listener = (DocListener) iterator.next();
851 listener.setMarginMirroring(marginMirroring);
852 }
853 return true;
854 }
855
856 /**
857 * Gets the margin mirroring flag.
858 *
859 * @return the margin mirroring flag
860 */
861 public boolean isMarginMirroring() {
862 return marginMirroring;
863 }
864 }