0
votes

I have PDF template file with fields. Template created by customer. It has some text, field labels and fields itself. Text and labels uses some font which is embedded within the template.

Problems occur when I try to fill fields with cyrillic values - there is no cyrillic symbols in result document.

I saw a lot of similar problems which were solved by using substitution font for AcroFields. But here I can't use one specific font for substitution, because I can't define field font in template.

I tried to set different fonts for fields in Acrobat Editor - Times New Roman, Arial and other well known Windows fonts, but there is no effect in resulting pdf.

Code sample:

        FontFactory.RegisterDirectory(Environment.GetFolderPath(Environment.SpecialFolder.Fonts));
        using (var dest = File.Create(@"result.pdf"))
        {
            using (var stamper = new PdfStamper(reader, dest))
            {
                var fields = stamper.AcroFields;
                fields.SetField("ClientName", "Имя клиента");
                stamper.FormFlattening = true;
                stamper.Close();
            }
        }

I even registered all available fonts in FontFactory, but there was no effect.

So the questions are: 1. If I can embed font in Adobe Acrobat used for fields only, then how to do it? 2. If I can define font family for existing field with iTextSharp, then how to do it?

1
So the font is embedded in the PDF but you can't use it? I'd start there. Can you manually type into the field in Adobe Acrobat and see what you expect? If not, you either don't have embedded fonts or they have some form of corruption. Can you post the PDF? - Chris Haas
I can't use embedded fonts because I don't have that fonts installed on my machine. Fields uses Arial or Times New Roman, and I can type cyrillic symbos there, but when I do that with iTextSharp, no cyrillic symbols are shown. - Nogard

1 Answers

0
votes

Well, I wrote solution suitable for me.

  1. Register all existing system fonts in FontFactory.
  2. Read document metadata to extract all used fonts in documents.
  3. Read fields metadata and try to create BaseFont suitable to field font. If there is no suitable font - use fallback font (arial with encoding IDENTITY_H).

So full code looks like:

    static IEnumerable<PdfFontInfo> ReadDocumentFonts(PdfReader reader)
    {
        if (reader.AcroForm == null)
            yield break;
        var dr = reader.AcroForm.GetAsDict(PdfName.DR);

        // Read font information from resources
        var fontDict = dr.GetAsDict(PdfName.FONT);
        foreach (var fontKey in fontDict.Keys)
        {
            var data = fontDict.GetAsDict(fontKey);
            // Read font descriptor if it possible
            var descriptor = data.GetAsDict(PdfName.FONTDESCRIPTOR);
            if (descriptor != null)
            {
                // Read font name and family
                var family = descriptor.GetAsString(PdfName.FONTFAMILY);
                yield return new PdfFontInfo(fontKey, family.ToUnicodeString());
            }
        }
    }

    static IReadOnlyList<BaseFont> CreateSubstitutionFontsForFields(PdfReader reader)
    {
        if (reader.AcroForm.Fields == null)
            return new List<BaseFont>(0);
        var documentFontMap = ReadDocumentFonts(reader).ToDictionary(f => f.Name, StringComparer.InvariantCultureIgnoreCase);
        var substFonts = new Dictionary<string, BaseFont>();
        var fallbackRequired = false;

        // Read font information of each field
        foreach (var field in reader.AcroForm.Fields)
        {
            var fieldFontDa = field.Info.GetAsString(PdfName.DA);
            if (fieldFontDa == null)
                continue;
            var parts = AcroFields.SplitDAelements(fieldFontDa.ToUnicodeString());
            if (parts.Length == 0)
                continue;
            var fontName = (string) parts[0];
            PdfFontInfo inf;
            if (documentFontMap.TryGetValue(fontName, out inf))
            {
                if (!substFonts.ContainsKey(fontName))
                {
                    var font = FontFactory.GetFont(fontName, BaseFont.IDENTITY_H, true).BaseFont;
                    substFonts.Add(fontName, font);
                }
            }
            else
                fallbackRequired = true;
        }
        var allFonts = new List<BaseFont>(substFonts.Values);
        if (fallbackRequired)
            allFonts.Add(FALLBACK_FONT);
        return allFonts;
    }

If you can find any errors, you are welcome to comment.