logo
Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

Options
Go to last post Go to first unread
richard  
#1 Posted : Tuesday, July 2, 2019 9:58:25 AM(UTC)
richard

Rank: Member

Groups: Registered
Joined: 5/9/2019(UTC)
Posts: 10
Australia
Location: Sydney

Hello,

I'm trying to get the width of the text in a textbox in order to compare it with the width of the textbox itself. I've found the various methods for getting fonts and string widths but haven't had much luck with them (most certainly I'm missing something). Here's a sample of code I've played around with:

Code:

if (Document.SelectedField is PdfFieldViewModel fld)
{
   PdfControl ctrl = fld.Field.Controls.FirstOrDefault();

   if (ctrl != null)
   {
      var text = "MMMMMMMMMM";
      var font = Pdfium.FPDFFormControl_GetDefaultControlFont(ctrl.Handle);
      var fontName = Pdfium.FPDFFont_GetFontTypeName(font);
      var textWidth = Pdfium.FPDFFont_GetStringWidth(font, text);
   }
}


The control in question 132.93 wide and has a font (in its Dictionary) of "/Helv 10 Tf 0 g". However the fontName from the above code evaluates as "Type1" and the textWidth of the text string (which just fills the textbox) as 8330.

I've also tried the PDFFont.GetStringWidth() method with similar results.

Am I going about this the wrong way? Or is there some step I'm missing? Font size doesn't seem to come into the equation with these string width methods, maybe I need a conversion for the width values I'm getting?

Thanks a lot!

Richard
richard  
#2 Posted : Tuesday, July 2, 2019 10:00:09 AM(UTC)
richard

Rank: Member

Groups: Registered
Joined: 5/9/2019(UTC)
Posts: 10
Australia
Location: Sydney

Just reading that again and realised the code was getting/using a default font. Is there a way to use these methods (or similar) with the actual font of the control?
Paul Rayman  
#3 Posted : Tuesday, July 2, 2019 10:28:50 AM(UTC)
Paul Rayman

Rank: Administration

Groups: Administrators
Joined: 1/5/2016(UTC)
Posts: 760

Thanks: 1 times
Was thanked: 92 time(s) in 91 post(s)
Try the following

Code:
PdfTextObject tempTextObj = PdfTextObject.Create(text, 0, 0, font, fontSize);
var width = tempTextObj.BoundingBox.Width;


Also, if the text is already contained in the control, you can try the following.
In fact, the control is a widget annotation. Thus, you can convert it to an annotation and get all the text objects from its normal appearance stream.

Code:
PdfWidgetAnnotation annot = new PdfWidgetAnnotation(page, ctrl.Dictionary);
foreach( var obj in annot.NormalAppearance)
{
    if (!(obj is PdfTextObject))
        continue;
    var width = obj.BoundingBox.Width;
}


Originally Posted by: richard Go to Quoted Post
"/Helv 10 Tf 0 g"

Also the following should work
Code:
string text = "abcdefg";
float fontSize = 10.0f;
PdfFont font = PdfFont.CreateStock(doc, FontStockNames.Helvetica);
var width = font.GetStringWidth(text) / 1000.0f * fontSize;

Edited by user Tuesday, July 2, 2019 11:17:05 AM(UTC)  | Reason: Not specified

richard  
#4 Posted : Wednesday, July 3, 2019 2:15:12 AM(UTC)
richard

Rank: Member

Groups: Registered
Joined: 5/9/2019(UTC)
Posts: 10
Australia
Location: Sydney

Hi Paul,

Thanks again for the always excellent help!

FYI the first two methods give the exact same width result while the third one is always +1.5 bigger. Also getting from "Helv" to FontStockNames.Helvetica for the third method is a bit fraught. Think I'll stick with the annots method.

Regards,

Richard
richard  
#5 Posted : Thursday, July 4, 2019 9:18:10 AM(UTC)
richard

Rank: Member

