#1 2011-02-28 12:01:42

reddwarf
Member
Registered: 2010-06-28
Posts: 27
Website

Wrong character width

Hi Arnaud,

I am facing a similar problem as Pavel in his "Problems with Courier New font" thread.

The thing is, that for example the Tahoma bold 8pt font is not drawn exactly 1:1 as on normal window canvas. Particularly the character width is wrong. This results in displaced words when exporting RTF or words being clipped when using ExtTextOutW with ETO_CLIPPED. The problem can be observed when importing the PDF into Adobe InDesign (I tested CS5), in which the characters are shown twice and are overlapping.

Look at this code. With SynPdf v1.12 the last ":" is cut off from the rectangle (8pt font):

procedure TForm1.BtnTestFontClick(Sender: TObject);
var
  xRect: TRect;
const
  Text: WideString = 'RERERERE:';
begin
  with TPdfDocumentGDI.Create do
  try
    AddPage;
    UseUniScribe := true; //uniscribe does not change anything about the problem
    with VCLCanvas do begin
      Font.Name := 'Tahoma';
      Font.Size := 8;
      Font.Style := [fsBold];
      Pen.Color := $AAAAAA;

      xRect := Rect(0, 0, TextWidth(Text), TextHeight(Text));
      OffsetRect(xRect, 100, 100);

      Rectangle(xRect);

      Windows.ExtTextOutW(Handle, xRect.Left, xRect.Top, ETO_CLIPPED,
        @xRect, PWideChar(Text), Length(Text), nil);

      Font.Size := 12;

      xRect := Rect(0, 0, TextWidth(Text), TextHeight(Text));
      OffsetRect(xRect, 100, 200);

      Rectangle(xRect);

      Windows.ExtTextOutW(Handle, xRect.Left, xRect.Top, ETO_CLIPPED,
        @xRect, PWideChar(Text), Length(Text), nil);

    end;
    SaveToFile('TestVcl.pdf');
  finally
    Free;
  end;
end;

Thanks for great PDF library!

Offline

#2 2011-02-28 17:09:02

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 5,651
Website

Re: Wrong character width

I think this is (maybe) because our PDF library doesn't implement font kerning.

Therefore, the TextWidth() size is not the same as the one written with our PDF library.

You reached the limit of the engine.

It's possible to add some kerning-like handling in the pdf, but the pdf file will be bigger than it is currently, because it would have to position exactly each character, whereas the current implementation handle whole string as a whole...

Offline

#3 2011-03-02 10:34:59

reddwarf
Member
Registered: 2010-06-28
Posts: 27
Website

Re: Wrong character width

I also had the idea to draw each character separately, but as you said, the resulting file is just toooo big when using this method for all texts although the characters are placed (nearly) exactly.
something like this:

  procedure PDFExactTextOut(aCanvas: TCanvas; aText: String; aRect: TRect);
  var I, xX: Integer;
    xS: String;
  begin
    for I := 1 to Length(aText) do begin
      xX := aCanvas.TextWidth(Copy(aText, 1, I-1));
      xS := Copy(aText, I, 1);
      Windows.ExtTextOut(aCanvas.Handle, aRect.Left+xX, aRect.Top, ETO_CLIPPED,
        @aRect, PChar(xS), 1, nil);
    end;
  end;

do you think is there any way how to get the string length without kerning?
I tried to compute the width of each character and to sum them, but it did not help.
The problem can be observed also when using underline/strikeout font:

procedure TForm1.BtnTestUnderlineClick(Sender: TObject);
const
  Text: String = 'RERERERE:';
begin
  with TPdfDocumentGDI.Create do
  try
    AddPage;
    //UseUniScribe := true; //uniscribe does not change anything about the problem
    with VCLCanvas do begin
      Font.Name := 'Tahoma';
      Font.Size := 8;
      Font.Style := [fsBold, fsUnderline];

      TextOut(100, 100, Text);
    end;
    SaveToFile('TestUnderline.pdf');
  finally
    Free;
  end;
end;

I don't need kerning. But what would be great is to get the exact string width as the string is shown in the pdf - if it is possible. I am sorry to say that I cannot solve this problem by myself.

Offline

#4 2011-03-02 13:59:34

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 5,651
Website

Re: Wrong character width

Take a look at TPdfEnum.TextOut

The DX: PIntegerArray is filled with individual positions of each glyph.

It's currently not used by the method for individual character positioning.

Two improvements possibilities:

1) use the DX[] values to call MoveTextPoint then ShowText/ShowGlyph for each char.

2) set globally the SetHorizontalScaling() value, in a similar way of SetWordSpace() is called to allow a SetTextJustification() call.

IMHO

1) would increase the pdf size a lot. We could make this kerning optional.

2) is perhaps the easiest to implement this global horizontal scaling, like this:

hscale := (W*100)div measW;
if (hscale<99) or (hscale>106) then
  // implement some global kerning to avoid clipping
  Canvas.SetHorizontalScaling(hscale);

I found out that the 2nd solution seems sufficient.
The PDF file size won't increase, and in most cases, this horizontal scaling won't apply (99,100..106 are allowed scales).

