When working with page layout in scryber, it is important to understand the default units and how measurements are made on the page.
At the heart of these are the main structures : PDFUnit; PDFSize; PDFPoint; PDFRect and PDFThickness
A single unit is a combination of a magnitude and a scale.
For example 72pt or 1in or 25.4mm. All these are equivalent as there are 72 points in an inch and 25.4 millimetres. If no scale is provided then Points (a typographic Point, not an XY position) is used by the scryber library. There is no equivalent of the pixel measurement in scryber as it is a display agnostic description of a page.
<pdf:Page style:width="210mm" style:height="290mm" style:border-width="1" >
PDFPage page = new PDFPage();
page.Width = new PDFUnit(210, PageUnits.Millimeters);
page.Height = new PDFUnit(290, PageUnits.Millimeters);
page.BorderWidth = 1;//implicict cast to point value
The PDFUnit supports only the 3 units specified
However the actual value is always stored within the PDFUnit structure as a double representation of Points.
This makes calculations fast and accurate with no required conversions during calculation, and only the extraction of the value requiring re-conversion back to the specified unit. This is actually done very infrequently within the library and most extraction is to retrieve the PointsValue rather than the units value - because points are what PDF documents use (by default).
For .NET languages that support operator overloading (inc. C#) the PDFUnit structure offers full comparison and calculation operators, that can be used within program flow.
PDFUnits also include conversion to and from string values with units (mm, pt, in).
PDFUnit seventytwo = new PDFUnit(72, PageUnits.Points); // 72pt
PDFUnit oneIn = PDFUnit.Inch(1); // 1in
System.Diagnostics.Debug.Assert(seventytwo == oneIn) //compare independant of units
PDFUnit sum = seventytwo + oneIn;
PDFUnit converted = PDFUnit.Convert(sum, PageUnits.Millimeters);
string s = converted.ToString();
System.Diagnostics.Debug.Assert(s == "50.8mm");
PDFUnit parsed = PDFUnit.Parse(s); //conversion back from a string
parsed *= 2; //double it, retaining units
s = parsed.ToString();
System.Diagnostics.Debug.Assert(s == "101.6mm");
System.Diagnostics.Debug.Assert(parsed / 4 == 72); //implicit conversion from number to Points value
For those languages that do not support operator overloading there are matching static methods on the PDFUnit class - Add, Subtract, Divide, Multiply, Equals, NotEquals etc.
Points, Sizes, Rects and Thickness
Most of these structures are direct matches to the System.Drawing structures in the core framework. but store their values as PDFUnits, rather than integers or floats
- A location specified in vertical and horizontal units (X,Y). Supports comparison to another PDFPoint or Empty and the equatable operators.
- A vertical and horizontal dimension (Width and Height). Also supports comparison to another PDFSize or Empty and the equatable operators.
- A rectangle structure with a top, left, width and height. Includes properties for Location and Size of PDFPoint and PDFSize, a property for IsEmpty - if all values are zero, and also calculation and manipulation methods for comparison, containment, insetting inflating, union and intersection.
- Whilst there is no direct .NET matching structure, the PDFThickness encapsulates dimensions on each side of the rectangle (top, left, bottom and right). The PDFThickness is used in Padding and Margin calculations, and supports addition, subtraction, equality comparison and also parsing from a string value.
Page location and positioning
All drawing operations and layout within scryber is by default done from the top left of the page. This is in contrast to the native positioning within a PDF document where all positioning is from the bottom left of the page
The top left is a more natural origin (within western cultures at least) for page layout and flowing. Content moves down the page as it fills up.
It is the PDFGraphics class that handles this translation from top left measurement to bottom left PDF native positioning, but any output that does not go through the PDFGraphics class must handle this conversion before writing.
using (PDFDocument doc = new PDFDocument())
doc.Compression = OutputCompression.None;
PDFPage pg = new PDFPage();
PDFLabel lbl = new PDFLabel();
lbl.Text = "Positioned text";
lbl.X = 100;
lbl.Y = 200;
3 0 obj
<< /Type /Page
/Parent 2 0 R
/MediaBox [0.00 0.00 596.13 841.89 ]
/Contents 4 0 R
/Resources 7 0 R
4 0 obj
<< /Length 199
q BT 0.00 0.00 0.00 rg /frsc1 24.00 Tf 0 Tr 100.00 620.16 Td <003300520056004C0057004C0052005100480047000300570048005B0057> Tj ET Q q 72.00 0.00 0.00 19.50 495.78 28.35 cm /imgx1 Do Q
As we can see the label was defined as position 100pt, 200pt. and is at this position from the top left of the rendered page, but within the actual generated PDF document it's rendered position is 100.00 620.16.
This is because the page is 841 points high (so 200 points down is 641 points up) and the base line height, where text is rendered from in PDF, for a 24 point font pushes this down to 620.16. Exactly where we wanted this text to be.