title: Reportlab: rebuild a document after changing the margins?
body: <p>In a program I'm writing for a friend's business, I am using the reportlab module to build PDF documents for various reports.  In most cases, the report can be contained on a single page, but it may span across two pages in some rare circumstances.  In those rare situations, what I'd like to do is re-format the page with smaller top and bottom margins to see if I can get it to fit on a single page.  If not, I'll just use minimal margins and let it span two pages.</p>
  
  <p>To build a report, I'm creating an instance of the <code>SimpleDocTemplate</code> class.  After passing in all my flowables to the <code>build</code> method, I found that I can query the <code>page</code> attribute to see how many pages it used.  Here's the first thing I tried:</p>
  
  <pre><code>parts = []
  path = "/path/to/my/file.pdf"
  # ... a bunch of code that appends various flowables to 'parts'
  doc = SimpleDocTemplate(path, pagesize=landscape(letter))
  doc.build(parts)
  # Shrink the margins while it's more than one page and the margins are above zero
  while doc.page &gt; 1 and not any([doc.topMargin &lt;= 0, doc.bottomMargin &lt;= 0]):
      # Decrease the page margins and rebuild the page
      doc.topMargin -= inch * .125
      doc.bottomMargin -= inch * .125
      doc.build(parts)
  # If the margins are nil and we're still at 2 or more pages, use minimal margins
  if doc.page &gt; 1:
      doc.topMargin = inch * .25
      doc.bottomMargin = inch * .25
      doc.build(parts)
  </code></pre>
  
  <p>I assumed that calling the <code>build</code> method with the same parts after changing the margins would recalculate everything.  However, after much trial and error, I learned that the <code>parts</code> list that's passed to the <code>build</code> method is essentially stripped clean during the build process, leaving <code>parts</code> as an empty list.  Once this was passed back to the <code>build</code> method again, it created a document with zero pages.</p>
  
  <p>To work around that, I tried building the document using a copy of the <code>parts</code> list:</p>
  
  <pre><code>doc.build(parts[:])
  </code></pre>
  
  <p>That led to some bizarre exception, so I tried a deep copy using the <code>copy</code> module:</p>
  
  <pre><code>doc.build(copy.deepcopy(parts))
  </code></pre>
  
  <p>This didn't throw any exceptions, but didn't change the margins, either.</p>
  
  <p>Getting a little desperate, I probed deeper into the <code>SimpleDocTemplate</code> attributes and found a method named <code>_calc</code>.  Thinking that this might recalculate the page, I tried calling it after changing the margins.  It didn't throw any exceptions, but didn't work, either.</p>
  
  <p>The only way I've managed to make it work is to use the <code>deepcopy</code> process and build brand new documents every time I tweak the margins:</p>
  
  <pre><code>doc = SimpleDocTemplate(path, pagesize=landscape(letter))
  doc.build(copy.deepcopy(parts))
  # Shrink the margins while it's more than one page and the margins are above zero
  while doc.page &gt; 1 and not any([doc.topMargin &lt;= 0, doc.bottomMargin &lt;= 0]):
      doc.topMargin -= inch * .125
      doc.bottomMargin -= inch * .125
      doc = SimpleDocTemplate(path, pagesize=landscape(letter),
                              topMargin = doc.topMargin,
                              bottomMargin = doc.bottomMargin)
      doc.build(copy.deepcopy(parts))
  # If the margins are nil and we're still at 2 or more pages, use minimal margins
  if doc.page &gt; 1:
      doc.topMargin = inch * .25
      doc.bottomMargin = inch * .25
      doc = SimpleDocTemplate(path, pagesize=landscape(letter),
                              topMargin = doc.topMargin,
                              bottomMargin = doc.bottomMargin)
      doc.build(copy.deepcopy(parts))
  </code></pre>
  
  <p>However, it feels like a lot of unnecessary work to go that route.  I'd prefer to just change the document margins and tell the document to rebuild itself using those new values, but I can't figure out how.  Is that even possible?</p>