private InlineImage ParseInlineImage( ) { /* * NOTE: Inline images use a peculiar syntax that's an exception to the usual rule * that the data in a content stream is interpreted according to the standard PDF syntax * for objects. */ InlineImageHeader header; { List <PdfDirectObject> operands = new List <PdfDirectObject>(); // Parsing the image entries... while (MoveNext() && tokenType != TokenTypeEnum.Keyword) // Not keyword (i.e. end at image data beginning (ID operator)). { operands.Add(ParsePdfObject()); } header = new InlineImageHeader(operands); } InlineImageBody body; { MoveNext(); bytes::Buffer data = new bytes::Buffer(); byte c1 = 0, c2 = 0; do { try { while (true) { c1 = (byte)stream.ReadByte(); c2 = (byte)stream.ReadByte(); if (c1 == 'E' && c2 == 'I') { break; } data.Append(c1); data.Append(c2); } break; } catch { /* NOTE: Current stream has finished. */ // Move to the next stream! MoveNextStream(); } } while(stream != null); body = new InlineImageBody(data); } return(new InlineImage( header, body )); }
private InlineImage ParseInlineImage() { /* * NOTE: Inline images use a peculiar syntax that's an exception to the usual rule * that the data in a content stream is interpreted according to the standard PDF syntax * for objects. */ InlineImageHeader header; { List <PdfDirectObject> operands = new List <PdfDirectObject>(); // Parsing the image entries... while (MoveNext() && TokenType != TokenTypeEnum.Keyword) // Not keyword (i.e. end at image data beginning (ID operator)). { operands.Add((PdfDirectObject)ParsePdfObject()); } header = new InlineImageHeader(operands); } InlineImageBody body; { // [FIX:51,74] Wrong 'EI' token handling on inline image parsing. bytes::IInputStream stream = Stream; stream.ReadByte(); // Should be the whitespace following the 'ID' token. bytes::Buffer data = new bytes::Buffer(); while (true) { int curByte1 = stream.ReadByte(); if (curByte1 == -1) { break; } int curByte2 = stream.ReadByte(); if (curByte2 == -1) { break; } if (((char)curByte1 == 'E' && (char)curByte2 == 'I')) { break; } if (((char)curByte1 == ' ' && (char)curByte2 == 'E')) { break; } data.Append((byte)curByte1); data.Append((byte)curByte2); } body = new InlineImageBody(data); } return(new InlineImage(header, body)); }
private InlineImage ParseInlineImage( ) { /* * NOTE: Inline images use a peculiar syntax that's an exception to the usual rule * that the data in a content stream is interpreted according to the standard PDF syntax * for objects. */ InlineImageHeader header; { List <PdfDirectObject> operands = new List <PdfDirectObject>(); // Parsing the image entries... while (MoveNext() && TokenType != TokenTypeEnum.Keyword) // Not keyword (i.e. end at image data beginning (ID operator)). { operands.Add((PdfDirectObject)ParsePdfObject()); } header = new InlineImageHeader(operands); } InlineImageBody body; { bytes::IInputStream stream = Stream; MoveNext(); bytes::Buffer data = new bytes::Buffer(); byte prevByte = 0; while (true) { byte curByte = (byte)stream.ReadByte(); if (prevByte == 'E' && curByte == 'I') { break; } data.Append(prevByte = curByte); } body = new InlineImageBody(data); } return(new InlineImage( header, body )); }
private InlineImage ParseInlineImage( ) { /* NOTE: Inline images use a peculiar syntax that's an exception to the usual rule that the data in a content stream is interpreted according to the standard PDF syntax for objects. */ InlineImageHeader header; { List<PdfDirectObject> operands = new List<PdfDirectObject>(); // Parsing the image entries... while(MoveNext() && TokenType != TokenTypeEnum.Keyword) // Not keyword (i.e. end at image data beginning (ID operator)). {operands.Add((PdfDirectObject)ParsePdfObject());} header = new InlineImageHeader(operands); } InlineImageBody body; { bytes::IInputStream stream = Stream; MoveNext(); bytes::Buffer data = new bytes::Buffer(); while(true) { byte c1 = (byte)stream.ReadByte(); byte c2 = (byte)stream.ReadByte(); if(c1 == 'E' && c2 == 'I') break; data.Append(c1); data.Append(c2); } body = new InlineImageBody(data); } return new InlineImage( header, body ); }
private void RefreshAppearance( ) { Widget widget = Widgets[0]; FormXObject normalAppearance; { AppearanceStates normalAppearances = widget.Appearance.Normal; normalAppearance = normalAppearances[null]; if(normalAppearance == null) {normalAppearances[null] = normalAppearance = new FormXObject(Document, widget.Box.Size);} } PdfName fontName = null; double fontSize = 0; { PdfString defaultAppearanceState = DefaultAppearanceState; if(defaultAppearanceState == null) { // Retrieving the font to define the default appearance... fonts::Font defaultFont = null; PdfName defaultFontName = null; { // Field fonts. FontResources normalAppearanceFonts = normalAppearance.Resources.Fonts; foreach(KeyValuePair<PdfName,fonts::Font> entry in normalAppearanceFonts) { if(!entry.Value.Symbolic) { defaultFont = entry.Value; defaultFontName = entry.Key; break; } } if(defaultFontName == null) { // Common fonts. FontResources formFonts = Document.Form.Resources.Fonts; foreach(KeyValuePair<PdfName,fonts::Font> entry in formFonts) { if(!entry.Value.Symbolic) { defaultFont = entry.Value; defaultFontName = entry.Key; break; } } if(defaultFontName == null) { //TODO:manage name collision! formFonts[ defaultFontName = new PdfName("default") ] = defaultFont = new fonts::StandardType1Font( Document, fonts::StandardType1Font.FamilyEnum.Helvetica, false, false ); } normalAppearanceFonts[defaultFontName] = defaultFont; } } bytes::Buffer buffer = new bytes::Buffer(); new SetFont(defaultFontName, IsMultiline ? 10 : 0).WriteTo(buffer, Document); widget.BaseDataObject[PdfName.DA] = defaultAppearanceState = new PdfString(buffer.ToByteArray()); } // Retrieving the font to use... ContentParser parser = new ContentParser(defaultAppearanceState.ToByteArray()); foreach(ContentObject content in parser.ParseContentObjects()) { if(content is SetFont) { SetFont setFontOperation = (SetFont)content; fontName = setFontOperation.Name; fontSize = setFontOperation.Size; break; } } normalAppearance.Resources.Fonts[fontName] = Document.Form.Resources.Fonts[fontName]; } // Refreshing the field appearance... /* * TODO: resources MUST be resolved both through the apperance stream resource dictionary and * from the DR-entry acroform resource dictionary */ PrimitiveComposer baseComposer = new PrimitiveComposer(normalAppearance); BlockComposer composer = new BlockComposer(baseComposer); ContentScanner currentLevel = composer.Scanner; bool textShown = false; while(currentLevel != null) { if(!currentLevel.MoveNext()) { currentLevel = currentLevel.ParentLevel; continue; } ContentObject content = currentLevel.Current; if(content is MarkedContent) { MarkedContent markedContent = (MarkedContent)content; if(PdfName.Tx.Equals(((BeginMarkedContent)markedContent.Header).Tag)) { // Remove old text representation! markedContent.Objects.Clear(); // Add new text representation! baseComposer.Scanner = currentLevel.ChildLevel; // Ensures the composer places new contents within the marked content block. ShowText(composer, fontName, fontSize); textShown = true; } } else if(content is Text) {currentLevel.Remove();} else if(currentLevel.ChildLevel != null) {currentLevel = currentLevel.ChildLevel;} } if(!textShown) { baseComposer.BeginMarkedContent(PdfName.Tx); ShowText(composer, fontName, fontSize); baseComposer.End(); } baseComposer.Flush(); }
private InlineImage ParseInlineImage( ) { /* * NOTE: Inline images use a peculiar syntax that's an exception to the usual rule * that the data in a content stream is interpreted according to the standard PDF syntax * for objects. */ InlineImageHeader header; { List <PdfDirectObject> operands = new List <PdfDirectObject>(); // Parsing the image entries... while (MoveNext() && TokenType != TokenTypeEnum.Keyword) // Not keyword (i.e. end at image data beginning (ID operator)). { operands.Add((PdfDirectObject)ParsePdfObject()); } header = new InlineImageHeader(operands); } InlineImageBody body; { // [FIX:51,74] Wrong 'EI' token handling on inline image parsing. bytes::IInputStream stream = Stream; stream.ReadByte(); // Should be the whitespace following the 'ID' token. bytes::Buffer data = new bytes::Buffer(); var endChunkBuffer = new sysIO::MemoryStream(3); int endChunkIndex = -1; while (true) { int curByte = stream.ReadByte(); if (curByte == -1) { throw new PostScriptParseException("No 'EI' token found to close inline image data stream."); } if (endChunkIndex == -1) { if (IsWhitespace(curByte)) { /* * NOTE: Whitespace characters may announce the beginning of the end image operator. */ endChunkBuffer.WriteByte((byte)curByte); endChunkIndex++; } else { data.Append((byte)curByte); } } else if (endChunkIndex == 0 && IsWhitespace(curByte)) { /* * NOTE: Only the last whitespace character may announce the beginning of the end image * operator. */ data.Append(endChunkBuffer.ToArray()); endChunkBuffer.SetLength(0); endChunkBuffer.WriteByte((byte)curByte); } else if ((endChunkIndex == 0 && curByte == 'E') || (endChunkIndex == 1 && curByte == 'I')) { /* * NOTE: End image operator characters. */ endChunkBuffer.WriteByte((byte)curByte); endChunkIndex++; } else if (endChunkIndex == 2 && IsWhitespace(curByte)) { /* * NOTE: The whitespace character after the end image operator completes the pattern. */ break; } else { if (endChunkIndex > -1) { data.Append(endChunkBuffer.ToArray()); endChunkBuffer.SetLength(0); endChunkIndex = -1; } data.Append((byte)curByte); } } body = new InlineImageBody(data); } return(new InlineImage( header, body )); }
private void RefreshAppearance( ) { Widget widget = Widgets[0]; FormXObject normalAppearance; { AppearanceStates normalAppearances = widget.Appearance.Normal; normalAppearance = normalAppearances[null]; if (normalAppearance == null) { normalAppearances[null] = normalAppearance = new FormXObject(Document, widget.Box.Size); } } PdfName fontName = null; double fontSize = 0; { PdfString defaultAppearanceState = DefaultAppearanceState; if (defaultAppearanceState == null) { // Retrieving the font to define the default appearance... fonts::Font defaultFont = null; PdfName defaultFontName = null; { // Field fonts. FontResources normalAppearanceFonts = normalAppearance.Resources.Fonts; foreach (KeyValuePair <PdfName, fonts::Font> entry in normalAppearanceFonts) { if (!entry.Value.Symbolic) { defaultFont = entry.Value; defaultFontName = entry.Key; break; } } if (defaultFontName == null) { // Common fonts. FontResources formFonts = Document.Form.Resources.Fonts; foreach (KeyValuePair <PdfName, fonts::Font> entry in formFonts) { if (!entry.Value.Symbolic) { defaultFont = entry.Value; defaultFontName = entry.Key; break; } } if (defaultFontName == null) { //TODO:manage name collision! formFonts[ defaultFontName = new PdfName("default") ] = defaultFont = new fonts::StandardType1Font( Document, fonts::StandardType1Font.FamilyEnum.Helvetica, false, false ); } normalAppearanceFonts[defaultFontName] = defaultFont; } } bytes::Buffer buffer = new bytes::Buffer(); new SetFont(defaultFontName, IsMultiline ? 10 : 0).WriteTo(buffer, Document); widget.BaseDataObject[PdfName.DA] = defaultAppearanceState = new PdfString(buffer.ToByteArray()); } // Retrieving the font to use... ContentParser parser = new ContentParser(defaultAppearanceState.ToByteArray()); foreach (ContentObject content in parser.ParseContentObjects()) { if (content is SetFont) { SetFont setFontOperation = (SetFont)content; fontName = setFontOperation.Name; fontSize = setFontOperation.Size; break; } } normalAppearance.Resources.Fonts[fontName] = Document.Form.Resources.Fonts[fontName]; } // Refreshing the field appearance... /* * TODO: resources MUST be resolved both through the apperance stream resource dictionary and * from the DR-entry acroform resource dictionary */ PrimitiveComposer baseComposer = new PrimitiveComposer(normalAppearance); BlockComposer composer = new BlockComposer(baseComposer); ContentScanner currentLevel = composer.Scanner; bool textShown = false; while (currentLevel != null) { if (!currentLevel.MoveNext()) { currentLevel = currentLevel.ParentLevel; continue; } ContentObject content = currentLevel.Current; if (content is MarkedContent) { MarkedContent markedContent = (MarkedContent)content; if (PdfName.Tx.Equals(((BeginMarkedContent)markedContent.Header).Tag)) { // Remove old text representation! markedContent.Objects.Clear(); // Add new text representation! baseComposer.Scanner = currentLevel.ChildLevel; // Ensures the composer places new contents within the marked content block. ShowText(composer, fontName, fontSize); textShown = true; } } else if (content is Text) { currentLevel.Remove(); } else if (currentLevel.ChildLevel != null) { currentLevel = currentLevel.ChildLevel; } } if (!textShown) { baseComposer.BeginMarkedContent(PdfName.Tx); ShowText(composer, fontName, fontSize); baseComposer.End(); } baseComposer.Flush(); }
/** * <summary>Builds a CMap according to the specified arguments.</summary> * <param name="entryType"></param> * <param name="cmapName">CMap name (<code>null</code> in case no custom name is needed).</param> * <param name="codes"></param> * <param name="outCodeFunction"></param> * <returns>Buffer containing the serialized CMap.</returns> */ public static bytes::IBuffer Build( EntryTypeEnum entryType, string cmapName, SortedDictionary <ByteArray, int> codes, GetOutCodeDelegate outCodeFunction ) { bytes::IBuffer buffer = new bytes::Buffer(); // Header. string outCodeFormat; switch (entryType) { case EntryTypeEnum.BaseFont: { if (cmapName == null) { cmapName = "Adobe-Identity-UCS"; } buffer.Append( "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n" + "/CIDSystemInfo\n" + "<< /Registry (Adobe)\n" + "/Ordering (UCS)\n" + "/Supplement 0\n" + ">> def\n" + "/CMapName /").Append(cmapName).Append(" def\n" + "/CMapVersion 10.001 def\n" + "/CMapType 2 def\n" + "1 begincodespacerange\n" + "<0000> <FFFF>\n" + "endcodespacerange\n" ); outCodeFormat = "<{0:X4}>"; break; } case EntryTypeEnum.CID: { if (cmapName == null) { cmapName = "Custom"; } buffer.Append( "%!PS-Adobe-3.0 Resource-CMap\n" + "%%DocumentNeededResources: ProcSet (CIDInit)\n" + "%%IncludeResource: ProcSet (CIDInit)\n" + "%%BeginResource: CMap (").Append(cmapName).Append(")\n" + "%%Title: (").Append(cmapName).Append(" Adobe Identity 0)\n" + "%%Version: 1\n" + "%%EndComments\n" + "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n" + "/CIDSystemInfo 3 dict dup begin\n" + "/Registry (Adobe) def\n" + "/Ordering (Identity) def\n" + "/Supplement 0 def\n" + "end def\n" + "/CMapVersion 1 def\n" + "/CMapType 1 def\n" + "/CMapName /").Append(cmapName).Append(" def\n" + "/WMode 0 def\n" + "1 begincodespacerange\n" + "<0000> <FFFF>\n" + "endcodespacerange\n" ); outCodeFormat = "{0}"; break; } default: throw new NotImplementedException(); } // Entries. { IList <KeyValuePair <ByteArray, int> > cidChars = new List <KeyValuePair <ByteArray, int> >(); IList <KeyValuePair <ByteArray, int>[]> cidRanges = new List <KeyValuePair <ByteArray, int>[]>(); { KeyValuePair <ByteArray, int>? lastCodeEntry = null; KeyValuePair <ByteArray, int>[] lastCodeRange = null; foreach (KeyValuePair <ByteArray, int> codeEntry in codes) { if (lastCodeEntry.HasValue) { int codeLength = codeEntry.Key.Data.Length; if (codeLength == lastCodeEntry.Value.Key.Data.Length && codeEntry.Key.Data[codeLength - 1] - lastCodeEntry.Value.Key.Data[codeLength - 1] == 1 && outCodeFunction(codeEntry) - outCodeFunction(lastCodeEntry.Value) == 1) // Contiguous codes. { if (lastCodeRange == null) { lastCodeRange = new KeyValuePair <ByteArray, int>[] { lastCodeEntry.Value, default(KeyValuePair <ByteArray, int>) }; } } else // Separated codes. { AddEntry(cidRanges, cidChars, lastCodeEntry.Value, lastCodeRange); lastCodeRange = null; } } lastCodeEntry = codeEntry; } AddEntry(cidRanges, cidChars, lastCodeEntry.Value, lastCodeRange); } // Ranges section. BuildEntriesSection(buffer, entryType, cidRanges, BuildRangeEntry, "range", outCodeFunction, outCodeFormat); // Chars section. BuildEntriesSection(buffer, entryType, cidChars, BuildCharEntry, "char", outCodeFunction, outCodeFormat); } // Trailer. switch (entryType) { case EntryTypeEnum.BaseFont: buffer.Append( "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end\n" ); break; case EntryTypeEnum.CID: buffer.Append( "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end\n" + "%%EndResource\n" + "%%EOF" ); break; default: throw new NotImplementedException(); } return(buffer); }
/** <summary>Creates the character code mapping for composite fonts.</summary> */ private void Load_CreateEncoding( PdfDictionary font, PdfDictionary cidFont ) { // CMap [PDF:1.6:5.6.4]. bytes::Buffer cmapBuffer = new bytes::Buffer(); cmapBuffer.Append( "%!PS-Adobe-3.0 Resource-CMap\n" + "%%DocumentNeededResources: ProcSet (CIDInit)\n" + "%%IncludeResource: ProcSet (CIDInit)\n" + "%%BeginResource: CMap (Adobe-Identity-UCS)\n" + "%%Title: (Adobe-Identity-UCS Adobe Identity 0)\n" + "%%Version: 1\n" + "%%EndComments\n" + "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n" + "/CIDSystemInfo\n" + "3 dict dup begin\n" + "/Registry (Adobe) def\n" + "/Ordering (Identity) def\n" + "/Supplement 0 def\n" + "end def\n" + "/CMapName /Adobe-Identity-UCS def\n" + "/CMapVersion 1 def\n" + "/CMapType 0 def\n" + "/WMode 0 def\n" + "2 begincodespacerange\n" + "<20> <20>\n" + "<0000> <19FF>\n" + "endcodespacerange\n" + glyphIndexes.Count + " begincidchar\n" ); // ToUnicode [PDF:1.6:5.9.2]. bytes::Buffer toUnicodeBuffer = new bytes::Buffer(); toUnicodeBuffer.Append( "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n" + "/CIDSystemInfo\n" + "<< /Registry (Adobe)\n" + "/Ordering (UCS)\n" + "/Supplement 0\n" + ">> def\n" + "/CMapName /Adobe-Identity-UCS def\n" + "/CMapVersion 10.001 def\n" + "/CMapType 2 def\n" + "2 begincodespacerange\n" + "<20> <20>\n" + "<0000> <19FF>\n" + "endcodespacerange\n" + glyphIndexes.Count + " beginbfchar\n" ); // CIDToGIDMap [PDF:1.6:5.6.3]. bytes::Buffer gIdBuffer = new bytes::Buffer(); gIdBuffer.Append((byte)0); gIdBuffer.Append((byte)0); int code = 0; codes = new BiDictionary<ByteArray,int>(glyphIndexes.Count); PdfArray widthsObject = new PdfArray(glyphWidths.Count); foreach(KeyValuePair<int,int> glyphIndexEntry in glyphIndexes) { // Character code (unicode to codepoint) entry. code++; byte[] charCode = (glyphIndexEntry.Key == 32 ? new byte[]{32} : new byte[] { (byte)((code >> 8) & 0xFF), (byte)(code & 0xFF) }); codes[new ByteArray(charCode)] = glyphIndexEntry.Key; // CMap entry. cmapBuffer.Append("<"); toUnicodeBuffer.Append("<"); for(int charCodeBytesIndex = 0, charCodeBytesLength = charCode.Length; charCodeBytesIndex < charCodeBytesLength; charCodeBytesIndex++ ) { string hex = ((int)charCode[charCodeBytesIndex]).ToString("X2"); cmapBuffer.Append(hex); toUnicodeBuffer.Append(hex); } cmapBuffer.Append("> " + code + "\n"); toUnicodeBuffer.Append("> <" + glyphIndexEntry.Key.ToString("X4") + ">\n"); // CID-to-GID entry. int glyphIndex = glyphIndexEntry.Value; gIdBuffer.Append((byte)((glyphIndex >> 8) & 0xFF)); gIdBuffer.Append((byte)(glyphIndex & 0xFF)); // Width. int width; if(!glyphWidths.TryGetValue(glyphIndex, out width)) {width = 0;} else if(width > 1000) {width = 1000;} widthsObject.Add(PdfInteger.Get(width)); } cmapBuffer.Append( "endcidchar\n" + "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end\n" + "%%EndResource\n" + "%%EOF" ); PdfStream cmapStream = new PdfStream(cmapBuffer); PdfDictionary cmapHead = cmapStream.Header; cmapHead[PdfName.Type] = PdfName.CMap; cmapHead[PdfName.CMapName] = new PdfName("Adobe-Identity-UCS"); cmapHead[PdfName.CIDSystemInfo] = new PdfDictionary( new PdfName[] { PdfName.Registry, PdfName.Ordering, PdfName.Supplement }, new PdfDirectObject[] { new PdfTextString("Adobe"), new PdfTextString("Identity"), PdfInteger.Get(0) } ); // Generic predefined CMap (Identity-H/V (Adobe-Identity-0)) [PDF:1.6:5.6.4]. font[PdfName.Encoding] = File.Register(cmapStream); PdfStream gIdStream = new PdfStream(gIdBuffer); cidFont[PdfName.CIDToGIDMap] = File.Register(gIdStream); cidFont[PdfName.W] = new PdfArray(new PdfDirectObject[]{PdfInteger.Get(1),widthsObject}); toUnicodeBuffer.Append( "endbfchar\n" + "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end\n" ); PdfStream toUnicodeStream = new PdfStream(toUnicodeBuffer); font[PdfName.ToUnicode] = File.Register(toUnicodeStream); }
/** * <summary>Creates the character code mapping for composite fonts.</summary> */ private void Load_CreateEncoding( PdfDictionary font, PdfDictionary cidFont ) { // CMap [PDF:1.6:5.6.4]. bytes::Buffer cmapBuffer = new bytes::Buffer(); cmapBuffer.Append( "%!PS-Adobe-3.0 Resource-CMap\n" + "%%DocumentNeededResources: ProcSet (CIDInit)\n" + "%%IncludeResource: ProcSet (CIDInit)\n" + "%%BeginResource: CMap (Adobe-Identity-UCS)\n" + "%%Title: (Adobe-Identity-UCS Adobe Identity 0)\n" + "%%Version: 1\n" + "%%EndComments\n" + "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n" + "/CIDSystemInfo\n" + "3 dict dup begin\n" + "/Registry (Adobe) def\n" + "/Ordering (Identity) def\n" + "/Supplement 0 def\n" + "end def\n" + "/CMapName /Adobe-Identity-UCS def\n" + "/CMapVersion 1 def\n" + "/CMapType 0 def\n" + "/WMode 0 def\n" + "2 begincodespacerange\n" + "<20> <20>\n" + "<0000> <19FF>\n" + "endcodespacerange\n" + glyphIndexes.Count + " begincidchar\n" ); // ToUnicode [PDF:1.6:5.9.2]. bytes::Buffer toUnicodeBuffer = new bytes::Buffer(); toUnicodeBuffer.Append( "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n" + "/CIDSystemInfo\n" + "<< /Registry (Adobe)\n" + "/Ordering (UCS)\n" + "/Supplement 0\n" + ">> def\n" + "/CMapName /Adobe-Identity-UCS def\n" + "/CMapVersion 10.001 def\n" + "/CMapType 2 def\n" + "2 begincodespacerange\n" + "<20> <20>\n" + "<0000> <19FF>\n" + "endcodespacerange\n" + glyphIndexes.Count + " beginbfchar\n" ); // CIDToGIDMap [PDF:1.6:5.6.3]. bytes::Buffer gIdBuffer = new bytes::Buffer(); gIdBuffer.Append((byte)0); gIdBuffer.Append((byte)0); int code = 0; codes = new BiDictionary <ByteArray, int>(glyphIndexes.Count); PdfArray widthsObject = new PdfArray(glyphWidths.Count); foreach (KeyValuePair <int, int> glyphIndexEntry in glyphIndexes) { // Character code (unicode to codepoint) entry. code++; byte[] charCode = (glyphIndexEntry.Key == 32 ? new byte[] { 32 } : new byte[] { (byte)((code >> 8) & 0xFF), (byte)(code & 0xFF) }); codes[new ByteArray(charCode)] = glyphIndexEntry.Key; // CMap entry. cmapBuffer.Append("<"); toUnicodeBuffer.Append("<"); for (int charCodeBytesIndex = 0, charCodeBytesLength = charCode.Length; charCodeBytesIndex < charCodeBytesLength; charCodeBytesIndex++ ) { string hex = ((int)charCode[charCodeBytesIndex]).ToString("X2"); cmapBuffer.Append(hex); toUnicodeBuffer.Append(hex); } cmapBuffer.Append("> " + code + "\n"); toUnicodeBuffer.Append("> <" + glyphIndexEntry.Key.ToString("X4") + ">\n"); // CID-to-GID entry. int glyphIndex = glyphIndexEntry.Value; gIdBuffer.Append((byte)((glyphIndex >> 8) & 0xFF)); gIdBuffer.Append((byte)(glyphIndex & 0xFF)); // Width. int width; if (!glyphWidths.TryGetValue(glyphIndex, out width)) { width = 0; } else if (width > 1000) { width = 1000; } widthsObject.Add(PdfInteger.Get(width)); } cmapBuffer.Append( "endcidchar\n" + "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end\n" + "%%EndResource\n" + "%%EOF" ); PdfStream cmapStream = new PdfStream(cmapBuffer); PdfDictionary cmapHead = cmapStream.Header; cmapHead[PdfName.Type] = PdfName.CMap; cmapHead[PdfName.CMapName] = new PdfName("Adobe-Identity-UCS"); cmapHead[PdfName.CIDSystemInfo] = new PdfDictionary( new PdfName[] { PdfName.Registry, PdfName.Ordering, PdfName.Supplement }, new PdfDirectObject[] { new PdfTextString("Adobe"), new PdfTextString("Identity"), PdfInteger.Get(0) } ); // Generic predefined CMap (Identity-H/V (Adobe-Identity-0)) [PDF:1.6:5.6.4]. font[PdfName.Encoding] = File.Register(cmapStream); PdfStream gIdStream = new PdfStream(gIdBuffer); cidFont[PdfName.CIDToGIDMap] = File.Register(gIdStream); cidFont[PdfName.W] = new PdfArray(new PdfDirectObject[] { PdfInteger.Get(1), widthsObject }); toUnicodeBuffer.Append( "endbfchar\n" + "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end\n" ); PdfStream toUnicodeStream = new PdfStream(toUnicodeBuffer); font[PdfName.ToUnicode] = File.Register(toUnicodeStream); }