Ways to reproduce the problem:
1. Create simple one table one column report, with right horizontal text alignment in the column.
2. Set a data source with multiple rows, with different lengths for the string, displayed in the table's column.
3. Render the report under Windows and under Linux - the column text on different rows will not be aligned properly on linux, but will be shifted to the left or right depending on string's contents and length.
I've done a bit reserch on PDF rendering, concerning libgdiplus "MeasureString" method deficiencies, and here's what I discovered so far:
1. The source of the problem is the fact that libgdiplus ends up using cairo to measure string glyphs, and it does it's font metric in whole pixels (integer), using the DPI of the context it is called in. In short, if the context is using 96DPI, cairo will use these 96DPI to calculate (and round) the font metrics, and to write strings eventually.
2. When libgdiplus is compiled to use pango, the problem is much smaller (several millimeter differences become part of the millimeter), because internally pango scales the measurement 1024 times, but it appears that this is not enough to resolve the problem.
3. When using libgdiplus (with or without pango inbetween it and cairo) for both "DrawString" and "MeasureString", everything is perfect, but the fundamental problem in Relerik Reporting to PDF is that it renders in a vector format, but uses text metering for pixel format - it uses System.Graphics.MeasureString which is a DPI vased metering (on Linux at least) but uses proprietary "PdfRenderer.DrawString" of the "PdfRendered" class, not the native Graphics.DrawString. Since cairo rounds every character glyph metrics to an integer value, the longer the string is, the larger the discrepancy will be between string rendering when PDF is viewed (based on floats/doubles) and Graphics.MeasureString (based on integers, at least on Linux).
Ways to resolve the issue:
1. The best way to resolve the issue is not to call Graphics.MeasureString at all, when rendering to vector based format, because it measures string based on the DPI on graphics context it is in. In other words, proprietary "PdfRenderer.MeasureString" must be implemented, which works with font glyphs directly, entirely using single or double precision math to calculate glyph dimensions and string dimensions.
2. Another, not so perfect, but HUGELY EASIER way is to set DPI of the rendering context of the PDF to a higher value, here's how:
For the moment, PdfContext's constructor looks like thispublic PdfContext(string ownerPassword, string userPassword)
{
this.dataFormatter = string.IsNullOrWhiteSpace(ownerPassword) ?
new DataFormatter() :
new DataFormatterEncypted(ownerPassword, userPassword);
this.bmp = new Bitmap(1, 1);
this.graphics = Graphics.FromImage(this.bmp);
this.hdc = this.graphics.GetHdc();
this.pdfFontCache = new PdfFontCache();
}
public PdfContext(string ownerPassword, string userPassword)
{
this.dataFormatter = string.IsNullOrWhiteSpace(ownerPassword) ?
new DataFormatter() :
new DataFormatterEncypted(ownerPassword, userPassword);
this.bmp = new Bitmap(1, 1);
bmp.SetResolution(9600, 9600); //ADDED
this.graphics = Graphics.FromImage(this.bmp);
this.hdc = this.graphics.GetHdc();
this.pdfFontCache = new PdfFontCache();
}