I'm implementing PDF export of 'dashboards' using XSL-FO (with Apache FOP as the rendering engine). These dashboards are defined as a set of rectangular elements rendered within an area of a particular size, whose positions are defined by percentage top/left offsets and percentage widths/heights. Rendering this using XSL-FO absolute positioning seemed like the obvious solution, and for the most part works well.
The following XSL-FO:
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-size="10pt" id="sequence-id">
<fo:layout-master-set>
<fo:simple-page-master master-name="page-layout" page-width=" 4in " page-height=" 4in ">
<fo:region-body region-name="document-content" margin="0.5in"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="page-layout" initial-page-number="1">
<fo:flow flow-name="document-content">
<fo:block-container border="1px solid green" height="3in">
<fo:block-container absolute-position="absolute" width="50%" left="0" top="0" height="40%" border="1px solid red">
<fo:block border="1px solid blue" background-color="green">I am some content. It is possible for me to occupy the entire width of my allocated area.</fo:block>
</fo:block-container>
<fo:block-container absolute-position="absolute" width="50%" left="50%" top="0" height="50%" border="1px solid red">
<fo:block border="1px solid blue" background-color="green">I am some content</fo:block>
</fo:block-container>
<fo:block-container absolute-position="absolute" width="50%" left="0" top="40%" height="60%" border="1px solid red">
<fo:block border="1px solid blue" background-color="green">I am some content</fo:block>
</fo:block-container>
<fo:block-container position="absolute" width="50%" left="50%" top="50%" height="50%" border="1px solid red">
<fo:block border="1px solid blue" background-color="green">I am some content</fo:block>
</fo:block-container>
</fo:block-container>
</fo:flow>
</fo:page-sequence>
</fo:root>
...results in the following output:
The green areas, representing the content of a dashboard element, butt up against each other, which is ugly. So, I want to add some padding around the content. If I add padding to the block-container
representing the first dashboard element:
<fo:block-container absolute-position="absolute" padding="5px" width="50%" left="0" top="0" height="40%" border="1px solid red">
...the (red) border of the dashboard element expands outward rather than adding padding inward:
This is because, in contrast to CSS, absolute positioning offsets in XSL-FO address the content rectangle (i.e., inside the padding) rather than a rectangle including padding and borders. Per the spec:
These properties set the position of the content-rectangle of the associated area.
I want some way to add padding/a border inside the area I allocate to the dashboard element via absolute positioning attributes. I've tried various things around putting the content inside another nested block-container
or block
, but nothing I've tried works properly. As a last resort, I could compute the offsets/dimensions to effectively leave some blank space between elements, but this feels like something that should be handled automatically by the rendering engine. Any suggestions?
Easiest way for all dimensions ... add a block inside the block container in which you add all your content inside. This block should have your desired margin. So:
<fo:flow flow-name="document-content">
<fo:block-container border="1px solid green" height="3in">
<fo:block-container absolute-position="absolute" width="50%" left="0" top="0" height="40%" border="1px solid red">
<fo:block margin="5px">
<fo:block border="1px solid blue" background-color="green">I am some content. It is possible for me to occupy the entire width of my allocated area.</fo:block>
</fo:block>
</fo:block-container>
<fo:block-container absolute-position="absolute" width="50%" left="50%" top="0" height="50%" border="1px solid red">
<fo:block margin="5px">
<fo:block border="1px solid blue" background-color="green">I am some content</fo:block>
</fo:block>
</fo:block-container>
<fo:block-container absolute-position="absolute" width="50%" left="0" top="40%" height="60%" border="1px solid red">
<fo:block margin="5px">
<fo:block border="1px solid blue" background-color="green">I am some content</fo:block>
</fo:block>
</fo:block-container>
<fo:block-container position="absolute" width="50%" left="50%" top="50%" height="50%" border="1px solid red">
<fo:block margin="5px">
<fo:block border="1px solid blue" background-color="green">I am some content</fo:block>
</fo:block>
</fo:block-container>
</fo:block-container>
</fo:flow>
Yields this: