Exemple #1
0
        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");
        }
Exemple #2
0
        /**
         * <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();
        }
Exemple #3
0
 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"));
 }
Exemple #4
0
        /**
         * <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));
            }
        }
Exemple #5
0
        /**
         * <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.
        }