I've implemented this 2nd fix in the main source code repository.
See http://synopse.info/fossil/info/66177c6117 and http://synopse.info/fossil/info/d454918df9

Hope it will fix your problem.
Could also solve issue when using underline/strikeout font.

Offline

#5 2011-03-02 16:13:09

reddwarf
Member
Registered: 2010-06-28
Posts: 27
Website

Re: Wrong character width

Arnauld, it is clear, that you are the great guru here. Your starting point (2) was the right solution! I played with it a little bit and came to a result that satisfies me completely. Particularly I made these additional changes to your code so that the characters are printed exactly 1:1:
A) I changed the HorizontalScaling property to SINGLE for more precision
B) I used the HorizontalScaling for all fonts, not only for: if (hscale<99) or (hscale>106)

with these 2 small changes, the string positions are EXACT. Thanks for help! (I send you the changes by email, but there are not many wink)

Offline

#6 2011-03-02 20:55:34

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 5,651
Website

Re: Wrong character width

I've received the emails...

reddwarf wrote:

Sujet: Clipping correction
Date: 2/3/2011 19:50:18

Sorry for so much spam from me, I promise this is the last one!


I found that the clip rect and the Y-position of the text are not where
they should be - some characters (yg...) were clipped at the bottom and
the underline was not drawn since it came out of the clip rect.


I played and optimized the TPdfEnum.TextOut procedure to get the best
possible result with different fonts and sizes.

Best

Ondrej

Here are some comments:

Arnaud wrote:

Hello Ondrej,

Great corrections!

I've just a concern with this one:

Canvas.MoveToI(Left-0.5, Top+0.5);
Canvas.LineToI(Left-0.5, Bottom+0.5);
Canvas.LineToI(Right-0.5, Bottom+0.5);
Canvas.LineToI(Right-0.5, Top+0.5);

Since R.emrText.rcl .Left/right/... are integer, the correction above just substract 1 or add 1..

So here is my version sent to the source code repository (similar effect, but no conversion to/from single):

Canvas.MoveToI(Left-1, Top+1);
Canvas.LineToI(Left-1, Bottom+1);
Canvas.LineToI(Right-1, Bottom+1);
Canvas.LineToI(Right-1, Top+1);

And... to be honest, I don't like the fact that SetWordSpace() has been disabled.
It was introduced on purpose, when the GDI SetTextJustification() API was used, to justify some text.
I've changed the trigerring effect with this one:

      if (nspace>0) and (W-measW>nspace*2) then begin

Then it will use SetHorizontalScaling() for smaller modifications.
The result is just as expected with the "05 - Report created from code" sample program, using SQLIte3Pages to generate some justificated text using the GDI SetTextJustification() API.

And, in order to maintain a minimal PDF size, I've still maintained an hysteresis around 100:

        if (hscale<99) or (hscale>100) then
          Canvas.SetHorizontalScaling(hscale) else
          hscale := 100;

Thanks a lot!!!!

A bientôt

A. Bouchez

And now it's committed to the source code repository...
see http://synopse.info/fossil/info/dc221de85a

Let's play with this version, and all feedback is welcome!

Thanks a lot, reddwarf!!!!
OpenSource is sometimes a very rewarding adventure... and Delphi rocks!
smile

Offline

#7 2011-03-02 22:23:43

reddwarf
Member
Registered: 2010-06-28
Posts: 27
Website

Re: Wrong character width

Hello Arnauld,

thanks for checking my suggestions.

          Canvas.MoveToI(Left-1, Top+1);
          Canvas.LineToI(Left-1, Bottom+1);
          Canvas.LineToI(Right-1, Bottom+1);
          Canvas.LineToI(Right-1, Top+1);

this is fine - I just did not look at that precisely.

SetWordSpace() problem:
I am sure that you know more background information than me and therefore I suppose that your code is better. So keep it as you want. I will test it and perhaps I'll come with some clear example where I'd like to have another behaviour. Until then, keep your code.
To be honest, I don't use align=justify (since standard delphi doesn't like it either) so I did not realise this purpose.

I'd like to make a suggestion regarding the SetHorizontalScaling hysteresis:
I think you could add a property to TPdfDocument that would define the range the programmer prefers. then you could use something like:

        if (hscale<Canvas.FDoc.IgnoreHScaleBottomLimit) or (hscale>Canvas.FDoc.IgnoreHScaleTopLimit) then
          Canvas.SetHorizontalScaling(hscale) else
          hscale := 100;

I think it is the best solution to offer each programmer the possibility to change the range.

+ Opening PDF Files: I have always used Adobe Acrobat 9 Pro and I am sorry to say that I have observed the problem with "asking to save document at close" since the very first version of SynPDF I used. Additionally, Adobe Acrobat shows a message that the file has to be repaired when opening a SynPDF generated file (but only for 0-2 seconds and then it automatically closes). I haven't tested other versions of Acrobat since I've bought just 9 Pro.
To be honest, I have no idea what Acrobat doesn't like about your PDF source. Everything I know about PDF is what I have been able to learn from your code... I tried the Adobe Preflight but without success...

