public static ArrayToken GetOutputIntentsArray(Func <IToken, ObjectToken> objectWriter) { var rgbColorCondition = new StringToken(SrgbIec61966OutputCondition); var profileBytes = ProfileStreamReader.GetSRgb2014(); var compressedBytes = DataCompresser.CompressBytes(profileBytes); var profileStreamDictionary = new Dictionary <NameToken, IToken> { { NameToken.Length, new NumericToken(compressedBytes.Length) }, { NameToken.N, new NumericToken(3) }, { NameToken.Filter, NameToken.FlateDecode } }; var stream = new StreamToken(new DictionaryToken(profileStreamDictionary), compressedBytes); var written = objectWriter(stream); return(new ArrayToken(new IToken[] { new DictionaryToken(new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.OutputIntent }, { NameToken.S, NameToken.GtsPdfa1 }, { NameToken.OutputCondition, rgbColorCondition }, { NameToken.OutputConditionIdentifier, rgbColorCondition }, { NameToken.RegistryName, new StringToken(RegistryName) }, { NameToken.Info, rgbColorCondition }, { NameToken.DestOutputProfile, new IndirectReferenceToken(written.Number) } }), })); }
public void ApplyXObject(StreamToken xObjectStream) { // For now we will determine the type and store the object with the graphics state information preceding it. // Then consumers of the page can request the object/s to be retrieved by type. var subType = (NameToken)xObjectStream.StreamDictionary.Data[NameToken.Subtype.Data]; var state = GetCurrentState(); var matrix = state.CurrentTransformationMatrix; if (subType.Equals(NameToken.Ps)) { xObjects[XObjectType.PostScript].Add(new XObjectContentRecord(XObjectType.PostScript, xObjectStream, matrix)); } else if (subType.Equals(NameToken.Image)) { xObjects[XObjectType.Image].Add(new XObjectContentRecord(XObjectType.Image, xObjectStream, matrix)); } else if (subType.Equals(NameToken.Form)) { xObjects[XObjectType.Form].Add(new XObjectContentRecord(XObjectType.Form, xObjectStream, matrix)); } else { throw new InvalidOperationException($"XObject encountered with unexpected SubType {subType}. {xObjectStream.StreamDictionary}."); } }
internal EmbeddedFile(string name, string fileSpecification, IReadOnlyList <byte> bytes, StreamToken stream) { Name = name ?? throw new ArgumentNullException(nameof(name)); FileSpecification = fileSpecification; Bytes = bytes ?? throw new ArgumentNullException(nameof(bytes)); Stream = stream ?? throw new ArgumentNullException(nameof(stream)); }
public XObjectContentRecord(XObjectType type, StreamToken stream, TransformationMatrix appliedTransformation, RenderingIntent defaultRenderingIntent) { Type = type; Stream = stream ?? throw new ArgumentNullException(nameof(stream)); AppliedTransformation = appliedTransformation; DefaultRenderingIntent = defaultRenderingIntent; }
private static void WriteStream(StreamToken streamToken, Stream outputStream) { WriteDictionary(streamToken.StreamDictionary, outputStream); WriteLineBreak(outputStream); outputStream.Write(StreamStart, 0, StreamStart.Length); WriteLineBreak(outputStream); outputStream.Write(streamToken.Data.ToArray(), 0, streamToken.Data.Count); WriteLineBreak(outputStream); outputStream.Write(StreamEnd, 0, StreamEnd.Length); }
public StreamToken GetToken() { RemoveExpiredTokens(); var token = new StreamToken() { Token = Guid.NewGuid().ToString(), ExpiresOn = DateTime.Now.AddHours(1) }; _tokens.Add(token); return(token); }
public static StreamToken CompressToStream(byte[] bytes) { var compressed = CompressBytes(bytes); var stream = new StreamToken(new DictionaryToken(new Dictionary <NameToken, IToken> { { NameToken.Length, new NumericToken(compressed.Length) }, { NameToken.Length1, new NumericToken(bytes.Length) }, { NameToken.Filter, new ArrayToken(new [] { NameToken.FlateDecode }) } }), compressed); return(stream); }
private void ProcessFormXObject(StreamToken formStream) { /* * When a form XObject is invoked the following should happen: * * 1. Save the current graphics state, as if by invoking the q operator. * 2. Concatenate the matrix from the form dictionary's Matrix entry with the current transformation matrix. * 3. Clip according to the form dictionary's BBox entry. * 4. Paint the graphics objects specified in the form's content stream. * 5. Restore the saved graphics state, as if by invoking the Q operator. */ var hasResources = formStream.StreamDictionary.TryGet <DictionaryToken>(NameToken.Resources, pdfScanner, out var formResources); if (hasResources) { resourceStore.LoadResourceDictionary(formResources, isLenientParsing); } // 1. Save current state. PushState(); var startState = GetCurrentState(); var formMatrix = TransformationMatrix.Identity; if (formStream.StreamDictionary.TryGet <ArrayToken>(NameToken.Matrix, pdfScanner, out var formMatrixToken)) { formMatrix = TransformationMatrix.FromArray(formMatrixToken.Data.OfType <NumericToken>().Select(x => (double)x.Data).ToArray()); } // 2. Update current transformation matrix. var resultingTransformationMatrix = startState.CurrentTransformationMatrix.Multiply(formMatrix); startState.CurrentTransformationMatrix = resultingTransformationMatrix; var contentStream = formStream.Decode(filterProvider); var operations = pageContentParser.Parse(pageNumber, new ByteArrayInputBytes(contentStream)); // 3. We don't respect clipping currently. // 4. Paint the objects. ProcessOperations(operations); // 5. Restore saved state. PopState(); if (hasResources) { resourceStore.UnloadResourceDictionary(); } }
internal static IReadOnlyList <byte> Decode(this StreamToken stream, IFilterProvider filterProvider) { var filters = filterProvider.GetFilters(stream.StreamDictionary); var transform = stream.Data; for (var i = 0; i < filters.Count; i++) { transform = filters[i].Decode(transform, stream.StreamDictionary, i); } return(transform); }
public void ReplacesObjects() { var path = IntegrationHelpers.GetDocumentPath("Single Page Simple - from inkscape.pdf"); using (var document = PdfDocument.Open(path)) { var dict = new Dictionary <NameToken, IToken>(); dict[NameToken.Length] = new NumericToken(0); var replacement = new StreamToken(new DictionaryToken(dict), new List <byte>()); var pg = document.Structure.Catalog.GetPageNode(1).NodeDictionary; var contents = pg.Data[NameToken.Contents] as IndirectReferenceToken; document.Advanced.ReplaceIndirectObject(contents.Data, replacement); var page = document.GetPage(1); Assert.Empty(page.Letters); } }
private static StreamToken WriteContentStream(IReadOnlyList <IGraphicsStateOperation> content) { using (var memoryStream = new MemoryStream()) { foreach (var operation in content) { operation.Write(memoryStream); } var bytes = memoryStream.ToArray(); var streamDictionary = new Dictionary <NameToken, IToken> { { NameToken.Length, new NumericToken(bytes.Length) } }; var stream = new StreamToken(new DictionaryToken(streamDictionary), bytes); return(stream); } }
internal IndirectReferenceToken AddImage(DictionaryToken dictionary, byte[] bytes) { var streamToken = new StreamToken(dictionary, bytes); return(context.WriteToken(streamToken)); }
/// <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()); } }
internal XmpMetadata(StreamToken stream, IFilterProvider filterProvider) { this.filterProvider = filterProvider ?? throw new ArgumentNullException(nameof(filterProvider)); MetadataStreamToken = stream ?? throw new ArgumentNullException(nameof(stream)); }
internal XmpMetadata(StreamToken stream, ILookupFilterProvider filterProvider, IPdfTokenScanner pdfTokenScanner) { this.filterProvider = filterProvider ?? throw new ArgumentNullException(nameof(filterProvider)); this.pdfTokenScanner = pdfTokenScanner; MetadataStreamToken = stream ?? throw new ArgumentNullException(nameof(stream)); }
public ObjectToken WriteFont(NameToken fontKeyName, Stream outputStream, BuilderContext context) { var bytes = CompressBytes(); var embeddedFile = new StreamToken(new DictionaryToken(new Dictionary <NameToken, IToken> { { NameToken.Length, new NumericToken(bytes.Length) }, { NameToken.Length1, new NumericToken(fontFileBytes.Count) }, { NameToken.Filter, new ArrayToken(new [] { NameToken.FlateDecode }) } }), bytes); var fileRef = context.WriteObject(outputStream, embeddedFile); var baseFont = NameToken.Create(font.TableRegister.NameTable.GetPostscriptName()); var charCodeToGlyphId = new CharacterCodeToGlyphIdMapper(font); 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(postscript.ItalicAngle) }, { NameToken.Ascent, new NumericToken(hhead.Ascender * scaling) }, { NameToken.Descent, new NumericToken(hhead.Descender * scaling) }, { NameToken.CapHeight, new NumericToken(90) }, { NameToken.StemV, new NumericToken(90) }, { NameToken.FontFile2, new IndirectReferenceToken(fileRef.Number) } }; 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(bbox.Width * scaling * 0.13m); var metrics = charCodeToGlyphId.GetMetrics(scaling); var widthsRef = context.WriteObject(outputStream, metrics.Widths); var descriptor = context.WriteObject(outputStream, new DictionaryToken(descriptorDictionary)); var dictionary = new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Font }, { NameToken.Subtype, NameToken.TrueType }, { NameToken.BaseFont, baseFont }, { NameToken.FontDescriptor, new IndirectReferenceToken(descriptor.Number) }, { NameToken.FirstChar, metrics.FirstChar }, { NameToken.LastChar, metrics.LastChar }, { NameToken.Widths, new IndirectReferenceToken(widthsRef.Number) }, { NameToken.Encoding, NameToken.WinAnsiEncoding } }; var token = new DictionaryToken(dictionary); var result = context.WriteObject(outputStream, token); return(result); }
/// <summary> /// Parses through the unfiltered stream and populates the xrefTable HashMap. /// </summary> public CrossReferenceTablePart Parse(long streamOffset, StreamToken stream) { var decoded = stream.Decode(filterProvider); var fieldSizes = new CrossReferenceStreamFieldSize(stream.StreamDictionary); var lineCount = decoded.Count / fieldSizes.LineLength; long previousOffset = -1; if (stream.StreamDictionary.TryGet(NameToken.Prev, out var prevToken) && prevToken is NumericToken prevNumeric) { previousOffset = prevNumeric.Long; } var builder = new CrossReferenceTablePartBuilder { Offset = streamOffset, Previous = previousOffset, Dictionary = stream.StreamDictionary, XRefType = CrossReferenceType.Stream }; var objectNumbers = GetObjectNumbers(stream.StreamDictionary); var lineNumber = 0; var lineBuffer = new byte[fieldSizes.LineLength]; foreach (var objectNumber in objectNumbers) { if (lineNumber >= lineCount) { break; } var byteOffset = lineNumber * fieldSizes.LineLength; for (var i = 0; i < fieldSizes.LineLength; i++) { lineBuffer[i] = decoded[byteOffset + i]; } int type; if (fieldSizes.Field1Size == 0) { type = 1; } else { type = 0; for (var i = 0; i < fieldSizes.Field1Size; i++) { type += (lineBuffer[i] & 0x00ff) << ((fieldSizes.Field1Size - i - 1) * 8); } } ReadNextStreamObject(type, objectNumber, fieldSizes, builder, lineBuffer); lineNumber++; } return(builder.Build()); }
public static XObjectImage ReadImage(XObjectContentRecord xObject, IPdfTokenScanner pdfScanner, ILookupFilterProvider filterProvider, IResourceStore resourceStore) { if (xObject == null) { throw new ArgumentNullException(nameof(xObject)); } if (xObject.Type != XObjectType.Image) { throw new InvalidOperationException($"Cannot create an image from an XObject with type: {xObject.Type}."); } var dictionary = xObject.Stream.StreamDictionary; var bounds = xObject.AppliedTransformation.Transform(new PdfRectangle(new PdfPoint(0, 0), new PdfPoint(1, 1))); var width = dictionary.Get <NumericToken>(NameToken.Width, pdfScanner).Int; var height = dictionary.Get <NumericToken>(NameToken.Height, pdfScanner).Int; var isImageMask = dictionary.TryGet(NameToken.ImageMask, pdfScanner, out BooleanToken isMaskToken) && isMaskToken.Data; var isJpxDecode = dictionary.TryGet(NameToken.Filter, out var token) && token is NameToken filterName && filterName.Equals(NameToken.JpxDecode); int bitsPerComponent = 0; if (!isImageMask && !isJpxDecode) { if (!dictionary.TryGet(NameToken.BitsPerComponent, pdfScanner, out NumericToken bitsPerComponentToken)) { throw new PdfDocumentFormatException($"No bits per component defined for image: {dictionary}."); } bitsPerComponent = bitsPerComponentToken.Int; } else if (isImageMask) { bitsPerComponent = 1; } var intent = xObject.DefaultRenderingIntent; if (dictionary.TryGet(NameToken.Intent, out NameToken renderingIntentToken)) { intent = renderingIntentToken.Data.ToRenderingIntent(); } var interpolate = dictionary.TryGet(NameToken.Interpolate, pdfScanner, out BooleanToken interpolateToken) && interpolateToken.Data; DictionaryToken filterDictionary = xObject.Stream.StreamDictionary; if (xObject.Stream.StreamDictionary.TryGet(NameToken.Filter, out var filterToken) && filterToken is IndirectReferenceToken) { if (filterDictionary.TryGet(NameToken.Filter, pdfScanner, out ArrayToken filterArray)) { filterDictionary = filterDictionary.With(NameToken.Filter, filterArray); } else if (filterDictionary.TryGet(NameToken.Filter, pdfScanner, out NameToken filterNameToken)) { filterDictionary = filterDictionary.With(NameToken.Filter, filterNameToken); } else { filterDictionary = null; } } var supportsFilters = filterDictionary != null; if (filterDictionary != null) { var filters = filterProvider.GetFilters(filterDictionary, pdfScanner); foreach (var filter in filters) { if (!filter.IsSupported) { supportsFilters = false; break; } } } var decodeParams = dictionary.GetObjectOrDefault(NameToken.DecodeParms, NameToken.Dp); if (decodeParams is IndirectReferenceToken refToken) { dictionary = dictionary.With(NameToken.DecodeParms, pdfScanner.Get(refToken.Data).Data); } var streamToken = new StreamToken(dictionary, xObject.Stream.Data); var decodedBytes = supportsFilters ? new Lazy <IReadOnlyList <byte> >(() => streamToken.Decode(filterProvider, pdfScanner)) : null; var decode = EmptyArray <decimal> .Instance; if (dictionary.TryGet(NameToken.Decode, pdfScanner, out ArrayToken decodeArrayToken)) { decode = decodeArrayToken.Data.OfType <NumericToken>() .Select(x => x.Data) .ToArray(); } var colorSpace = default(ColorSpace?); if (!isImageMask) { if (dictionary.TryGet(NameToken.ColorSpace, pdfScanner, out NameToken colorSpaceNameToken) && TryMapColorSpace(colorSpaceNameToken, resourceStore, out var colorSpaceResult)) { colorSpace = colorSpaceResult; } else if (dictionary.TryGet(NameToken.ColorSpace, pdfScanner, out ArrayToken colorSpaceArrayToken) && colorSpaceArrayToken.Length > 0) { var first = colorSpaceArrayToken.Data[0]; if ((first is NameToken firstColorSpaceName) && TryMapColorSpace(firstColorSpaceName, resourceStore, out colorSpaceResult)) { colorSpace = colorSpaceResult; } } else if (!isJpxDecode) { colorSpace = xObject.DefaultColorSpace; } } var details = ColorSpaceDetailsParser.GetColorSpaceDetails(colorSpace, dictionary, pdfScanner, resourceStore, filterProvider); return(new XObjectImage( bounds, width, height, bitsPerComponent, colorSpace, isJpxDecode, isImageMask, intent, interpolate, decode, dictionary, xObject.Stream.Data, decodedBytes, details)); }
public void ApplyXObject(StreamToken xObjectStream) { }
private IToken DecryptInternal(IndirectReference reference, IToken token) { switch (token) { case StreamToken stream: { if (cryptHandler?.StreamDictionary?.IsIdentity == true || cryptHandler?.StreamDictionary?.Name == CryptDictionary.Method.None) { // TODO: No idea if this is right. return(token); } if (stream.StreamDictionary.TryGet(NameToken.Type, out NameToken typeName)) { if (NameToken.Xref.Equals(typeName)) { return(token); } if (!encryptionDictionary.EncryptMetadata && NameToken.Metadata.Equals(typeName)) { return(token); } // TODO: check unencrypted metadata } var streamDictionary = (DictionaryToken)DecryptInternal(reference, stream.StreamDictionary); var decrypted = DecryptData(stream.Data.ToArray(), reference); token = new StreamToken(streamDictionary, decrypted); break; } case StringToken stringToken: { if (cryptHandler?.StringDictionary?.IsIdentity == true || cryptHandler?.StringDictionary?.Name == CryptDictionary.Method.None) { // TODO: No idea if this is right. return(token); } var data = OtherEncodings.StringAsLatin1Bytes(stringToken.Data); var decrypted = DecryptData(data, reference); token = GetStringTokenFromDecryptedData(decrypted); break; } case HexToken hexToken: { var data = hexToken.Bytes.ToArray(); var decrypted = DecryptData(data, reference); token = new HexToken(Hex.GetString(decrypted).ToCharArray()); break; } case DictionaryToken dictionary: { // PDFBOX-2936: avoid orphan /CF dictionaries found in US govt "I-" files if (dictionary.TryGet(NameToken.Cf, out _)) { return(token); } var isSignatureDictionary = dictionary.TryGet(NameToken.Type, out NameToken typeName) && (typeName.Equals(NameToken.Sig) || typeName.Equals(NameToken.DocTimeStamp)); foreach (var keyValuePair in dictionary.Data) { if (isSignatureDictionary && keyValuePair.Key == NameToken.Contents.Data) { continue; } if (keyValuePair.Value is StringToken || keyValuePair.Value is ArrayToken || keyValuePair.Value is DictionaryToken || keyValuePair.Value is HexToken) { var inner = DecryptInternal(reference, keyValuePair.Value); dictionary = dictionary.With(keyValuePair.Key, inner); } } token = dictionary; break; } case ArrayToken array: { var result = new IToken[array.Length]; for (var i = 0; i < array.Length; i++) { result[i] = DecryptInternal(reference, array.Data[i]); } token = new ArrayToken(result); break; } } return(token); }