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