using System; using System.Collections; using System.IO; using System.util.collections; using iTextSharp.text; using iTextSharp.text.pdf.intern; using iTextSharp.text.pdf.draw; using iTextSharp.text.pdf.collection; using System.util; /* * * $Id: PdfDocument.cs,v 1.75 2008/05/13 11:25:19 psoares33 Exp $ * * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie. * * The contents of this file are subject to the Mozilla Public License Version 1.1 * (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the License. * * The Original Code is 'iText, a free JAVA-PDF library'. * * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. * All Rights Reserved. * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. * * Contributor(s): all the names of the contributors are added in the source code * where applicable. * * Alternatively, the contents of this file may be used under the terms of the * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the * provisions of LGPL are applicable instead of those above. If you wish to * allow use of your version of this file only under the terms of the LGPL * License and not to allow others to use your version of this file under * the MPL, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the LGPL. * If you do not delete the provisions above, a recipient may use your version * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. * * This library is free software; you can redistribute it and/or modify it * under the terms of the MPL as stated above or under the terms of the GNU * Library General Public License as published by the Free Software Foundation; * either version 2 of the License, or any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more * details. * * If you didn't download this code from the following link, you should check if * you aren't using an obsolete version: * http://www.lowagie.com/iText/ */ namespace iTextSharp.text.pdf { /** * PdfDocument is the class that is used by PdfWriter * to translate a Document into a PDF with different pages. *

* A PdfDocument always listens to a Document * and adds the Pdf representation of every Element that is * added to the Document. * * @see com.lowagie.text.Document * @see com.lowagie.text.DocListener * @see PdfWriter */ public class PdfDocument : Document { /** * PdfInfo is the PDF InfoDictionary. *

* A document's trailer may contain a reference to an Info dictionary that provides information * about the document. This optional dictionary may contain one or more keys, whose values * should be strings.
* This object is described in the 'Portable Document Format Reference Manual version 1.3' * section 6.10 (page 120-121) */ public class PdfInfo : PdfDictionary { // constructors /** * Construct a PdfInfo-object. */ internal PdfInfo() { AddProducer(); AddCreationDate(); } /** * Constructs a PdfInfo-object. * * @param author name of the author of the document * @param title title of the document * @param subject subject of the document */ internal PdfInfo(String author, String title, String subject) : base() { AddTitle(title); AddSubject(subject); AddAuthor(author); } /** * Adds the title of the document. * * @param title the title of the document */ internal void AddTitle(String title) { Put(PdfName.TITLE, new PdfString(title, PdfObject.TEXT_UNICODE)); } /** * Adds the subject to the document. * * @param subject the subject of the document */ internal void AddSubject(String subject) { Put(PdfName.SUBJECT, new PdfString(subject, PdfObject.TEXT_UNICODE)); } /** * Adds some keywords to the document. * * @param keywords the keywords of the document */ internal void AddKeywords(String keywords) { Put(PdfName.KEYWORDS, new PdfString(keywords, PdfObject.TEXT_UNICODE)); } /** * Adds the name of the author to the document. * * @param author the name of the author */ internal void AddAuthor(String author) { Put(PdfName.AUTHOR, new PdfString(author, PdfObject.TEXT_UNICODE)); } /** * Adds the name of the creator to the document. * * @param creator the name of the creator */ internal void AddCreator(String creator) { Put(PdfName.CREATOR, new PdfString(creator, PdfObject.TEXT_UNICODE)); } /** * Adds the name of the producer to the document. */ internal void AddProducer() { // This line may only be changed by Bruno Lowagie or Paulo Soares Put(PdfName.PRODUCER, new PdfString(Version)); // Do not edit the line above! } /** * Adds the date of creation to the document. */ internal void AddCreationDate() { PdfString date = new PdfDate(); Put(PdfName.CREATIONDATE, date); Put(PdfName.MODDATE, date); } internal void Addkey(String key, String value) { if (key.Equals("Producer") || key.Equals("CreationDate")) return; Put(new PdfName(key), new PdfString(value, PdfObject.TEXT_UNICODE)); } } /** * PdfCatalog is the PDF Catalog-object. *

* The Catalog is a dictionary that is the root node of the document. It contains a reference * to the tree of pages contained in the document, a reference to the tree of objects representing * the document's outline, a reference to the document's article threads, and the list of named * destinations. In addition, the Catalog indicates whether the document's outline or thumbnail * page images should be displayed automatically when the document is viewed and wether some location * other than the first page should be shown when the document is opened.
* In this class however, only the reference to the tree of pages is implemented.
* This object is described in the 'Portable Document Format Reference Manual version 1.3' * section 6.2 (page 67-71) */ internal class PdfCatalog : PdfDictionary { internal PdfWriter writer; // constructors /** * Constructs a PdfCatalog. * * @param pages an indirect reference to the root of the document's Pages tree. * @param writer the writer the catalog applies to */ internal PdfCatalog(PdfIndirectReference pages, PdfWriter writer) : base(CATALOG) { this.writer = writer; Put(PdfName.PAGES, pages); } /** * Adds the names of the named destinations to the catalog. * @param localDestinations the local destinations * @param documentJavaScript the javascript used in the document * @param writer the writer the catalog applies to */ internal void AddNames(k_Tree localDestinations, Hashtable documentLevelJS, Hashtable documentFileAttachment, PdfWriter writer) { if (localDestinations.Count == 0 && documentLevelJS.Count == 0 && documentFileAttachment.Count == 0) return; PdfDictionary names = new PdfDictionary(); if (localDestinations.Count > 0) { PdfArray ar = new PdfArray(); foreach (DictionaryEntry entry in localDestinations) { String name = (String)entry.Key; Object[] obj = (Object[])entry.Value; PdfIndirectReference refi = (PdfIndirectReference)obj[1]; ar.Add(new PdfString(name, null)); ar.Add(refi); } PdfDictionary dests = new PdfDictionary(); dests.Put(PdfName.NAMES, ar); names.Put(PdfName.DESTS, writer.AddToBody(dests).IndirectReference); } if (documentLevelJS.Count > 0) { PdfDictionary tree = PdfNameTree.WriteTree(documentLevelJS, writer); names.Put(PdfName.JAVASCRIPT, writer.AddToBody(tree).IndirectReference); } if (documentFileAttachment.Count > 0) { names.Put(PdfName.EMBEDDEDFILES, writer.AddToBody(PdfNameTree.WriteTree(documentFileAttachment, writer)).IndirectReference); } Put(PdfName.NAMES, writer.AddToBody(names).IndirectReference); } internal PdfAction OpenAction { set { Put(PdfName.OPENACTION, value); } } /** Sets the document level additional actions. * @param actions dictionary of actions */ internal PdfDictionary AdditionalActions { set { Put(PdfName.AA, writer.AddToBody(value).IndirectReference); } } } // CONSTRUCTING A PdfDocument/PdfWriter INSTANCE /** * Constructs a new PDF document. * @throws DocumentException on error */ internal PdfDocument() { AddProducer(); AddCreationDate(); } /** The PdfWriter. */ protected internal PdfWriter writer; /** * Adds a PdfWriter to the PdfDocument. * * @param writer the PdfWriter that writes everything * what is added to this document to an outputstream. * @throws DocumentException on error */ internal void AddWriter(PdfWriter writer) { if (this.writer == null) { this.writer = writer; annotationsImp = new PdfAnnotationsImp(writer); return; } throw new DocumentException("You can only add a writer to a PdfDocument once."); } // LISTENER METHODS START // [L0] ElementListener interface /** This is the PdfContentByte object, containing the text. */ protected internal PdfContentByte text; /** This is the PdfContentByte object, containing the borders and other Graphics. */ protected internal PdfContentByte graphics; /** This represents the leading of the lines. */ protected internal float leading = 0; /** * Getter for the current leading. * @return the current leading * @since 2.1.2 */ public float Leading { get { return leading; } } /** This is the current height of the document. */ protected internal float currentHeight = 0; /** * Signals that onParagraph is valid (to avoid that a Chapter/Section title is treated as a Paragraph). * @since 2.1.2 */ protected bool isSectionTitle = false; /** * Signals that the current leading has to be subtracted from a YMark object. * @since 2.1.2 */ protected int leadingCount = 0; /** This represents the current alignment of the PDF Elements. */ protected internal int alignment = Element.ALIGN_LEFT; /** The current active PdfAction when processing an Anchor. */ protected internal PdfAction anchorAction = null; /** * Signals that an Element was added to the Document. * * @param element the element to add * @return true if the element was added, false if not. * @throws DocumentException when a document isn't open yet, or has been closed */ public override bool Add(IElement element) { if (writer != null && writer.IsPaused()) { return false; } switch (element.Type) { // Information (headers) case Element.HEADER: info.Addkey(((Meta)element).Name, ((Meta)element).Content); break; case Element.TITLE: info.AddTitle(((Meta)element).Content); break; case Element.SUBJECT: info.AddSubject(((Meta)element).Content); break; case Element.KEYWORDS: info.AddKeywords(((Meta)element).Content); break; case Element.AUTHOR: info.AddAuthor(((Meta)element).Content); break; case Element.CREATOR: info.AddCreator(((Meta)element).Content); break; case Element.PRODUCER: // you can not change the name of the producer info.AddProducer(); break; case Element.CREATIONDATE: // you can not set the creation date, only reset it info.AddCreationDate(); break; // content (text) case Element.CHUNK: { // if there isn't a current line available, we make one if (line == null) { CarriageReturn(); } // we cast the element to a chunk PdfChunk chunk = new PdfChunk((Chunk) element, anchorAction); // we try to add the chunk to the line, until we succeed { PdfChunk overflow; while ((overflow = line.Add(chunk)) != null) { CarriageReturn(); chunk = overflow; chunk.TrimFirstSpace(); } } pageEmpty = false; if (chunk.IsAttribute(Chunk.NEWPAGE)) { NewPage(); } break; } case Element.ANCHOR: { leadingCount++; Anchor anchor = (Anchor) element; String url = anchor.Reference; leading = anchor.Leading; if (url != null) { anchorAction = new PdfAction(url); } // we process the element element.Process(this); anchorAction = null; leadingCount--; break; } case Element.ANNOTATION: { if (line == null) { CarriageReturn(); } Annotation annot = (Annotation) element; Rectangle rect = new Rectangle(0, 0); if (line != null) rect = new Rectangle(annot.GetLlx(IndentRight - line.WidthLeft), annot.GetLly(IndentTop - currentHeight), annot.GetUrx(IndentRight - line.WidthLeft + 20), annot.GetUry(IndentTop - currentHeight - 20)); PdfAnnotation an = PdfAnnotationsImp.ConvertAnnotation(writer, annot, rect); annotationsImp.AddPlainAnnotation(an); pageEmpty = false; break; } case Element.PHRASE: { leadingCount++; // we cast the element to a phrase and set the leading of the document leading = ((Phrase) element).Leading; // we process the element element.Process(this); leadingCount--; break; } case Element.PARAGRAPH: { leadingCount++; // we cast the element to a paragraph Paragraph paragraph = (Paragraph) element; AddSpacing(paragraph.SpacingBefore, leading, paragraph.Font); // we adjust the parameters of the document alignment = paragraph.Alignment; leading = paragraph.TotalLeading; CarriageReturn(); // we don't want to make orphans/widows if (currentHeight + line.Height + leading > IndentTop - IndentBottom) { NewPage(); } indentation.indentLeft += paragraph.IndentationLeft; indentation.indentRight += paragraph.IndentationRight; CarriageReturn(); IPdfPageEvent pageEvent = writer.PageEvent; if (pageEvent != null && !isSectionTitle) pageEvent.OnParagraph(writer, this, IndentTop - currentHeight); // if a paragraph has to be kept together, we wrap it in a table object if (paragraph.KeepTogether) { CarriageReturn(); PdfPTable table = new PdfPTable(1); table.WidthPercentage = 100f; PdfPCell cell = new PdfPCell(); cell.AddElement(paragraph); cell.Border = Rectangle.NO_BORDER; cell.Padding = 0; table.AddCell(cell); indentation.indentLeft -= paragraph.IndentationLeft; indentation.indentRight -= paragraph.IndentationRight; this.Add(table); indentation.indentLeft += paragraph.IndentationLeft; indentation.indentRight += paragraph.IndentationRight; } else { line.SetExtraIndent(paragraph.FirstLineIndent); element.Process(this); CarriageReturn(); AddSpacing(paragraph.SpacingAfter, paragraph.TotalLeading, paragraph.Font); } if (pageEvent != null && !isSectionTitle) pageEvent.OnParagraphEnd(writer, this, IndentTop - currentHeight); alignment = Element.ALIGN_LEFT; indentation.indentLeft -= paragraph.IndentationLeft; indentation.indentRight -= paragraph.IndentationRight; CarriageReturn(); leadingCount--; break; } case Element.SECTION: case Element.CHAPTER: { // Chapters and Sections only differ in their constructor // so we cast both to a Section Section section = (Section) element; IPdfPageEvent pageEvent = writer.PageEvent; bool hasTitle = section.NotAddedYet && section.Title != null; // if the section is a chapter, we begin a new page if (section.TriggerNewPage) { NewPage(); } if (hasTitle) { float fith = IndentTop - currentHeight; int rotation = pageSize.Rotation; if (rotation == 90 || rotation == 180) fith = pageSize.Height - fith; PdfDestination destination = new PdfDestination(PdfDestination.FITH, fith); while (currentOutline.Level >= section.Depth) { currentOutline = currentOutline.Parent; } PdfOutline outline = new PdfOutline(currentOutline, destination, section.GetBookmarkTitle(), section.BookmarkOpen); currentOutline = outline; } // some values are set CarriageReturn(); indentation.sectionIndentLeft += section.IndentationLeft; indentation.sectionIndentRight += section.IndentationRight; if (section.NotAddedYet && pageEvent != null) if (element.Type == Element.CHAPTER) pageEvent.OnChapter(writer, this, IndentTop - currentHeight, section.Title); else pageEvent.OnSection(writer, this, IndentTop - currentHeight, section.Depth, section.Title); // the title of the section (if any has to be printed) if (hasTitle) { isSectionTitle = true; Add(section.Title); isSectionTitle = false; } indentation.sectionIndentLeft += section.Indentation; // we process the section element.Process(this); // some parameters are set back to normal again indentation.sectionIndentLeft -= (section.IndentationLeft + section.Indentation); indentation.sectionIndentRight -= section.IndentationRight; if (section.ElementComplete && pageEvent != null) if (element.Type == Element.CHAPTER) pageEvent.OnChapterEnd(writer, this, IndentTop - currentHeight); else pageEvent.OnSectionEnd(writer, this, IndentTop - currentHeight); break; } case Element.LIST: { // we cast the element to a List List list = (List) element; if (list.Alignindent) { list.NormalizeIndentation(); } // we adjust the document indentation.listIndentLeft += list.IndentationLeft; indentation.indentRight += list.IndentationRight; // we process the items in the list element.Process(this); // some parameters are set back to normal again indentation.listIndentLeft -= list.IndentationLeft; indentation.indentRight -= list.IndentationRight; CarriageReturn(); break; } case Element.LISTITEM: { leadingCount++; // we cast the element to a ListItem ListItem listItem = (ListItem) element; AddSpacing(listItem.SpacingBefore, leading, listItem.Font); // we adjust the document alignment = listItem.Alignment; indentation.listIndentLeft += listItem.IndentationLeft; indentation.indentRight += listItem.IndentationRight; leading = listItem.TotalLeading; CarriageReturn(); // we prepare the current line to be able to show us the listsymbol line.ListItem = listItem; // we process the item element.Process(this); AddSpacing(listItem.SpacingAfter, listItem.TotalLeading, listItem.Font); // if the last line is justified, it should be aligned to the left if (line.HasToBeJustified()) { line.ResetAlignment(); } // some parameters are set back to normal again CarriageReturn(); indentation.listIndentLeft -= listItem.IndentationLeft; indentation.indentRight -= listItem.IndentationRight; leadingCount--; break; } case Element.RECTANGLE: { Rectangle rectangle = (Rectangle) element; graphics.Rectangle(rectangle); pageEmpty = false; break; } case Element.PTABLE: { PdfPTable ptable = (PdfPTable)element; if (ptable.Size <= ptable.HeaderRows) break; //nothing to do // before every table, we add a new line and flush all lines EnsureNewLine(); FlushLines(); AddPTable(ptable); pageEmpty = false; NewLine(); break; } case Element.MULTI_COLUMN_TEXT: { EnsureNewLine(); FlushLines(); MultiColumnText multiText = (MultiColumnText) element; float height = multiText.Write(writer.DirectContent, this, IndentTop - currentHeight); currentHeight += height; text.MoveText(0, -1f* height); pageEmpty = false; break; } case Element.TABLE : { if (element is SimpleTable) { PdfPTable ptable = ((SimpleTable)element).CreatePdfPTable(); if (ptable.Size <= ptable.HeaderRows) break; //nothing to do // before every table, we add a new line and flush all lines EnsureNewLine(); FlushLines(); AddPTable(ptable); pageEmpty = false; break; } else if (element is Table) { try { PdfPTable ptable = ((Table)element).CreatePdfPTable(); if (ptable.Size <= ptable.HeaderRows) break; //nothing to do // before every table, we add a new line and flush all lines EnsureNewLine(); FlushLines(); AddPTable(ptable); pageEmpty = false; break; } catch (BadElementException) { // constructing the PdfTable // Before the table, add a blank line using offset or default leading float offset = ((Table)element).Offset; if (float.IsNaN(offset)) offset = leading; CarriageReturn(); lines.Add(new PdfLine(IndentLeft, IndentRight, alignment, offset)); currentHeight += offset; AddPdfTable((Table)element); } } else { return false; } break; } case Element.JPEG: case Element.JPEG2000: case Element.IMGRAW: case Element.IMGTEMPLATE: { //carriageReturn(); suggestion by Marc Campforts Add((Image) element); break; } case Element.YMARK: { IDrawInterface zh = (IDrawInterface)element; zh.Draw(graphics, IndentLeft, IndentBottom, IndentRight, IndentTop, IndentTop - currentHeight - (leadingCount > 0 ? leading : 0)); pageEmpty = false; break; } case Element.MARKED: { MarkedObject mo; if (element is MarkedSection) { mo = ((MarkedSection)element).Title; if (mo != null) { mo.Process(this); } } mo = (MarkedObject)element; mo.Process(this); break; } default: return false; } lastElementType = element.Type; return true; } // [L1] DocListener interface /** * Opens the document. *

* You have to open the document before you can begin to add content * to the body of the document. */ public override void Open() { if (!open) { base.Open(); writer.Open(); rootOutline = new PdfOutline(writer); currentOutline = rootOutline; } InitPage(); } // [L2] DocListener interface /** * Closes the document. * * Once all the content has been written in the body, you have to close * the body. After that nothing can be written to the body anymore. */ public override void Close() { if (close) { return; } bool wasImage = (imageWait != null); NewPage(); if (imageWait != null || wasImage) NewPage(); if (annotationsImp.HasUnusedAnnotations()) throw new Exception("Not all annotations could be added to the document (the document doesn't have enough pages)."); IPdfPageEvent pageEvent = writer.PageEvent; if (pageEvent != null) pageEvent.OnCloseDocument(writer, this); base.Close(); writer.AddLocalDestinations(localDestinations); CalculateOutlineCount(); WriteOutlines(); writer.Close(); } // [L3] DocListener interface protected internal int textEmptySize; // [C9] Metadata for the page /** XMP Metadata for the page. */ protected byte[] xmpMetadata = null; /** * Use this method to set the XMP Metadata. * @param xmpMetadata The xmpMetadata to set. */ public byte[] XmpMetadata { set { xmpMetadata = value; } } /** * Makes a new page and sends it to the PdfWriter. * * @return a bool * @throws DocumentException on error */ public override bool NewPage() { lastElementType = -1; if (writer == null || (writer.DirectContent.Size == 0 && writer.DirectContentUnder.Size == 0 && (pageEmpty || writer.IsPaused()))) { SetNewPageSizeAndMargins(); return false; } if (!open || close) { throw new Exception("The document isn't open."); } IPdfPageEvent pageEvent = writer.PageEvent; if (pageEvent != null) pageEvent.OnEndPage(writer, this); //Added to inform any listeners that we are moving to a new page (added by David Freels) base.NewPage(); // the following 2 lines were added by Pelikan Stephan indentation.imageIndentLeft = 0; indentation.imageIndentRight = 0; // we flush the arraylist with recently written lines FlushLines(); // we prepare the elements of the page dictionary // [U1] page size and rotation int rotation = pageSize.Rotation; // [C10] if (writer.IsPdfX()) { if (thisBoxSize.ContainsKey("art") && thisBoxSize.ContainsKey("trim")) throw new PdfXConformanceException("Only one of ArtBox or TrimBox can exist in the page."); if (!thisBoxSize.ContainsKey("art") && !thisBoxSize.ContainsKey("trim")) { if (thisBoxSize.ContainsKey("crop")) thisBoxSize["trim"] = thisBoxSize["crop"]; else thisBoxSize["trim"] = new PdfRectangle(pageSize, pageSize.Rotation); } } // [M1] pageResources.AddDefaultColorDiff(writer.DefaultColorspace); if (writer.RgbTransparencyBlending) { PdfDictionary dcs = new PdfDictionary(); dcs.Put(PdfName.CS, PdfName.DEVICERGB); pageResources.AddDefaultColorDiff(dcs); } PdfDictionary resources = pageResources.Resources; // we create the page dictionary PdfPage page = new PdfPage(new PdfRectangle(pageSize, rotation), thisBoxSize, resources, rotation); // we complete the page dictionary // [C9] if there is XMP data to add: add it if (xmpMetadata != null) { PdfStream xmp = new PdfStream(xmpMetadata); xmp.Put(PdfName.TYPE, PdfName.METADATA); xmp.Put(PdfName.SUBTYPE, PdfName.XML); PdfEncryption crypto = writer.Encryption; if (crypto != null && !crypto.IsMetadataEncrypted()) { PdfArray ar = new PdfArray(); ar.Add(PdfName.CRYPT); xmp.Put(PdfName.FILTER, ar); } page.Put(PdfName.METADATA, writer.AddToBody(xmp).IndirectReference); } // [U3] page actions: transition, duration, additional actions if (this.transition!=null) { page.Put(PdfName.TRANS, this.transition.TransitionDictionary); transition = null; } if (this.duration>0) { page.Put(PdfName.DUR,new PdfNumber(this.duration)); duration = 0; } if (pageAA != null) { page.Put(PdfName.AA, writer.AddToBody(pageAA).IndirectReference); pageAA = null; } // [U4] we add the thumbs if (thumb != null) { page.Put(PdfName.THUMB, thumb); thumb = null; } // [U8] we check if the userunit is defined if (writer.Userunit > 0f) { page.Put(PdfName.USERUNIT, new PdfNumber(writer.Userunit)); } // [C5] and [C8] we add the annotations if (annotationsImp.HasUnusedAnnotations()) { PdfArray array = annotationsImp.RotateAnnotations(writer, pageSize); if (array.Size != 0) page.Put(PdfName.ANNOTS, array); } // [F12] we add tag info if (writer.IsTagged()) page.Put(PdfName.STRUCTPARENTS, new PdfNumber(writer.CurrentPageNumber - 1)); if (text.Size > textEmptySize) text.EndText(); else text = null; writer.Add(page, new PdfContents(writer.DirectContentUnder, graphics, text, writer.DirectContent, pageSize)); // we initialize the new page InitPage(); return true; } // [L4] DocListener interface /** * Sets the pagesize. * * @param pageSize the new pagesize * @return true if the page size was set */ public override bool SetPageSize(Rectangle pageSize) { if (writer != null && writer.IsPaused()) { return false; } nextPageSize = new Rectangle(pageSize); return true; } /** margin in x direction starting from the left. Will be valid in the next page */ protected float nextMarginLeft; /** margin in x direction starting from the right. Will be valid in the next page */ protected float nextMarginRight; /** margin in y direction starting from the top. Will be valid in the next page */ protected float nextMarginTop; /** margin in y direction starting from the bottom. Will be valid in the next page */ protected float nextMarginBottom; /** * Sets the margins. * * @param marginLeft the margin on the left * @param marginRight the margin on the right * @param marginTop the margin on the top * @param marginBottom the margin on the bottom * @return a bool */ public override bool SetMargins(float marginLeft, float marginRight, float marginTop, float marginBottom) { if (writer != null && writer.IsPaused()) { return false; } nextMarginLeft = marginLeft; nextMarginRight = marginRight; nextMarginTop = marginTop; nextMarginBottom = marginBottom; return true; } // [L6] DocListener interface /** * @see com.lowagie.text.DocListener#setMarginMirroring(bool) */ public override bool SetMarginMirroring(bool MarginMirroring) { if (writer != null && writer.IsPaused()) { return false; } return base.SetMarginMirroring(MarginMirroring); } // [L7] DocListener interface /** * Sets the page number. * * @param pageN the new page number */ public override int PageCount { set { if (writer != null && writer.IsPaused()) { return; } base.PageCount = value; } } // [L8] DocListener interface /** * Sets the page number to 0. */ public override void ResetPageCount() { if (writer != null && writer.IsPaused()) { return; } base.ResetPageCount(); } /** * Changes the header of this document. * * @param header the new header */ public override HeaderFooter Header { set { if (writer != null && writer.IsPaused()) { return; } base.Header = value; } } /** * Resets the header of this document. */ public override void ResetHeader() { if (writer != null && writer.IsPaused()) { return; } base.ResetHeader(); } /** * Changes the footer of this document. * * @param footer the new footer */ public override HeaderFooter Footer { set { if (writer != null && writer.IsPaused()) { return; } base.Footer = value; } } /** * Resets the footer of this document. */ public override void ResetFooter() { if (writer != null && writer.IsPaused()) { return; } base.ResetFooter(); } // DOCLISTENER METHODS END /** Signals that OnOpenDocument should be called. */ protected internal bool firstPageEvent = true; /** * Initializes a page. *

* If the footer/header is set, it is printed. * @throws DocumentException on error */ protected internal void InitPage() { // the pagenumber is incremented pageN++; // initialisation of some page objects annotationsImp.ResetAnnotations(); pageResources = new PageResources(); writer.ResetContent(); graphics = new PdfContentByte(writer); text = new PdfContentByte(writer); text.Reset(); text.BeginText(); textEmptySize = text.Size; markPoint = 0; SetNewPageSizeAndMargins(); imageEnd = -1; indentation.imageIndentRight = 0; indentation.imageIndentLeft = 0; indentation.indentBottom = 0; indentation.indentTop = 0; currentHeight = 0; // backgroundcolors, etc... thisBoxSize = new Hashtable(boxSize); if (pageSize.BackgroundColor != null || pageSize.HasBorders() || pageSize.BorderColor != null) { Add(pageSize); } float oldleading = leading; int oldAlignment = alignment; // if there is a footer, the footer is added DoFooter(); // we move to the left/top position of the page text.MoveText(Left, Top); DoHeader(); pageEmpty = true; // if there is an image waiting to be drawn, draw it if (imageWait != null) { Add(imageWait); imageWait = null; } leading = oldleading; alignment = oldAlignment; CarriageReturn(); IPdfPageEvent pageEvent = writer.PageEvent; if (pageEvent != null) { if (firstPageEvent) { pageEvent.OnOpenDocument(writer, this); } pageEvent.OnStartPage(writer, this); } firstPageEvent = false; } /** The line that is currently being written. */ protected internal PdfLine line = null; /** The lines that are written until now. */ protected internal ArrayList lines = new ArrayList(); /** * Adds the current line to the list of lines and also adds an empty line. * @throws DocumentException on error */ protected internal void NewLine() { lastElementType = -1; CarriageReturn(); if (lines != null && lines.Count > 0) { lines.Add(line); currentHeight += line.Height; } line = new PdfLine(IndentLeft, IndentRight, alignment, leading); } /** * If the current line is not empty or null, it is added to the arraylist * of lines and a new empty line is added. * @throws DocumentException on error */ protected internal void CarriageReturn() { // the arraylist with lines may not be null if (lines == null) { lines = new ArrayList(); } // If the current line is not null if (line != null) { // we check if the end of the page is reached (bugfix by Francois Gravel) if (currentHeight + line.Height + leading < IndentTop - IndentBottom) { // if so nonempty lines are added and the heigt is augmented if (line.Size > 0) { currentHeight += line.Height; lines.Add(line); pageEmpty = false; } } // if the end of the line is reached, we start a new page else { NewPage(); } } if (imageEnd > -1 && currentHeight > imageEnd) { imageEnd = -1; indentation.imageIndentRight = 0; indentation.imageIndentLeft = 0; } // a new current line is constructed line = new PdfLine(IndentLeft, IndentRight, alignment, leading); } /** * Gets the current vertical page position. * @param ensureNewLine Tells whether a new line shall be enforced. This may cause side effects * for elements that do not terminate the lines they've started because those lines will get * terminated. * @return The current vertical page position. */ public float GetVerticalPosition(bool ensureNewLine) { // ensuring that a new line has been started. if (ensureNewLine) { EnsureNewLine(); } return Top - currentHeight - indentation.indentTop; } /** Holds the type of the last element, that has been added to the document. */ protected internal int lastElementType = -1; /** * Ensures that a new line has been started. */ protected internal void EnsureNewLine() { if ((lastElementType == Element.PHRASE) || (lastElementType == Element.CHUNK)) { NewLine(); FlushLines(); } } /** * Writes all the lines to the text-object. * * @return the displacement that was caused * @throws DocumentException on error */ protected internal float FlushLines() { // checks if the ArrayList with the lines is not null if (lines == null) { return 0; } // checks if a new Line has to be made. if (line != null && line.Size > 0) { lines.Add(line); line = new PdfLine(IndentLeft, IndentRight, alignment, leading); } // checks if the ArrayList with the lines is empty if (lines.Count == 0) { return 0; } // initialisation of some parameters Object[] currentValues = new Object[2]; PdfFont currentFont = null; float displacement = 0; currentValues[1] = (float)0; // looping over all the lines foreach (PdfLine l in lines) { // this is a line in the loop float moveTextX = l.IndentLeft - IndentLeft + indentation.indentLeft + indentation.listIndentLeft + indentation.sectionIndentLeft; text.MoveText(moveTextX, -l.Height); // is the line preceeded by a symbol? if (l.ListSymbol != null) { ColumnText.ShowTextAligned(graphics, Element.ALIGN_LEFT, new Phrase(l.ListSymbol), text.XTLM - l.ListIndent, text.YTLM, 0); } currentValues[0] = currentFont; WriteLineToContent(l, text, graphics, currentValues, writer.SpaceCharRatio); currentFont = (PdfFont)currentValues[0]; displacement += l.Height; text.MoveText(-moveTextX, 0); } lines = new ArrayList(); return displacement; } /** The characters to be applied the hanging punctuation. */ internal const String hangingPunctuation = ".,;:'"; /** * Writes a text line to the document. It takes care of all the attributes. *

* Before entering the line position must have been established and the * text argument must be in text object scope (beginText()). * @param line the line to be written * @param text the PdfContentByte where the text will be written to * @param graphics the PdfContentByte where the graphics will be written to * @param currentValues the current font and extra spacing values * @param ratio * @throws DocumentException on error */ internal void WriteLineToContent(PdfLine line, PdfContentByte text, PdfContentByte graphics, Object[] currentValues, float ratio) { PdfFont currentFont = (PdfFont)(currentValues[0]); float lastBaseFactor = (float)currentValues[1]; //PdfChunk chunkz; int numberOfSpaces; int lineLen; bool isJustified; float hangingCorrection = 0; float hScale = 1; float lastHScale = float.NaN; float baseWordSpacing = 0; float baseCharacterSpacing = 0; float glueWidth = 0; numberOfSpaces = line.NumberOfSpaces; lineLen = line.GetLineLengthUtf32(); // does the line need to be justified? isJustified = line.HasToBeJustified() && (numberOfSpaces != 0 || lineLen > 1); int separatorCount = line.GetSeparatorCount(); if (separatorCount > 0) { glueWidth = line.WidthLeft / separatorCount; } else if (isJustified) { if (line.NewlineSplit && line.WidthLeft >= (lastBaseFactor * (ratio * numberOfSpaces + lineLen - 1))) { if (line.RTL) { text.MoveText(line.WidthLeft - lastBaseFactor * (ratio * numberOfSpaces + lineLen - 1), 0); } baseWordSpacing = ratio * lastBaseFactor; baseCharacterSpacing = lastBaseFactor; } else { float width = line.WidthLeft; PdfChunk last = line.GetChunk(line.Size - 1); if (last != null) { String s = last.ToString(); char c; if (s.Length > 0 && hangingPunctuation.IndexOf((c = s[s.Length - 1])) >= 0) { float oldWidth = width; width += last.Font.Width(c) * 0.4f; hangingCorrection = width - oldWidth; } } float baseFactor = width / (ratio * numberOfSpaces + lineLen - 1); baseWordSpacing = ratio * baseFactor; baseCharacterSpacing = baseFactor; lastBaseFactor = baseFactor; } } int lastChunkStroke = line.LastStrokeChunk; int chunkStrokeIdx = 0; float xMarker = text.XTLM; float baseXMarker = xMarker; float yMarker = text.YTLM; bool adjustMatrix = false; float tabPosition = 0; // looping over all the chunks in 1 line foreach (PdfChunk chunk in line) { Color color = chunk.Color; hScale = 1; if (chunkStrokeIdx <= lastChunkStroke) { float width; if (isJustified) { width = chunk.GetWidthCorrected(baseCharacterSpacing, baseWordSpacing); } else { width = chunk.Width; } if (chunk.IsStroked()) { PdfChunk nextChunk = line.GetChunk(chunkStrokeIdx + 1); if (chunk.IsSeparator()) { width = glueWidth; Object[] sep = (Object[])chunk.GetAttribute(Chunk.SEPARATOR); IDrawInterface di = (IDrawInterface)sep[0]; bool vertical = (bool)sep[1]; float fontSize = chunk.Font.Size; float ascender = chunk.Font.Font.GetFontDescriptor(BaseFont.ASCENT, fontSize); float descender = chunk.Font.Font.GetFontDescriptor(BaseFont.DESCENT, fontSize); if (vertical) { di.Draw(graphics, baseXMarker, yMarker + descender, baseXMarker + line.OriginalWidth, ascender - descender, yMarker); } else { di.Draw(graphics, xMarker, yMarker + descender, xMarker + width, ascender - descender, yMarker); } } if (chunk.IsTab()) { Object[] tab = (Object[])chunk.GetAttribute(Chunk.TAB); IDrawInterface di = (IDrawInterface)tab[0]; tabPosition = (float)tab[1] + (float)tab[3]; float fontSize = chunk.Font.Size; float ascender = chunk.Font.Font.GetFontDescriptor(BaseFont.ASCENT, fontSize); float descender = chunk.Font.Font.GetFontDescriptor(BaseFont.DESCENT, fontSize); if (tabPosition > xMarker) { di.Draw(graphics, xMarker, yMarker + descender, tabPosition, ascender - descender, yMarker); } float tmp = xMarker; xMarker = tabPosition; tabPosition = tmp; } if (chunk.IsAttribute(Chunk.BACKGROUND)) { float subtract = lastBaseFactor; if (nextChunk != null && nextChunk.IsAttribute(Chunk.BACKGROUND)) subtract = 0; if (nextChunk == null) subtract += hangingCorrection; float fontSize = chunk.Font.Size; float ascender = chunk.Font.Font.GetFontDescriptor(BaseFont.ASCENT, fontSize); float descender = chunk.Font.Font.GetFontDescriptor(BaseFont.DESCENT, fontSize); Object[] bgr = (Object[])chunk.GetAttribute(Chunk.BACKGROUND); graphics.SetColorFill((Color)bgr[0]); float[] extra = (float[])bgr[1]; graphics.Rectangle(xMarker - extra[0], yMarker + descender - extra[1] + chunk.TextRise, width - subtract + extra[0] + extra[2], ascender - descender + extra[1] + extra[3]); graphics.Fill(); graphics.SetGrayFill(0); } if (chunk.IsAttribute(Chunk.UNDERLINE)) { float subtract = lastBaseFactor; if (nextChunk != null && nextChunk.IsAttribute(Chunk.UNDERLINE)) subtract = 0; if (nextChunk == null) subtract += hangingCorrection; Object[][] unders = (Object[][])chunk.GetAttribute(Chunk.UNDERLINE); Color scolor = null; for (int k = 0; k < unders.Length; ++k) { Object[] obj = unders[k]; scolor = (Color)obj[0]; float[] ps = (float[])obj[1]; if (scolor == null) scolor = color; if (scolor != null) graphics.SetColorStroke(scolor); float fsize = chunk.Font.Size; graphics.SetLineWidth(ps[0] + fsize * ps[1]); float shift = ps[2] + fsize * ps[3]; int cap2 = (int)ps[4]; if (cap2 != 0) graphics.SetLineCap(cap2); graphics.MoveTo(xMarker, yMarker + shift); graphics.LineTo(xMarker + width - subtract, yMarker + shift); graphics.Stroke(); if (scolor != null) graphics.ResetGrayStroke(); if (cap2 != 0) graphics.SetLineCap(0); } graphics.SetLineWidth(1); } if (chunk.IsAttribute(Chunk.ACTION)) { float subtract = lastBaseFactor; if (nextChunk != null && nextChunk.IsAttribute(Chunk.ACTION)) subtract = 0; if (nextChunk == null) subtract += hangingCorrection; text.AddAnnotation(new PdfAnnotation(writer, xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.Font.Size, (PdfAction)chunk.GetAttribute(Chunk.ACTION))); } if (chunk.IsAttribute(Chunk.REMOTEGOTO)) { float subtract = lastBaseFactor; if (nextChunk != null && nextChunk.IsAttribute(Chunk.REMOTEGOTO)) subtract = 0; if (nextChunk == null) subtract += hangingCorrection; Object[] obj = (Object[])chunk.GetAttribute(Chunk.REMOTEGOTO); String filename = (String)obj[0]; if (obj[1] is String) RemoteGoto(filename, (String)obj[1], xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.Font.Size); else RemoteGoto(filename, (int)obj[1], xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.Font.Size); } if (chunk.IsAttribute(Chunk.LOCALGOTO)) { float subtract = lastBaseFactor; if (nextChunk != null && nextChunk.IsAttribute(Chunk.LOCALGOTO)) subtract = 0; if (nextChunk == null) subtract += hangingCorrection; LocalGoto((String)chunk.GetAttribute(Chunk.LOCALGOTO), xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.Font.Size); } if (chunk.IsAttribute(Chunk.LOCALDESTINATION)) { float subtract = lastBaseFactor; if (nextChunk != null && nextChunk.IsAttribute(Chunk.LOCALDESTINATION)) subtract = 0; if (nextChunk == null) subtract += hangingCorrection; LocalDestination((String)chunk.GetAttribute(Chunk.LOCALDESTINATION), new PdfDestination(PdfDestination.XYZ, xMarker, yMarker + chunk.Font.Size, 0)); } if (chunk.IsAttribute(Chunk.GENERICTAG)) { float subtract = lastBaseFactor; if (nextChunk != null && nextChunk.IsAttribute(Chunk.GENERICTAG)) subtract = 0; if (nextChunk == null) subtract += hangingCorrection; Rectangle rect = new Rectangle(xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.Font.Size); IPdfPageEvent pev = writer.PageEvent; if (pev != null) pev.OnGenericTag(writer, this, rect, (String)chunk.GetAttribute(Chunk.GENERICTAG)); } if (chunk.IsAttribute(Chunk.PDFANNOTATION)) { float subtract = lastBaseFactor; if (nextChunk != null && nextChunk.IsAttribute(Chunk.PDFANNOTATION)) subtract = 0; if (nextChunk == null) subtract += hangingCorrection; float fontSize = chunk.Font.Size; float ascender = chunk.Font.Font.GetFontDescriptor(BaseFont.ASCENT, fontSize); float descender = chunk.Font.Font.GetFontDescriptor(BaseFont.DESCENT, fontSize); PdfAnnotation annot = PdfFormField.ShallowDuplicate((PdfAnnotation)chunk.GetAttribute(Chunk.PDFANNOTATION)); annot.Put(PdfName.RECT, new PdfRectangle(xMarker, yMarker + descender, xMarker + width - subtract, yMarker + ascender)); text.AddAnnotation(annot); } float[] paramsx = (float[])chunk.GetAttribute(Chunk.SKEW); object hs = chunk.GetAttribute(Chunk.HSCALE); if (paramsx != null || hs != null) { float b = 0, c = 0; if (paramsx != null) { b = paramsx[0]; c = paramsx[1]; } if (hs != null) hScale = (float)hs; text.SetTextMatrix(hScale, b, c, 1, xMarker, yMarker); } if (chunk.IsImage()) { Image image = chunk.Image; float[] matrix = image.Matrix; matrix[Image.CX] = xMarker + chunk.ImageOffsetX - matrix[Image.CX]; matrix[Image.CY] = yMarker + chunk.ImageOffsetY - matrix[Image.CY]; graphics.AddImage(image, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); text.MoveText(xMarker + lastBaseFactor + image.ScaledWidth - text.XTLM, 0); } } xMarker += width; ++chunkStrokeIdx; } if (chunk.Font.CompareTo(currentFont) != 0) { currentFont = chunk.Font; text.SetFontAndSize(currentFont.Font, currentFont.Size); } float rise = 0; Object[] textRender = (Object[])chunk.GetAttribute(Chunk.TEXTRENDERMODE); int tr = 0; float strokeWidth = 1; Color strokeColor = null; object fr = chunk.GetAttribute(Chunk.SUBSUPSCRIPT); if (textRender != null) { tr = (int)textRender[0] & 3; if (tr != PdfContentByte.TEXT_RENDER_MODE_FILL) text.SetTextRenderingMode(tr); if (tr == PdfContentByte.TEXT_RENDER_MODE_STROKE || tr == PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE) { strokeWidth = (float)textRender[1]; if (strokeWidth != 1) text.SetLineWidth(strokeWidth); strokeColor = (Color)textRender[2]; if (strokeColor == null) strokeColor = color; if (strokeColor != null) text.SetColorStroke(strokeColor); } } if (fr != null) rise = (float)fr; if (color != null) text.SetColorFill(color); if (rise != 0) text.SetTextRise(rise); if (chunk.IsImage()) { adjustMatrix = true; } else if (chunk.IsHorizontalSeparator()) { PdfTextArray array = new PdfTextArray(); array.Add(-glueWidth * 1000f / chunk.Font.Size / hScale); text.ShowText(array); } else if (chunk.IsTab()) { PdfTextArray array = new PdfTextArray(); array.Add((tabPosition - xMarker) * 1000f / chunk.Font.Size / hScale); text.ShowText(array); } // If it is a CJK chunk or Unicode TTF we will have to simulate the // space adjustment. else if (isJustified && numberOfSpaces > 0 && chunk.IsSpecialEncoding()) { if (hScale != lastHScale) { lastHScale = hScale; text.SetWordSpacing(baseWordSpacing / hScale); text.SetCharacterSpacing(baseCharacterSpacing / hScale); } String s = chunk.ToString(); int idx = s.IndexOf(' '); if (idx < 0) text.ShowText(s); else { float spaceCorrection = - baseWordSpacing * 1000f / chunk.Font.Size / hScale; PdfTextArray textArray = new PdfTextArray(s.Substring(0, idx)); int lastIdx = idx; while ((idx = s.IndexOf(' ', lastIdx + 1)) >= 0) { textArray.Add(spaceCorrection); textArray.Add(s.Substring(lastIdx, idx - lastIdx)); lastIdx = idx; } textArray.Add(spaceCorrection); textArray.Add(s.Substring(lastIdx)); text.ShowText(textArray); } } else { if (isJustified && hScale != lastHScale) { lastHScale = hScale; text.SetWordSpacing(baseWordSpacing / hScale); text.SetCharacterSpacing(baseCharacterSpacing / hScale); } text.ShowText(chunk.ToString()); } if (rise != 0) text.SetTextRise(0); if (color != null) text.ResetRGBColorFill(); if (tr != PdfContentByte.TEXT_RENDER_MODE_FILL) text.SetTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL); if (strokeColor != null) text.ResetRGBColorStroke(); if (strokeWidth != 1) text.SetLineWidth(1); if (chunk.IsAttribute(Chunk.SKEW) || chunk.IsAttribute(Chunk.HSCALE)) { adjustMatrix = true; text.SetTextMatrix(xMarker, yMarker); } } if (isJustified) { text.SetWordSpacing(0); text.SetCharacterSpacing(0); if (line.NewlineSplit) lastBaseFactor = 0; } if (adjustMatrix) text.MoveText(baseXMarker - text.XTLM, 0); currentValues[0] = currentFont; currentValues[1] = lastBaseFactor; } protected internal Indentation indentation = new Indentation(); public class Indentation { /** This represents the current indentation of the PDF Elements on the left side. */ internal float indentLeft = 0; /** Indentation to the left caused by a section. */ internal float sectionIndentLeft = 0; /** This represents the current indentation of the PDF Elements on the left side. */ internal float listIndentLeft = 0; /** This is the indentation caused by an image on the left. */ internal float imageIndentLeft = 0; /** This represents the current indentation of the PDF Elements on the right side. */ internal float indentRight = 0; /** Indentation to the right caused by a section. */ internal float sectionIndentRight = 0; /** This is the indentation caused by an image on the right. */ internal float imageIndentRight = 0; /** This represents the current indentation of the PDF Elements on the top side. */ internal float indentTop = 0; /** This represents the current indentation of the PDF Elements on the bottom side. */ internal float indentBottom = 0; } /** * Gets the indentation on the left side. * * @return a margin */ protected internal float IndentLeft { get { return GetLeft(indentation.indentLeft + indentation.listIndentLeft + indentation.imageIndentLeft + indentation.sectionIndentLeft); } } /** * Gets the indentation on the right side. * * @return a margin */ protected internal float IndentRight { get { return GetRight(indentation.indentRight + indentation.sectionIndentRight + indentation.imageIndentRight); } } /** * Gets the indentation on the top side. * * @return a margin */ protected internal float IndentTop { get { return GetTop(indentation.indentTop); } } /** * Gets the indentation on the bottom side. * * @return a margin */ protected internal float IndentBottom { get { return GetBottom(indentation.indentBottom); } } /** * Adds extra space. * This method should probably be rewritten. */ protected internal void AddSpacing(float extraspace, float oldleading, Font f) { if (extraspace == 0) return; if (pageEmpty) return; if (currentHeight + line.Height + leading > IndentTop - IndentBottom) return; leading = extraspace; CarriageReturn(); if (f.IsUnderlined() || f.IsStrikethru()) { f = new Font(f); int style = f.Style; style &= ~Font.UNDERLINE; style &= ~Font.STRIKETHRU; f.SetStyle(Font.UNDEFINED); f.SetStyle(style); } Chunk space = new Chunk(" ", f); space.Process(this); CarriageReturn(); leading = oldleading; } // Info Dictionary and Catalog /** some meta information about the Document. */ protected internal PdfInfo info = new PdfInfo(); /** * Gets the PdfInfo-object. * * @return PdfInfo */ internal PdfInfo Info { get { return info; } } /** * Gets the PdfCatalog-object. * * @param pages an indirect reference to this document pages * @return PdfCatalog */ internal PdfCatalog GetCatalog(PdfIndirectReference pages) { PdfCatalog catalog = new PdfCatalog(pages, writer); // [C1] outlines if (rootOutline.Kids.Count > 0) { catalog.Put(PdfName.PAGEMODE, PdfName.USEOUTLINES); catalog.Put(PdfName.OUTLINES, rootOutline.IndirectReference); } // [C2] version writer.GetPdfVersion().AddToCatalog(catalog); // [C3] preferences viewerPreferences.AddToCatalog(catalog); // [C4] pagelabels if (pageLabels != null) { catalog.Put(PdfName.PAGELABELS, pageLabels.GetDictionary(writer)); } // [C5] named objects catalog.AddNames(localDestinations, GetDocumentLevelJS(), documentFileAttachment, writer); // [C6] actions if (openActionName != null) { PdfAction action = GetLocalGotoAction(openActionName); catalog.OpenAction = action; } else if (openActionAction != null) catalog.OpenAction = openActionAction; if (additionalActions != null) { catalog.AdditionalActions = additionalActions; } // [C7] portable collections if (collection != null) { catalog.Put(PdfName.COLLECTION, collection); } // [C8] AcroForm if (annotationsImp.HasValidAcroForm()) { catalog.Put(PdfName.ACROFORM, writer.AddToBody(annotationsImp.AcroForm).IndirectReference); } return catalog; } // [C1] outlines /** This is the root outline of the document. */ protected internal PdfOutline rootOutline; /** This is the current PdfOutline in the hierarchy of outlines. */ protected internal PdfOutline currentOutline; /** * Adds a named outline to the document . * @param outline the outline to be added * @param name the name of this local destination */ internal void AddOutline(PdfOutline outline, String name) { LocalDestination(name, outline.PdfDestination); } /** * Gets the root outline. All the outlines must be created with a parent. * The first level is created with this outline. * @return the root outline */ public PdfOutline RootOutline { get { return rootOutline; } } internal void CalculateOutlineCount() { if (rootOutline.Kids.Count == 0) return; TraverseOutlineCount(rootOutline); } internal void TraverseOutlineCount(PdfOutline outline) { ArrayList kids = outline.Kids; PdfOutline parent = outline.Parent; if (kids.Count == 0) { if (parent != null) { parent.Count = parent.Count + 1; } } else { for (int k = 0; k < kids.Count; ++k) { TraverseOutlineCount((PdfOutline)kids[k]); } if (parent != null) { if (outline.Open) { parent.Count = outline.Count + parent.Count + 1; } else { parent.Count = parent.Count + 1; outline.Count = -outline.Count; } } } } internal void WriteOutlines() { if (rootOutline.Kids.Count == 0) return; OutlineTree(rootOutline); writer.AddToBody(rootOutline, rootOutline.IndirectReference); } internal void OutlineTree(PdfOutline outline) { outline.IndirectReference = writer.PdfIndirectReference; if (outline.Parent != null) outline.Put(PdfName.PARENT, outline.Parent.IndirectReference); ArrayList kids = outline.Kids; int size = kids.Count; for (int k = 0; k < size; ++k) OutlineTree((PdfOutline)kids[k]); for (int k = 0; k < size; ++k) { if (k > 0) ((PdfOutline)kids[k]).Put(PdfName.PREV, ((PdfOutline)kids[k - 1]).IndirectReference); if (k < size - 1) ((PdfOutline)kids[k]).Put(PdfName.NEXT, ((PdfOutline)kids[k + 1]).IndirectReference); } if (size > 0) { outline.Put(PdfName.FIRST, ((PdfOutline)kids[0]).IndirectReference); outline.Put(PdfName.LAST, ((PdfOutline)kids[size - 1]).IndirectReference); } for (int k = 0; k < size; ++k) { PdfOutline kid = (PdfOutline)kids[k]; writer.AddToBody(kid, kid.IndirectReference); } } // [C3] PdfViewerPreferences interface /** Contains the Viewer preferences of this PDF document. */ protected PdfViewerPreferencesImp viewerPreferences = new PdfViewerPreferencesImp(); /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#setViewerPreferences(int) */ internal int ViewerPreferences { set { this.viewerPreferences.ViewerPreferences = value; } } /** @see com.lowagie.text.pdf.interfaces.PdfViewerPreferences#addViewerPreference(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfObject) */ internal void AddViewerPreference(PdfName key, PdfObject value) { this.viewerPreferences.AddViewerPreference(key, value); } // [C4] Page labels protected internal PdfPageLabels pageLabels; internal PdfPageLabels PageLabels { set { this.pageLabels = value; } } // [C5] named objects: local destinations, javascript, embedded files /** * Implements a link to other part of the document. The jump will * be made to a local destination with the same name, that must exist. * @param name the name for this link * @param llx the lower left x corner of the activation area * @param lly the lower left y corner of the activation area * @param urx the upper right x corner of the activation area * @param ury the upper right y corner of the activation area */ internal void LocalGoto(String name, float llx, float lly, float urx, float ury) { PdfAction action = GetLocalGotoAction(name); annotationsImp.AddPlainAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, action)); } /** * Implements a link to another document. * @param filename the filename for the remote document * @param name the name to jump to * @param llx the lower left x corner of the activation area * @param lly the lower left y corner of the activation area * @param urx the upper right x corner of the activation area * @param ury the upper right y corner of the activation area */ internal void RemoteGoto(String filename, String name, float llx, float lly, float urx, float ury) { annotationsImp.AddPlainAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, new PdfAction(filename, name))); } /** * Implements a link to another document. * @param filename the filename for the remote document * @param page the page to jump to * @param llx the lower left x corner of the activation area * @param lly the lower left y corner of the activation area * @param urx the upper right x corner of the activation area * @param ury the upper right y corner of the activation area */ internal void RemoteGoto(String filename, int page, float llx, float lly, float urx, float ury) { AddAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, new PdfAction(filename, page))); } /** Implements an action in an area. * @param action the PdfAction * @param llx the lower left x corner of the activation area * @param lly the lower left y corner of the activation area * @param urx the upper right x corner of the activation area * @param ury the upper right y corner of the activation area */ internal void SetAction(PdfAction action, float llx, float lly, float urx, float ury) { AddAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, action)); } /** * Stores the destinations keyed by name. Value is * Object[]{PdfAction,PdfIndirectReference,PdfDestintion}. */ protected internal k_Tree localDestinations = new k_Tree(); internal PdfAction GetLocalGotoAction(String name) { PdfAction action; Object[] obj = (Object[])localDestinations[name]; if (obj == null) obj = new Object[3]; if (obj[0] == null) { if (obj[1] == null) { obj[1] = writer.PdfIndirectReference; } action = new PdfAction((PdfIndirectReference)obj[1]); obj[0] = action; localDestinations[name] = obj; } else { action = (PdfAction)obj[0]; } return action; } /** * The local destination to where a local goto with the same * name will jump to. * @param name the name of this local destination * @param destination the PdfDestination with the jump coordinates * @return true if the local destination was added, * false if a local destination with the same name * already existed */ internal bool LocalDestination(String name, PdfDestination destination) { Object[] obj = (Object[])localDestinations[name]; if (obj == null) obj = new Object[3]; if (obj[2] != null) return false; obj[2] = destination; localDestinations[name] = obj; destination.AddPage(writer.CurrentPage); return true; } /** * Stores a list of document level JavaScript actions. */ private int jsCounter; protected internal Hashtable documentLevelJS = new Hashtable(); internal void AddJavaScript(PdfAction js) { if (js.Get(PdfName.JS) == null) throw new ArgumentException("Only JavaScript actions are allowed."); documentLevelJS[jsCounter.ToString().PadLeft(16, '0')] = writer.AddToBody(js).IndirectReference; jsCounter++; } internal void AddJavaScript(String name, PdfAction js) { if (js.Get(PdfName.JS) == null) throw new ArgumentException("Only JavaScript actions are allowed."); documentLevelJS[name] = writer.AddToBody(js).IndirectReference; } internal Hashtable GetDocumentLevelJS() { return documentLevelJS; } protected internal Hashtable documentFileAttachment = new Hashtable(); internal void AddFileAttachment(String description, PdfFileSpecification fs) { if (description == null) { PdfString desc = (PdfString)fs.Get(PdfName.DESC); if (desc == null) { description = ""; } else { description = PdfEncodings.ConvertToString(desc.GetBytes(), null); } } fs.AddDescription(description, true); if (description.Length == 0) description = "Unnamed"; String fn = PdfEncodings.ConvertToString(new PdfString(description, PdfObject.TEXT_UNICODE).GetBytes(), null); int k = 0; while (documentFileAttachment.ContainsKey(fn)) { ++k; fn = PdfEncodings.ConvertToString(new PdfString(description + " " + k, PdfObject.TEXT_UNICODE).GetBytes(), null); } documentFileAttachment[fn] = fs.Reference; } internal Hashtable GetDocumentFileAttachment() { return documentFileAttachment; } // [C6] document level actions protected internal String openActionName; internal void SetOpenAction(String name) { openActionName = name; openActionAction = null; } protected internal PdfAction openActionAction; internal void SetOpenAction(PdfAction action) { openActionAction = action; openActionName = null; } protected internal PdfDictionary additionalActions; internal void AddAdditionalAction(PdfName actionType, PdfAction action) { if (additionalActions == null) { additionalActions = new PdfDictionary(); } if (action == null) additionalActions.Remove(actionType); else additionalActions.Put(actionType, action); if (additionalActions.Size == 0) additionalActions = null; } // [C7] portable collections protected internal PdfCollection collection; /** * Sets the collection dictionary. * @param collection a dictionary of type PdfCollection */ public PdfCollection Collection { set { this.collection = value; } } // [C8] AcroForm internal PdfAnnotationsImp annotationsImp; /** * Gets the AcroForm object. * @return the PdfAcroform object of the PdfDocument */ public PdfAcroForm AcroForm { get { return annotationsImp.AcroForm; } } internal int SigFlags { set { annotationsImp.SigFlags = value; } } internal void AddCalculationOrder(PdfFormField formField) { annotationsImp.AddCalculationOrder(formField); } internal void AddAnnotation(PdfAnnotation annot) { pageEmpty = false; annotationsImp.AddAnnotation(annot); } // [F12] tagged PDF protected int markPoint; internal int GetMarkPoint() { return markPoint; } internal void IncMarkPoint() { ++markPoint; } // [U1] page sizes /** This is the size of the next page. */ protected Rectangle nextPageSize = null; /** This is the size of the several boxes of the current Page. */ protected Hashtable thisBoxSize = new Hashtable(); /** This is the size of the several boxes that will be used in * the next page. */ protected Hashtable boxSize = new Hashtable(); internal Rectangle CropBoxSize { set { SetBoxSize("crop", value); } } internal void SetBoxSize(String boxName, Rectangle size) { if (size == null) boxSize.Remove(boxName); else boxSize[boxName] = new PdfRectangle(size); } protected internal void SetNewPageSizeAndMargins() { pageSize = nextPageSize; if (marginMirroring && (PageNumber & 1) == 0) { marginRight = nextMarginLeft; marginLeft = nextMarginRight; } else { marginLeft = nextMarginLeft; marginRight = nextMarginRight; } marginTop = nextMarginTop; marginBottom = nextMarginBottom; } /** * Gives the size of a trim, art, crop or bleed box, or null if not defined. * @param boxName crop, trim, art or bleed */ internal Rectangle GetBoxSize(String boxName) { PdfRectangle r = (PdfRectangle)thisBoxSize[boxName]; if (r != null) return r.Rectangle; return null; } // [U2] empty pages /** This checks if the page is empty. */ protected internal bool pageEmpty = true; internal bool PageEmpty { set { this.pageEmpty = value; } } // [U3] page actions /** The duration of the page */ protected int duration=-1; // negative values will indicate no duration /** The page transition */ protected PdfTransition transition=null; /** * Sets the display duration for the page (for presentations) * @param seconds the number of seconds to display the page */ internal int Duration { set { if (value > 0) this.duration=value; else this.duration=-1; } } /** * Sets the transition for the page * @param transition the PdfTransition object */ internal PdfTransition Transition { set { this.transition=value; } } protected PdfDictionary pageAA = null; internal void SetPageAction(PdfName actionType, PdfAction action) { if (pageAA == null) { pageAA = new PdfDictionary(); } pageAA.Put(actionType, action); } // [U8] thumbnail images protected internal PdfIndirectReference thumb; internal Image Thumbnail { set { thumb = writer.GetImageReference(writer.AddDirectImageSimple(value)); } } // [M0] Page resources contain references to fonts, extgstate, images,... /** This are the page resources of the current Page. */ protected internal PageResources pageResources; internal PageResources PageResources { get { return pageResources; } } // [M3] Images /** Holds value of property strictImageSequence. */ protected internal bool strictImageSequence = false; /** Setter for property strictImageSequence. * @param strictImageSequence New value of property strictImageSequence. * */ internal bool StrictImageSequence { set { this.strictImageSequence = value; } get { return strictImageSequence; } } /** This is the position where the image ends. */ protected internal float imageEnd = -1; /** * Method added by Pelikan Stephan * @see com.lowagie.text.DocListener#clearTextWrap() */ public void ClearTextWrap() { float tmpHeight = imageEnd - currentHeight; if (line != null) { tmpHeight += line.Height; } if ((imageEnd > -1) && (tmpHeight > 0)) { CarriageReturn(); currentHeight += tmpHeight; } } /** This is the image that could not be shown on a previous page. */ protected internal Image imageWait = null; /** * Adds an image to the document. * @param image the Image to add * @throws PdfException on error * @throws DocumentException on error */ protected internal void Add(Image image) { if (image.HasAbsolutePosition()) { graphics.AddImage(image); pageEmpty = false; return; } // if there isn't enough room for the image on this page, save it for the next page if (currentHeight != 0 && IndentTop - currentHeight - image.ScaledHeight < IndentBottom) { if (!strictImageSequence && imageWait == null) { imageWait = image; return; } NewPage(); if (currentHeight != 0 && IndentTop - currentHeight - image.ScaledHeight < IndentBottom) { imageWait = image; return; } } pageEmpty = false; // avoid endless loops if (image == imageWait) imageWait = null; bool textwrap = (image.Alignment & Image.TEXTWRAP) == Image.TEXTWRAP && !((image.Alignment & Image.MIDDLE_ALIGN) == Image.MIDDLE_ALIGN); bool underlying = (image.Alignment & Image.UNDERLYING) == Image.UNDERLYING; float diff = leading / 2; if (textwrap) { diff += leading; } float lowerleft = IndentTop - currentHeight - image.ScaledHeight - diff; float[] mt = image.Matrix; float startPosition = IndentLeft - mt[4]; if ((image.Alignment & Image.RIGHT_ALIGN) == Image.RIGHT_ALIGN) startPosition = IndentRight - image.ScaledWidth - mt[4]; if ((image.Alignment & Image.MIDDLE_ALIGN) == Image.MIDDLE_ALIGN) startPosition = IndentLeft + ((IndentRight - IndentLeft - image.ScaledWidth) / 2) - mt[4]; if (image.HasAbsoluteX()) startPosition = image.AbsoluteX; if (textwrap) { if (imageEnd < 0 || imageEnd < currentHeight + image.ScaledHeight + diff) { imageEnd = currentHeight + image.ScaledHeight + diff; } if ((image.Alignment & Image.RIGHT_ALIGN) == Image.RIGHT_ALIGN) { // indentation suggested by Pelikan Stephan indentation.imageIndentRight += image.ScaledWidth + image.IndentationLeft; } else { // indentation suggested by Pelikan Stephan indentation.imageIndentLeft += image.ScaledWidth + image.IndentationRight; } } else { if ((image.Alignment & Image.RIGHT_ALIGN) == Image.RIGHT_ALIGN) startPosition -= image.IndentationRight; else if ((image.Alignment & Image.MIDDLE_ALIGN) == Image.MIDDLE_ALIGN) startPosition += image.IndentationLeft - image.IndentationRight; else startPosition -= image.IndentationRight; } graphics.AddImage(image, mt[0], mt[1], mt[2], mt[3], startPosition, lowerleft - mt[5]); if (!(textwrap || underlying)) { currentHeight += image.ScaledHeight + diff; FlushLines(); text.MoveText(0, - (image.ScaledHeight + diff)); NewLine(); } } // [M4] Adding a PdfPTable /** Adds a PdfPTable to the document. * @param ptable the PdfPTable to be added to the document. * @throws DocumentException on error */ internal void AddPTable(PdfPTable ptable) { ColumnText ct = new ColumnText(writer.DirectContent); if (currentHeight > 0) { Paragraph p = new Paragraph(); p.Leading = 0; ct.AddElement(p); //if the table prefers to be on a single page, and it wouldn't //fit on the current page, start a new page. if (ptable.KeepTogether && !FitsPage(ptable, 0f)) NewPage(); } ct.AddElement(ptable); bool he = ptable.HeadersInEvent; ptable.HeadersInEvent = true; int loop = 0; while (true) { ct.SetSimpleColumn(IndentLeft, IndentBottom, IndentRight, IndentTop - currentHeight); int status = ct.Go(); if ((status & ColumnText.NO_MORE_TEXT) != 0) { text.MoveText(0, ct.YLine - IndentTop + currentHeight); currentHeight = IndentTop - ct.YLine; break; } if (IndentTop - currentHeight == ct.YLine) ++loop; else loop = 0; if (loop == 3) { Add(new Paragraph("ERROR: Infinite table loop")); break; } NewPage(); } ptable.HeadersInEvent = he; } internal bool FitsPage(PdfPTable table, float margin) { if (!table.LockedWidth) { float totalWidth = (IndentRight - IndentLeft) * table.WidthPercentage / 100; table.TotalWidth = totalWidth; } // ensuring that a new line has been started. EnsureNewLine(); return table.TotalHeight <= IndentTop - currentHeight - IndentBottom - margin; } // [M4'] Adding a Table protected internal class RenderingContext { internal float pagetop = -1; internal float oldHeight = -1; internal PdfContentByte cellGraphics = null; internal float lostTableBottom; internal float maxCellBottom; //internal float maxCellHeight; internal Hashtable rowspanMap; internal Hashtable pageMap = new Hashtable(); /** * A PdfPTable */ public PdfTable table; /** * Consumes the rowspan * @param c * @return a rowspan. */ public int ConsumeRowspan(PdfCell c) { if (c.Rowspan == 1) { return 1; } object i = rowspanMap[c]; if (i == null) { i = c.Rowspan; } i = (int)i - 1; rowspanMap[c] = i; if ((int)i < 1) { return 1; } return (int)i; } /** * Looks at the current rowspan. * @param c * @return the current rowspan */ public int CurrentRowspan(PdfCell c) { object i = rowspanMap[c]; if (i == null) { return c.Rowspan; } else { return (int)i; } } public int CellRendered(PdfCell cell, int pageNumber) { object i = pageMap[cell]; if (i == null) { i = 1; } else { i = (int)i + 1; } pageMap[cell] = i; Hashtable seti = (Hashtable)pageMap[pageNumber]; if (seti == null) { seti = new Hashtable(); pageMap[pageNumber] = seti; } seti[cell] = null; return (int)i; } public int NumCellRendered(PdfCell cell) { object i = pageMap[cell]; if (i == null) { i = 0; } return (int)i; } public bool IsCellRenderedOnPage(PdfCell cell, int pageNumber) { Hashtable seti = (Hashtable) pageMap[pageNumber]; if (seti != null) { return seti.ContainsKey(cell); } return false; } }; /** * Adds a new table to * @param table Table to add. Rendered rows will be deleted after processing. * @param onlyFirstPage Render only the first full page * @throws DocumentException */ private void AddPdfTable(Table t) { // before every table, we flush all lines FlushLines(); PdfTable table = new PdfTable(t, IndentLeft, IndentRight, IndentTop - currentHeight); RenderingContext ctx = new RenderingContext(); ctx.pagetop = IndentTop; ctx.oldHeight = currentHeight; ctx.cellGraphics = new PdfContentByte(writer); ctx.rowspanMap = new Hashtable(); ctx.table = table; // initialisation of parameters PdfCell cell; // drawing the table ArrayList headercells = table.HeaderCells; ArrayList cells = table.Cells; ArrayList rows = ExtractRows(cells, ctx); bool isContinue = false; while (cells.Count != 0) { // initialisation of some extra parameters; ctx.lostTableBottom = 0; // loop over the cells bool cellsShown = false; // draw the cells (line by line) ListIterator iterator = new ListIterator(rows); bool atLeastOneFits = false; while (iterator.HasNext()) { ArrayList row = (ArrayList) iterator.Next(); AnalyzeRow(rows, ctx); RenderCells(ctx, row, table.HasToFitPageCells() & atLeastOneFits); if (!MayBeRemoved(row)) { break; } ConsumeRowspan(row, ctx); iterator.Remove(); atLeastOneFits = true; } // compose cells array list for subsequent code cells.Clear(); Hashtable opt = new Hashtable(); foreach (ArrayList row in rows) { foreach (PdfCell cellp in row) { if (!opt.ContainsKey(cellp)) { cells.Add(cellp); opt[cellp] = null; } } } // we paint the graphics of the table after looping through all the cells Rectangle tablerec = new Rectangle(table); tablerec.Border = table.Border; tablerec.BorderWidth = table.BorderWidth; tablerec.BorderColor = table.BorderColor; tablerec.BackgroundColor = table.BackgroundColor; PdfContentByte under = writer.DirectContentUnder; under.Rectangle(tablerec.GetRectangle(Top, IndentBottom)); under.Add(ctx.cellGraphics); // bugfix by Gerald Fehringer: now again add the border for the table // since it might have been covered by cell backgrounds tablerec.BackgroundColor = null; tablerec = tablerec.GetRectangle(Top, IndentBottom); tablerec.Border = table.Border; under.Rectangle(tablerec); // end bugfix ctx.cellGraphics = new PdfContentByte(null); // if the table continues on the next page if (rows.Count != 0) { isContinue = true; graphics.SetLineWidth(table.BorderWidth); if (cellsShown && (table.Border & Rectangle.BOTTOM_BORDER) == Rectangle.BOTTOM_BORDER) { // Draw the bottom line // the color is set to the color of the element Color tColor = table.BorderColor; if (tColor != null) { graphics.SetColorStroke(tColor); } graphics.MoveTo(table.Left, Math.Max(table.Bottom, IndentBottom)); graphics.LineTo(table.Right, Math.Max(table.Bottom, IndentBottom)); graphics.Stroke(); if (tColor != null) { graphics.ResetRGBColorStroke(); } } // old page pageEmpty = false; float difference = ctx.lostTableBottom; // new page NewPage(); // G.F.: if something added in page event i.e. currentHeight > 0 float heightCorrection = 0; bool somethingAdded = false; if (currentHeight > 0) { heightCorrection = 6; currentHeight += heightCorrection; somethingAdded = true; NewLine(); FlushLines(); indentation.indentTop = currentHeight - leading; currentHeight = 0; } else { FlushLines(); } // this part repeats the table headers (if any) int size = headercells.Count; if (size > 0) { // this is the top of the headersection cell = (PdfCell) headercells[0]; float oldTop = cell.GetTop(0); // loop over all the cells of the table header for (int ii = 0; ii < size; ii++) { cell = (PdfCell) headercells[ii]; // calculation of the new cellpositions cell.Top = IndentTop - oldTop + cell.GetTop(0); cell.Bottom = IndentTop - oldTop + cell.GetBottom(0); ctx.pagetop = cell.Bottom; // we paint the borders of the cell ctx.cellGraphics.Rectangle(cell.Rectangle(IndentTop, IndentBottom)); // we write the text of the cell ArrayList images = cell.GetImages(IndentTop, IndentBottom); foreach (Image image in images) { cellsShown = true; graphics.AddImage(image); } lines = cell.GetLines(IndentTop, IndentBottom); float cellTop = cell.GetTop(IndentTop); text.MoveText(0, cellTop-heightCorrection); float cellDisplacement = FlushLines() - cellTop+heightCorrection; text.MoveText(0, cellDisplacement); } currentHeight = IndentTop - ctx.pagetop + table.Cellspacing; text.MoveText(0, ctx.pagetop - IndentTop - currentHeight); } else { if (somethingAdded) { ctx.pagetop = IndentTop; text.MoveText(0, -table.Cellspacing); } } ctx.oldHeight = currentHeight - heightCorrection; // calculating the new positions of the table and the cells size = Math.Min(cells.Count, table.Columns); int i = 0; while (i < size) { cell = (PdfCell) cells[i]; if (cell.GetTop(-table.Cellspacing) > ctx.lostTableBottom) { float newBottom = ctx.pagetop - difference + cell.Bottom; float neededHeight = cell.RemainingHeight; if (newBottom > ctx.pagetop - neededHeight) { difference += newBottom - (ctx.pagetop - neededHeight); } } i++; } size = cells.Count; table.Top = IndentTop; table.Bottom = ctx.pagetop - difference + table.GetBottom(table.Cellspacing); for (i = 0; i < size; i++) { cell = (PdfCell) cells[i]; float newBottom = ctx.pagetop - difference + cell.Bottom; float newTop = ctx.pagetop - difference + cell.GetTop(-table.Cellspacing); if (newTop > IndentTop - currentHeight) { newTop = IndentTop - currentHeight; } cell.Top = newTop ; cell.Bottom = newBottom ; } } } float tableHeight = table.Top - table.Bottom; // bugfix by Adauto Martins when have more than two tables and more than one page // If continuation of table in other page (bug report #1460051) if (isContinue) { currentHeight = tableHeight; text.MoveText(0, -(tableHeight - (ctx.oldHeight * 2))); } else { currentHeight = ctx.oldHeight + tableHeight; text.MoveText(0, -tableHeight); } pageEmpty = false; } protected internal void AnalyzeRow(ArrayList rows, RenderingContext ctx) { ctx.maxCellBottom = IndentBottom; // determine whether Row(index) is in a rowspan int rowIndex = 0; ArrayList row = (ArrayList) rows[rowIndex]; int maxRowspan = 1; foreach (PdfCell cell in row) { maxRowspan = Math.Max(ctx.CurrentRowspan(cell), maxRowspan); } rowIndex += maxRowspan; bool useTop = true; if (rowIndex == rows.Count) { rowIndex = rows.Count - 1; useTop = false; } if (rowIndex < 0 || rowIndex >= rows.Count) return; row = (ArrayList) rows[rowIndex]; foreach (PdfCell cell in row) { Rectangle cellRect = cell.Rectangle(ctx.pagetop, IndentBottom); if (useTop) { ctx.maxCellBottom = Math.Max(ctx.maxCellBottom, cellRect.Top); } else { if (ctx.CurrentRowspan(cell) == 1) { ctx.maxCellBottom = Math.Max(ctx.maxCellBottom, cellRect.Bottom); } } } } protected internal bool MayBeRemoved(ArrayList row) { bool mayBeRemoved = true; foreach (PdfCell cell in row) { mayBeRemoved &= cell.MayBeRemoved(); } return mayBeRemoved; } protected internal void ConsumeRowspan(ArrayList row, RenderingContext ctx) { foreach (PdfCell c in row) { ctx.ConsumeRowspan(c); } } protected internal ArrayList ExtractRows(ArrayList cells, RenderingContext ctx) { PdfCell cell; PdfCell previousCell = null; ArrayList rows = new ArrayList(); ArrayList rowCells = new ArrayList(); ListIterator iterator = new ListIterator(cells); while (iterator.HasNext()) { cell = (PdfCell) iterator.Next(); bool isAdded = false; bool isEndOfRow = !iterator.HasNext(); bool isCurrentCellPartOfRow = !iterator.HasNext(); if (previousCell != null) { if (cell.Left <= previousCell.Left) { isEndOfRow = true; isCurrentCellPartOfRow = false; } } if (isCurrentCellPartOfRow) { rowCells.Add(cell); isAdded = true; } if (isEndOfRow) { if (rowCells.Count != 0) { // add to rowlist rows.Add(rowCells); } // start a new list for next line rowCells = new ArrayList(); } if (!isAdded) { rowCells.Add(cell); } previousCell = cell; } if (rowCells.Count != 0) { rows.Add(rowCells); } // fill row information with rowspan cells to get complete "scan lines" for (int i = rows.Count - 1; i >= 0; i--) { ArrayList row = (ArrayList) rows[i]; // iterator through row for (int j = 0; j < row.Count; j++) { PdfCell c = (PdfCell) row[j]; int rowspan = c.Rowspan; // fill in missing rowspan cells to complete "scan line" for (int k = 1; k < rowspan && rows.Count < i+k; k++) { ArrayList spannedRow = ((ArrayList) rows[i + k]); if (spannedRow.Count > j) spannedRow.Insert(j, c); } } } return rows; } protected internal void RenderCells(RenderingContext ctx, ArrayList cells, bool hasToFit) { if (hasToFit) { foreach (PdfCell cell in cells) { if (!cell.Header) { if (cell.Bottom < IndentBottom) return; } } } foreach (PdfCell cell in cells) { if (!ctx.IsCellRenderedOnPage(cell, PageNumber)) { float correction = 0; if (ctx.NumCellRendered(cell) >= 1) { correction = 1.0f; } lines = cell.GetLines(ctx.pagetop, IndentBottom - correction); // if there is still text to render we render it if (lines != null && lines.Count > 0) { // we write the text float cellTop = cell.GetTop(ctx.pagetop - ctx.oldHeight); text.MoveText(0, cellTop); float cellDisplacement = FlushLines() - cellTop; text.MoveText(0, cellDisplacement); if (ctx.oldHeight + cellDisplacement > currentHeight) { currentHeight = ctx.oldHeight + cellDisplacement; } ctx.CellRendered(cell, PageNumber); } float indentBottom = Math.Max(cell.Bottom, IndentBottom); Rectangle tableRect = ctx.table.GetRectangle(ctx.pagetop, IndentBottom); indentBottom = Math.Max(tableRect.Bottom, indentBottom); // we paint the borders of the cells Rectangle cellRect = cell.GetRectangle(tableRect.Top, indentBottom); //cellRect.Bottom = cellRect.Bottom; if (cellRect.Height > 0) { ctx.lostTableBottom = indentBottom; ctx.cellGraphics.Rectangle(cellRect); } // and additional graphics ArrayList images = cell.GetImages(ctx.pagetop, IndentBottom); foreach (Image image in images) { graphics.AddImage(image); } } } } /** * Returns the bottomvalue of a Table if it were added to this document. * * @param table the table that may or may not be added to this document * @return a bottom value */ internal float GetBottom(Table table) { // constructing a PdfTable PdfTable tmp = new PdfTable(table, IndentLeft, IndentRight, IndentTop - currentHeight); return tmp.Bottom; } // [M5] header/footer protected internal void DoFooter() { if (footer == null) return; // Begin added by Edgar Leonardo Prieto Perilla // Avoid footer identation float tmpIndentLeft = indentation.indentLeft; float tmpIndentRight = indentation.indentRight; // Begin added: Bonf (Marc Schneider) 2003-07-29 float tmpListIndentLeft = indentation.listIndentLeft; float tmpImageIndentLeft = indentation.imageIndentLeft; float tmpImageIndentRight = indentation.imageIndentRight; // End added: Bonf (Marc Schneider) 2003-07-29 indentation.indentLeft = indentation.indentRight = 0; // Begin added: Bonf (Marc Schneider) 2003-07-29 indentation.listIndentLeft = 0; indentation.imageIndentLeft = 0; indentation.imageIndentRight = 0; // End added: Bonf (Marc Schneider) 2003-07-29 // End Added by Edgar Leonardo Prieto Perilla footer.PageNumber = pageN; leading = footer.Paragraph.TotalLeading; Add(footer.Paragraph); // adding the footer limits the height indentation.indentBottom = currentHeight; text.MoveText(Left, IndentBottom); FlushLines(); text.MoveText(-Left, -Bottom); footer.Top = GetBottom(currentHeight); footer.Bottom = Bottom - (0.75f * leading); footer.Left = Left; footer.Right = Right; graphics.Rectangle(footer); indentation.indentBottom = currentHeight + leading * 2; currentHeight = 0; // Begin added by Edgar Leonardo Prieto Perilla indentation.indentLeft = tmpIndentLeft; indentation.indentRight = tmpIndentRight; // Begin added: Bonf (Marc Schneider) 2003-07-29 indentation.listIndentLeft = tmpListIndentLeft; indentation.imageIndentLeft = tmpImageIndentLeft; indentation.imageIndentRight = tmpImageIndentRight; // End added: Bonf (Marc Schneider) 2003-07-29 // End added by Edgar Leonardo Prieto Perilla } protected internal void DoHeader() { // if there is a header, the header = added if (header == null) return; // Begin added by Edgar Leonardo Prieto Perilla // Avoid header identation float tmpIndentLeft = indentation.indentLeft; float tmpIndentRight = indentation.indentRight; // Begin added: Bonf (Marc Schneider) 2003-07-29 float tmpListIndentLeft = indentation.listIndentLeft; float tmpImageIndentLeft = indentation.imageIndentLeft; float tmpImageIndentRight = indentation.imageIndentRight; // End added: Bonf (Marc Schneider) 2003-07-29 indentation.indentLeft = indentation.indentRight = 0; // Added: Bonf indentation.listIndentLeft = 0; indentation.imageIndentLeft = 0; indentation.imageIndentRight = 0; // End added: Bonf // Begin added by Edgar Leonardo Prieto Perilla header.PageNumber = pageN; leading = header.Paragraph.TotalLeading; text.MoveText(0, leading); Add(header.Paragraph); NewLine(); indentation.indentTop = currentHeight - leading; header.Top = Top + leading; header.Bottom = IndentTop + leading * 2 / 3; header.Left = Left; header.Right = Right; graphics.Rectangle(header); FlushLines(); currentHeight = 0; // Begin added by Edgar Leonardo Prieto Perilla // Restore identation indentation.indentLeft = tmpIndentLeft; indentation.indentRight = tmpIndentRight; // Begin added: Bonf (Marc Schneider) 2003-07-29 indentation.listIndentLeft = tmpListIndentLeft; indentation.imageIndentLeft = tmpImageIndentLeft; indentation.imageIndentRight = tmpImageIndentRight; // End added: Bonf (Marc Schneider) 2003-07-29 // End Added by Edgar Leonardo Prieto Perilla } } }