private static bool CheckIfIsPage(DictionaryToken nodeDictionary, IndirectReference parentReference, bool isRoot, IPdfTokenScanner pdfTokenScanner, bool isLenientParsing) { var isPage = false; if (!nodeDictionary.TryGet(NameToken.Type, pdfTokenScanner, out NameToken type)) { if (!isLenientParsing) { throw new PdfDocumentFormatException($"Node in the document pages tree did not define a type: {nodeDictionary}."); } if (!nodeDictionary.TryGet(NameToken.Kids, pdfTokenScanner, out ArrayToken _)) { isPage = true; } } else { isPage = type.Equals(NameToken.Page); if (!isPage && !type.Equals(NameToken.Pages) && !isLenientParsing) { throw new PdfDocumentFormatException($"Node in the document pages tree defined invalid type: {nodeDictionary}."); } } if (!isLenientParsing && !isRoot) { if (!nodeDictionary.TryGet(NameToken.Parent, pdfTokenScanner, out IndirectReferenceToken parentReferenceToken)) { throw new PdfDocumentFormatException($"Could not find parent indirect reference token on pages tree node: {nodeDictionary}."); } if (!parentReferenceToken.Data.Equals(parentReference)) { throw new PdfDocumentFormatException($"Pages tree node parent reference {parentReferenceToken.Data} did not match actual parent {parentReference}."); } } return(isPage); }
private static (IndirectReference, DictionaryToken) ParseTrailer(CrossReferenceTable crossReferenceTable, bool isLenientParsing, IPdfTokenScanner pdfTokenScanner, out EncryptionDictionary encryptionDictionary) { encryptionDictionary = null; if (crossReferenceTable.Trailer.EncryptionToken != null) { if (!DirectObjectFinder.TryGet(crossReferenceTable.Trailer.EncryptionToken, pdfTokenScanner, out DictionaryToken encryptionDictionaryToken)) { throw new PdfDocumentFormatException($"Unrecognized encryption token in trailer: {crossReferenceTable.Trailer.EncryptionToken}."); } encryptionDictionary = EncryptionDictionaryFactory.Read(encryptionDictionaryToken, pdfTokenScanner); } var rootDictionary = DirectObjectFinder.Get <DictionaryToken>(crossReferenceTable.Trailer.Root, pdfTokenScanner); if (!rootDictionary.ContainsKey(NameToken.Type) && isLenientParsing) { rootDictionary = rootDictionary.With(NameToken.Type, NameToken.Catalog); } return(crossReferenceTable.Trailer.Root, rootDictionary); }
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); }
private static bool TryGetAction(DictionaryToken actionDictionary, Catalog catalog, IPdfTokenScanner pdfScanner, IReadOnlyDictionary <string, ExplicitDestination> namedDestinations, ILog log, out (bool isExternal, string externalFileName, ExplicitDestination destination) result)
public IReadOnlyList <IFilter> GetFilters(DictionaryToken dictionary, IPdfTokenScanner scanner) { return(new List <IFilter>()); }
public InlineImage CreateInlineImage(TransformationMatrix transformationMatrix, IFilterProvider filterProvider, IPdfTokenScanner tokenScanner, RenderingIntent defaultRenderingIntent, IResourceStore resourceStore) { if (Properties == null || Bytes == null) { throw new InvalidOperationException($"Inline image builder not completely defined before calling {nameof(CreateInlineImage)}."); } bool TryMapColorSpace(NameToken name, out ColorSpace colorSpaceResult) { if (name.TryMapToColorSpace(out colorSpaceResult)) { return(true); } if (TryExtendedColorSpaceNameMapping(name, out colorSpaceResult)) { return(true); } if (!resourceStore.TryGetNamedColorSpace(name, out var colorSpaceNamedToken)) { return(false); } if (colorSpaceNamedToken.Name.TryMapToColorSpace(out colorSpaceResult)) { return(true); } if (TryExtendedColorSpaceNameMapping(colorSpaceNamedToken.Name, out colorSpaceResult)) { return(true); } return(false); } var bounds = transformationMatrix.Transform(new PdfRectangle(new PdfPoint(1, 1), new PdfPoint(0, 0))); var width = GetByKeys <NumericToken>(NameToken.Width, NameToken.W, true).Int; var height = GetByKeys <NumericToken>(NameToken.Height, NameToken.H, true).Int; var maskToken = GetByKeys <BooleanToken>(NameToken.ImageMask, NameToken.Im, false); var isMask = maskToken?.Data == true; var bitsPerComponent = GetByKeys <NumericToken>(NameToken.BitsPerComponent, NameToken.Bpc, !isMask)?.Int ?? 1; var colorSpace = default(ColorSpace?); if (!isMask) { var colorSpaceName = GetByKeys <NameToken>(NameToken.ColorSpace, NameToken.Cs, false); if (colorSpaceName == null) { var colorSpaceArray = GetByKeys <ArrayToken>(NameToken.ColorSpace, NameToken.Cs, true); if (colorSpaceArray.Length == 0) { throw new PdfDocumentFormatException("Empty ColorSpace array defined for inline image."); } if (!(colorSpaceArray.Data[0] is NameToken firstColorSpaceName)) { throw new PdfDocumentFormatException($"Invalid ColorSpace array defined for inline image: {colorSpaceArray}."); } if (!TryMapColorSpace(firstColorSpaceName, out var colorSpaceMapped)) { throw new PdfDocumentFormatException($"Invalid ColorSpace defined for inline image: {firstColorSpaceName}."); } colorSpace = colorSpaceMapped; } else { if (!TryMapColorSpace(colorSpaceName, out var colorSpaceMapped)) { throw new PdfDocumentFormatException($"Invalid ColorSpace defined for inline image: {colorSpaceName}."); } colorSpace = colorSpaceMapped; } } var renderingIntent = GetByKeys <NameToken>(NameToken.Intent, null, false)?.Data?.ToRenderingIntent() ?? defaultRenderingIntent; var filterNames = new List <NameToken>(); var filterName = GetByKeys <NameToken>(NameToken.Filter, NameToken.F, false); if (filterName == null) { var filterArray = GetByKeys <ArrayToken>(NameToken.Filter, NameToken.F, false); if (filterArray != null) { filterNames.AddRange(filterArray.Data.OfType <NameToken>()); } } else { filterNames.Add(filterName); } var filters = filterProvider.GetNamedFilters(filterNames); var decodeRaw = GetByKeys <ArrayToken>(NameToken.Decode, NameToken.D, false) ?? new ArrayToken(EmptyArray <IToken> .Instance); var decode = decodeRaw.Data.OfType <NumericToken>().Select(x => x.Data).ToArray(); var filterDictionaryEntries = new Dictionary <NameToken, IToken>(); var decodeParamsDict = GetByKeys <DictionaryToken>(NameToken.DecodeParms, NameToken.Dp, false); if (decodeParamsDict == null) { var decodeParamsArray = GetByKeys <ArrayToken>(NameToken.DecodeParms, NameToken.Dp, false); if (decodeParamsArray != null) { filterDictionaryEntries[NameToken.DecodeParms] = decodeParamsArray; } } else { filterDictionaryEntries[NameToken.DecodeParms] = decodeParamsDict; } var streamDictionary = new DictionaryToken(filterDictionaryEntries); var interpolate = GetByKeys <BooleanToken>(NameToken.Interpolate, NameToken.I, false)?.Data ?? false; return(new InlineImage(bounds, width, height, bitsPerComponent, isMask, renderingIntent, interpolate, colorSpace, decode, Bytes, filters, streamDictionary)); }
public BookmarksProvider(ILog log, IPdfTokenScanner pdfScanner) { this.log = log; this.pdfScanner = pdfScanner; }
public static EncryptionDictionary Read(DictionaryToken encryptionDictionary, IPdfTokenScanner tokenScanner) { if (encryptionDictionary == null) { throw new ArgumentNullException(nameof(encryptionDictionary)); } var filter = encryptionDictionary.Get <NameToken>(NameToken.Filter, tokenScanner); var code = EncryptionAlgorithmCode.Unrecognized; if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.V, tokenScanner, out NumericToken vNum)) { code = (EncryptionAlgorithmCode)vNum.Int; } var length = default(int?); if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.Length, tokenScanner, out NumericToken lengthToken)) { length = lengthToken.Int; } var revision = default(int); if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.R, tokenScanner, out NumericToken revisionToken)) { revision = revisionToken.Int; } byte[] ownerBytes = null; if (encryptionDictionary.TryGet(NameToken.O, out IToken ownerToken)) { if (ownerToken is StringToken ownerString) { ownerBytes = ownerString.GetBytes(); } else if (ownerToken is HexToken ownerHex) { ownerBytes = ownerHex.Bytes.ToArray(); } } byte[] userBytes = null; if (encryptionDictionary.TryGet(NameToken.U, out IToken userToken)) { if (userToken is StringToken userString) { userBytes = userString.GetBytes(); } else if (userToken is HexToken userHex) { userBytes = userHex.Bytes.ToArray(); } } var access = default(UserAccessPermissions); if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.P, tokenScanner, out NumericToken accessToken)) { access = (UserAccessPermissions)accessToken.Int; } byte[] userEncryptionBytes = null, ownerEncryptionBytes = null; if (revision >= 5) { ownerEncryptionBytes = GetEncryptionBytesOrDefault(encryptionDictionary, tokenScanner, false); userEncryptionBytes = GetEncryptionBytesOrDefault(encryptionDictionary, tokenScanner, true); } encryptionDictionary.TryGetOptionalTokenDirect(NameToken.EncryptMetaData, tokenScanner, out BooleanToken encryptMetadata); return(new EncryptionDictionary(filter.Data, code, length, revision, ownerBytes, userBytes, ownerEncryptionBytes, userEncryptionBytes, access, encryptionDictionary, encryptMetadata?.Data ?? true)); }
private static byte[] GetEncryptionBytesOrDefault(DictionaryToken encryptionDictionary, IPdfTokenScanner tokenScanner, bool isUser) { var name = isUser ? NameToken.Ue : NameToken.Oe; if (encryptionDictionary.TryGet(name, tokenScanner, out StringToken stringToken)) { return(OtherEncodings.StringAsLatin1Bytes(stringToken.Data)); } if (encryptionDictionary.TryGet(name, tokenScanner, out HexToken hexToken)) { return(hexToken.Bytes.ToArray()); } return(null); }
public static bool TryGetOptionalStringDirect(this DictionaryToken token, NameToken name, IPdfTokenScanner scanner, out string result) { result = default(string); if (token.TryGetOptionalTokenDirect(name, scanner, out StringToken stringToken)) { result = stringToken.Data; return(true); } if (token.TryGetOptionalTokenDirect(name, scanner, out HexToken hexToken)) { result = hexToken.Data; return(true); } return(false); }
internal Pages(ILog log, Catalog catalog, IPageFactory pageFactory, bool isLenientParsing, IPdfTokenScanner pdfScanner) { if (catalog == null) { throw new ArgumentNullException(nameof(catalog)); } rootPageDictionary = catalog.PagesDictionary; Count = rootPageDictionary.GetIntOrDefault(NameToken.Count); this.log = log; this.catalog = catalog; this.pageFactory = pageFactory; this.isLenientParsing = isLenientParsing; this.pdfScanner = pdfScanner; }
public static bool TryGetOptionalTokenDirect <T>(this DictionaryToken token, NameToken name, IPdfTokenScanner scanner, out T result) where T : IToken { result = default(T); if (token.TryGet(name, out var appearancesToken) && DirectObjectFinder.TryGet(appearancesToken, scanner, out T innerResult)) { result = innerResult; return(true); } return(false); }
public static IReadOnlyDictionary <string, TResult> FlattenNameTreeToDictionary <TResult>(DictionaryToken nameTreeNodeDictionary, IPdfTokenScanner pdfScanner, bool isLenientParsing, Func <IToken, TResult> valuesFactory) where TResult : class { var result = new Dictionary <string, TResult>(); FlattenNameTree(nameTreeNodeDictionary, pdfScanner, isLenientParsing, valuesFactory, result); return(result); }
private static PageTreeNode ProcessPagesNode(IndirectReference referenceInput, DictionaryToken nodeDictionaryInput, IndirectReference parentReferenceInput, bool isRoot, IPdfTokenScanner pdfTokenScanner, bool isLenientParsing, PageCounter pageNumber) { bool isPage = CheckIfIsPage(nodeDictionaryInput, parentReferenceInput, isRoot, pdfTokenScanner, isLenientParsing); if (isPage) { pageNumber.Increment(); return(new PageTreeNode(nodeDictionaryInput, referenceInput, true, pageNumber.PageCount).WithChildren(EmptyArray <PageTreeNode> .Instance)); } //If we got here, we have to iterate till we manage to exit var toProcess = new Queue <(PageTreeNode thisPage, IndirectReference reference, DictionaryToken nodeDictionary, IndirectReference parentReference, List <PageTreeNode> nodeChildren)>(); var firstPage = new PageTreeNode(nodeDictionaryInput, referenceInput, false, null); var setChildren = new List <Action>(); var firstPageChildren = new List <PageTreeNode>(); setChildren.Add(() => firstPage.WithChildren(firstPageChildren)); toProcess.Enqueue( (thisPage: firstPage, reference: referenceInput, nodeDictionary: nodeDictionaryInput, parentReference: parentReferenceInput, nodeChildren: firstPageChildren)); do { var current = toProcess.Dequeue(); if (!current.nodeDictionary.TryGet(NameToken.Kids, pdfTokenScanner, out ArrayToken kids)) { if (!isLenientParsing) { throw new PdfDocumentFormatException($"Pages node in the document pages tree did not define a kids array: {current.nodeDictionary}."); } kids = new ArrayToken(EmptyArray <IToken> .Instance); } foreach (var kid in kids.Data) { if (!(kid is IndirectReferenceToken kidRef)) { throw new PdfDocumentFormatException($"Kids array contained invalid entry (must be indirect reference): {kid}."); } if (!DirectObjectFinder.TryGet(kidRef, pdfTokenScanner, out DictionaryToken kidDictionaryToken)) { throw new PdfDocumentFormatException($"Could not find dictionary associated with reference in pages kids array: {kidRef}."); } bool isChildPage = CheckIfIsPage(kidDictionaryToken, current.reference, false, pdfTokenScanner, isLenientParsing); if (isChildPage) { pageNumber.Increment(); var kidPageNode = new PageTreeNode(kidDictionaryToken, kidRef.Data, true, pageNumber.PageCount).WithChildren(EmptyArray <PageTreeNode> .Instance); current.nodeChildren.Add(kidPageNode); } else { var kidChildNode = new PageTreeNode(kidDictionaryToken, kidRef.Data, false, null); var kidChildren = new List <PageTreeNode>(); toProcess.Enqueue( (thisPage: kidChildNode, reference: kidRef.Data, nodeDictionary: kidDictionaryToken, parentReference: current.reference, nodeChildren: kidChildren)); setChildren.Add(() => kidChildNode.WithChildren(kidChildren)); current.nodeChildren.Add(kidChildNode); } } } while (toProcess.Count > 0); foreach (var action in setChildren) { action(); } return(firstPage); }
private static DictionaryToken ParseTrailer(CrossReferenceTable crossReferenceTable, bool isLenientParsing, IPdfTokenScanner pdfTokenScanner) { if (crossReferenceTable.Dictionary.ContainsKey(NameToken.Encrypt)) { throw new NotSupportedException("Cannot currently parse a document using encryption: " + crossReferenceTable.Dictionary); } if (!crossReferenceTable.Dictionary.TryGet(NameToken.Root, out var rootToken)) { throw new PdfDocumentFormatException($"Missing root object specification in trailer: {crossReferenceTable.Dictionary}."); } var rootDictionary = DirectObjectFinder.Get <DictionaryToken>(rootToken, pdfTokenScanner); if (!rootDictionary.ContainsKey(NameToken.Type) && isLenientParsing) { rootDictionary = rootDictionary.With(NameToken.Type, NameToken.Catalog); } return(rootDictionary); }
public MarkedContentElement Build(IPdfTokenScanner pdfScanner) { var mcid = -1; if (properties.TryGet(NameToken.Mcid, pdfScanner, out NumericToken mcidToken)) { mcid = mcidToken.Int; } var language = GetOptional(NameToken.Lang, pdfScanner); var actualText = GetOptional(NameToken.ActualText, pdfScanner); var alternateDescription = GetOptional(NameToken.Alternate, pdfScanner); var expandedForm = GetOptional(NameToken.E, pdfScanner); if (name != NameToken.Artifact) { return(new MarkedContentElement(mcid, name, properties, language, actualText, alternateDescription, expandedForm, false, Children, letters, paths, images, number)); } var artifactType = ArtifactMarkedContentElement.ArtifactType.Unknown; if (properties.TryGet(NameToken.Type, pdfScanner, out IDataToken <string> typeToken) && Enum.TryParse(typeToken.Data, true, out ArtifactMarkedContentElement.ArtifactType parsedType)) { artifactType = parsedType; } var subType = GetOptional(NameToken.Subtype, pdfScanner); var attributeOwners = GetOptional(NameToken.O, pdfScanner); var boundingBox = default(PdfRectangle?); if (properties.TryGet(NameToken.Bbox, pdfScanner, out ArrayToken arrayToken) && arrayToken.Length == 6) { var left = arrayToken[2] as NumericToken; var bottom = arrayToken[3] as NumericToken; var right = arrayToken[4] as NumericToken; var top = arrayToken[5] as NumericToken; if (left != null && bottom != null && right != null && top != null) { boundingBox = new PdfRectangle(left.Double, bottom.Double, right.Double, top.Double); } } var attached = new List <NameToken>(); if (properties.TryGet(NameToken.Attached, out ArrayToken attachedToken)) { foreach (var token in attachedToken.Data) { if (token is NameToken aName) { attached.Add(aName); } } } return(new ArtifactMarkedContentElement(mcid, name, properties, language, actualText, alternateDescription, expandedForm, artifactType, subType, attributeOwners, boundingBox, attached, Children, letters, paths, images, number)); }
public CrossReferenceTable Parse(IInputBytes bytes, bool isLenientParsing, long crossReferenceLocation, long offsetCorrection, IPdfTokenScanner pdfScanner, ISeekableTokenScanner tokenScanner) { long fixedOffset = offsetValidator.CheckXRefOffset(crossReferenceLocation, tokenScanner, bytes, isLenientParsing); if (fixedOffset > -1) { crossReferenceLocation = fixedOffset; log.Debug($"Found the first cross reference table or stream at {fixedOffset}."); } var table = new CrossReferenceTableBuilder(); var prevSet = new HashSet <long>(); long previousCrossReferenceLocation = crossReferenceLocation; var missedAttempts = 0; // Parse all cross reference tables and streams. while (previousCrossReferenceLocation > 0 && missedAttempts < 100) { log.Debug($"Reading cross reference table or stream at {previousCrossReferenceLocation}."); if (previousCrossReferenceLocation >= bytes.Length) { break; } // seek to xref table tokenScanner.Seek(previousCrossReferenceLocation); tokenScanner.MoveNext(); if (tokenScanner.CurrentToken is OperatorToken tableToken && tableToken.Data == "xref") { missedAttempts = 0; log.Debug("Element was cross reference table."); CrossReferenceTablePart tablePart = crossReferenceTableParser.Parse(tokenScanner, previousCrossReferenceLocation, isLenientParsing); var nextOffset = tablePart.GetPreviousOffset(); if (nextOffset >= 0) { nextOffset += offsetCorrection; } previousCrossReferenceLocation = nextOffset; DictionaryToken tableDictionary = tablePart.Dictionary; CrossReferenceTablePart streamPart = null; // check for a XRef stream, it may contain some object ids of compressed objects if (tableDictionary.ContainsKey(NameToken.XrefStm)) { log.Debug("Cross reference table contained referenced to stream. Reading the stream."); int streamOffset = ((NumericToken)tableDictionary.Data[NameToken.XrefStm]).Int; // check the xref stream reference fixedOffset = offsetValidator.CheckXRefOffset(streamOffset, tokenScanner, bytes, isLenientParsing); if (fixedOffset > -1 && fixedOffset != streamOffset) { log.Warn($"/XRefStm offset {streamOffset} is incorrect, corrected to {fixedOffset}"); streamOffset = (int)fixedOffset; // Update the cross reference table to be a stream instead. tableDictionary = tableDictionary.With(NameToken.XrefStm, new NumericToken(streamOffset)); tablePart = new CrossReferenceTablePart(tablePart.ObjectOffsets, streamOffset, tablePart.Previous, tableDictionary, tablePart.Type); } // Read the stream from the table. if (streamOffset > 0) { try { TryParseCrossReferenceStream(streamOffset, pdfScanner, out streamPart); } catch (InvalidOperationException ex) { if (isLenientParsing) { log.Error("Failed to parse /XRefStm at offset " + streamOffset, ex); } else { throw; } } } else { if (isLenientParsing) { log.Error("Skipped XRef stream due to a corrupt offset:" + streamOffset); } else { throw new PdfDocumentFormatException("Skipped XRef stream due to a corrupt offset:" + streamOffset); } } } table.Add(tablePart); if (streamPart != null) { table.Add(streamPart); } }
internal OptionalContentGroupElement(MarkedContentElement markedContentElement, IPdfTokenScanner pdfTokenScanner) { MarkedContent = markedContentElement; // Type - Required if (markedContentElement.Properties.TryGet(NameToken.Type, pdfTokenScanner, out NameToken type)) { Type = type.Data; } else if (markedContentElement.Properties.TryGet(NameToken.Type, pdfTokenScanner, out StringToken typeStr)) { Type = typeStr.Data; } else { throw new ArgumentException($"Cannot parse optional content's {nameof(Type)} from {nameof(markedContentElement.Properties)}. This is a required field.", nameof(markedContentElement.Properties)); } switch (Type) { case "OCG": // Optional content group dictionary // Name - Required if (markedContentElement.Properties.TryGet(NameToken.Name, pdfTokenScanner, out NameToken name)) { Name = name.Data; } else if (markedContentElement.Properties.TryGet(NameToken.Name, pdfTokenScanner, out StringToken nameStr)) { Name = nameStr.Data; } else { throw new ArgumentException($"Cannot parse optional content's {nameof(Name)} from {nameof(markedContentElement.Properties)}. This is a required field.", nameof(markedContentElement.Properties)); } // Intent - Optional if (markedContentElement.Properties.TryGet(NameToken.Intent, pdfTokenScanner, out NameToken intentName)) { Intent = new string[] { intentName.Data }; } else if (markedContentElement.Properties.TryGet(NameToken.Intent, pdfTokenScanner, out StringToken intentStr)) { Intent = new string[] { intentStr.Data }; } else if (markedContentElement.Properties.TryGet(NameToken.Intent, pdfTokenScanner, out ArrayToken intentArray)) { List <string> intentList = new List <string>(); foreach (var token in intentArray.Data) { if (token is NameToken nameA) { intentList.Add(nameA.Data); } else if (token is StringToken strA) { intentList.Add(strA.Data); } else { throw new NotImplementedException(); } } Intent = intentList; } else { // Default value is 'View'. Intent = new string[] { "View" }; } // Usage - Optional if (markedContentElement.Properties.TryGet(NameToken.Usage, pdfTokenScanner, out DictionaryToken usage)) { this.Usage = usage.Data; } break; case "OCMD": // OCGs - Optional if (markedContentElement.Properties.TryGet(NameToken.Ocgs, pdfTokenScanner, out DictionaryToken ocgsD)) { // dictionary or array throw new NotImplementedException($"{NameToken.Ocgs}"); } else if (markedContentElement.Properties.TryGet(NameToken.Ocgs, pdfTokenScanner, out ArrayToken ocgsA)) { // dictionary or array throw new NotImplementedException($"{NameToken.Ocgs}"); } // P - Optional if (markedContentElement.Properties.TryGet(NameToken.P, pdfTokenScanner, out NameToken p)) { throw new NotImplementedException($"{NameToken.P}"); } // VE - Optional if (markedContentElement.Properties.TryGet(NameToken.VE, pdfTokenScanner, out ArrayToken ve)) { throw new NotImplementedException($"{NameToken.VE}"); } break; default: throw new ArgumentException($"Unknown Optional Content of type '{Type}' not known.", nameof(Type)); } }
private static IReadOnlyDictionary <string, ExplicitDestination> ReadNamedDestinations(Catalog catalog, IPdfTokenScanner pdfScanner, ILog log) { var result = new Dictionary <string, ExplicitDestination>(); if (catalog.CatalogDictionary.TryGet(NameToken.Dests, pdfScanner, out DictionaryToken dests)) { /* * In PDF 1.1, the correspondence between name objects and destinations is defined by the /Dests entry in the document catalog. * The value of this entry is a dictionary in which each key is a destination name and the corresponding value is either an array * defining the destination, using the explicit destination syntax, or a dictionary with a /D entry whose value is such an array. */ foreach (var kvp in dests.Data) { var value = kvp.Value; if (TryReadExplicitDestination(value, catalog, pdfScanner, log, out var destination)) { result[kvp.Key] = destination; } } } else if (catalog.CatalogDictionary.TryGet(NameToken.Names, pdfScanner, out DictionaryToken names) && names.TryGet(NameToken.Dests, pdfScanner, out dests)) { /* * In PDF 1.2, the correspondence between strings and destinations is defined by the /Dests entry in the document's name dictionary. * The value of the /Dests entry is a name tree mapping name strings to destinations. * The keys in the name tree may be treated as text strings for display purposes. * The destination value associated with a key in the name tree may be either an array or a dictionary. */ NameTreeParser.FlattenNameTree(dests, pdfScanner, value => { if (TryReadExplicitDestination(value, catalog, pdfScanner, log, out var destination)) { return(destination); } return(null); }, result); } return(result); }
public static ColorSpaceDetails GetColorSpaceDetails(ColorSpace?colorSpace, DictionaryToken imageDictionary, IPdfTokenScanner scanner, IResourceStore resourceStore, IFilterProvider filterProvider, bool cannotRecurse = false) { if (!colorSpace.HasValue) { return(UnsupportedColorSpaceDetails.Instance); } switch (colorSpace.Value) { case ColorSpace.DeviceGray: return(DeviceGrayColorSpaceDetails.Instance); case ColorSpace.DeviceRGB: return(DeviceRgbColorSpaceDetails.Instance); case ColorSpace.DeviceCMYK: return(DeviceCmykColorSpaceDetails.Instance); case ColorSpace.CalGray: return(UnsupportedColorSpaceDetails.Instance); case ColorSpace.CalRGB: return(UnsupportedColorSpaceDetails.Instance); case ColorSpace.Lab: return(UnsupportedColorSpaceDetails.Instance); case ColorSpace.ICCBased: return(UnsupportedColorSpaceDetails.Instance); case ColorSpace.Indexed: { if (cannotRecurse) { return(UnsupportedColorSpaceDetails.Instance); } if (!imageDictionary.TryGet(NameToken.ColorSpace, scanner, out ArrayToken colorSpaceArray) || colorSpaceArray.Length != 4) { // Error instead? return(UnsupportedColorSpaceDetails.Instance); } var first = colorSpaceArray[0] as NameToken; if (first == null || !ColorSpaceMapper.TryMap(first, resourceStore, out var innerColorSpace) || innerColorSpace != ColorSpace.Indexed) { return(UnsupportedColorSpaceDetails.Instance); } var second = colorSpaceArray[1]; ColorSpaceDetails baseDetails; if (DirectObjectFinder.TryGet(second, scanner, out NameToken baseColorSpaceNameToken) && ColorSpaceMapper.TryMap(baseColorSpaceNameToken, resourceStore, out var baseColorSpaceName)) { baseDetails = GetColorSpaceDetails( baseColorSpaceName, imageDictionary, scanner, resourceStore, filterProvider, true); } else if (DirectObjectFinder.TryGet(second, scanner, out ArrayToken baseColorSpaceArrayToken) && baseColorSpaceArrayToken.Length > 0 && baseColorSpaceArrayToken[0] is NameToken baseColorSpaceArrayNameToken && ColorSpaceMapper.TryMap(baseColorSpaceArrayNameToken, resourceStore, out var baseColorSpaceArrayColorSpace)) { var pseudoImageDictionary = new DictionaryToken( new Dictionary <NameToken, IToken> { { NameToken.ColorSpace, baseColorSpaceArrayToken } }); baseDetails = GetColorSpaceDetails( baseColorSpaceArrayColorSpace, pseudoImageDictionary, scanner, resourceStore, filterProvider, true); }
private static bool TryReadExplicitDestination(IToken value, Catalog catalog, IPdfTokenScanner pdfScanner, ILog log, out ExplicitDestination destination) { destination = null; if (DirectObjectFinder.TryGet(value, pdfScanner, out ArrayToken valueArray) && TryGetExplicitDestination(valueArray, catalog, log, out destination)) { return(true); } if (DirectObjectFinder.TryGet(value, pdfScanner, out DictionaryToken valueDictionary) && valueDictionary.TryGet(NameToken.D, pdfScanner, out valueArray) && TryGetExplicitDestination(valueArray, catalog, log, out destination)) { return(true); } return(false); }
/// <summary> /// Try and get the entry with a given name and type or look-up the object if it's an indirect reference. /// </summary> internal static bool TryGet <T>(this DictionaryToken dictionary, NameToken name, IPdfTokenScanner tokenScanner, out T token) where T : IToken { token = default(T); if (!dictionary.TryGet(name, out var t) || !(t is T typedToken)) { if (t is IndirectReferenceToken reference) { return(DirectObjectFinder.TryGet(reference, tokenScanner, out token)); } return(false); } token = typedToken; return(true); }
public ResourceContainer(IPdfTokenScanner scanner, IFontFactory fontFactory) { this.scanner = scanner; this.fontFactory = fontFactory; }
internal static IReadOnlyList <byte> Decode(this StreamToken stream, ILookupFilterProvider filterProvider, IPdfTokenScanner scanner) { var filters = filterProvider.GetFilters(stream.StreamDictionary, scanner); var transform = stream.Data; for (var i = 0; i < filters.Count; i++) { transform = filters[i].Decode(transform, stream.StreamDictionary, i); } return(transform); }
public AcroFormFactory(IPdfTokenScanner tokenScanner, IFilterProvider filterProvider, CrossReferenceTable crossReferenceTable) { this.tokenScanner = tokenScanner ?? throw new ArgumentNullException(nameof(tokenScanner)); this.filterProvider = filterProvider ?? throw new ArgumentNullException(nameof(filterProvider)); this.crossReferenceTable = crossReferenceTable ?? throw new ArgumentNullException(nameof(crossReferenceTable)); }
/// <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> private 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: { if (referencesFromDocument.TryGetValue(referenceToken, out var newReferenceToken)) { return(newReferenceToken); } var tokenObject = DirectObjectFinder.Get <IToken>(referenceToken.Data, tokenScanner); Debug.Assert(!(tokenObject is IndirectReferenceToken)); var newToken = CopyToken(tokenObject, tokenScanner); newReferenceToken = context.WriteToken(newToken); referencesFromDocument.Add(referenceToken, newReferenceToken); return(newReferenceToken); } 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); }
public CidFontFactory(IPdfTokenScanner pdfScanner, IFilterProvider filterProvider) { this.pdfScanner = pdfScanner; this.filterProvider = filterProvider; }
public static XObjectImage ReadImage(XObjectContentRecord xObject, IPdfTokenScanner pdfScanner, IFilterProvider 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); foreach (var filter in filters) { if (!filter.IsSupported) { supportsFilters = false; break; } } } var decodedBytes = supportsFilters ? new Lazy <IReadOnlyList <byte> >(() => xObject.Stream.Decode(filterProvider)) : 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; } } return(new XObjectImage(bounds, width, height, bitsPerComponent, colorSpace, isJpxDecode, isImageMask, intent, interpolate, decode, dictionary, xObject.Stream.Data, decodedBytes)); }
private ObjectToken CopyPageNode(PageTreeNode pageNode, IndirectReferenceToken parentPagesObject, IPdfTokenScanner tokenScanner) { 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)); } return(context.WriteObject(memory, new DictionaryToken(pageDictionary))); }
public void AppendDocument(Catalog catalog, decimal version, IPdfTokenScanner tokenScanner, IReadOnlyList <int> pages) { IEnumerable <int> pageIndices; if (pages == null) { var pagesCount = catalog.PagesDictionary.GetIntOrDefault(NameToken.Count); if (pagesCount < 1) { return; } pageIndices = Enumerable.Range(1, pagesCount); } else if (pages.Count < 1) { return; } else { pageIndices = pages; } currentVersion = Math.Max(version, currentVersion); var referencesFromDocument = new Dictionary <IndirectReference, IndirectReferenceToken>(); var currentNodeReference = context.ReserveNumberToken(); var pagesReferences = new List <IndirectReferenceToken>(); var resources = new Dictionary <string, IToken>(); bool DoesAEntryCollide(PageTreeNode node) { while (node != null) { var dictionary = node.NodeDictionary; if (dictionary.TryGet(NameToken.Resources, tokenScanner, out DictionaryToken resourcesDictionary)) { var nonCollidingResources = resourcesDictionary.Data.Keys.Except(resources.Keys); if (nonCollidingResources.Count() != resourcesDictionary.Data.Count) { // This means that at least one of the resources collided return(true); } } /* TODO: How to handle? * `Rotate` * `CropBox` * `MediaBox` */ // No colliding entry was found, in this node // Keep walking up into the tree node = node.Parent; } return(false); } void CopyEntries(PageTreeNode node) { while (node != null) { var dictionary = node.NodeDictionary; if (dictionary.TryGet(NameToken.Resources, tokenScanner, out DictionaryToken resourcesDictionary)) { foreach (var pair in resourcesDictionary.Data) { resources.Add(pair.Key, CopyToken(pair.Value, tokenScanner, referencesFromDocument)); } } /* TODO: How to handle? * `Rotate` * `CropBox` * `MediaBox` */ // Keep walking up into the tree node = node.Parent; } } void CreateTree() { if (pagesReferences.Count < 1) { throw new InvalidOperationException("Pages reference should always be more than 1 when executing this function"); } var newPagesNode = new Dictionary <NameToken, IToken> { { NameToken.Type, NameToken.Pages }, { NameToken.Kids, new ArrayToken(pagesReferences) }, { NameToken.Count, new NumericToken(pagesReferences.Count) }, { NameToken.Parent, rootPagesReference } }; if (resources.Count > 0) { newPagesNode.Add(NameToken.Resources, DictionaryToken.With(resources)); } var pagesDictionary = new DictionaryToken(newPagesNode); pagesTokenReferences.Add(context.WriteToken(pagesDictionary, (int)currentNodeReference.Data.ObjectNumber)); pageCount += pagesReferences.Count; }; foreach (var pageIndex in pageIndices) { var pageNode = catalog.GetPageNode(pageIndex); if (pagesReferences.Count >= ARTIFICIAL_NODE_LIMIT || DoesAEntryCollide(pageNode)) { CreateTree(); currentNodeReference = context.ReserveNumberToken(); pagesReferences = new List <IndirectReferenceToken>(); resources = new Dictionary <string, IToken>(); } CopyEntries(pageNode.Parent); pagesReferences.Add(CopyPageNode(pageNode, currentNodeReference, tokenScanner, referencesFromDocument)); } if (pagesReferences.Count < 1) { throw new InvalidOperationException("Pages reference couldn't be less than 1 because we have reserved a indirect reference token"); } CreateTree(); }