git-svn-id: https://pdfsharp.svn.codeplex.com/svn@39620 56d0cb2f-6006-4f69-a5a2-94740...
[pdfsharp:pdfsharp.git] / PDFsharp / code / PdfSharp / PdfSharp.Pdf / PdfDocument.cs
1 #region PDFsharp - A .NET library for processing PDF\r
2 //\r
3 // Authors:\r
4 //   Stefan Lange (mailto:Stefan.Lange@pdfsharp.com)\r
5 //\r
6 // Copyright (c) 2005-2009 empira Software GmbH, Cologne (Germany)\r
7 //\r
8 // http://www.pdfsharp.com\r
9 // http://sourceforge.net/projects/pdfsharp\r
10 //\r
11 // Permission is hereby granted, free of charge, to any person obtaining a\r
12 // copy of this software and associated documentation files (the "Software"),\r
13 // to deal in the Software without restriction, including without limitation\r
14 // the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
15 // and/or sell copies of the Software, and to permit persons to whom the\r
16 // Software is furnished to do so, subject to the following conditions:\r
17 //\r
18 // The above copyright notice and this permission notice shall be included\r
19 // in all copies or substantial portions of the Software.\r
20 //\r
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
24 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
26 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER \r
27 // DEALINGS IN THE SOFTWARE.\r
28 #endregion\r
29 \r
30 using System;\r
31 using System.Diagnostics;\r
32 using System.Collections;\r
33 using System.Reflection;\r
34 using System.Text;\r
35 using System.IO;\r
36 using PdfSharp.Internal;\r
37 using PdfSharp.Pdf;\r
38 using PdfSharp.Pdf.Advanced;\r
39 using PdfSharp.Pdf.Internal;\r
40 using PdfSharp.Pdf.IO;\r
41 using PdfSharp.Pdf.AcroForms;\r
42 using PdfSharp.Pdf.Security;\r
43 \r
44 namespace PdfSharp.Pdf\r
45 {\r
46   /// <summary>\r
47   /// Represents a PDF document.\r
48   /// </summary>\r
49   [DebuggerDisplay("(Name={Name})")] // A name makes debugging easier\r
50   public sealed class PdfDocument : PdfObject, IDisposable\r
51   {\r
52     internal DocumentState state;\r
53     internal PdfDocumentOpenMode openMode;\r
54 \r
55 #if DEBUG_\r
56     static PdfDocument()\r
57     {\r
58       PSSR.TestResourceMessages();\r
59       //string test = PSSR.ResMngr.GetString("SampleMessage1");\r
60       //test.GetType();\r
61     }\r
62 #endif\r
63 \r
64     /// <summary>\r
65     /// Creates a new PDF document in memory.\r
66     /// To open an existing PDF file, use the PdfReader class.\r
67     /// </summary>\r
68     public PdfDocument()\r
69     {\r
70       //PdfDocument.Gob.AttatchDocument(this.Handle);\r
71 \r
72       this.creation = DateTime.Now;\r
73       this.state = DocumentState.Created;\r
74       this.version = 14;\r
75       Initialize();\r
76       Info.CreationDate = this.creation;\r
77     }\r
78 \r
79     /// <summary>\r
80     /// Creates a new PDF document with the specified file name. The file is immediately created and keeps\r
81     /// looked until the document is saved.\r
82     /// To open an existing PDF file and import it, use the PdfReader class.\r
83     /// </summary>\r
84     public PdfDocument(string filename)\r
85     {\r
86       //PdfDocument.Gob.AttatchDocument(this.Handle);\r
87 \r
88       this.creation = DateTime.Now;\r
89       this.state = DocumentState.Created;\r
90       this.version = 14;\r
91       Initialize();\r
92       Info.CreationDate = this.creation;\r
93 \r
94       this.outStream = new FileStream(filename, FileMode.Create);\r
95     }\r
96 \r
97     /// <summary>\r
98     /// Creates a new PDF document using the specified stream.\r
99     /// To open an existing PDF file, use the PdfReader class.\r
100     /// </summary>\r
101     public PdfDocument(Stream outputStream)\r
102     {\r
103       //PdfDocument.Gob.AttatchDocument(this.Handle);\r
104 \r
105       this.creation = DateTime.Now;\r
106       this.state = DocumentState.Created;\r
107       Initialize();\r
108       Info.CreationDate = this.creation;\r
109 \r
110       this.outStream = outputStream;\r
111     }\r
112 \r
113     internal PdfDocument(Lexer lexer)\r
114     {\r
115       //PdfDocument.Gob.AttatchDocument(this.Handle);\r
116 \r
117       this.creation = DateTime.Now;\r
118       this.state = DocumentState.Imported;\r
119 \r
120       //this.info = new PdfInfo(this);\r
121       //this.pages = new PdfPages(this);\r
122       //this.fontTable = new PdfFontTable();\r
123       //this.catalog = new PdfCatalog(this);\r
124       ////this.font = new PdfFont();\r
125       //this.objects = new PdfObjectTable(this);\r
126       //this.trailer = new PdfTrailer(this);\r
127       this.irefTable = new PdfReferenceTable(this);\r
128       this.lexer = lexer;\r
129     }\r
130 \r
131     void Initialize()\r
132     {\r
133       //this.info       = new PdfInfo(this);\r
134       this.fontTable = new PdfFontTable(this);\r
135       this.imageTable = new PdfImageTable(this);\r
136       this.trailer = new PdfTrailer(this);\r
137       this.irefTable = new PdfReferenceTable(this);\r
138       this.trailer.CreateNewDocumentIDs();\r
139     }\r
140 \r
141     //~PdfDocument()\r
142     //{\r
143     //  Dispose(false);\r
144     //}\r
145 \r
146     /// <summary>\r
147     /// Disposes all references to this document stored in other documents. This function should be called\r
148     /// for documents you finished importing pages from. Calling Dispose is technically not necessary but\r
149     /// useful for earlier reclaiming memory of documents you do not need anymore.\r
150     /// </summary>\r
151     public void Dispose()\r
152     {\r
153       Dispose(true);\r
154       //GC.SuppressFinalize(this);\r
155     }\r
156 \r
157     void Dispose(bool disposing)\r
158     {\r
159       if (this.state != DocumentState.Disposed)\r
160       {\r
161         if (disposing)\r
162         {\r
163           // Dispose managed resources.\r
164         }\r
165         //PdfDocument.Gob.DetatchDocument(Handle);\r
166       }\r
167       this.state = DocumentState.Disposed;\r
168     }\r
169 \r
170     /// <summary>\r
171     /// Gets or sets a user defined object that contains arbitrary information associated with this document.\r
172     /// The tag is not used by PDFsharp.\r
173     /// </summary>\r
174     public object Tag\r
175     {\r
176       get { return this.tag; }\r
177       set { this.tag = value; }\r
178     }\r
179     object tag;\r
180 \r
181     /// <summary>\r
182     /// Gets or sets a value used to distinguish PdfDocument objects.\r
183     /// The name is not used by PDFsharp.\r
184     /// </summary>\r
185     string Name\r
186     {\r
187       get { return this.name; }\r
188       set { this.name = value; }\r
189     }\r
190     string name = NewName();\r
191 \r
192     /// <summary>\r
193     /// Get a new default name for a new document.\r
194     /// </summary>\r
195     static string NewName()\r
196     {\r
197 #if DEBUG_\r
198       if (PdfDocument.nameCount == 57)\r
199         PdfDocument.nameCount.GetType();\r
200 #endif\r
201       return "Document " + nameCount++;\r
202     }\r
203     static int nameCount;\r
204 \r
205     internal bool CanModify\r
206     {\r
207       //get {return this.state == DocumentState.Created || this.state == DocumentState.Modifyable;}\r
208       get { return true; }\r
209     }\r
210 \r
211     /// <summary>\r
212     /// Closes this instance.\r
213     /// </summary>\r
214     public void Close()\r
215     {\r
216       if (!CanModify)\r
217         throw new InvalidOperationException(PSSR.CannotModify);\r
218 \r
219       if (this.outStream != null)\r
220       {\r
221         // Get security handler if document gets encrypted\r
222         PdfStandardSecurityHandler securityHandler = null;\r
223         if (SecuritySettings.DocumentSecurityLevel != PdfDocumentSecurityLevel.None)\r
224           securityHandler = SecuritySettings.SecurityHandler;\r
225 \r
226         PdfWriter writer = new PdfWriter(this.outStream, securityHandler);\r
227         try\r
228         {\r
229           DoSave(writer);\r
230         }\r
231         finally\r
232         {\r
233           if (writer != null)\r
234             writer.Close();\r
235         }\r
236       }\r
237     }\r
238 \r
239     /// <summary>\r
240     /// Saves the document to the specified path. If a file already exists, it will be overwritten.\r
241     /// </summary>\r
242     public void Save(string path)\r
243     {\r
244       if (!CanModify)\r
245         throw new InvalidOperationException(PSSR.CannotModify);\r
246 \r
247       Stream stream = new FileStream(path, FileMode.Create, FileAccess.Write);\r
248       Save(stream);\r
249     }\r
250 \r
251     /// <summary>\r
252     /// Saves the document to the specified stream.\r
253     /// </summary>\r
254     public void Save(Stream stream, bool closeStream)\r
255     {\r
256       if (!CanModify)\r
257         throw new InvalidOperationException(PSSR.CannotModify);\r
258 \r
259       // TODO: more diagnostic checks\r
260       string message = "";\r
261       if (!CanSave(ref message))\r
262         throw new PdfSharpException(message);\r
263 \r
264       // Get security handler if document gets encrypted\r
265       PdfStandardSecurityHandler securityHandler = null;\r
266       if (SecuritySettings.DocumentSecurityLevel != PdfDocumentSecurityLevel.None)\r
267         securityHandler = SecuritySettings.SecurityHandler;\r
268 \r
269       PdfWriter writer = new PdfWriter(stream, securityHandler);\r
270       try\r
271       {\r
272         DoSave(writer);\r
273       }\r
274       finally\r
275       {\r
276         if (stream != null && closeStream)\r
277           stream.Close();\r
278         if (writer != null)\r
279           writer.Close(closeStream);\r
280       }\r
281     }\r
282 \r
283     /// <summary>\r
284     /// Saves the document to the specified stream and closes the stream.\r
285     /// </summary>\r
286     public void Save(Stream stream)\r
287     {\r
288       Save(stream, true);\r
289     }\r
290 \r
291     /// <summary>\r
292     /// Implements saving a PDF file.\r
293     /// </summary>\r
294     void DoSave(PdfWriter writer)\r
295     {\r
296       if (this.pages == null || this.pages.Count == 0)\r
297         throw new InvalidOperationException("Cannot save a PDF document with no pages.");\r
298 \r
299       try\r
300       {\r
301         bool encrypt = this.securitySettings.DocumentSecurityLevel != PdfDocumentSecurityLevel.None;\r
302         if (encrypt)\r
303         {\r
304           PdfStandardSecurityHandler securityHandler = this.securitySettings.SecurityHandler;\r
305           if (securityHandler.Reference == null)\r
306             this.irefTable.Add(securityHandler);\r
307           else\r
308             Debug.Assert(this.irefTable.Contains(securityHandler.ObjectID));\r
309           this.trailer.Elements[PdfTrailer.Keys.Encrypt] = this.securitySettings.SecurityHandler.Reference;\r
310         }\r
311         else\r
312           this.trailer.Elements.Remove(PdfTrailer.Keys.Encrypt);\r
313 \r
314         PrepareForSave();\r
315 \r
316         if (encrypt)\r
317           this.securitySettings.SecurityHandler.PrepareEncryption();\r
318 \r
319         writer.WriteFileHeader(this);\r
320         PdfReference[] irefs = this.irefTable.AllReferences;\r
321         int count = irefs.Length;\r
322         for (int idx = 0; idx < count; idx++)\r
323         {\r
324           PdfReference iref = irefs[idx];\r
325 #if DEBUG_\r
326           if (iref.ObjectNumber == 378)\r
327             GetType();\r
328 #endif\r
329           iref.Position = writer.Position;\r
330           iref.Value.WriteObject(writer);\r
331         }\r
332         int startxref = writer.Position;\r
333         this.irefTable.WriteObject(writer);\r
334         writer.WriteRaw("trailer\n");\r
335         this.trailer.Elements.SetInteger("/Size", count + 1);\r
336         this.trailer.WriteObject(writer);\r
337         writer.WriteEof(this, startxref);\r
338 \r
339         //if (encrypt)\r
340         //{\r
341         //  this.state &= ~DocumentState.SavingEncrypted;\r
342         //  //this.securitySettings.SecurityHandler.EncryptDocument();\r
343         //}\r
344       }\r
345       finally\r
346       {\r
347         if (writer != null)\r
348         {\r
349           writer.Stream.Flush();\r
350           // DO NOT CLOSE WRITER HERE\r
351           //writer.Close();\r
352         }\r
353       }\r
354     }\r
355 \r
356     /// <summary>\r
357     /// Dispatches PrepareForSave to the objects that need it.\r
358     /// </summary>\r
359     internal override void PrepareForSave()\r
360     {\r
361       PdfDocumentInformation info = Info;\r
362 \r
363       // Set Creator if value is undefined\r
364       if (info.Elements[PdfDocumentInformation.Keys.Creator] == null)\r
365         info.Creator = VersionInfo.Producer;\r
366 \r
367       // Keep original producer if file was imported\r
368       string producer = info.Producer;\r
369       if (producer.Length == 0)\r
370         producer = VersionInfo.Producer;\r
371       else\r
372       {\r
373         // Prevent endless concatenation if file is edited with PDFsharp more than once\r
374         if (!producer.StartsWith(VersionInfo.Title))\r
375           producer = VersionInfo.Producer + " (Original: " + producer + ")";\r
376       }\r
377       info.Elements.SetString(PdfDocumentInformation.Keys.Producer, producer);\r
378 \r
379       // Prepare used fonts\r
380       if (this.fontTable != null)\r
381         this.fontTable.PrepareForSave();\r
382 \r
383       // Let catalog do the rest\r
384       Catalog.PrepareForSave();\r
385 \r
386 #if true\r
387       // Remove all unreachable objects (e.g. from deleted pages)\r
388       int removed = this.irefTable.Compact();\r
389       if (removed != 0)\r
390         Debug.WriteLine("PrepareForSave: Number of deleted unreachable objects: " + removed);\r
391       this.irefTable.Renumber();\r
392 #endif\r
393     }\r
394 \r
395     /// <summary>\r
396     /// Determines whether the document can be saved.\r
397     /// </summary>\r
398     public bool CanSave(ref string message)\r
399     {\r
400       if (!SecuritySettings.CanSave(ref message))\r
401         return false;\r
402 \r
403       return true;\r
404     }\r
405 \r
406     internal bool HasVersion(string version)\r
407     {\r
408       return String.Compare(Catalog.Version, version) >= 0;\r
409     }\r
410 \r
411     /// <summary>\r
412     /// Gets the document options used for saving the document.\r
413     /// </summary>\r
414     public PdfDocumentOptions Options\r
415     {\r
416       get\r
417       {\r
418         if (this.options == null)\r
419           this.options = new PdfDocumentOptions(this);\r
420         return this.options;\r
421       }\r
422     }\r
423     PdfDocumentOptions options;\r
424 \r
425     /// <summary>\r
426     /// Gets PDF specific document settings.\r
427     /// </summary>\r
428     public PdfDocumentSettings Settings\r
429     {\r
430       get\r
431       {\r
432         if (this.settings == null)\r
433           this.settings = new PdfDocumentSettings(this);\r
434         return this.settings;\r
435       }\r
436     }\r
437     PdfDocumentSettings settings;\r
438 \r
439     /// <summary>\r
440     /// NYI Indicates whether large objects are written immediately to the output stream to relieve\r
441     /// memory consumption.\r
442     /// </summary>\r
443     internal bool EarlyWrite\r
444     {\r
445       get { return false; }\r
446     }\r
447 \r
448     /// <summary>\r
449     /// Gets or sets the PDF version number. Return value 14 e.g. means PDF 1.4 / Acrobat 5 etc.\r
450     /// </summary>\r
451     public int Version\r
452     {\r
453       get { return this.version; }\r
454       set\r
455       {\r
456         if (!CanModify)\r
457           throw new InvalidOperationException(PSSR.CannotModify);\r
458         if (value < 12 || value > 17) // TODO not really implemented\r
459           throw new ArgumentException(PSSR.InvalidVersionNumber, "value");\r
460         this.version = value;\r
461       }\r
462     }\r
463     internal int version;\r
464 \r
465     /// <summary>\r
466     /// Gets the number of pages in the document.\r
467     /// </summary>\r
468     public int PageCount\r
469     {\r
470       get\r
471       {\r
472         if (CanModify)\r
473           return Pages.Count;\r
474         // PdfOpenMode is InformationOnly\r
475         PdfDictionary pageTreeRoot = (PdfDictionary)Catalog.Elements.GetObject(PdfCatalog.Keys.Pages);\r
476         return pageTreeRoot.Elements.GetInteger(PdfPages.Keys.Count);\r
477       }\r
478     }\r
479 \r
480     /// <summary>\r
481     /// Gets the file size of the document.\r
482     /// </summary>\r
483     public long FileSize\r
484     {\r
485       get { return this.fileSize; }\r
486     }\r
487     internal long fileSize;\r
488 \r
489     /// <summary>\r
490     /// Gets the full qualified file name if the document was read form a file, or an empty string otherwise.\r
491     /// </summary>\r
492     public string FullPath\r
493     {\r
494       get { return this.fullPath; }\r
495     }\r
496     internal string fullPath = String.Empty;\r
497 \r
498     /// <summary>\r
499     /// Gets a Guid that uniquely identifies this instance of PdfDocument.\r
500     /// </summary>\r
501     public Guid Guid\r
502     {\r
503       get { return this.guid; }\r
504     }\r
505     Guid guid = Guid.NewGuid();\r
506 \r
507     internal DocumentHandle Handle\r
508     {\r
509       get\r
510       {\r
511         if (this.handle == null)\r
512           this.handle = new DocumentHandle(this);\r
513         return this.handle;\r
514       }\r
515     }\r
516     DocumentHandle handle;\r
517 \r
518     /// <summary>\r
519     /// Returns a value indicating whether the document was newly created or opened from an existing document.\r
520     /// Returns true if the document was opened with the PdfReader.Open function, false otherwise.\r
521     /// </summary>\r
522     public bool IsImported\r
523     {\r
524       get { return (this.state & DocumentState.Imported) != 0; }\r
525     }\r
526 \r
527     /// <summary>\r
528     /// Returns a value indicating whether the document is read only or can be modified.\r
529     /// </summary>\r
530     public bool IsReadOnly\r
531     {\r
532       get { return (this.openMode != PdfDocumentOpenMode.Modify); }\r
533     }\r
534 \r
535     internal Exception DocumentNotImported()\r
536     {\r
537       return new InvalidOperationException("Document not imported.");\r
538     }\r
539 \r
540     /// <summary>\r
541     /// Gets information about the document.\r
542     /// </summary>\r
543     public PdfDocumentInformation Info\r
544     {\r
545       get\r
546       {\r
547         if (this.info == null)\r
548           this.info = this.trailer.Info;\r
549         return this.info;\r
550       }\r
551     }\r
552     PdfDocumentInformation info;  // never changes if once created\r
553 \r
554     /// <summary>\r
555     /// This function is intended to be undocumented.\r
556     /// </summary>\r
557     public PdfCustomValues CustomValues\r
558     {\r
559       get\r
560       {\r
561         if (this.customValues == null)\r
562           this.customValues = PdfCustomValues.Get(Catalog.Elements);\r
563         return this.customValues;\r
564       }\r
565       set\r
566       {\r
567         if (value != null)\r
568           throw new ArgumentException("Only null is allowed to clear all custom values.");\r
569         PdfCustomValues.Remove(Catalog.Elements);\r
570         this.customValues = null;\r
571       }\r
572     }\r
573     PdfCustomValues customValues;\r
574 \r
575     /// <summary>\r
576     /// Get the pages dictionary.\r
577     /// </summary>\r
578     public PdfPages Pages\r
579     {\r
580       get\r
581       {\r
582         if (this.pages == null)\r
583           this.pages = Catalog.Pages;\r
584         return this.pages;\r
585       }\r
586     }\r
587     PdfPages pages;  // never changes if once created\r
588 \r
589     /// <summary>\r
590     /// Gets or sets a value specifying the page layout to be used when the document is opened.\r
591     /// </summary>\r
592     public PdfPageLayout PageLayout\r
593     {\r
594       get { return Catalog.PageLayout; }\r
595       set\r
596       {\r
597         if (!CanModify)\r
598           throw new InvalidOperationException(PSSR.CannotModify);\r
599         Catalog.PageLayout = value;\r
600       }\r
601     }\r
602 \r
603     /// <summary>\r
604     /// Gets or sets a value specifying how the document should be displayed when opened.\r
605     /// </summary>\r
606     public PdfPageMode PageMode\r
607     {\r
608       get { return Catalog.PageMode; }\r
609       set\r
610       {\r
611         if (!CanModify)\r
612           throw new InvalidOperationException(PSSR.CannotModify);\r
613         Catalog.PageMode = value;\r
614       }\r
615     }\r
616 \r
617     /// <summary>\r
618     /// Gets the viewer preferences of this document.\r
619     /// </summary>\r
620     public PdfViewerPreferences ViewerPreferences\r
621     {\r
622       get { return Catalog.ViewerPreferences; }\r
623     }\r
624 \r
625     /// <summary>\r
626     /// Gets the root of the outline (or bookmark) tree.\r
627     /// </summary>\r
628     public PdfOutline.PdfOutlineCollection Outlines\r
629     {\r
630       get { return Catalog.Outlines; }\r
631     }\r
632 \r
633     /// <summary>\r
634     /// Get the AcroForm dictionary.\r
635     /// </summary>\r
636     public PdfAcroForm AcroForm\r
637     {\r
638       get { return Catalog.AcroForm; }\r
639     }\r
640 \r
641     /// <summary>\r
642     /// Gets or sets the default language of the document.\r
643     /// </summary>\r
644     public string Language\r
645     {\r
646       get { return Catalog.Elements.GetString(PdfCatalog.Keys.Lang); }\r
647       set { Catalog.Elements.SetString(PdfCatalog.Keys.Lang, value); }\r
648     }\r
649 \r
650     /// <summary>\r
651     /// Gets the security settings of this document.\r
652     /// </summary>\r
653     public PdfSecuritySettings SecuritySettings\r
654     {\r
655       get\r
656       {\r
657         if (this.securitySettings == null)\r
658           this.securitySettings = new PdfSecuritySettings(this);\r
659         return this.securitySettings;\r
660       }\r
661     }\r
662     internal PdfSecuritySettings securitySettings;\r
663 \r
664     /// <summary>\r
665     /// Gets the document font table that holds all fonts used in the current document.\r
666     /// </summary>\r
667     internal PdfFontTable FontTable\r
668     {\r
669       get\r
670       {\r
671         if (this.fontTable == null)\r
672           this.fontTable = new PdfFontTable(this);\r
673         return this.fontTable;\r
674       }\r
675     }\r
676     PdfFontTable fontTable;\r
677 \r
678     /// <summary>\r
679     /// Gets the document image table that holds all images used in the current document.\r
680     /// </summary>\r
681     internal PdfImageTable ImageTable\r
682     {\r
683       get\r
684       {\r
685         if (this.imageTable == null)\r
686           this.imageTable = new PdfImageTable(this);\r
687         return this.imageTable;\r
688       }\r
689     }\r
690     PdfImageTable imageTable;\r
691 \r
692     /// <summary>\r
693     /// Gets the document form table that holds all form external objects used in the current document.\r
694     /// </summary>\r
695     internal PdfFormXObjectTable FormTable\r
696     {\r
697       get\r
698       {\r
699         if (this.formTable == null)\r
700           this.formTable = new PdfFormXObjectTable(this);\r
701         return this.formTable;\r
702       }\r
703     }\r
704     PdfFormXObjectTable formTable;\r
705 \r
706     /// <summary>\r
707     /// Gets the document ExtGState table that holds all form state objects used in the current document.\r
708     /// </summary>\r
709     internal PdfExtGStateTable ExtGStateTable\r
710     {\r
711       get\r
712       {\r
713         if (this.extGStateTable == null)\r
714           this.extGStateTable = new PdfExtGStateTable(this);\r
715         return this.extGStateTable;\r
716       }\r
717     }\r
718     PdfExtGStateTable extGStateTable;\r
719 \r
720     /// <summary>\r
721     /// Gets the PdfCatalog of the current document.\r
722     /// </summary>\r
723     internal PdfCatalog Catalog\r
724     {\r
725       get\r
726       {\r
727         if (this.catalog == null)\r
728           this.catalog = this.trailer.Root;\r
729         return catalog;\r
730       }\r
731     }\r
732     PdfCatalog catalog;  // never changes if once created\r
733 \r
734     /// <summary>\r
735     /// Gets the PdfInternals object of this document, that grants access to some internal structures\r
736     /// which are not part of the public interface of PdfDocument.\r
737     /// </summary>\r
738     public new PdfInternals Internals\r
739     {\r
740       get\r
741       {\r
742         if (this.internals == null)\r
743           this.internals = new PdfInternals(this);\r
744         return this.internals;\r
745       }\r
746     }\r
747     PdfInternals internals;\r
748 \r
749     /// <summary>\r
750     /// Creates a new page and adds it to this document.\r
751     /// </summary>\r
752     public PdfPage AddPage()\r
753     {\r
754       if (!CanModify)\r
755         throw new InvalidOperationException(PSSR.CannotModify);\r
756       return Catalog.Pages.Add();\r
757     }\r
758 \r
759     /// <summary>\r
760     /// Adds the specified page to this document. If the page is from an external document,\r
761     /// it is imported to this document. In this case the returned page is not the same\r
762     /// object as the specified one.\r
763     /// </summary>\r
764     public PdfPage AddPage(PdfPage page)\r
765     {\r
766       if (!CanModify)\r
767         throw new InvalidOperationException(PSSR.CannotModify);\r
768       return Catalog.Pages.Add(page);\r
769     }\r
770 \r
771     /// <summary>\r
772     /// Creates a new page and inserts it in this document at the specified position.\r
773     /// </summary>\r
774     public PdfPage InsertPage(int index)\r
775     {\r
776       if (!CanModify)\r
777         throw new InvalidOperationException(PSSR.CannotModify);\r
778       return Catalog.Pages.Insert(index);\r
779     }\r
780 \r
781     /// <summary>\r
782     /// Inserts the specified page in this document. If the page is from an external document,\r
783     /// it is imported to this document. In this case the returned page is not the same\r
784     /// object as the specified one.\r
785     /// </summary>\r
786     public PdfPage InsertPage(int index, PdfPage page)\r
787     {\r
788       if (!CanModify)\r
789         throw new InvalidOperationException(PSSR.CannotModify);\r
790       return Catalog.Pages.Insert(index, page);\r
791     }\r
792 \r
793     /// <summary>\r
794     /// Gets the security handler.\r
795     /// </summary>\r
796     public PdfStandardSecurityHandler SecurityHandler\r
797     {\r
798       get { return this.trailer.SecurityHandler; }\r
799     }\r
800 \r
801     internal PdfTrailer trailer;\r
802     internal PdfReferenceTable irefTable;\r
803     internal Stream outStream;\r
804 \r
805     // Imported Document\r
806     internal Lexer lexer;\r
807 \r
808     internal DateTime creation;\r
809 \r
810     /// <summary>\r
811     /// Occurs when the specified document is not used anymore for importing content.\r
812     /// </summary>\r
813     internal void OnExternalDocumentFinalized(PdfDocument.DocumentHandle handle)\r
814     {\r
815       if (tls != null)\r
816       {\r
817         //PdfDocument[] documents = tls.Documents;\r
818         tls.DetachDocument(handle);\r
819       }\r
820 \r
821       if (this.formTable != null)\r
822         formTable.DetachDocument(handle);\r
823     }\r
824 \r
825     //internal static GlobalObjectTable Gob = new GlobalObjectTable();\r
826 \r
827     /// <summary>\r
828     /// Gets the ThreadLocalStorage object. It is used for caching objects that should created\r
829     /// only once.\r
830     /// </summary>\r
831     internal static ThreadLocalStorage Tls\r
832     {\r
833       get\r
834       {\r
835         if (tls == null)\r
836           tls = new ThreadLocalStorage();\r
837         return tls;\r
838       }\r
839     }\r
840     [ThreadStatic]\r
841     static ThreadLocalStorage tls;\r
842 \r
843     [DebuggerDisplay("(ID={ID}, alive={IsAlive})")]\r
844     internal class DocumentHandle\r
845     {\r
846       public DocumentHandle(PdfDocument document)\r
847       {\r
848         this.weakRef = new WeakReference(document);\r
849         this.ID = document.guid.ToString("B").ToUpper();\r
850       }\r
851 \r
852       public bool IsAlive\r
853       {\r
854         get { return this.weakRef.IsAlive; }\r
855       }\r
856 \r
857       public PdfDocument Target\r
858       {\r
859         get { return this.weakRef.Target as PdfDocument; }\r
860       }\r
861       WeakReference weakRef;\r
862 \r
863       public string ID;\r
864 \r
865       public override bool Equals(object obj)\r
866       {\r
867         DocumentHandle handle = obj as DocumentHandle;\r
868         if (!Object.ReferenceEquals(handle, null))\r
869           return this.ID == handle.ID;\r
870         return false;\r
871       }\r
872 \r
873       public override int GetHashCode()\r
874       {\r
875         return this.ID.GetHashCode();\r
876       }\r
877 \r
878       public static bool operator ==(DocumentHandle left, DocumentHandle right)\r
879       {\r
880         if (Object.ReferenceEquals(left, null))\r
881           return Object.ReferenceEquals(right, null);\r
882         return left.Equals(right);\r
883       }\r
884 \r
885       public static bool operator !=(DocumentHandle left, DocumentHandle right)\r
886       {\r
887         return !(left == right);\r
888       }\r
889     }\r
890   }\r
891 }