protected override void WriteStandard() { // 1. Header [PDF:1.6:3.4.1]. WriteHeader(); // 2. Body [PDF:1.6:3.4.2]. int xrefSize = file.IndirectObjects.Count; StringBuilder xrefBuilder = new StringBuilder(XRefChunk); { /* * NOTE: A standard xref table comprises just one section composed by just one subsection. * NOTE: As xref-table free entries MUST be arrayed as a linked list, * it's needed to cache intermingled in-use entries in order to properly render * the object number of the next free entry inside the previous one. */ AppendXRefSubsectionIndexer(xrefBuilder, 0, xrefSize); StringBuilder xrefInUseBlockBuilder = new StringBuilder(); IndirectObjects indirectObjects = file.IndirectObjects; PdfReference freeReference = indirectObjects[0].Reference; // Initialized to the first free entry. for (int index = 1; index < xrefSize; index++) { // Current entry insertion. PdfIndirectObject indirectObject = indirectObjects[index]; if (indirectObject.IsInUse()) // In-use entry. { // Add in-use entry! AppendXRefEntry(xrefInUseBlockBuilder, indirectObject.Reference, stream.Length); // Add in-use entry content! indirectObject.WriteTo(stream, file); } else // Free entry. { // Add free entry! AppendXRefEntry(xrefBuilder, freeReference, index); // End current block! xrefBuilder.Append(xrefInUseBlockBuilder); // Initialize next block! xrefInUseBlockBuilder.Length = 0; freeReference = indirectObject.Reference; } } // Add last free entry! AppendXRefEntry(xrefBuilder, freeReference, 0); // End last block! xrefBuilder.Append(xrefInUseBlockBuilder); } // 3. XRef table (unique section) [PDF:1.6:3.4.3]. long startxref = stream.Length; stream.Write(xrefBuilder.ToString()); // 4. Trailer [PDF:1.6:3.4.4]. WriteTrailer(startxref, xrefSize, null); }
/** * <summary>Adds an indirect object entry to the specified xref stream.</summary> * <param name="xrefEntry">Indirect object's xref entry.</param> * <param name="indirectObject">Indirect object.</param> * <param name="xrefStream">XRef stream.</param> * <param name="prevFreeEntry">Previous free xref entry.</param> * <returns>Current free xref entry.</returns> */ private XRefEntry AddXRefEntry( XRefEntry xrefEntry, PdfIndirectObject indirectObject, XRefStream xrefStream, XRefEntry prevFreeEntry ) { xrefStream[xrefEntry.Number] = xrefEntry; switch (xrefEntry.Usage) { case XRefEntry.UsageEnum.InUse: // Set entry content's offset! xrefEntry.Offset = (int)stream.Length; // Add entry content! indirectObject.WriteTo(stream); break; case XRefEntry.UsageEnum.InUseCompressed: /* NOOP: Serialization is delegated to the containing object stream. */ break; case XRefEntry.UsageEnum.Free: if (prevFreeEntry != null) { prevFreeEntry.Offset = xrefEntry.Number; } // Object number of the next free object. prevFreeEntry = xrefEntry; break; default: throw new NotSupportedException(); } return(prevFreeEntry); }
/** * <summary>Adds an indirect object entry to the specified xref stream.</summary> * <param name="xrefEntry">Indirect object's xref entry.</param> * <param name="indirectObject">Indirect object.</param> * <param name="xrefStream">XRef stream.</param> * <param name="prevFreeEntry">Previous free xref entry.</param> * <param name="extensionObjectStreams">Object streams used in incremental updates to extend * modified ones.</param> * <returns>Current free xref entry.</returns> */ private XRefEntry AddXRefEntry( XRefEntry xrefEntry, PdfIndirectObject indirectObject, XRefStream xrefStream, XRefEntry prevFreeEntry, IDictionary <int, ObjectStream> extensionObjectStreams ) { xrefStream[xrefEntry.Number] = xrefEntry; switch (xrefEntry.Usage) { case XRefEntry.UsageEnum.InUse: { int offset = (int)stream.Length; // Add entry content! indirectObject.WriteTo(stream, file); // Set entry content's offset! xrefEntry.Offset = offset; } break; case XRefEntry.UsageEnum.InUseCompressed: /* * NOTE: Serialization is delegated to the containing object stream. */ if (extensionObjectStreams != null) // Incremental update. { int baseStreamNumber = xrefEntry.StreamNumber; PdfIndirectObject baseStreamIndirectObject = file.IndirectObjects[baseStreamNumber]; if (baseStreamIndirectObject.IsOriginal()) // Extension stream needed in order to preserve the original object stream. { // Get the extension object stream associated to the original object stream! ObjectStream extensionObjectStream; if (!extensionObjectStreams.TryGetValue(baseStreamNumber, out extensionObjectStream)) { file.Register(extensionObjectStream = new ObjectStream()); // Link the extension to the base object stream! extensionObjectStream.BaseStream = (ObjectStream)baseStreamIndirectObject.DataObject; extensionObjectStreams[baseStreamNumber] = extensionObjectStream; } // Insert the data object into the extension object stream! extensionObjectStream[xrefEntry.Number] = indirectObject.DataObject; // Update the data object's xref entry! xrefEntry.StreamNumber = extensionObjectStream.Reference.ObjectNumber; xrefEntry.Offset = XRefEntry.UndefinedOffset; // Internal object index unknown (to set on object stream serialization -- see ObjectStream). } } break; case XRefEntry.UsageEnum.Free: if (prevFreeEntry != null) { prevFreeEntry.Offset = xrefEntry.Number; } // Object number of the next free object. prevFreeEntry = xrefEntry; break; default: throw new NotSupportedException(); } return(prevFreeEntry); }