Restore the text encoding of a font in a PDF document using C# application.

Blog category: PDF; .NET

September 25, 2026

Sometimes, when attempting to extract text from a PDF document, you may end up with a jumble of unrelated characters. This can happen if the PDF contains a font with corrupted text encoding - corruption that was likely intentional. The document's creator simply did not want anyone to extract text from the document.

If you still need to restore the text encoding of font within the PDF document, follow these steps:


VintaSoft Imaging .NET SDK + VintaSoft PDF .NET Plug-in (Reader+Writer) + VintaSoft OCR .NET Plug-in allows you to perform all the necessary steps to restore the font text encoding in a PDF document.

Here is C# code demonstrating how to restore "corrupted" font text encoding in a PDF document:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using Vintasoft.Imaging;
using Vintasoft.Imaging.Drawing;
using Vintasoft.Imaging.Fonts;
using Vintasoft.Imaging.Ocr;
using Vintasoft.Imaging.Ocr.Results;
using Vintasoft.Imaging.Ocr.Tesseract;
using Vintasoft.Imaging.Pdf;
using Vintasoft.Imaging.Pdf.Content.TextExtraction;
using Vintasoft.Imaging.Pdf.Tree.Fonts;

namespace Vintasoft.Demos
{
    internal static class PdfFontExamples
    {

        /// <summary>
        /// Restores text encodings in fonts of PDF document.
        /// </summary>
        /// <param name="pdfFilename">The filename of PDF document.</param>
        public static void RestoreTextEncodingInAllPdfFontsOfPdfDocument(string pdfFilename)
        {
            // open PDF document
            using (PdfDocument pdfDocument = new PdfDocument(pdfFilename))
            {
                // get fonts of PDF document
                PdfFont[] pdfFonts = pdfDocument.GetFonts();
                // for each PDF font
                for (int i = 0; i < pdfFonts.Length; i++)
                {
                    // restore text encoding in PDF font
                    RestoreTextEncodingInPdfFont(pdfFonts[i]);
                }
                // save changed document to a new PDF file
                pdfDocument.Save("result.pdf");
            }
        }

