You are not logged in.
Pages: 1
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
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...
Online
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
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.
Online
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 )
Offline
I've received the emails...
Sujet: Clipping correction
Date: 2/3/2011 19:50:18Sorry 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:
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!
Online
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
Offline
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
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.
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...
Online
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
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
Offline
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!
3 little characters... just one space in the pdf content...
Nice catch, indeed!
Online
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
Offline
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
Thanks for the report.
Online
Pages: 1