private static void BuildEntriesSection <T>( bytes::IBuffer buffer, EntryTypeEnum entryType, IList <T> items, BuildEntryDelegate <T> buildEntryFunction, string operatorSuffix, GetOutCodeDelegate outCodeFunction, string outCodeFormat ) { if (items.Count == 0) { return; } for (int index = 0, count = items.Count; index < count; index++) { if (index % SubSectionMaxCount == 0) { if (index > 0) { buffer.Append("end").Append(entryType.Tag()).Append(operatorSuffix).Append("\n"); } buffer.Append(Math.Min(count - index, SubSectionMaxCount).ToString()).Append(" ").Append("begin").Append(entryType.Tag()).Append(operatorSuffix).Append("\n"); } buildEntryFunction(items[index], buffer, outCodeFunction, outCodeFormat); } buffer.Append("end").Append(entryType.Tag()).Append(operatorSuffix).Append("\n"); }
/** * <summary>Serializes the contents into the content stream.</summary> */ public void Flush( ) { PdfStream stream; PdfDataObject baseDataObject = BaseDataObject; // Are contents just a single stream object? if (baseDataObject is PdfStream) // Single stream. { stream = (PdfStream)baseDataObject; } else // Array of streams. { PdfArray streams = (PdfArray)baseDataObject; // No stream available? if (streams.Count == 0) // No stream. { // Add first stream! stream = new PdfStream(); streams.Add( // Inserts the new stream into the content stream. File.Register(stream) // Inserts the new stream into the file. ); } else // Streams exist. { // Eliminating exceeding streams... /* * NOTE: Applications that consume or produce PDF files are not required to preserve * the existing structure of the Contents array [PDF:1.6:3.6.2]. */ while (streams.Count > 1) { File.Unregister((PdfReference)streams[1]); // Removes the exceeding stream from the file. streams.RemoveAt(1); // Removes the exceeding stream from the content stream. } PdfReference streamReference = (PdfReference)streams[0]; File.Update(streamReference); // Updates the existing stream into the file. stream = (PdfStream)streamReference.DataObject; } } // Get the stream buffer! bytes::IBuffer buffer = stream.Body; // Delete old contents from the stream buffer! buffer.SetLength(0); // Serializing the new contents into the stream buffer... foreach (ContentObject item in items) { item.WriteTo(buffer); } // Update the content stream container! Update(); }
private static bytes::IBuffer BuildCharEntry( KeyValuePair <ByteArray, int> cidChar, bytes::IBuffer buffer, GetOutCodeDelegate outCodeFunction, string outCodeFormat ) { return(buffer.Append("<").Append(ConvertUtils.ByteArrayToHex(cidChar.Key.Data)).Append("> ") .Append(String.Format(outCodeFormat, outCodeFunction(cidChar))).Append("\n")); }
/** * <summary>Gets the Javascript script from the specified base data object.</summary> */ internal static string GetScript(PdfDictionary baseDataObject, PdfName key) { PdfDataObject scriptObject = baseDataObject.Resolve(key); if (scriptObject == null) { return(null); } else if (scriptObject is PdfTextString) { return(((PdfTextString)scriptObject).StringValue); } else { bytes::IBuffer scriptBuffer = ((PdfStream)scriptObject).Body; return(scriptBuffer.GetString(0, (int)scriptBuffer.Length)); } }
/** * <summary>Sets the Javascript script into the specified base data object.</summary> */ internal static void SetScript(PdfDictionary baseDataObject, PdfName key, string value) { PdfDataObject scriptObject = baseDataObject.Resolve(key); if (!(scriptObject is PdfStream) && value.Length > 256) { baseDataObject[key] = baseDataObject.File.Register(scriptObject = new PdfStream()); } // Insert the script! if (scriptObject is PdfStream) { bytes::IBuffer scriptBuffer = ((PdfStream)scriptObject).Body; scriptBuffer.Clear(); scriptBuffer.Append(value); } else { baseDataObject[key] = new PdfTextString(value); } }
/** * <summary>Creates the character code mapping for composite fonts.</summary> */ private void Load_CreateEncoding( PdfDictionary font, PdfDictionary cidFont ) { /* * NOTE: Composite fonts map text shown by content stream strings through a 2-level encoding * scheme: * character code -> CID (character index) -> GID (glyph index) * This works for rendering purposes, but if we want our text data to be intrinsically meaningful, * we need a further mapping towards some standard character identification scheme (Unicode): * Unicode <- character code -> CID -> GID * Such mapping may be provided by a known CID collection or (in case of custom encodings like * Identity-H) by an explicit ToUnicode CMap. * CID -> GID mapping is typically identity, that is CIDS correspond to GIDS, so we don't bother * about that. Our base encoding is Identity-H, that is character codes correspond to CIDs; * however, sometimes a font maps multiple Unicode codepoints to the same GID (for example, the * hyphen glyph may be associated to the hyphen (\u2010) and minus (\u002D) symbols), breaking * the possibility to recover their original Unicode values once represented as character codes * in content stream strings. In this case, we are forced to remap the exceeding codes and * generate an explicit CMap (TODO: I tried to emit a differential CMap using the usecmap * operator in order to import Identity-H as base encoding, but it failed in several engines * (including Acrobat, Ghostscript, Poppler, whilst it surprisingly worked with pdf.js), so we * have temporarily to stick with full CMaps). */ // Encoding [PDF:1.7:5.6.1,5.6.4]. PdfDirectObject encodingObject = PdfName.IdentityH; SortedDictionary <ByteArray, int> sortedCodes; { codes = new BiDictionary <ByteArray, int>(glyphIndexes.Count); int lastRemappedCharCodeValue = 0; IList <int> removedGlyphIndexKeys = null; foreach (KeyValuePair <int, int> glyphIndexEntry in glyphIndexes.ToList()) { int glyphIndex = glyphIndexEntry.Value; ByteArray charCode = new ByteArray(new byte[] { (byte)((glyphIndex >> 8) & 0xFF), (byte)(glyphIndex & 0xFF) }); // Checking for multiple Unicode codepoints which map to the same glyph index... /* * NOTE: In case the same glyph index maps to multiple Unicode codepoints, we are forced to * alter the identity encoding creating distinct cmap entries for the exceeding codepoints. */ if (codes.ContainsKey(charCode)) { if (glyphIndex == 0) // .notdef glyph already mapped. { if (removedGlyphIndexKeys == null) { removedGlyphIndexKeys = new List <int>(); } removedGlyphIndexKeys.Add(glyphIndexEntry.Key); continue; } // Assigning the new character code... /* * NOTE: As our base encoding is identity, we have to look for a value that doesn't * collide with existing glyph indices. */ while (glyphIndexes.ContainsValue(++lastRemappedCharCodeValue)) { ; } charCode.Data[0] = (byte)((lastRemappedCharCodeValue >> 8) & 0xFF); charCode.Data[1] = (byte)(lastRemappedCharCodeValue & 0xFF); } else if (glyphIndex == 0) // .notdef glyph. { DefaultCode = glyphIndexEntry.Key; } codes[charCode] = glyphIndexEntry.Key; } if (removedGlyphIndexKeys != null) { foreach (int removedGlyphIndexKey in removedGlyphIndexKeys) { glyphIndexes.Remove(removedGlyphIndexKey); } } sortedCodes = new SortedDictionary <ByteArray, int>(codes); if (lastRemappedCharCodeValue > 0) // Custom encoding. { string cmapName = "Custom"; bytes::IBuffer cmapBuffer = CMapBuilder.Build( CMapBuilder.EntryTypeEnum.CID, cmapName, sortedCodes, delegate(KeyValuePair <ByteArray, int> codeEntry) { return(glyphIndexes[codeEntry.Value]); } ); encodingObject = File.Register( new PdfStream( new PdfDictionary( new PdfName[] { PdfName.Type, PdfName.CMapName, PdfName.CIDSystemInfo }, new PdfDirectObject[] { PdfName.CMap, new PdfName(cmapName), new PdfDictionary( new PdfName[] { PdfName.Registry, PdfName.Ordering, PdfName.Supplement }, new PdfDirectObject[] { PdfTextString.Get("Adobe"), PdfTextString.Get("Identity"), PdfInteger.Get(0) } ) } ), cmapBuffer ) ); } } font[PdfName.Encoding] = encodingObject; // Character-code-to-CID mapping. cidFont[PdfName.CIDToGIDMap] = PdfName.Identity; // CID-to-glyph-index mapping. // ToUnicode [PDF:1.6:5.9.2]. PdfDirectObject toUnicodeObject = null; { bytes::IBuffer toUnicodeBuffer = CMapBuilder.Build( CMapBuilder.EntryTypeEnum.BaseFont, null, sortedCodes, delegate(KeyValuePair <ByteArray, int> codeEntry) { return(codeEntry.Value); } ); toUnicodeObject = File.Register(new PdfStream(toUnicodeBuffer)); } font[PdfName.ToUnicode] = toUnicodeObject; // Character-code-to-Unicode mapping. // Glyph widths. PdfArray widthsObject = new PdfArray(); { int lastGlyphIndex = -10; PdfArray lastGlyphWidthRangeObject = null; foreach (int glyphIndex in glyphIndexes.Values.OrderBy(x => x).ToList()) { int width; if (!glyphWidths.TryGetValue(glyphIndex, out width)) { width = 0; } if (glyphIndex - lastGlyphIndex != 1) { widthsObject.Add(PdfInteger.Get(glyphIndex)); widthsObject.Add(lastGlyphWidthRangeObject = new PdfArray()); } lastGlyphWidthRangeObject.Add(PdfInteger.Get(width)); lastGlyphIndex = glyphIndex; } } cidFont[PdfName.W] = widthsObject; // Glyph widths. }