        /// <summary>
        /// Restores text excoding in PDF font.
        /// </summary>
        /// <param name="pdfFont">PDF font.</param>
        private static void RestoreTextEncodingInPdfFont(PdfFont pdfFont)
        {
            // get the PDF text symbols from PDF font
            PdfTextSymbol[] pdfTextSymbols = PdfTextSymbol.GetFontSymbols(pdfFont);
            if (pdfTextSymbols == null)
            {
                return;
            }
            // if font contains more than 256 symbols
            if (pdfTextSymbols.Length > 256)
            {
                MessageBox.Show("This demo project does not allow to process PDF font if font contains more than 256 symbols. Please change the code if necessary.");
                return;
            }

            UInt64[] charCodes = new UInt64[pdfTextSymbols.Length];
            for (int i = 0; i < pdfTextSymbols.Length; i++)
                charCodes[i] = pdfTextSymbols[i].ContentSymbolCode;
            // sort text symbols by the symbol content code
            Array.Sort(charCodes, pdfTextSymbols);

            try
            {
                // the Unicode overrides
                Dictionary<uint, char> unicodeOverrides = new Dictionary<uint, char>();

                // create Tesseract OCR engine
                using (TesseractOcr tesseractOcr = new TesseractOcr())
                {
                    // initialize OCR engine
                    OcrEngineSettings ocrEngineSettings = new OcrEngineSettings(new OcrLanguage[] { OcrLanguage.English });
                    ocrEngineSettings.RecognitionRegionType = RecognitionRegionType.RecognizeSingleBlock;
                    tesseractOcr.Init(ocrEngineSettings);

                    // dictionary: PDF text symbol => a rectangle on image, where symbol drawn
                    Dictionary<PdfTextSymbol, Rectangle> pdfTextSymbolToImageRectMap = new Dictionary<PdfTextSymbol, Rectangle>();
                    // create image with glyphs of PDF text symbols
                    using (VintasoftImage imageWithTextSymbols = CreateImageWithGlyphsOfPdfTextSymbols(pdfTextSymbols, ref pdfTextSymbolToImageRectMap))
                    {
#if DEBUG_FONT_ENCODING
                        // for debug purposes save the image with glyphs of PDF text symbols
                        imageWithTextSymbols.Save(string.Format("Font-{0}-symbols.png", pdfFont.FontName));
#endif

                        // recognizes text in image
                        OcrPage ocrResult = tesseractOcr.Recognize(imageWithTextSymbols);

                        // get the dictionary from PDF text symbol to the Unicode symbol
                        Dictionary<PdfTextSymbol, char> pdfTextSymbolToCharMap = GetPdfTextSymbolToCharMap(pdfFont, ocrResult, pdfTextSymbolToImageRectMap);
                        // for each PDF text symbol
                        foreach (PdfTextSymbol pdfTextSymbol in pdfTextSymbolToCharMap.Keys)
                        {
                            // get Unicode symbol for PDF text symbol
                            char unicodeSymbol = pdfTextSymbolToCharMap[pdfTextSymbol];
                            // if OCR recognized new Unicode symbol for PDF text symbol
                            if (pdfTextSymbol.Symbol != unicodeSymbol)
                            {
                                // save information about new Unicode symbol for PDF text symbol
                                unicodeOverrides[(uint)pdfTextSymbol.ContentSymbolCode] = unicodeSymbol;
                            }
                        }
                    }
                }

                // create the dictionary: content code => Unicode symbol
                Dictionary<uint, char> contentCodeToUnicodeSymbol = GetContentCodeToUnicodeSymbols(pdfTextSymbols, unicodeOverrides);

                // set the Unicode mapping for PDF font
                pdfFont.SetUnicodeMapping(contentCodeToUnicodeSymbol);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: " + ex.Message);
            }
        }

        /// <summary>
        /// Creates image with glyphs of PDF text symbols.
        /// </summary>
        /// <param name="pdfTextSymbols">PDF text symbols.</param>
        /// <param name="pdfTextSymbolToImageRectMap">Dictionary: PDF text symbol => a rectangle on image, where symbol drawn.</param>
        /// <returns>An image with font symbols.</returns>
        private static VintasoftImage CreateImageWithGlyphsOfPdfTextSymbols(
            PdfTextSymbol[] pdfTextSymbols,
            ref Dictionary<PdfTextSymbol, Rectangle> pdfTextSymbolToImageRectMap)
        {
            // a string that contains characters, which are available in PDF font
            string availableChars = "";

            // for each PDF text symbol
            foreach (PdfTextSymbol fontSymbol in pdfTextSymbols)
            {
                // add character to a string with available characters
                availableChars += fontSymbol.Symbol;
            }

            // the size of cell, where symbol is drawn
            const int CELL_SIZE = 200;

            // calculate the image width
            int imageWidth = CELL_SIZE * 8;
            // calculate the image height
            int imageHeight = CELL_SIZE * (availableChars.Length / 8);
            if ((availableChars.Length % 8) != 0)
                imageHeight += CELL_SIZE;
            imageHeight += CELL_SIZE;

            // create image that will contain glyphs of PDF text symbols
            VintasoftImage imageWithTextSymbols = new VintasoftImage(imageWidth, imageHeight, PixelFormat.Bgr24);

            // create graphics object for image
            using (DrawingEngine drawingEngine = imageWithTextSymbols.CreateDrawingEngine())
            {
                // fill the image
                drawingEngine.FillRectangle(Color.FromArgb(255, Color.White), new RectangleF(0, 0, imageWidth, imageHeight));

                using (IDrawingBrush brush = drawingEngine.DrawingFactory.CreateSolidBrush(Color.FromArgb(255, Color.Black)))
                {
                    using (IDrawingPen pen = drawingEngine.DrawingFactory.CreatePen(brush, 1))
                    {
                        // for each PDF text symbol
                        for (int i = 0; i < pdfTextSymbols.Length; i++)
                        {
                            PdfTextSymbol fontSymbol = pdfTextSymbols[i];

                            // get the outline of PDF text symbol
                            FontSymbolOutline fontSymbolOutline = fontSymbol.GetOutline();
                            // get graphics path that represents outline of PDF text symbol
                            IGraphicsPath graphicsPath = fontSymbolOutline.GetGraphicsPath();
                            // if graphics path exists
                            if (graphicsPath != null)
                            {
                                // calculate the position of graphics path on image

                                int x = (i % 8) * CELL_SIZE + CELL_SIZE;
                                int y = (i / 8) * CELL_SIZE + CELL_SIZE;

                                // save the transform for drawing engine
                                drawingEngine.SaveTransform();

                                // apply necessary transform to the drawing engine
                                AffineMatrix transform = new AffineMatrix();
                                transform.Translate(x, y);
                                transform.ScalePrepend(50f / 1000, 50f / 1000);
                                drawingEngine.MultiplyTransformPrepend(transform);

                                // fill the graphics path on image
                                drawingEngine.FillPath(brush, graphicsPath);

                                // restore the transform for drawing engine
                                drawingEngine.RestoreTransform();

                                // save information about position of PDF text symbol on image
                                pdfTextSymbolToImageRectMap.Add(pdfTextSymbols[i], new Rectangle(x, y, CELL_SIZE, CELL_SIZE));
                            }
                        }
                    }
                }
            }

            return imageWithTextSymbols;
        }

