/// <summary> /// Adds a TrueType font to the builder so that pages in this document can use it. /// </summary> /// <param name="fontFileBytes">The bytes of a TrueType font.</param> /// <returns>An identifier which can be passed to <see cref="PdfPageBuilder.AddText"/>.</returns> public AddedFont AddTrueTypeFont(IReadOnlyList <byte> fontFileBytes) { try { var font = TrueTypeFontParser.Parse(new TrueTypeDataBytes(new ByteArrayInputBytes(fontFileBytes))); var id = Guid.NewGuid(); var added = new AddedFont(id, context.ReserveObjectNumber()); fonts[id] = new FontStored(added, new TrueTypeWritingFont(font, fontFileBytes)); return(added); } catch (Exception ex) { throw new InvalidOperationException("Writing only supports TrueType fonts, please provide a valid TrueType font.", ex); } }
/// <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="writer">PDF stream writer</param> /// <param name="tokenToCopy">Token to inspect for reference</param> /// <param name="tokenScanner">scanner get the content from the original document</param> /// <param name="referencesFromDocument">Map of previously copied tokens for original document.</param> /// <param name="callstack">Call stack of indirect references</param> /// <returns>A reference of the token that was copied. With all the reference updated</returns> public static IToken CopyToken(IPdfStreamWriter writer, IToken tokenToCopy, IPdfTokenScanner tokenScanner, IDictionary <IndirectReference, IndirectReferenceToken> referencesFromDocument, Dictionary <IndirectReference, IndirectReferenceToken> callstack = null) { if (callstack == null) { callstack = new Dictionary <IndirectReference, IndirectReferenceToken>(); } // 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(writer, token, tokenScanner, referencesFromDocument, callstack)); } return(new DictionaryToken(newContent)); } case ArrayToken arrayToken: { var newArray = new List <IToken>(arrayToken.Length); foreach (var token in arrayToken.Data) { newArray.Add(CopyToken(writer, token, tokenScanner, referencesFromDocument, callstack)); } return(new ArrayToken(newArray)); } case IndirectReferenceToken referenceToken: { if (referencesFromDocument.TryGetValue(referenceToken.Data, out var newReferenceToken)) { return(newReferenceToken); } if (callstack.ContainsKey(referenceToken.Data) && callstack[referenceToken.Data] == null) { newReferenceToken = writer.ReserveObjectNumber(); callstack[referenceToken.Data] = newReferenceToken; referencesFromDocument.Add(referenceToken.Data, newReferenceToken); return(newReferenceToken); } callstack.Add(referenceToken.Data, null); // we add the token to referencesFromDocument to prevent stackoverflow on references cycles // newReferenceToken = context.ReserveNumberToken(); // callstack.Add(newReferenceToken.Data.ObjectNumber); // referencesFromDocument.Add(referenceToken.Data, newReferenceToken); // var tokenObject = DirectObjectFinder.Get <IToken>(referenceToken.Data, tokenScanner); if (tokenObject is null) //NullToken allowed { return(null); } Debug.Assert(!(tokenObject is IndirectReferenceToken)); var result = CopyToken(writer, tokenObject, tokenScanner, referencesFromDocument, callstack); if (callstack[referenceToken.Data] != null) { return(writer.WriteToken(result, callstack[referenceToken.Data])); } newReferenceToken = writer.WriteToken(result); referencesFromDocument.Add(referenceToken.Data, newReferenceToken); return(newReferenceToken); } case StreamToken streamToken: { var properties = CopyToken(writer, streamToken.StreamDictionary, tokenScanner, referencesFromDocument, callstack) 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); }