Skip to content

Margins

Margins are the white space Chrome leaves around the rendered page, applied at print time rather than as CSS. Set all four at once, or set them individually with a unit of your choosing.

if err := doc.SetPageSize(sizes.A5); err != nil {
    return err
}
doc.SetMarginLeft(sizes.Millimeter(10))
doc.SetMarginRight(sizes.Point(10))
doc.SetMarginTop(sizes.Millimeter(10))
doc.SetMarginBottom(sizes.Millimeter(13))

The SetPageSize call at the top is not decoration. Without it, or without some other call that switches the document to absolute positioning, all four individual setters are thrown away.

Two calls that look interchangeable

CallUnitPositioning
SetMargins(left, right, top, bottom)float64, always pointsSwitches to absolute
SetMarginLeft(sizes.Length) and siblingsAny sizes.LengthLeaves it alone

SetMargins takes four bare float64 values interpreted as points, in the order left, right, top, bottom. Check that order against your own code. CSS shorthand goes clockwise from the top, and this does not.

The individual setters accept sizes.Millimeter, sizes.Point or sizes.Inch, which is more convenient and is where both of the problems below come from.

A lone margin setter does nothing

A new unihtml.Document starts in relative positioning. While it stays relative, all four margins are overwritten with 1mm immediately before the request is built, whatever you set. Measured on a Letter page:

CallsLeft margin in the output
none2pt, the forced 1mm
SetMarginLeft(sizes.Millimeter(50))2pt, the value discarded
SetMargins(0, 0, 0, 0) then SetMarginLeft(sizes.Millimeter(50))141pt, correct
SetPageSize(sizes.A5) then SetMarginLeft(sizes.Millimeter(50))141pt, correct

SetMargins, SetPageSize, SetPageWidth, SetPageHeight and SetPos all flip positioning to absolute. Any one of them, called first, makes the individual setters work. SetLandscapeOrientation does not, so it is no help here.

Once positioning is absolute, sides you never set default to 10mm rather than 1mm.

Fractional values round to whole units

Lengths are serialized with no decimal places, so the value the server sees is rounded before it is used. sizes.Inch(0.5) becomes 0in:

You writeServer receives
sizes.Inch(0.5)0in, no margin at all
sizes.Inch(1)1in
sizes.Millimeter(12.7)13mm
sizes.Point(10.4)10pt

Half an inch is the case that bites, because it is a natural margin to ask for and it lands on zero. Two of the examples in the repository call SetMarginBottom(sizes.Inch(0.5)) and neither gets a bottom margin.

Millimeters and points are fine granularity for page margins, so use those. Half an inch is sizes.Millimeter(13).

Do not reach for sizes.Inch(1).Points() to convert your way around this. That conversion is wrong in the current release and returns 0.0pt.

Limitations

Margins are ignored entirely on the c.Draw path, where the creator’s page margins apply instead.

A negative value is rejected before the request goes out, with negative value for MarginLeft or the equivalent for the other sides.

Margins are print parameters, not CSS, so they do not cascade and cannot be overridden per page from the stylesheet. For space that varies through the document, use CSS padding on the elements instead.

Run the example

margins-and-properties/main.go converts basic.html on A5 landscape, sets each of the four margins with a different unit type, then sets the PDF’s author, title, subject and dates through UniPDF’s model package before writing result.pdf.

Its SetMarginBottom(sizes.Inch(0.5)) is the rounding case above, so the output has no bottom margin. The other three sides come through.

git clone https://github.com/unidoc/unihtml-examples.git
cd unihtml-examples/margins-and-properties
go run main.go localhost:8080

If this is your first time using UniHTML, follow the getting started guide to create an API key and set up your development environment.

View the full source

Sample output

A5 landscape page with a 10mm left margin and no bottom margin

Last updated on