        /// <summary>
        /// Returns the dictionary from content code to the Unicode symbol for selected PDF font.
        /// </summary>
        /// <param name="textSymbols">The text symbols.</param>
        /// <param name="unicodeOverrides">The Unicode overrides.</param>
        /// <returns>The dictionary from content code to the Unicode symbol for selected PDF font.</returns>
        private static Dictionary<uint, char> GetContentCodeToUnicodeSymbols(PdfTextSymbol[] textSymbols, Dictionary<uint, char> unicodeOverrides)
        {
            Dictionary<uint, char> result = new Dictionary<uint, char>();
            foreach (PdfTextSymbol symbol in textSymbols)
                result[(uint)symbol.ContentSymbolCode] = symbol.Symbol;
            foreach (KeyValuePair<uint, char> item in unicodeOverrides)
                result[item.Key] = item.Value;
            return result;
        }

        /// <summary>
        /// Returns the dictionary from PDF text symbol to the Unicode symbol.
        /// </summary>
        /// <param name="pdfFont">PDF font.</param>
        /// <param name="ocrResult">OCR result.</param>
        /// <param name="pdfTextSymbolToImageRectMap">The dictionary from PDF text symbol to the image rectangle.</param>
        /// <returns>The dictionary from PDF text symbol to the Unicode symbol.</returns>
        private static Dictionary<PdfTextSymbol, char> GetPdfTextSymbolToCharMap(
            PdfFont pdfFont,
            OcrPage ocrResult,
            Dictionary<PdfTextSymbol, Rectangle> pdfTextSymbolToImageRectMap)
        {
            // Dictionary: PDF text symbol => Unicode char
            Dictionary<PdfTextSymbol, char> pdfTextSymbolToCharMap = new Dictionary<PdfTextSymbol, char>();
            // Dictionary: PDF text symbol => char bounding box
            Dictionary<PdfTextSymbol, Rectangle> pdfTextSymbolToCharBoundingBoxMap = new Dictionary<PdfTextSymbol, Rectangle>();

            // for each region in OCR result
            foreach (OcrTextRegion ocrRegion in ocrResult.Regions)
            {
                // for each paragraph in region' paragraphs
                foreach (OcrParagraph ocrParagraph in ocrRegion.Paragraphs)
                {
                    // for each text line in text lines of paragraph
                    foreach (OcrTextLine ocrTextLine in ocrParagraph.TextLines)
                    {
                        // for each word in words of text line
                        foreach (OcrWord ocrWord in ocrTextLine.Words)
                        {
                            // for each OCR symbol in word's symbols
                            foreach (OcrSymbol ocrSymbol in ocrWord.Symbols)
                            {
                                // get the bounding box of OCR symbol
                                Rectangle ocrSymbolBoundingBox = ocrSymbol.GetBoundingBox();
                                // the maximum area for intersection rectangle between the bounding box of OCR symbol and bounding box of PDF text symbol
                                int intersectionRectAreaMax = 0;
                                // for each PDF text symbol in dictionary with bounding boxes
                                foreach (PdfTextSymbol pdfTextSymbol in pdfTextSymbolToImageRectMap.Keys)
                                {
                                    // get the intersection rectangle between the bounding box of OCR symbol and bounding box of PDF text symbol
                                    Rectangle intersectionRect = Rectangle.Intersect(ocrSymbolBoundingBox, pdfTextSymbolToImageRectMap[pdfTextSymbol]);
                                    // get the area of intersection rectangle
                                    int intersectionRectArea = intersectionRect.Width * intersectionRect.Height;
                                    // if the area of intersection rectangle is greater than the maximum area for intersection rectangle
                                    if (intersectionRectArea > intersectionRectAreaMax)
                                    {
                                        // if OCR symbol contains only 1 text char
                                        if (ocrSymbol.Text.Length == 1)
                                        {
                                            // if we do not have information about Unicode char for PDF text symbol
                                            if (!pdfTextSymbolToCharMap.ContainsKey(pdfTextSymbol))
                                            {
                                                // save information about Unicode char for PDF text symbol
                                                pdfTextSymbolToCharMap.Add(pdfTextSymbol, ocrSymbol.Text[0]);
                                                // save information about char bounding box for PDF text symbol
                                                pdfTextSymbolToCharBoundingBoxMap.Add(pdfTextSymbol, ocrSymbolBoundingBox);

                                                // update the maximum area for intersection rectangle
                                                intersectionRectAreaMax = intersectionRectArea;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

#if DEBUG_FONT_ENCODING
            // create image that contains glyph and Unicode symbol for each recognized PDF text symbol

            string imageWithTextSymbolsName = string.Format("Font-{0}-symbols.png", pdfFont.FontName);
            using (VintasoftImage image = new VintasoftImage(imageWithTextSymbolsName))
            {
                using (DrawingEngine drawingEngine = image.CreateDrawingEngine())
                {
                    foreach (PdfTextSymbol pdfTextSymbol1 in pdfTextSymbolToCharMap.Keys)
                    {
                        drawingEngine.DrawString(
                            string.Format("{0}", pdfTextSymbolToCharMap[pdfTextSymbol1]),
                            drawingEngine.DrawingFactory.CreateSystemFont("Arial", 16, false, false),
                            drawingEngine.DrawingFactory.CreateSolidBrush(Color.Red),
                            new PointF(pdfTextSymbolToImageRectMap[pdfTextSymbol1].X, pdfTextSymbolToImageRectMap[pdfTextSymbol1].Y));

                        drawingEngine.DrawRectangle(drawingEngine.DrawingFactory.CreatePen(Color.Green), pdfTextSymbolToImageRectMap[pdfTextSymbol1]);

                        drawingEngine.DrawRectangle(drawingEngine.DrawingFactory.CreatePen(Color.Blue), pdfTextSymbolToCharBoundingBoxMap[pdfTextSymbol1]);
                    }
                }
                image.Save(string.Format("Font-{0}-symbols-OCR.png", pdfFont.FontName));
            }
#endif

            return pdfTextSymbolToCharMap;
        }

    }
}


The VintaSoft Imaging .NET SDK distribution package includes the VintaSoft PDF Editor Demo project, which allows you to restore or modify the font text encoding in a PDF document. To test this functionality in the VintaSoft PDF Editor Demo, follow these steps: