text: # ===============================================================================
  # Copyright 2013 Jake Ross
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #   http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  # ===============================================================================
  
  # ============= enthought library imports =======================
  from __future__ import absolute_import
  
  from reportlab.lib.pagesizes import landscape, letter
  from reportlab.lib.styles import getSampleStyleSheet
  from reportlab.lib.units import inch
  from reportlab.lib.units import mm
  from reportlab.pdfgen import canvas
  from reportlab.platypus.doctemplate import BaseDocTemplate, PageTemplate
  from reportlab.platypus.flowables import Spacer, PageBreak
  from reportlab.platypus.frames import Frame
  from reportlab.platypus.paragraph import Paragraph
  from traits.api import Instance
  
  from pychron.core.helpers.formatting import floatfmt
  from pychron.core.pdf.items import Anchor
  from pychron.core.pdf.options import BasePDFOptions
  from pychron.loggable import Loggable
  
  
  class NumberedCanvas(canvas.Canvas):
      page_number_format = None
  
      def __init__(self, *args, **kwargs):
          canvas.Canvas.__init__(self, *args, **kwargs)
          self._saved_page_states = []
  
      def showPage(self):
          self._saved_page_states.append(dict(self.__dict__))
          self._startPage()
  
      def save(self):
          """add page info to each page (page x of y)"""
          num_pages = len(self._saved_page_states)
          for state in self._saved_page_states:
              self.__dict__.update(state)
              self.draw_page_number(num_pages)
              canvas.Canvas.showPage(self)
          canvas.Canvas.save(self)
  
      def draw_page_number(self, page_count):
          self.setFont("Helvetica", 10)
  
          fmt = self.page_number_format
          try:
              s = fmt.format(page=self._pageNumber, total=page_count)
          except BaseException:
              s = "Page %d of %d" % (self._pageNumber, page_count)
          self.drawRightString(200 * mm, 20 * mm, s)
  
  
  class BasePDFWriter(Loggable):
      _footnotes = None
  
      options = Instance(BasePDFOptions)
      _options_klass = BasePDFOptions
  
      def _options_default(self):
          opt = self._options_klass()
          opt.load()
          return opt
  
      def _new_base_doc_template(self, path):
          pagesize = letter
          opt = self.options
          if opt.orientation == "landscape":
              pagesize = landscape(letter)
              leftMargin = opt.bottom_margin * inch
              rightMargin = opt.top_margin * inch
              topMargin = opt.left_margin * inch
              bottomMargin = opt.right_margin * inch
          else:
              leftMargin = opt.left_margin * inch
              rightMargin = opt.right_margin * inch
              topMargin = opt.top_margin * inch
              bottomMargin = opt.bottom_margin * inch
  
          doc = BaseDocTemplate(
              path,
              leftMargin=leftMargin,
              rightMargin=rightMargin,
              topMargin=topMargin,
              bottomMargin=bottomMargin,
              pagesize=pagesize,
          )
          return doc
  
      def build(self, path, *args, **kw):
          self.info("saving pdf to {}".format(path))
          doc = self._new_base_doc_template(path)
          self._doc = doc
          flowables, templates = self._build(doc, *args, **kw)
  
          if templates is None:
              frames = self._default_frame(doc)
              templates = [self._new_page_template(frames)]
  
          for ti in templates:
              doc.addPageTemplates(ti)
  
          if self.options.show_page_numbers and self.options.page_number_format:
              NumberedCanvas.page_number_format = self.options.page_number_format
  
              doc.build(flowables, canvasmaker=NumberedCanvas)
          else:
              doc.build(flowables)
  
      def _build(self, *args, **kw):
          """
          return a tuple of reportlab flowables and templates.
          templates are optional but you must at least return None
          e.g [f1,f2],[]
          """
          raise NotImplementedError
  
      def _new_paragraph(self, t, s="Normal", klass=None, **skw):
          if klass is None:
              klass = Paragraph
  
          style = getSampleStyleSheet()[s]
          for k, v in skw.items():
              setattr(style, k, v)
  
          p = klass(t, style)
          return p
  
      def _page_break(self):
          return PageBreak()
  
      def _default_frame(self, doc):
          return Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height)
  
      def _new_page_template(self, frames):
          temp = PageTemplate(frames=frames)
          return temp
  
      def _new_spacer(self, w, h):
          return Spacer(w * inch, h * inch)
  
      def _vspacer(self, h):
          return self._new_spacer(0, h)
  
      def _make_footnote(self, tagname, tagName, tagText, linkname, link_extra=None):
          if self._footnotes is None:
              self._footnotes = []
  
          n = len(self._footnotes)
          link, tag = Anchor("{}_{}".format(tagname, id(self)), n + 1)
          para = link(linkname, extra=link_extra)
  
          self._footnotes.append(tag(tagName, tagText))
          return para
  
      def _fmt_attr(self, v, key="nominal_value", n=5, scale=1, **kw):
          if v is None:
              return ""
  
          if isinstance(v, tuple):
              if key == "std_dev":
                  v = v[1]
              else:
                  v = v[0]
          elif isinstance(v, (float, int)):
              pass
          else:
              if hasattr(v, key):
                  v = getattr(v, key)
  
          if isinstance(v, (float, int)):
              v /= float(scale)
  
          return floatfmt(v, n=n, max_width=10, **kw)
  
      def _error(self, **kw):
          return lambda x: self._fmt_attr(x, key="std_dev", **kw)
  
      def _value(self, **kw):
          return lambda x: self._fmt_attr(x, key="nominal_value", **kw)
  
  
  # ============= EOF =============================================
  