Groups: Registered
Joined: 5/9/2019(UTC)
Posts: 10
Australia
Location: Sydney

Hi Paul,

I'd like to use the first method you mentioned, but as I said in my last reply it's a bit of a bother to go from "Helv" to FontStockNames.Helvetica when creating a font (or whatever other font a control might happen to have). Is there a way to get a PdfFont from an existing control which (seemingly) only has the "/Helv 10 Tf 0 g" dictionary item?

Regards,

Richard
Paul Rayman  
#6 Posted : Saturday, July 6, 2019 2:02:17 AM(UTC)
Paul Rayman

Rank: Administration

Groups: Administrators
Joined: 1/5/2016(UTC)
Posts: 760

Thanks: 1 times
Was thanked: 92 time(s) in 91 post(s)
Do you mean the dictionary key DA?

The DA is the default appearance string containing a sequence of valid page-content graphics or text state operators that define such properties as the field’s text size and color.

You can try to parse the DA string using Pdfium.FPDFTOOLS_ParseDefaultAppearance(...).
Then create the font using the PdfFont.CreateStandardFont(doc, fontName, 1), where fontName is a string representation of the standard font.
The CreateStandardFont method can only parse the following font names, so I think "/Helv 10 Tf 0 g" is either a mistakenly formed font name, or is a reference to the font in the "DR" entry or in the page’s resource dictionary. Try to explore each of them: document.Root["AcroForm"]->"DR"->"Font", page.Dictionary["Resources"]->"Font", ctrl.Dictionary["DR"], perhaps there is a key named Helv, which contains the actual font, an instance of which can be created through a call of PdfFont.CreateFont(document, fontDictionary) method. Where fontDictionary is a font dictionary contained under Helv key as direct or indirect object.


The valid names for standard fonts are:
Quote:
{"Arial", 4},
{"Arial,Bold", 5},
{"Arial,BoldItalic", 6},
{"Arial,Italic", 7},
{"Arial-Bold", 5},
{"Arial-BoldItalic", 6},
{"Arial-BoldItalicMT", 6},
{"Arial-BoldMT", 5},
{"Arial-Italic", 7},
{"Arial-ItalicMT", 7},
{"ArialBold", 5},
{"ArialBoldItalic", 6},
{"ArialItalic", 7},
{"ArialMT", 4},
{"ArialMT,Bold", 5},
{"ArialMT,BoldItalic", 6},
{"ArialMT,Italic", 7},
{"ArialRoundedMTBold", 5},
{"Courier", 0},
{"Courier,Bold", 1},
{"Courier,BoldItalic", 2},
{"Courier,Italic", 3},
{"Courier-Bold", 1},
{"Courier-BoldOblique", 2},
{"Courier-Oblique", 3},
{"CourierBold", 1},
{"CourierBoldItalic", 2},
{"CourierItalic", 3},
{"CourierNew", 0},
{"CourierNew,Bold", 1},
{"CourierNew,BoldItalic", 2},
{"CourierNew,Italic", 3},
{"CourierNew-Bold", 1},
{"CourierNew-BoldItalic", 2},
{"CourierNew-Italic", 3},
{"CourierNewBold", 1},
{"CourierNewBoldItalic", 2},
{"CourierNewItalic", 3},
{"CourierNewPS-BoldItalicMT", 2},
{"CourierNewPS-BoldMT", 1},
{"CourierNewPS-ItalicMT", 3},
{"CourierNewPSMT", 0},
{"CourierStd", 0},
{"CourierStd-Bold", 1},
{"CourierStd-BoldOblique", 2},
{"CourierStd-Oblique", 3},
{"Helvetica", 4},
{"Helvetica,Bold", 5},
{"Helvetica,BoldItalic", 6},
{"Helvetica,Italic", 7},
{"Helvetica-Bold", 5},
{"Helvetica-BoldItalic", 6},
{"Helvetica-BoldOblique", 6},
{"Helvetica-Italic", 7},
{"Helvetica-Oblique", 7},
{"HelveticaBold", 5},
{"HelveticaBoldItalic", 6},
{"HelveticaItalic", 7},
{"Symbol", 12},
{"SymbolMT", 12},
{"Times-Bold", 9},
{"Times-BoldItalic", 10},
{"Times-Italic", 11},
{"Times-Roman", 8},
{"TimesBold", 9},
{"TimesBoldItalic", 10},
{"TimesItalic", 11},
{"TimesNewRoman", 8},
{"TimesNewRoman,Bold", 9},
{"TimesNewRoman,BoldItalic", 10},
{"TimesNewRoman,Italic", 11},
{"TimesNewRoman-Bold", 9},
{"TimesNewRoman-BoldItalic", 10},
{"TimesNewRoman-Italic", 11},
{"TimesNewRomanBold", 9},
{"TimesNewRomanBoldItalic", 10},
{"TimesNewRomanItalic", 11},
{"TimesNewRomanPS", 8},
{"TimesNewRomanPS-Bold", 9},
{"TimesNewRomanPS-BoldItalic", 10},
{"TimesNewRomanPS-BoldItalicMT", 10},
{"TimesNewRomanPS-BoldMT", 9},
{"TimesNewRomanPS-Italic", 11},
{"TimesNewRomanPS-ItalicMT", 11},
{"TimesNewRomanPSMT", 8},
{"TimesNewRomanPSMT,Bold", 9},
{"TimesNewRomanPSMT,BoldItalic", 10},
{"TimesNewRomanPSMT,Italic", 11},
{"ZapfDingbats", 13},


