Page breaking algorithm

First of all, keep in mind that "smart" page breaking algorithm is available in 2.0 branch since 2.0.9; there's no reason to use 2.1 experimental versions, as all features were ported. The difference between 2.1 and 2.0 is, in particular, in the following: 2.1 has smart page breaks enabled by default, while 2.0 uses it only if 'smartpagebreak' setting is set to true. If you're calling 'configure' when initializing pipeline, you notice no difference, as it will be enabled by default. If you're not using 'configure' call ($g_config is set up manually) you should explicitly set 'smartpagebreak' element in your $g_config to 'true'.

To find the exact position of the next page break, script calculates a penalty (a badness, if you prefer) of every possible page break position, and selects one with the minimal penalty value. Possible break positions are horizontal lines between line and block-level boxes; inside tables they're horizontal row boundaries and horizontal lines passing between line boxes in all table cells in a row (thus, if you're using different font sizes or aligments in different cells, there's a big chance there won't be suitable page break places inside this row at all).

Penalty is assigned for the following:
breaking the 'page-break-inside: avoid' rule PAGE_BREAK_INSIDE_AVOID_PENALTY
breaking the 'page-break-after: avoid' rule PAGE_BREAK_AFTER_AVOID_PENALTY
breaking the 'page-break-before: avoid' rule PAGE_BREAK_BEFORE_AVOID_PENALTY
leaving less than 'orphans' lines on the next page PAGE_BREAK_ORPHANS_PENALTY
leaving less than 'widows' lines on the next page PAGE_BREAK_WIDOWS_PENALTY
breaking between line boxes instead of block-level boxes PAGE_BREAK_LINE_PENALTY
breaking inside box having non-zero padding or border PAGE_BREAK_BORDER_PENALTY

All assigned penalties stack for each positions; after that a "free space" penalty is added. This additional value is used to prevent too much free space at the bottom of the page and is calculated using the following rules:

free space penalty = 0, if distance from the bottom page edge / page height < MAX_UNPENALIZED_FREE_FACTION, or
free space penalty = MAX_PAGE_BREAK_HEIGHT_PENALTY, if distance from the bottom page edge / page height > MAX_FREE_FACTION, or
free space penalty = MAX_PAGE_BREAK_HEIGHT_PENALTY * ((distance from the bottom page edge / page height) - MAX_UNPENALIZED_FREE_FRACTION) / (MAX_FREE_FRACTION - MAX_UNPENALIZED_FREE_FRACTION), otherwise

After this, script selects the position with the least penalty value and proceeds to the next page.

Refer to default.css for the default CSS rules related to page breaking, and to config.inc.php for default values of penalties