As a second program I use SumatraPDF which is a tiny freeware pdf viewer (http://blog.kowalczyk.info/software/sum … eader.html) and it has absolutely no problem viewing your PDFs.

BEST
Ondrej

+ yesss delphi rocks wink

Offline

#8 2011-03-03 01:27:53

reddwarf
Member
Registered: 2010-06-28
Posts: 27
Website

Re: Wrong character width

this is a code example where I need UseSetTextJustification := False;

procedure TForm1.BtnTestFontClick(Sender: TObject);
var
  xRect: TRect;
  I: Integer;
const
  Text: WideString = 'Ondrej, RedDwarf:';
begin
  with TPdfDocumentGDI.Create do
  try
    KerningHScaleBottomLimit := 100;
    KerningHScaleTopLimit := 100;
    UseSetTextJustification := False;//try to use True

    AddPage;
    with VCLCanvas do begin
      Font.Name := 'Tahoma';//'Times New Roman';
      Font.Style := [fsBold, fsUnderline];
      Pen.Color := $AAAAAA;
      Img1.Canvas.Font.Assign(Font);
      Img1.Canvas.Pen.Assign(Pen);
      Img1.Canvas.Brush.Style := bsClear;

      for I := 5 to 15 do begin
        Font.Size := I;

        xRect := Rect(0, 0, TextWidthXP(Text), TextHeightXP(Text));
        OffsetRect(xRect, 100, 100+(I-5)*30);
        Rectangle(xRect);
        Windows.ExtTextOutW(Handle, xRect.Left, xRect.Top, ETO_CLIPPED,
          @xRect, PWideChar(Text), Length(Text), nil);

        //just to check with an image canvas
        Img1.Canvas.Font.Assign(Font);
        Img1.Canvas.Rectangle(xRect);
        Windows.ExtTextOutW(Img1.Canvas.Handle, xRect.Left, xRect.Top, ETO_CLIPPED,
          @xRect, PWideChar(Text), Length(Text), nil);
      end;
    end;
    SaveToFile('TestVcl.pdf');
  finally
    Free;
  end;
end;

Offline

#9 2011-03-03 17:56:40

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 5,651
Website

Re: Wrong character width

I've updated the modification to the main source code tree.
See http://synopse.info/fossil/info/81a74b3a0f

I've put 3 new values as parameters to TPdfCanvas.RenderMetaFile.
And added them as properties to TPdfDocumentGDI.
It was not a good place to put such new properties in TPdfDocument, IMHO.

redddwarf wrote:

after 2 hours of testing, debugging and hex editing, I got the problem:

procedure TPdfXrefEntry.SaveToPdfWrite(var W: TPdfWrite);
begin
  W.Add(FByteOffset,10).Add(FGenerationNumber,5).Add(FEntryType).Add('
'#10);//ONDREJ SAVE-WHEN-CLOSING-ACROBAT BUG
end;

as you can see I only added a space character there.

About the issue, thanks A LOT about spending so much time... and finding the cause! I'll check with Acrobat Reader X later...

Offline

#10 2011-03-03 22:56:50

reddwarf
Member
Registered: 2010-06-28
Posts: 27
Website

Re: Wrong character width

I downloaded your code and everything is working fine. IMHO it is not necessary to have the 3 new parameters defined at 2 places, but the most important is that it's working wink

I downloaded Acrobat Reader X and checked the new generated pdfs and they are valid.
+ I am sure that you worked much longer than 2 hours to give us SynPDF, so I am glad that I could help you a little. The only thing is, that changing 3 characters of code makes such a difference big_smile

Offline

#11 2011-03-04 06:34:24

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 5,651
Website

Re: Wrong character width

The problem was that there are two ways of rendering a metafile content into pdf:
- one with TPdfDocumentGDI
- one with TPdfCanvas.RenderMetaFile

That's the reason why I had to define those parameters at two places...
If you find of a better solution, that's fine!
wink

3 little characters... just one space in the pdf content...
Nice catch, indeed! big_smile

Offline

#12 2011-03-04 15:00:58

reddwarf
Member
Registered: 2010-06-28
Posts: 27
Website

Re: Wrong character width

If you find of a better solution, that's fine!

=> the author is always right wink

no - I am really happy that we solved the 2 issues! have a nice weekend!

Offline

#13 2011-04-18 10:35:08

mlaan
New member
Registered: 2010-10-21
Posts: 2

Re: Wrong character width

Thanks for the new parameters, encountered odd word spacing after updating to 1.12 as well but using the 1.13 SynPdf.pas from source control and UseSetTextJustification set to False all is fine again smile

Offline

#14 2011-04-18 17:48:00

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 5,651
Website

Re: Wrong character width

mlaan wrote:

Thanks for the new parameters, encountered odd word spacing after updating to 1.12 as well but using the 1.13 SynPdf.pas from source control and UseSetTextJustification set to False all is fine again smile

Thanks for the report.

Offline

Board footer

Powered by FluxBB