So the algorithm is:
1. Parse Default Appearance (DA) and get fontName (using FPDFTOOL_ParseDefaultAppearance);
2. Check if fontName is a standard name;
2.1. if so - Use CreateStandardFont;

3. find font dictionary under fontName entry in
3.1. document.Root -> AcroForm -> DR
3.2 "DR" entry in ctrl.Dictionary
3.3 page.Dictionary -> Resources -> Font
(i'm not sure about 3.2 and 3.3 - perhaps this is unnecessary actions)
4. if found - create font using CreateFont
5. if not found - DA is broken;


You can still use annotations instead.
Perhaps your task can be solved as follows.
Code:
public float GetStringWidth(PdfControl ctrl, string text, PdfPage page)
{
    if (ctrl == null || page == null || string.IsNullOrEmpty(text))
        return 0;

    string oldText = ctrl.Field.Value;
    try
    {
        ctrl.Field.Value = text;
        using (var ann = PdfAnnotation.Create(ctrl.Dictionary, page))
        {
            if(ann.NormalAppearance.Count>0)
                return ann.NormalAppearance[0].BoundingBox.Width;
            else
                return 0;
        }
    }
    finally
    {
        ctrl.Field.Value = oldText;
    }
}


Code:
using (var document = PdfDocument.Load(@"e:\9\test.pdf", new PdfForms()))
{
    var ctrl = document.FormFill.InterForm.Controls[0];
    var page = document.Pages[0]; //https://forum.patagames.com/posts/t559-Return-the-Page-a-Field-is-On

    float width = GetStringWidth(ctrl, "t1", page);
    width = GetStringWidth(ctrl, "MMM", page);
    width = GetStringWidth(ctrl, "qwerty", page);


}

Edited by user Saturday, July 6, 2019 3:15:25 AM(UTC)  | Reason: Not specified

richard  
#7 Posted : Monday, July 8, 2019 11:16:01 PM(UTC)
richard

Rank: Member

Groups: Registered
Joined: 5/9/2019(UTC)
Posts: 10
Australia
Location: Sydney

Hey Paul,

Looks like Document.Root Acroform DR does have a font entry with "Helv". Should be able to use that with your algorithm.

Thanks a lot!

Richard
Users browsing this topic
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.