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); }
public IndirectReferenceToken Write(IPdfStreamWriter writer) { using (var memoryStream = new MemoryStream()) { foreach (var operation in operations) { operation.Write(memoryStream); } var bytes = memoryStream.ToArray(); var stream = DataCompresser.CompressToStream(bytes); return(writer.WriteToken(stream)); } }
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)); }
internal IndirectReferenceToken AddImage(DictionaryToken dictionary, byte[] bytes) { var streamToken = new StreamToken(dictionary, bytes); return(context.WriteToken(streamToken)); }
/// <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); }