private IndirectReferenceToken CopyPageNode(PageTreeNode pageNode, IndirectReferenceToken parentPagesObject, IPdfTokenScanner tokenScanner, IDictionary <IndirectReferenceToken, IndirectReferenceToken> referencesFromDocument) { Debug.Assert(pageNode.IsPage); var pageDictionary = new Dictionary <NameToken, IToken> { { NameToken.Parent, parentPagesObject }, }; foreach (var setPair in pageNode.NodeDictionary.Data) { var name = setPair.Key; var token = setPair.Value; if (name == NameToken.Parent) { // Skip Parent token, since we have to reassign it continue; } pageDictionary.Add(NameToken.Create(name), CopyToken(token, tokenScanner, referencesFromDocument)); } return(context.WriteToken(new DictionaryToken(pageDictionary))); }
private static Dictionary <IToken, IToken> ConvertToDictionary(IReadOnlyList <IToken> tokens) { var result = new Dictionary <IToken, IToken>(); IToken key = null; for (var i = 0; i < tokens.Count; i++) { var token = tokens[i]; if (key == null) { key = token; continue; } // Combine indirect references, e.g. 12 0 R if (token is NumericToken num && PeekNext(tokens, i) is NumericToken gen) { var r = PeekNext(tokens, i + 1); if (r == OperatorToken.R) { result[key] = new IndirectReferenceToken(new IndirectReference(num.Long, gen.Int)); i = i + 2; } }
private static Dictionary <NameToken, IToken> ConvertToDictionary(IReadOnlyList <IToken> tokens) { var result = new Dictionary <NameToken, IToken>(); NameToken key = null; for (var i = 0; i < tokens.Count; i++) { var token = tokens[i]; if (key == null) { if (token is NameToken name) { key = name; continue; } throw new PdfDocumentFormatException($"Expected name as dictionary key, instead got: " + token); } // Combine indirect references, e.g. 12 0 R if (token is NumericToken num && PeekNext(tokens, i) is NumericToken gen) { var r = PeekNext(tokens, i + 1); if (r == OperatorToken.R) { result[key] = new IndirectReferenceToken(new IndirectReference(num.Long, gen.Int)); i = i + 2; } }
public DocumentMerger() { var reserved = context.ReserveNumber(); rootPagesIndirectReference = new IndirectReferenceToken(new IndirectReference(reserved, 0)); WriteHeaderToStream(); }
private IndirectReferenceToken AddToken(IToken token, int reservedNumber) { var reference = new IndirectReference(reservedNumber, 0); var referenceToken = new IndirectReferenceToken(reference); tokenReferences.Add(referenceToken, token); return(referenceToken); }
public IFont GetFontDirectly(IndirectReferenceToken fontReferenceToken, bool isLenientParsing) { if (!DirectObjectFinder.TryGet(fontReferenceToken, scanner, out DictionaryToken fontDictionaryToken)) { throw new PdfDocumentFormatException($"The requested font reference token {fontReferenceToken} wasn't a font."); } var font = fontFactory.Get(fontDictionaryToken, isLenientParsing); return(font); }
private static void WriteIndirectReference(IndirectReferenceToken reference, Stream outputStream) { WriteLong(reference.Data.ObjectNumber, outputStream); WriteWhitespace(outputStream); WriteInt(reference.Data.Generation, outputStream); WriteWhitespace(outputStream); outputStream.WriteByte(RByte); WriteWhitespace(outputStream); }
public virtual IndirectReferenceToken WriteToken(IToken token, IndirectReferenceToken indirectReference) { if (!Initialized) { InitializePdf(DefaultVersion); } offsets.Add(indirectReference.Data, Stream.Position); var obj = new ObjectToken(Stream.Position, indirectReference.Data, token); TokenWriter.WriteToken(obj, Stream); return(indirectReference); }
private (ObjectToken, int) CopyPagesTree(PageTreeNode treeNode, IndirectReferenceToken treeParentReference, IPdfTokenScanner tokenScanner) { Debug.Assert(!treeNode.IsPage); var currentNodeReserved = context.ReserveNumber(); var currentNodeReference = new IndirectReferenceToken(new IndirectReference(currentNodeReserved, 0)); var pageReferences = new List <IndirectReferenceToken>(); var nodeCount = 0; foreach (var pageNode in treeNode.Children) { ObjectToken newEntry; if (!pageNode.IsPage) { var count = 0; (newEntry, count) = CopyPagesTree(pageNode, currentNodeReference, tokenScanner); nodeCount += count; } else { newEntry = CopyPageNode(pageNode, currentNodeReference, tokenScanner); ++nodeCount; } pageReferences.Add(new IndirectReferenceToken(newEntry.Number)); } var newPagesNode = new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Pages }, { NameToken.Kids, new ArrayToken(pageReferences) }, { NameToken.Count, new NumericToken(nodeCount) }, { NameToken.Parent, treeParentReference } }; foreach (var pair in treeNode.NodeDictionary.Data) { if (IgnoreKeyForPagesNode(pair)) { continue; } newPagesNode[NameToken.Create(pair.Key)] = CopyToken(pair.Value, tokenScanner); } var pagesDictionary = new DictionaryToken(newPagesNode); return(context.WriteObject(memory, pagesDictionary, currentNodeReserved), nodeCount); }
public override IndirectReferenceToken WriteToken(IToken token, IndirectReferenceToken indirectReference) { if (!Initialized) { InitializePdf(DefaultVersion); } ms.SetLength(0); TokenWriter.WriteToken(token, ms); var contents = ms.ToArray(); hashes.Add(contents, indirectReference); offsets.Add(indirectReference.Data, Stream.Position); TokenWriter.WriteObject(indirectReference.Data.ObjectNumber, indirectReference.Data.Generation, contents, Stream); return(indirectReference); }
public void Flush(decimal version, IndirectReferenceToken catalogReference) { if (catalogReference == null) { throw new ArgumentNullException(nameof(catalogReference)); } WriteString($"%PDF-{version:0.0}", Stream); Stream.WriteText("%"); Stream.WriteByte(169); Stream.WriteByte(205); Stream.WriteByte(196); Stream.WriteByte(210); Stream.WriteNewLine(); var offsets = new Dictionary <IndirectReference, long>(); ObjectToken catalogToken = null; foreach (var pair in tokenReferences) { var referenceToken = pair.Key; var token = pair.Value; var offset = Stream.Position; var obj = new ObjectToken(offset, referenceToken.Data, token); TokenWriter.WriteToken(obj, Stream); offsets.Add(referenceToken.Data, offset); if (catalogToken == null && referenceToken == catalogReference) { catalogToken = obj; } } if (catalogToken == null) { throw new Exception("Catalog object wasn't found"); } // TODO: Support document information TokenWriter.WriteCrossReferenceTable(offsets, catalogToken, Stream, null); }
/// <summary> /// Adds the image previously added using <see cref="AddJpeg(byte[], PdfRectangle)"/> /// or <see cref="AddPng(byte[], PdfRectangle)"/> sharing the same image to prevent duplication. /// </summary> public void AddImage(AddedImage image, PdfRectangle placementRectangle) { var resources = pageDictionary.GetOrCreateDict(NameToken.Resources); var xObjects = resources.GetOrCreateDict(NameToken.Xobject); var key = NameToken.Create($"I{imageKey++}"); xObjects[key] = new IndirectReferenceToken(image.Reference); currentStream.Add(Push.Value); // This needs to be the placement rectangle. currentStream.Add(new ModifyCurrentTransformationMatrix(new[] { (decimal)placementRectangle.Width, 0, 0, (decimal)placementRectangle.Height, (decimal)placementRectangle.BottomLeft.X, (decimal)placementRectangle.BottomLeft.Y })); currentStream.Add(new InvokeNamedXObject(key)); currentStream.Add(Pop.Value); }
public IndirectReferenceToken WriteFont(IPdfStreamWriter writer, IndirectReferenceToken reservedIndirect = null) { var dictionary = new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Font }, { NameToken.Subtype, NameToken.Type1 }, { NameToken.BaseFont, NameToken.Create(metrics.FontName) }, { NameToken.Encoding, NameToken.MacRomanEncoding } }; var token = new DictionaryToken(dictionary); if (reservedIndirect != null) { return(writer.WriteToken(token, reservedIndirect)); } var result = writer.WriteToken(token); return(result); }
/// <summary> /// Builds a PDF document from the current content of this builder and its pages. /// </summary> /// <returns>The bytes of the resulting PDF document.</returns> public byte[] Build() { var context = new BuilderContext(); var fontsWritten = new Dictionary <Guid, ObjectToken>(); using (var memory = new MemoryStream()) { // Header WriteString("%PDF-1.7", memory); // Files with binary data should contain a 2nd comment line followed by 4 bytes with values > 127 memory.WriteText("%"); memory.WriteByte(169); memory.WriteByte(205); memory.WriteByte(196); memory.WriteByte(210); memory.WriteNewLine(); // Body foreach (var font in fonts) { var fontObj = font.Value.FontProgram.WriteFont(font.Value.FontKey.Name, memory, context); fontsWritten.Add(font.Key, fontObj); } var resources = new Dictionary <NameToken, IToken> { { NameToken.ProcSet, new ArrayToken(new [] { NameToken.Create("PDF"), NameToken.Create("Text") }) } }; if (fontsWritten.Count > 0) { var fontsDictionary = new DictionaryToken(fontsWritten.Select(x => (fonts[x.Key].FontKey.Name, (IToken) new IndirectReferenceToken(x.Value.Number))) .ToDictionary(x => x.Item1, x => x.Item2)); var fontsDictionaryRef = context.WriteObject(memory, fontsDictionary); resources.Add(NameToken.Font, new IndirectReferenceToken(fontsDictionaryRef.Number)); } var reserved = context.ReserveNumber(); var parentIndirect = new IndirectReferenceToken(new IndirectReference(reserved, 0)); var pageReferences = new List <IndirectReferenceToken>(); foreach (var page in pages) { var pageDictionary = new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Page }, { NameToken.Resources, new DictionaryToken(resources) }, { NameToken.MediaBox, RectangleToArray(page.Value.PageSize) }, { NameToken.Parent, parentIndirect } }; if (page.Value.Operations.Count > 0) { var contentStream = WriteContentStream(page.Value.Operations); var contentStreamObj = context.WriteObject(memory, contentStream); pageDictionary[NameToken.Contents] = new IndirectReferenceToken(contentStreamObj.Number); } var pageRef = context.WriteObject(memory, new DictionaryToken(pageDictionary)); pageReferences.Add(new IndirectReferenceToken(pageRef.Number)); } var pagesDictionary = new DictionaryToken(new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Pages }, { NameToken.Kids, new ArrayToken(pageReferences) }, { NameToken.Count, new NumericToken(pageReferences.Count) } }); var pagesRef = context.WriteObject(memory, pagesDictionary, reserved); var catalog = new DictionaryToken(new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Catalog }, { NameToken.Pages, new IndirectReferenceToken(pagesRef.Number) } }); var catalogRef = context.WriteObject(memory, catalog); var informationReference = default(IndirectReference?); if (IncludeDocumentInformation) { var informationDictionary = DocumentInformation.ToDictionary(); if (informationDictionary.Count > 0) { var dictionary = new DictionaryToken(informationDictionary); informationReference = context.WriteObject(memory, dictionary).Number; } } TokenWriter.WriteCrossReferenceTable(context.ObjectOffsets, catalogRef, memory, informationReference); return(memory.ToArray()); } }
public void WriteToken(IndirectReferenceToken referenceToken, IToken token) { tokenReferences.Add(referenceToken, token); }
public IndirectReferenceToken WriteFont(IPdfStreamWriter writer, IndirectReferenceToken reservedIndirect = null) { var newEncoding = new TrueTypeSubsetEncoding(characterMapping.Keys.ToList()); var subsetBytes = TrueTypeSubsetter.Subset(fontFileBytes.ToArray(), newEncoding); var embeddedFile = DataCompresser.CompressToStream(subsetBytes); var fileRef = writer.WriteToken(embeddedFile); var baseFont = NameToken.Create(font.TableRegister.NameTable.GetPostscriptName()); var postscript = font.TableRegister.PostScriptTable; var hhead = font.TableRegister.HorizontalHeaderTable; var bbox = font.TableRegister.HeaderTable.Bounds; var scaling = 1000m / font.TableRegister.HeaderTable.UnitsPerEm; var descriptorDictionary = new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.FontDescriptor }, { NameToken.FontName, baseFont }, // TODO: get flags TrueTypeEmbedder.java { NameToken.Flags, new NumericToken((int)FontDescriptorFlags.Symbolic) }, { NameToken.FontBbox, GetBoundingBox(bbox, scaling) }, { NameToken.ItalicAngle, new NumericToken((decimal)postscript.ItalicAngle) }, { NameToken.Ascent, new NumericToken(Math.Round(hhead.Ascent * scaling, 2)) }, { NameToken.Descent, new NumericToken(Math.Round(hhead.Descent * scaling, 2)) }, { NameToken.CapHeight, new NumericToken(90) }, { NameToken.StemV, new NumericToken(90) }, { NameToken.FontFile2, fileRef } }; var os2 = font.TableRegister.Os2Table; if (os2 == null) { throw new InvalidFontFormatException("Embedding TrueType font requires OS/2 table."); } if (os2 is Os2Version2To4OpenTypeTable twoPlus) { descriptorDictionary[NameToken.CapHeight] = new NumericToken(twoPlus.CapHeight); descriptorDictionary[NameToken.Xheight] = new NumericToken(twoPlus.XHeight); } descriptorDictionary[NameToken.StemV] = new NumericToken(((decimal)bbox.Width) * scaling * 0.13m); var lastCharacter = 0; var widths = new List <NumericToken> { NumericToken.Zero }; foreach (var kvp in characterMapping) { if (kvp.Value > lastCharacter) { lastCharacter = kvp.Value; } var glyphId = font.WindowsUnicodeCMap.CharacterCodeToGlyphIndex(kvp.Key); var width = decimal.Round(font.TableRegister.HorizontalMetricsTable.GetAdvanceWidth(glyphId) * scaling, 2); widths.Add(new NumericToken(width)); } var descriptor = writer.WriteToken(new DictionaryToken(descriptorDictionary)); var toUnicodeCMap = ToUnicodeCMapBuilder.ConvertToCMapStream(characterMapping); var toUnicodeStream = DataCompresser.CompressToStream(toUnicodeCMap); var toUnicode = writer.WriteToken(toUnicodeStream); var dictionary = new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Font }, { NameToken.Subtype, NameToken.TrueType }, { NameToken.BaseFont, baseFont }, { NameToken.FontDescriptor, descriptor }, { NameToken.FirstChar, new NumericToken(0) }, { NameToken.LastChar, new NumericToken(lastCharacter) }, { NameToken.Widths, new ArrayToken(widths) }, { NameToken.ToUnicode, toUnicode } }; var token = new DictionaryToken(dictionary); if (reservedIndirect != null) { return(writer.WriteToken(token, reservedIndirect)); } return(writer.WriteToken(token)); }
public bool MoveNext() { if (isDisposed) { throw new ObjectDisposedException(nameof(PdfTokenScanner)); } // Read until we find object-number generation obj, e.g. "69 420 obj". int tokensRead = 0; while (coreTokenScanner.MoveNext() && !Equals(coreTokenScanner.CurrentToken, OperatorToken.StartObject)) { if (coreTokenScanner.CurrentToken is CommentToken) { continue; } tokensRead++; previousTokens[0] = previousTokens[1]; previousTokenPositions[0] = previousTokenPositions[1]; previousTokens[1] = coreTokenScanner.CurrentToken; previousTokenPositions[1] = coreTokenScanner.CurrentTokenStart; } // We only read partial tokens. if (tokensRead < 2) { return(false); } var startPosition = previousTokenPositions[0]; var objectNumber = previousTokens[0] as NumericToken; var generation = previousTokens[1] as NumericToken; if (objectNumber == null || generation == null) { // Handle case where the scanner correctly reads most of an object token but includes too much of the first token // specifically %%EOF1 0 obj where scanning starts from 'F'. if (generation != null && previousTokens[0] is OperatorToken op) { var match = EndsWithNumberRegex.Match(op.Data); if (match.Success && int.TryParse(match.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var number)) { startPosition = previousTokenPositions[0] + match.Index; objectNumber = new NumericToken(number); } else { return(false); } } else { return(false); } } // Read all tokens between obj and endobj. while (coreTokenScanner.MoveNext() && !Equals(coreTokenScanner.CurrentToken, OperatorToken.EndObject)) { if (coreTokenScanner.CurrentToken is CommentToken) { continue; } if (ReferenceEquals(coreTokenScanner.CurrentToken, OperatorToken.StartObject)) { // This should never happen. Debug.Assert(false, "Encountered a start object 'obj' operator before the end of the previous object."); return(false); } if (ReferenceEquals(coreTokenScanner.CurrentToken, OperatorToken.StartStream)) { var streamIdentifier = new IndirectReference(objectNumber.Long, generation.Int); // Prevent an infinite loop where a stream's length references the stream or the stream's offset. var getLengthFromFile = !(callingObject.HasValue && callingObject.Value.Equals(streamIdentifier)); var outerCallingObject = callingObject; try { callingObject = streamIdentifier; // Read stream: special case. if (TryReadStream(coreTokenScanner.CurrentTokenStart, getLengthFromFile, out var stream)) { readTokens.Clear(); readTokens.Add(stream); } } finally { callingObject = outerCallingObject; } } else { readTokens.Add(coreTokenScanner.CurrentToken); } previousTokens[0] = previousTokens[1]; previousTokenPositions[0] = previousTokenPositions[1]; previousTokens[1] = coreTokenScanner.CurrentToken; previousTokenPositions[1] = coreTokenScanner.CurrentPosition; } if (!ReferenceEquals(coreTokenScanner.CurrentToken, OperatorToken.EndObject)) { readTokens.Clear(); return(false); } var reference = new IndirectReference(objectNumber.Long, generation.Int); IToken token; if (readTokens.Count == 3 && readTokens[0] is NumericToken objNum && readTokens[1] is NumericToken genNum && ReferenceEquals(readTokens[2], OperatorToken.R)) { // I have no idea if this can ever happen. token = new IndirectReferenceToken(new IndirectReference(objNum.Long, genNum.Int)); }
public AcroForm GetAcroForm(Catalog catalog) { if (!catalog.CatalogDictionary.TryGet(NameToken.AcroForm, out var acroRawToken)) { return(null); } if (!DirectObjectFinder.TryGet(acroRawToken, tokenScanner, out DictionaryToken acroDictionary)) { var fieldsRefs = new List <IndirectReferenceToken>(); // Invalid reference, try constructing the form from a Brute Force scan. foreach (var reference in crossReferenceTable.ObjectOffsets.Keys) { var referenceToken = new IndirectReferenceToken(reference); if (!DirectObjectFinder.TryGet(referenceToken, tokenScanner, out DictionaryToken dict)) { continue; } if (dict.TryGet(NameToken.Kids, tokenScanner, out ArrayToken _) && dict.TryGet(NameToken.T, tokenScanner, out StringToken _)) { fieldsRefs.Add(referenceToken); } } if (fieldsRefs.Count == 0) { return(null); } acroDictionary = new DictionaryToken(new Dictionary <NameToken, IToken> { { NameToken.Fields, new ArrayToken(fieldsRefs) } }); } var signatureFlags = (SignatureFlags)0; if (acroDictionary.TryGetOptionalTokenDirect(NameToken.SigFlags, tokenScanner, out NumericToken signatureToken)) { signatureFlags = (SignatureFlags)signatureToken.Int; } var needAppearances = false; if (acroDictionary.TryGetOptionalTokenDirect(NameToken.NeedAppearances, tokenScanner, out BooleanToken appearancesToken)) { needAppearances = appearancesToken.Data; } var calculationOrder = default(ArrayToken); acroDictionary.TryGetOptionalTokenDirect(NameToken.Co, tokenScanner, out calculationOrder); var formResources = default(DictionaryToken); acroDictionary.TryGetOptionalTokenDirect(NameToken.Dr, tokenScanner, out formResources); var da = default(string); if (acroDictionary.TryGetOptionalTokenDirect(NameToken.Da, tokenScanner, out StringToken daToken)) { da = daToken.Data; } else if (acroDictionary.TryGetOptionalTokenDirect(NameToken.Da, tokenScanner, out HexToken daHexToken)) { da = daHexToken.Data; } var q = default(int?); if (acroDictionary.TryGetOptionalTokenDirect(NameToken.Q, tokenScanner, out NumericToken qToken)) { q = qToken.Int; } if (!acroDictionary.TryGet(NameToken.Fields, tokenScanner, out ArrayToken fieldsArray)) { return(null); } var fields = new Dictionary <IndirectReference, AcroFieldBase>(fieldsArray.Length); foreach (var fieldToken in fieldsArray.Data) { if (!(fieldToken is IndirectReferenceToken fieldReferenceToken)) { throw new PdfDocumentFormatException($"The fields array should only contain indirect references, instead got: {fieldToken}."); } var fieldDictionary = DirectObjectFinder.Get <DictionaryToken>(fieldToken, tokenScanner); var field = GetAcroField(fieldDictionary, catalog, new List <DictionaryToken>(0)); fields[fieldReferenceToken.Data] = field; } return(new AcroForm(acroDictionary, signatureFlags, needAppearances, fields)); }
public DocumentMerger() { rootPagesReference = context.ReserveNumberToken(); }
public CopiedContentStream(IndirectReferenceToken indirectReferenceToken) { token = indirectReferenceToken; }
/// <summary> /// Builds a PDF document from the current content of this builder and its pages. /// </summary> /// <returns>The bytes of the resulting PDF document.</returns> public byte[] Build() { var fontsWritten = new Dictionary <Guid, ObjectToken>(); using (var memory = new MemoryStream()) { // Header WriteString("%PDF-1.7", memory); // Files with binary data should contain a 2nd comment line followed by 4 bytes with values > 127 memory.WriteText("%"); memory.WriteByte(169); memory.WriteByte(205); memory.WriteByte(196); memory.WriteByte(210); memory.WriteNewLine(); // Body foreach (var font in fonts) { var fontObj = font.Value.FontProgram.WriteFont(font.Value.FontKey.Name, memory, context); fontsWritten.Add(font.Key, fontObj); } foreach (var image in images) { var streamToken = new StreamToken(image.Value.StreamDictionary, image.Value.StreamData); context.WriteObject(memory, streamToken, image.Value.ObjectNumber); } var procSet = new List <NameToken> { NameToken.Create("PDF"), NameToken.Text, NameToken.ImageB, NameToken.ImageC, NameToken.ImageI }; var resources = new Dictionary <NameToken, IToken> { { NameToken.ProcSet, new ArrayToken(procSet) } }; if (fontsWritten.Count > 0) { var fontsDictionary = new DictionaryToken(fontsWritten.Select(x => (fonts[x.Key].FontKey.Name, (IToken) new IndirectReferenceToken(x.Value.Number))) .ToDictionary(x => x.Item1, x => x.Item2)); var fontsDictionaryRef = context.WriteObject(memory, fontsDictionary); resources.Add(NameToken.Font, new IndirectReferenceToken(fontsDictionaryRef.Number)); } var reserved = context.ReserveNumber(); var parentIndirect = new IndirectReferenceToken(new IndirectReference(reserved, 0)); var pageReferences = new List <IndirectReferenceToken>(); foreach (var page in pages) { var individualResources = new Dictionary <NameToken, IToken>(resources); var pageDictionary = new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Page }, { NameToken.MediaBox, RectangleToArray(page.Value.PageSize) }, { NameToken.Parent, parentIndirect } }; if (page.Value.Resources.Count > 0) { foreach (var kvp in page.Value.Resources) { // TODO: combine resources if value is dictionary or array, otherwise overwrite. individualResources[kvp.Key] = kvp.Value; } } pageDictionary[NameToken.Resources] = new DictionaryToken(individualResources); if (page.Value.Operations.Count > 0) { var contentStream = WriteContentStream(page.Value.Operations); var contentStreamObj = context.WriteObject(memory, contentStream); pageDictionary[NameToken.Contents] = new IndirectReferenceToken(contentStreamObj.Number); } var pageRef = context.WriteObject(memory, new DictionaryToken(pageDictionary)); pageReferences.Add(new IndirectReferenceToken(pageRef.Number)); } var pagesDictionaryData = new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Pages }, { NameToken.Kids, new ArrayToken(pageReferences) }, { NameToken.Count, new NumericToken(pageReferences.Count) } }; var pagesDictionary = new DictionaryToken(pagesDictionaryData); var pagesRef = context.WriteObject(memory, pagesDictionary, reserved); var catalogDictionary = new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Catalog }, { NameToken.Pages, new IndirectReferenceToken(pagesRef.Number) } }; if (ArchiveStandard != PdfAStandard.None) { Func <IToken, ObjectToken> writerFunc = x => context.WriteObject(memory, x); PdfABaselineRuleBuilder.Obey(catalogDictionary, writerFunc, DocumentInformation, ArchiveStandard); switch (ArchiveStandard) { case PdfAStandard.A1A: PdfA1ARuleBuilder.Obey(catalogDictionary); break; case PdfAStandard.A2B: break; case PdfAStandard.A2A: PdfA1ARuleBuilder.Obey(catalogDictionary); break; } } var catalog = new DictionaryToken(catalogDictionary); var catalogRef = context.WriteObject(memory, catalog); var informationReference = default(IndirectReference?); if (IncludeDocumentInformation) { var informationDictionary = DocumentInformation.ToDictionary(); if (informationDictionary.Count > 0) { var dictionary = new DictionaryToken(informationDictionary); informationReference = context.WriteObject(memory, dictionary).Number; } } TokenWriter.WriteCrossReferenceTable(context.ObjectOffsets, catalogRef, memory, informationReference); return(memory.ToArray()); } }
public void CompletePdf(IndirectReferenceToken catalogReference, IndirectReferenceToken documentInformationReference = null) { TokenWriter.WriteCrossReferenceTable(offsets, catalogReference.Data, Stream, documentInformationReference?.Data); }
public bool MoveNext() { // Read until we find object-number generation obj, e.g. "69 420 obj". int tokensRead = 0; while (coreTokenScanner.MoveNext() && !Equals(coreTokenScanner.CurrentToken, OperatorToken.StartObject)) { if (coreTokenScanner.CurrentToken is CommentToken) { continue; } tokensRead++; previousTokens[0] = previousTokens[1]; previousTokenPositions[0] = previousTokenPositions[1]; previousTokens[1] = coreTokenScanner.CurrentToken; previousTokenPositions[1] = coreTokenScanner.CurrentTokenStart; } // We only read partial tokens. if (tokensRead < 2) { return(false); } var startPosition = previousTokenPositions[0]; var objectNumber = previousTokens[0] as NumericToken; var generation = previousTokens[1] as NumericToken; if (objectNumber == null || generation == null) { throw new PdfDocumentFormatException("The obj operator (start object) was not preceded by a 2 numbers." + $"Instead got: {previousTokens[0]} {previousTokens[1]} obj"); } // Read all tokens between obj and endobj. while (coreTokenScanner.MoveNext() && !Equals(coreTokenScanner.CurrentToken, OperatorToken.EndObject)) { if (coreTokenScanner.CurrentToken is CommentToken) { continue; } if (ReferenceEquals(coreTokenScanner.CurrentToken, OperatorToken.StartObject)) { // This should never happen. Debug.Assert(false, "Encountered a start object 'obj' operator before the end of the previous object."); return(false); } if (ReferenceEquals(coreTokenScanner.CurrentToken, OperatorToken.StartStream)) { // Read stream: special case. if (TryReadStream(coreTokenScanner.CurrentTokenStart, out var stream)) { readTokens.Clear(); readTokens.Add(stream); } } else { readTokens.Add(coreTokenScanner.CurrentToken); } previousTokens[0] = previousTokens[1]; previousTokenPositions[0] = previousTokenPositions[1]; previousTokens[1] = coreTokenScanner.CurrentToken; previousTokenPositions[1] = coreTokenScanner.CurrentPosition; } if (!ReferenceEquals(coreTokenScanner.CurrentToken, OperatorToken.EndObject)) { readTokens.Clear(); return(false); } var reference = new IndirectReference(objectNumber.Long, generation.Int); IToken token; if (readTokens.Count == 3 && readTokens[0] is NumericToken objNum && readTokens[1] is NumericToken genNum && ReferenceEquals(readTokens[2], OperatorToken.R)) { // I have no idea if this can ever happen. token = new IndirectReferenceToken(new IndirectReference(objNum.Long, genNum.Int)); }
public IFont GetFontDirectly(IndirectReferenceToken fontReferenceToken, bool isLenientParsing) { return(null); }
/// <summary> /// The purpose of this method is to resolve indirect reference. That mean copy the reference's content to the new document's stream /// and replace the indirect reference with the correct/new one /// </summary> /// <param name="tokenToCopy">Token to inspect for reference</param> /// <param name="tokenScanner">scanner get the content from the original document</param> /// <returns>A reference of the token that was copied. With all the reference updated</returns> internal IToken CopyToken(IToken tokenToCopy, IPdfTokenScanner tokenScanner) { // This token need to be deep copied, because they could contain reference. So we have to update them. switch (tokenToCopy) { case DictionaryToken dictionaryToken: { var newContent = new Dictionary <NameToken, IToken>(); foreach (var setPair in dictionaryToken.Data) { var name = setPair.Key; var token = setPair.Value; newContent.Add(NameToken.Create(name), CopyToken(token, tokenScanner)); } return(new DictionaryToken(newContent)); } case ArrayToken arrayToken: { var newArray = new List <IToken>(arrayToken.Length); foreach (var token in arrayToken.Data) { newArray.Add(CopyToken(token, tokenScanner)); } return(new ArrayToken(newArray)); } case IndirectReferenceToken referenceToken: { var tokenObject = DirectObjectFinder.Get <IToken>(referenceToken.Data, tokenScanner); Debug.Assert(!(tokenObject is IndirectReferenceToken)); var newToken = CopyToken(tokenObject, tokenScanner); var reserved = context.ReserveNumber(); var newReference = new IndirectReferenceToken(new IndirectReference(reserved, 0)); unwrittenTokens.Add(newReference, newToken); return(newReference); } case StreamToken streamToken: { var properties = CopyToken(streamToken.StreamDictionary, tokenScanner) as DictionaryToken; Debug.Assert(properties != null); var bytes = streamToken.Data; return(new StreamToken(properties, bytes)); } case ObjectToken _: { // Since we don't write token directly to the stream. // We can't know the offset. Therefore the token would be invalid throw new NotSupportedException("Copying a Object token is not supported"); } } return(tokenToCopy); }
/// <summary> /// Adds the PNG image represented by the input stream at the specified location. /// </summary> public AddedImage AddPng(Stream pngStream, PdfRectangle placementRectangle) { var png = Png.Open(pngStream); byte[] data; var pixelBuffer = new byte[3]; using (var memoryStream = new MemoryStream()) { for (var rowIndex = 0; rowIndex < png.Height; rowIndex++) { for (var colIndex = 0; colIndex < png.Width; colIndex++) { var pixel = png.GetPixel(colIndex, rowIndex); pixelBuffer[0] = pixel.R; pixelBuffer[1] = pixel.G; pixelBuffer[2] = pixel.B; memoryStream.Write(pixelBuffer, 0, pixelBuffer.Length); } } data = memoryStream.ToArray(); } var widthToken = new NumericToken(png.Width); var heightToken = new NumericToken(png.Height); IndirectReferenceToken smaskReference = null; if (png.HasAlphaChannel && documentBuilder.ArchiveStandard != PdfAStandard.A1B && documentBuilder.ArchiveStandard != PdfAStandard.A1A) { var smaskData = new byte[data.Length / 3]; for (var rowIndex = 0; rowIndex < png.Height; rowIndex++) { for (var colIndex = 0; colIndex < png.Width; colIndex++) { var pixel = png.GetPixel(colIndex, rowIndex); var index = rowIndex * png.Width + colIndex; smaskData[index] = pixel.A; } } var compressedSmask = DataCompresser.CompressBytes(smaskData); // Create a soft-mask. var smaskDictionary = new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Xobject }, { NameToken.Subtype, NameToken.Image }, { NameToken.Width, widthToken }, { NameToken.Height, heightToken }, { NameToken.ColorSpace, NameToken.Devicegray }, { NameToken.BitsPerComponent, new NumericToken(png.Header.BitDepth) }, { NameToken.Decode, new ArrayToken(new IToken[] { new NumericToken(0), new NumericToken(1) }) }, { NameToken.Length, new NumericToken(compressedSmask.Length) }, { NameToken.Filter, NameToken.FlateDecode } }; smaskReference = documentBuilder.AddImage(new DictionaryToken(smaskDictionary), compressedSmask); } var compressed = DataCompresser.CompressBytes(data); var imgDictionary = new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Xobject }, { NameToken.Subtype, NameToken.Image }, { NameToken.Width, widthToken }, { NameToken.Height, heightToken }, { NameToken.BitsPerComponent, new NumericToken(png.Header.BitDepth) }, { NameToken.ColorSpace, NameToken.Devicergb }, { NameToken.Filter, NameToken.FlateDecode }, { NameToken.Length, new NumericToken(compressed.Length) } }; if (smaskReference != null) { imgDictionary.Add(NameToken.Smask, smaskReference); } var reference = documentBuilder.AddImage(new DictionaryToken(imgDictionary), compressed); var resources = pageDictionary.GetOrCreateDict(NameToken.Resources); var xObjects = resources.GetOrCreateDict(NameToken.Xobject); var key = NameToken.Create($"I{imageKey++}"); xObjects[key] = reference; currentStream.Add(Push.Value); // This needs to be the placement rectangle. currentStream.Add(new ModifyCurrentTransformationMatrix(new[] { (decimal)placementRectangle.Width, 0, 0, (decimal)placementRectangle.Height, (decimal)placementRectangle.BottomLeft.X, (decimal)placementRectangle.BottomLeft.Y })); currentStream.Add(new InvokeNamedXObject(key)); currentStream.Add(Pop.Value); return(new AddedImage(reference.Data, png.Width, png.Height)); }
/// <summary> /// Create a new <see cref="AddedFont"/>. /// </summary> internal AddedFont(Guid id, IndirectReferenceToken reference) { Id = id; Reference = reference; }
/// <summary> /// Create a new <see cref="DescriptorFontFile"/>. /// </summary> public DescriptorFontFile(IndirectReferenceToken key, FontFileType fileType) { ObjectKey = key; FileType = fileType; }
public DocumentMerger(Stream baseStream) { context = new PdfStreamWriter(baseStream, false); rootPagesReference = context.ReserveNumberToken(); }