I think the virtual page method could be done without repeating everything. It would involve adding a flag to items added to objects, indicating whether this is a real write or if it's tentative. A "tentative" write, if it didn't overflow the page (or otherwise displease you), would be converted into a "real" write by changing the flag. Otherwise, you either keep adding "tentative" content, or erase it and do something else (new page, etc.). It might even be possible to change X and Y values of "tentative" content to re-position it (e.g., to stretch text baselines slightly to fill the page). That might require additional changes to the object data structure to mark what is a changeable address, and what should be kept relative to another location (e.g., when drawing you might want to change only absolute addresses, and not relative addresses).
(As an alternative), in general, would it be useful to be able to "walk" all objects on the page, and move or delete items under program control? There could be "helper" functions to change text baseline spacing in a consistent manner, etc., or scale up/down some drawing (graphics). If a paragraph has overflowed, you could even chop off the bottom and move it to the next page, or call a paragraph shaper to do a little "nip and tuck" to get the paragraph to fit (replace the existing paragraph). None of this would be trivial, of course. It would probably involve keeping extra data with the page's objects, which would be purged when it's actually written to file.
If none of this works for you, perhaps you could describe some detailed examples of what you're trying to do here. I take it you're trying to output pages without a lot of (or perhaps, any) manual intervention, so trial-and-error fitting is unacceptable.