def setFont(self, family=None, style=None, size=None, font=None):
        """ Set the document font object, size given in points.
            If family, style, and/or size is given, generates
            a new Font object, checks to see if it is already
            in use, and selects it.

            May also use the font keyword to add an already
            instantiated PDFFont object.

        """

        if size is None:
            size = self.font.fontsize

        if font is not None:
            newfont = font
        else:
            newfont = PDFFont(family, style, size)

        #Test if font is already selected
        if not newfont._equals(self.font):
            #Test if used for the first time
            if newfont.fontkey not in self.fonts:
                i = len(self.fonts) + 1
                newfont._setIndex(i)
                self.fonts.append(newfont)

        #Select it
        self.font = newfont
        if(self.page.index > 0):
            self.SS._out('BT /F%d %.2f Tf ET' % (self.font.index, self.font.fontsize), self.page)
        else:
            del newfont
Exemple #2
0
 def _set_default_font(self):
     """ Internal method to set the initial default font. Change
         the font using set_font method."""
     self.font = PDFFont(self.session)
     self.font._set_index()
     self.fonts.append(self.font)
     self.fontkeys.append(self.font.font_key)
Exemple #3
0
 def _set_default_font(self):
     """ Internal method to set the initial default font. Change
         the font using set_font method."""
     self.font = PDFFont(self.session)
     self.font._set_index()
     self.fonts.append(self.font)
     self.fontkeys.append(self.font.font_key)
    def _setDefaultFont(self):
        """ Internal method to set the
            initial default font. Change
            the font using setFont method.

        """
        self.font = PDFFont()
        self.font._setIndex()
        self.fonts.append(self.font)
    def setFont(self, family, style=None, size=None):
        "Select a font; size given in points"
        if size is None:
            size = self.font.fontsize
        newfont = PDFFont(family, style, size)

        #Test if font is already selected
        if not newfont.equals(self.font):
            #Test if used for the first time
            if newfont.fontkey not in self.fonts:
                i = len(self.fonts) + 1
                newfont.setIndex(i)
                self.fonts[newfont.fontkey] = newfont

        #Select it
        self.font = self.fonts[newfont.fontkey]
        if(self.page.number > 0):
            self.SS.out('BT /F%d %.2f Tf ET' % (self.font.index, self.font.fontsize), self.page)
        else:
            del newfont
Exemple #6
0
    def set_font(self, family=None, style='', size=None, font=None):
        """ Set the document font object, size given in points. If family, style, and/or size is given, generates
            a new Font object, checks to see if it is already in use, and selects it. """
        if font:
            testfont = font
        elif isinstance(family, PDFFont):
            testfont = family
        else:
            # If size is not defined, keep the last size.
            if size is None:
                size = self.font.font_size

            # Create a font from givens to test its key
            if family in CORE_FONTS:
                testfont = PDFFont(self.session, family, style, size)
            else:
                testfont = PDFTTFont(self.session, family, style, size)

        testkey = testfont.font_key

        if testkey in self.fontkeys:
            index = self.fontkeys.index(testkey)
            self.font = self.fonts[index]
            if size != self.font.font_size:
                self.font._set_size(size)
            if style != self.font.style:
                self.font._set_style(style)
        else:
            self.font = testfont
            self._register_new_font(self.font)

        self.font.is_set = False

        if self.page.index > 0:
            self.session._out(
                'BT /F%d %.2f Tf ET' % (self.font.index, self.font.font_size),
                self.page)
            self.font.is_set = True
        return self.font
class PDFDocument(object):
    def __init__(self, session):
        self.SS = session
        self.pages = []
        self.fonts = []               # array of used fonts
        self._setDefaults()

    def _setDefaults(self):
        self._setColor()
        self._setFont()
        self.addPage()

    def _setColor(self):
        self.color = PDFColors()

    def _setFont(self):
        self.font = PDFFont()
        self.font.setIndex()
        self.fonts.append(self.font)

    def addPage(self):
        self.page = PDFPage(self.SS)
        self.page.setIndex(len(self.pages))
        self.pages.append(self.page)

    def setFont(self, family, style=None, size=None):
        "Select a font; size given in points"
        if size is None:
            size = self.font.fontsize
        newfont = PDFFont(family, style, size)

        #Test if font is already selected
        if not newfont.equals(self.font):
            #Test if used for the first time
            if newfont.fontkey not in self.fonts:
                i = len(self.fonts) + 1
                newfont.setIndex(i)
                self.fonts[newfont.fontkey] = newfont

        #Select it
        self.font = self.fonts[newfont.fontkey]
        if(self.page.number > 0):
            self.SS.out('BT /F%d %.2f Tf ET' % (self.font.index, self.font.fontsize), self.page)
        else:
            del newfont

    def setFontSize(self, size):
        "Set font size in points"
        if(self.font.fontsize == size):
            return
        else:
            self.setFont(self.font.family, self.font.style, size)

    def _getOrientationChanges(self):
        self.orientation_changes = []
        for page in self.pages:
            if page.orientation_change is True:
                self.orientation_changes.append(page.number)
            else:
                pass
        return self.orientation_changes

    def outputPages(self):
        if self.orientation_changes is None:
            self.getOrientationChanges()
        else:
            #Page
            for page in self.pages:
                self.SS.newobj()
                self.SS.out('<</Type /Page')
                self.SS.out('/Parent 1 0 R')
                if page in self.orientation_changes:
                    self.SS.out('/MediaBox [0 0 %.2f %.2f]' % (page.h, page.w))
                self.SS.out('/Resources 2 0 R')
                self.SS.out('/Contents %s 0 R>>' % len(self.SS.objects))
                self.SS.out('endobj')
                #Page content
                self.SS.newobj()
                self.SS.out('<<' + '/Filter /Length %s >>' % len(page.buffer))
                self.SS.putstream(page.buffer)
                self.SS.out('endobj')

    def outputFonts(self):
        for font in self.fonts:
            obj = self.SS.newobj()
            font.setNumber(obj.id)
            self.SS.out('<</Type /Font')
            self.SS.out('/BaseFont /' + font.name)
            self.SS.out('/Subtype /Type1')
            if(font.name != 'Symbol' and font.name != 'ZapfDingbats'):
                self.SS.out('/Encoding /WinAnsiEncoding')
            self.SS.out('>>')
            self.SS.out('endobj')

    def addText(self, x, y, text):
        cursor = PDFCursor(x, y)
        text = PDFText(self.SS, self.page, self.font, self.color, cursor, text)
        return cursor

    def addTextOnCursor(self, cursor, text):
        text = PDFText(self.SS, self.page, self.font, self.color, cursor, text)
 def _setFont(self):
     self.font = PDFFont()
     self.font.setIndex()
     self.fonts.append(self.font)
Exemple #9
0
class PDFDocument(object):
    """ The Document object is the base class that
        is used to add and manage the content of the
        pdf file.

        Document maintains lists of pages and other
        resources, provides for default selections,
        which can be changed through the convinence
        methods.

        Note that page and text objects do not maintain
        fonts themselves. If they must use them (for
        calculating widths or such), they must have
        the current font passed to them.

    """

    def __init__(self, session, orientation, layout):
        """Sets up a standard default document."""
        self.session = session
        self.pages = []
        self.fonts = []
        self.fontkeys = []               # array of used fonts

        self.images = []
        self.imagekeys = []
        self.image_filter = None

        self.margins = None
        self.orientation_default = orientation
        self.layout_default = layout
        self.justification = 'left'
        self.double_spacing = None
        self.diffs = []

        self._set_defaults()

    # Set Defaults
    def _set_defaults(self):
        """Set color scheme & font to defaults."""
        self._set_color_scheme()
        self._set_default_font()
        self.add_page()     # add first page

    def _set_color_scheme(self, draw_color=None, fill_color=None, text_color=None):
        """ Default color object is black letters
            & black lines."""
        if draw_color is None:
            draw_color = PDFColor()
            draw_color._set_type('d')
        if fill_color is None:
            fill_color = PDFColor()
            fill_color._set_type('f')
        if text_color is None:
            text_color = PDFColor()
            text_color._set_type('t')

        self.draw_color = draw_color
        self.fill_color = fill_color
        self.text_color = text_color

    def set_text_color(self, color):
        if isinstance(color, PDFColor):
            self.text_color = color
        else:
            raise ValueError("Not a PDFColor instance")

    def set_fill_color(self, color):
        if isinstance(color, PDFColor):
            self.fill_color = color
        else:
            raise ValueError("Not a PDFColor instance")

    def set_draw_color(self, color):
        if isinstance(color, PDFColor):
            self.draw_color = color
        else:
            raise ValueError("Not a PDFColor instance")

    def _set_default_font(self):
        """ Internal method to set the initial default font. Change
            the font using set_font method."""
        self.font = PDFFont(self.session)
        self.font._set_index()
        self.fonts.append(self.font)
        self.fontkeys.append(self.font.font_key)

    @staticmethod
    def get_color():
        return PDFColor()

    # Public methods, main interface
    # Pages
    def add_page(self, page=None):
        """ May generate and add a PDFPage separately, or use this to generate
            a default page."""
        if page is None:
            self.page = PDFPage(self.orientation_default, self.layout_default, self.margins)
        else:
            self.page = page
        self.page._set_index(len(self.pages))
        self.pages.append(self.page)
        currentfont = self.font
        self.set_font(font=currentfont)
        self.session._reset_colors()

    def get_page(self):
        """Returns reference to current page object."""
        return self.page

    def set_margins(self, left, top=None, right=None, bottom=None):
        if isinstance(left, PDFMargin):
            self.margins = left
        else:
            self.margins = PDFMargin(left, top, right, bottom)
        if self.page is not None:
            self.page.set_margins(self.margins)

    def set_justification(self, value='left'):
        if value in ['left', 'right', 'center']:
            self.justification = value
        else:
            raise ValueError("Incorrect value for justification %s" % value)

    def add_page_numbers(self, location='right', font=None, cursor=None, color=None, text1="Page %s", text2=None):
        self.page_numbers = location
        self.page_numbers_font = font
        self.page_numbers_cursor = cursor
        self.page_numbers_color = color
        self.page_numbers_text1 = text1
        self.page_numbers_text2 = text2

    def change_page_orientation(self):
        self.page._change_orientation()

    # Cursor
    def get_new_cursor(self):
        """ Returns a new default cursor """
        return PDFCursor()

    def get_cursor(self):
        return self.page.cursor.copy()

    def set_cursor(self, x=None, y=None):
        if x is not None and isinstance(x, PDFCursor):
            self.page.cursor = x
        elif x is not None and y is not None:
            self.page.cursor = PDFCursor(x, y, True)
        else:
            raise Exception("Invalid cursor input")

    # Font
    def set_font(self, family=None, style='', size=None, font=None):
        """ Set the document font object, size given in points. If family, style, and/or size is given, generates
            a new Font object, checks to see if it is already in use, and selects it. """
        if font:
            testfont = font
        elif isinstance(family, PDFFont):
            testfont = family
        else:
            # If size is not defined, keep the last size.
            if size is None:
                size = self.font.font_size

            # Create a font from givens to test its key
            if family in CORE_FONTS:
                testfont = PDFFont(self.session, family, style, size)
            else:
                testfont = PDFTTFont(self.session, family, style, size)

        testkey = testfont.font_key

        if testkey in self.fontkeys:
            index = self.fontkeys.index(testkey)
            self.font = self.fonts[index]
            if size != self.font.font_size:
                self.font._set_size(size)
            if style != self.font.style:
                self.font._set_style(style)
        else:
            self.font = testfont
            self._register_new_font(self.font)

        self.font.is_set = False

        if self.page.index > 0:
                self.session._out('BT /F%d %.2f Tf ET' %
                                  (self.font.index, self.font.font_size),
                                  self.page)
                self.font.is_set = True
        return self.font

    def get_font(self):
        """ Get the current font object. Useful for storing
            in variables, and switching between styles. """
        return self.font

    def set_font_size(self, size):
        """Convenience method for just changing font size."""
        if self.font.font_size == size:
            pass
        else:
            self.font._set_size(size)

    def get_font_size(self):
        return self.font.font_size

    def print_available_fonts(self):
        print PDFTTFont.available_fonts()

    # Writing
    def add_text(self, text, cursor=None, justification=None):
        """ Input text, short or long. Writes in order, within the defined page boundaries. Sequential add_text commands will print without
            additional whitespace. """
        if cursor is None:
            cursor = self.page.cursor

        text = re.sub("\s\s+" , " ", text)

        if justification is None:
            justification = self.justification

        if '\n' in text:
            text_list = text.split('\n')
            for text in text_list:
                PDFText(self.session, self.page, text, self.font, self.text_color, cursor, justification, self.double_spacing)
                self.add_newline()
        else:
            PDFText(self.session, self.page, text, self.font, self.text_color, cursor, justification, self.double_spacing)

    def draw_text(self, *args, **kwargs):
        self.add_text(*args, **kwargs)

    def text(self, *args, **kwargs):
        self.add_text(*args, **kwargs)

    def double_space(self, multiplier=1):
        self.double_spacing = multiplier

    def add_newline(self, number=1):
        """ Starts over again at the new line. If number is specified,
            it will leave multiple lines."""
        if isinstance(number, int):
            try:
                self.page._add_newline(self.font, number, self.double_spacing)
            except ValueError:
                self.add_page()
        else:
            raise TypeError("Number of newlines must be an integer.")

    def newline(self, *args, **kwargs):
        self.add_newline(*args, **kwargs)

    def add_indent(self, spaces=4):
        """ Adds a standard tab of 4 spaces.
        """
        self.page._add_indent(self.font, spaces)

    def indent(self, *args, **kwargs):
        self.add_indent(*args, **kwargs)

    def start_block_indent(self, px=20):
        self.px = px
        self.page.cursor.x_plus(self.px)
        self.page.cursor.xmin = self.page.cursor.x

    def end_block_indent(self):
        self.page.cursor.xmin -= self.px
        self.page.cursor.x_plus(-self.px)

    def add_list(self, *args, **kwargs):
        bullet_code = 149
        if 'bullet' in kwargs:
            if kwargs['bullet'] == 1:
                bullet_code = 149
            elif kwargs['bullet'] == 2:
                bullet_code = 186
            elif kwargs['bullet'] == 3:
                bullet_code = 150

        char = chr(bullet_code)

        if 'force' in kwargs and kwargs['force'] is True:
            saved_font = self.get_font()
            for arg in args:
                self.set_font(family='helvetica', size=saved_font.font_size)
                self.add_text(char)
                self.set_font(saved_font)
                self.add_text(' %s' % arg)
                self.add_newline(2)
        else:
            for arg in args:
                if isinstance(arg, list):
                    for item in arg:
                        self.add_text(char)
                        self.add_text(' %s' % item)
                        self.add_newline(1)
                else:
                    self.add_text(char)
                    self.add_text(' %s' % arg)
                    self.add_newline(2)

    def list(self, *args, **kwargs):
        self.add_list(*args, **kwargs)

    def add_html(self, htmltext, context=None, formats=None, cursor=None):
        if cursor is not None:
            self.set_cursor(cursor)

        if hasattr(htmltext, 'write'):
            htmltext = htmltext.read()

        PDFHtml(self, self.session, htmltext, formats, context)

    def html(self, *args, **kwargs):
        self.add_html(*args, **kwargs)

    # Shapes
    def add_line(self, x1=None, y1=None, x2=None, y2=None, cursor1=None, cursor2=None, stroke="solid"):
        if cursor1 is not None:
            if cursor2 is not None:
                pass
            elif x2 is not None and y2 is not None:
                cursor2 = self.page.cursor.copy()
                cursor2.x = x2
                cursor2.y = y2
            else:
                raise Exception("Line not fully specified")
        elif x1 is not None and y1 is not None:
            cursor1 = self.page.cursor.copy()
            cursor1.x = x1
            cursor1.y = y1
            if cursor2 is not None:
                pass
            elif x2 is not None and y2 is not None:
                cursor2 = self.page.cursor.copy()
                cursor2.x = x2
                cursor2.y = y2
            else:
                raise Exception("Line not fully specified")
        else:
            raise Exception("Line not specified")

        myline = PDFLine(self.session, self.page, cursor1, cursor2, self.draw_color, stroke)
        myline._draw()

    def draw_line(self, *args, **kwargs):
        self.add_line(*args, **kwargs)

    def line(self, *args, **kwargs):
        self.add_line(*args, **kwargs)

    def draw_horizontal_line(self):
        end_cursor = self.page.cursor.copy()
        end_cursor.x = end_cursor.xmax

        myline = PDFLine(self.session, self.page, self.page.cursor, end_cursor, self.draw_color, None)
        myline._draw()

    def add_horizontal_line(self):
        self.draw_horizontal_line()

    def horizontal_line(self):
        self.draw_horizontal_line()

    def draw_rectangle(self, x1=None, y1=None, x2=None, y2=None, width=None, height=None, cursor1=None, cursor2=None,
                       style='S', stroke="solid", size=1):
        if cursor1 is not None:
            if cursor2 is not None:
                pass
            elif width is not None and height is not None:
                dims = PDFCursor(width, height)
                cursor2 = cursor1 + dims
            elif x2 is not None and y2 is not None:
                cursor2 = PDFCursor(x2, y2)
            else:
                raise Exception("Rectangle not defined")
        else:
            if x1 is not None and y1 is not None:
                cursor1 = PDFCursor(x1, y1)
                if x2 is not None and y2 is not None:
                    cursor2 = PDFCursor(x2, y2)
                elif width is not None and height is not None:
                    dims = PDFCursor(width, height)
                    cursor2 = cursor1 + dims
                elif cursor2 is not None:
                    pass
                else:
                    raise Exception("Rectangle not defined")
            else:
                raise Exception("Rectangle not defined")

        rectangle = PDFRectangle(self.session, self.page, cursor1, cursor2, self.draw_color, self.fill_color, style, stroke, size)

        rectangle._draw()

    def add_rectangle(self, *args, **kwargs):
        self.draw_rectangle(*args, **kwargs)

    def rectangle(self, *args, **kwargs):
        self.draw_rectangle(*args, **kwargs)

    def draw_circle(self, center_cursor, radius, border_color=None, fill_color=None, style="S", stroke='solid', size=1):
        self.draw_ellipse(center_cursor, radius, radius, border_color, fill_color, style, stroke, size)

    def draw_ellipse(self, center_cursor, x_radius, y_radius, border_color=None, fill_color=None, style="S", stroke='solid', size=1):
        radius_cursor = PDFCursor(x_radius, y_radius)
        if isinstance(border_color, PDFColor):
            border_color = border_color
        else:
            border_color = self.draw_color
        if isinstance(fill_color, PDFColor):
            fill_color = fill_color
        else:
            fill_color = self.fill_color

        circle = PDFEllipse(self.session, self.page, center_cursor, radius_cursor, border_color, fill_color, style, stroke, size)
        circle._draw()

    def draw_arc(self, center_cursor, radius, starting_angle, arc_angle, inverted=False, end_angle=None, border_color=None, fill_color=None, style='S', stroke='solid', size=1):
        if isinstance(border_color, PDFColor):
            border_color = border_color
        else:
            border_color = self.draw_color
        if isinstance(fill_color, PDFColor):
            fill_color = fill_color
        else:
            fill_color = self.fill_color

        arc = PDFArc(self.session, self.page, center_cursor, radius, starting_angle, arc_angle, inverted, end_angle, border_color, fill_color, style, stroke, size)
        arc._draw()

    def draw_bezier_lines(self, points, color=None, style="S", stroke="solid"):
        save_draw_color = self.draw_color

        line = PDFBezier(self.session, self.page, points, color, style, stroke)

        self.set_draw_color(save_draw_color)

    # Graphs
    def add_line_graph(self, data, cursor, width, height, title=None, x_axis_limits=None, y_axis_limits=None, frequency=None, axis_titles=None, axis_labels=None, axis_font_size=None, line_colors=None,
                       background=None, legend=None, dots=None, style="S", axis=True):
        save_draw_color = self.draw_color
        save_fill_color = self.fill_color
        save_font_size = self.get_font_size()
        if axis_font_size is None:
            self.set_font_size(8)
        else:
            self.set_font_size(axis_font_size)

        graph = PDFLineGraph(self.session, self.page, cursor, data, width, height, title, x_axis_limits, y_axis_limits, frequency, axis_titles, axis_labels, line_colors, background, legend, dots, style, axis)

        self.set_font_size(save_font_size)
        self.set_draw_color(save_draw_color)
        self.set_fill_color(save_fill_color)

    def add_xy_scatter(self, data, cursor, width, height, title=None, x_axis_limits=None, y_axis_limits=None, frequency=None, axis_titles=None, axis_labels=None, axis_font_size=None, line_colors=None,
                       background=None, legend=None, dots=None, linear_regression=None, linear_regression_equation=None):

        save_draw_color = self.draw_color
        save_fill_color = self.fill_color
        save_font_size = self.get_font_size()
        if axis_font_size is None:
            self.set_font_size(8)
        else:
            self.set_font_size(axis_font_size)

        graph = PDFXYScatter(self.session, self.page, cursor, data, width, height, title, x_axis_limits, y_axis_limits, frequency, axis_titles, axis_labels, line_colors, background, legend, dots, linear_regression, linear_regression_equation)

        self.set_font_size(save_font_size)
        self.set_draw_color(save_draw_color)
        self.set_fill_color(save_fill_color)

    def add_simple_bar_chart(self, data, cursor, width, height, title=None, axis_titles=None, axis_font_size=None, y_axis_limits=None, y_axis_frequency=None, bar_style="B", bar_padding=0.1, bar_border_colors=None, bar_fill_colors=None,
                             background=None):

        save_draw_color = self.draw_color
        save_fill_color = self.fill_color
        save_font_size = self.get_font_size()

        if axis_font_size is None:
            self.set_font_size(8)
        else:
            self.set_font_size(axis_font_size)

        graph = PDFBarChart(self.session, self.page, data, cursor, width, height, title, axis_titles, y_axis_limits, y_axis_frequency, bar_style, bar_padding, bar_border_colors, bar_fill_colors,
                            background)

        self.set_font_size(save_font_size)
        self.set_draw_color(save_draw_color)
        self.set_fill_color(save_fill_color)

    def add_multi_bar_chart(self, data, cursor, width, height, title=None, axis_titles=None, axis_font_size=None, y_axis_limits=None, y_axis_frequency=None, bar_style="B", bar_padding=0.1, bar_border_colors=None, bar_fill_colors=None,
                            background=None, legend=None):
        save_draw_color = self.draw_color
        save_fill_color = self.fill_color
        save_font_size = self.get_font_size()

        if axis_font_size is None:
            self.set_font_size(8)
        else:
            self.set_font_size(axis_font_size)

        graph = PDFMultiBarChart(self.session, self.page, data, cursor, width, height, title, axis_titles, y_axis_limits, y_axis_frequency, bar_style, bar_padding, bar_border_colors, bar_fill_colors,
                                 background, legend)

        self.set_font_size(save_font_size)
        self.set_draw_color(save_draw_color)
        self.set_fill_color(save_fill_color)

    def add_pie_chart(self, data, cursor, width, height, title=None, data_type="raw", fill_colors=None, labels=False, background=None, legend=None):
        """ Data type may be "raw" or "percent" """
        save_draw_color = self.draw_color
        save_fill_color = self.fill_color

        chart = PDFPieChart(self.session, self.page, data, cursor, width, height, title, data_type, fill_colors, labels, background, legend)

        self.set_draw_color(save_draw_color)
        self.set_fill_color(save_fill_color)

    # Tables
    def add_table(self, rows, columns, cursor=None):
        if cursor is None:
            cursor = self.page.cursor

        table = PDFTable(self.session, self.page, rows, columns, cursor, self.font)

        return table

    def draw_table(self, table):
        if isinstance(table, PDFTable):
            table._draw()
            self.page.cursor = table.cursor
        else:
            raise Exception("Invalid Table")

    def add_cell_format(self, data=None, cell_font=None):
        if cell_font is None:
            cell_font = self.font
        if data is None:
            data = {}
        format = PDFCellFormat(data, cell_font)
        return format

    # Images
    def add_image(self, image, name=None):
        if isinstance(image, PDFImage):  # It's an image object
            myimage = self._get_image(image.name)
            if not myimage:
                self._register_new_image(image)
            myimage = image

        elif isinstance(image, str):  # Name or path
            image_string = image
            if image_string.find('.') == -1:  # Not a path, treat as name
                myimage = self._get_image(image_string)
                if not myimage:
                    raise Exception('Not a proper image path')
            else:  # Is a path
                if not name:  # But it doesn't have a name specified.
                    name = os.path.splitext(image_string)[0]  # Specify it
                myimage = self._get_image(name)
                if not myimage:  # New image
                    extension = os.path.splitext(image_string)[1]
                    if extension == '.png':
                        myimage = PDFPNG(self.session, image_string, name)
                    elif extension == '.jpg':
                        myimage = PDFJPG(self.session, image_string, name)
                    else:
                        raise Exception("Image format %s not supported" % extension)
                    self._register_new_image(myimage)

        return myimage

    def draw_image(self, image, cursor=None, width=None, height=None):
        if isinstance(image, PDFImage):  # It's an image object
            myimage = self._get_image(image.name)
        else:
            try:
                myimage = self._get_image(image)
            except:
                raise Exception("%s is an invalid image. Add it first.")

        if not cursor:
            imagecursor = self.page.cursor
        else:
            imagecursor = cursor
        myimage._set_cursor(imagecursor)
        myimage._set_size(width, height)
        myimage._draw(self.page)

        # If a cursor was not specified, place at the end
        if not cursor:
            self.page.cursor = myimage.cursor

    def set_background_image(self, image):
        background_cursor = PDFCursor(0, 0)
        background_cursor.set_bounds(xmin=0, ymin=0, xmax=self.page.width, ymax=self.page.height, ymaxmax=self.page.height)
        myimage = self.add_image(image)
        self.draw_image(myimage, background_cursor, width=self.page.width)

    # Private methods for outputting document
    def _get_image(self, image_name):
        if image_name in self.imagekeys:
            index = self.imagekeys.index(image_name)
            myimage = self.images[index]
            return myimage
        else:
            return False

    def _output_pages(self):
        """ Called by the PDFLite object to prompt creating
            the page objects."""
        if not self.orientation_changes:
            self._get_orientation_changes()

        # Page
        for page in self.pages:
            obj = self.session._add_object()
            self.session._out('<</Type /Page')
            self.session._out('/Parent 1 0 R')
            if self.orientation_changes:
                self.session._out('/MediaBox [0 0 %.2f %.2f]' % (page.width, page.height))
            self.session._out('/Resources 2 0 R')
            self.session._out('/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>')
            self.session._out('/Contents %s 0 R>>' % (obj.id + 1))
            self.session._out('endobj')

            # Page content
            self.session._add_object()
            if self.session.compression is True:
                textfilter = ' /Filter /FlateDecode '
                page._compress()
            else:
                textfilter = ''
            self.session._out('<<%s/Length %s >>' % (textfilter, len(page.buffer)))
            self.session._put_stream(page.buffer)
            self.session._out('endobj')

    def _set_page_numbers(self):
        if hasattr(self, 'page_numbers'):
            for page in self.pages:
                self._add_page_number(page, len(self.pages))

    def _add_page_number(self, page, number_of_pages):
        if self.page_numbers_font is not None:
            fs = 'BT /F%d %.2f Tf ET' % (self.page_numbers_font.index, self.page_numbers_font.font_size)
            self.session._out(fs, page)

        if self.page_numbers_color is not None:
            self.page_numbers_color._set_type('t')
            self.session._out(self.page_numbers_color._get_color_string(), page)

        text_string = self.page_numbers_text1 % (page.index + 1)
        if self.page_numbers_text2 is not None:
            text_string += self.page_numbers_text2 % number_of_pages

        if self.page_numbers_cursor is None:
            sw = self.font._string_width(text_string)
            y = page.page_size[1]
            y = y - self.page.margin.bottom

            if self.page_numbers == 'left':
                x = self.page.margin.left
            elif self.page_numbers == 'right':
                x = page.page_size[0] - page.margin.right - sw
            else:
                x = page.page_size[0] / 2 - int(sw / 2.0)
            cursor = PDFCursor(x, y)
        else:
            cursor = self.page_numbers_cursor

        text_string = '(%s)' % text_string
        s = 'BT %.2f %.2f Td %s Tj ET' % (cursor.x, cursor.y_prime, text_string)
        self.session._out(s, page)

    def _get_orientation_changes(self):
        """ Returns a list of the pages that have
            orientation changes."""
        self.orientation_changes = []
        for page in self.pages:
            if page.orientation_change is True:
                self.orientation_changes.append(page.index)
            else:
                pass
        return self.orientation_changes

    def _register_new_font(self, font):
        font._set_index(len(self.fonts) + 1)
        self.fonts.append(self.font)
        self.fontkeys.append(self.font.font_key)
        if hasattr(font, 'diffs') and font.diffs is not None:
            try:
                index_of_diff = self.diffs.index(font.diffs)
            except AttributeError:
                index_of_diff = len(self.diffs) + 1
                self.diffs.append(font.diffs)
            font.diff_number = index_of_diff

    def _output_fonts(self):
        """ Called by the PDFLite object to prompt creating
            the font objects."""
        self.session._save_object_number()
        self._output_encoding_diffs()
        self._output_font_files()

        for font in self.fonts:
            obj = self.session._add_object()
            font._set_number(obj.id)
            font._output()

    def _output_font_files(self):
        for font in self.fonts:
            if hasattr(font, 'file'):
                font.output_file()

    def _output_encoding_diffs(self):
        if self.diffs:
            for _ in self.diffs:
                self.session._add_object()
                self.session._out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [%s]>>')
                self.session._out('endobj')

    def _register_new_image(self, image):
        image._set_index(len(self.images) + 1)
        self.images.append(image)
        self.imagekeys.append(image.name)

    def _output_images(self):
        """ Creates reference images, that can be
            drawn throughout the document."""
        for image in self.images:
            obj = self.session._add_object()
            image._set_number(obj.id)
            image._output()
class PDFDocument(object):
    """ The Document object is the base class that
        is used to add and manage the content of the
        pdf file.

        Document maintains lists of pages and other
        resources, provides for default selections,
        which can be changed through the convinence
        methods.

        Note that page and text objects do not maintain
        fonts themselves. If they must use them (for
        calculating widths or such), they must have
        the current font passed to them.

    """
    def __init__(self, session):
        "Sets up a standard default document."
        self.SS = session
        self.pages = []
        self.fonts = []               # array of used fonts
        self._setDefaults()

    def _setDefaults(self):
        self.setColorScheme()
        self._setDefaultFont()
        self.addPage()

    def setColorScheme(self, colorscheme=None):
        """ Default color object is black.

        """
        if colorscheme is None:
            self.colorscheme = PDFColorScheme()
        else:
            if isinstance(colorscheme, PDFColorScheme):
                self.colorscheme = colorscheme
            else:
                raise Exception("invalid color scheme")

    def _setDefaultFont(self):
        """ Internal method to set the
            initial default font. Change
            the font using setFont method.

        """
        self.font = PDFFont()
        self.font._setIndex()
        self.fonts.append(self.font)

    def addPage(self, page=None):
        """ May generate and add a PDFPage
            separately, or use this to generate
            a default page.

        """
        if page is None:
            self.page = PDFPage()
        else:
            self.page = page
        self.page._setIndex(len(self.pages))
        self.pages.append(self.page)
        currentfont = self.font
        self.setFont(font=currentfont)

    def getPage(self):
        "Returns reference to current page object."
        return self.page

    def setFont(self, family=None, style=None, size=None, font=None):
        """ Set the document font object, size given in points.
            If family, style, and/or size is given, generates
            a new Font object, checks to see if it is already
            in use, and selects it.

            May also use the font keyword to add an already
            instantiated PDFFont object.

        """

        if size is None:
            size = self.font.fontsize

        if font is not None:
            newfont = font
        else:
            newfont = PDFFont(family, style, size)

        #Test if font is already selected
        if not newfont._equals(self.font):
            #Test if used for the first time
            if newfont.fontkey not in self.fonts:
                i = len(self.fonts) + 1
                newfont._setIndex(i)
                self.fonts.append(newfont)

        #Select it
        self.font = newfont
        if(self.page.index > 0):
            self.SS._out('BT /F%d %.2f Tf ET' % (self.font.index, self.font.fontsize), self.page)
        else:
            del newfont

    def getFont(self):
        """ Get the current font object. Useful for storing
            in variables, and switching between styles.

        """
        return self.font

    def setFontSize(self, size):
        "Convinence method for just changing font size."
        if(self.font.fontsize == size):
            pass
        else:
            self.setFont(self.font.family, self.font.style, size)

    def _getOrientationChanges(self):
        """ Returns a list of the pages that have
            orientation changes.

        """
        self.orientation_changes = []
        for page in self.pages:
            if page.orientation_change is True:
                self.orientation_changes.append(page.index)
            else:
                pass
        return self.orientation_changes

    def _outputPages(self):
        """ Called by the PDFLite object to prompt creating
            the page objects.

        """
        if self.orientation_changes is None:
            self._getOrientationChanges()
        else:
            #Page
            for page in self.pages:
                self.SS._addObject()
                self.SS._out('<</Type /Page')
                self.SS._out('/Parent 1 0 R')
                if page in self.orientation_changes:
                    self.SS._out('/MediaBox [0 0 %.2f %.2f]' % (page.width, page.height))
                self.SS._out('/Resources 2 0 R')
                self.SS._out('/Contents %s 0 R>>' % len(self.SS.objects))
                self.SS._out('endobj')
                #Page content
                self.SS._addObject()
                if self.SS.compression is True:
                    textfilter = '/Filter /FlateDecode'
                    page._compress()
                else:
                    textfilter = ''
                self.SS._out('<< %s /Length %s >>' % (textfilter, len(page.buffer)))
                self.SS._putStream(page.buffer)
                self.SS._out('endobj')

    def _outputFonts(self):
        """ Called by the PDFLite object to prompt creating
            the font objects.

        """
        for font in self.fonts:
            obj = self.SS._addObject()
            font._setNumber(obj.id)
            self.SS._out('<</Type /Font')
            self.SS._out('/BaseFont /' + font.name)
            self.SS._out('/Subtype /Type1')
            if(font.name != 'Symbol' and font.name != 'ZapfDingbats'):
                self.SS._out('/Encoding /WinAnsiEncoding')
            self.SS._out('>>')
            self.SS._out('endobj')

    """ The following methods are the core ways to input content.

    """
    def addText(self, text):
        """ Input text, short or long. Writes in order, within the
            pre-defined page boundaries. Use addNewline as a return
            character. Sequential addText commands will print without
            additional whitespace.

            Currently all "left-justified", although that may change in
            future version.

        """
        text = PDFText(self.SS, self.page, self.font, self.colorscheme, text)

    def addNewline(self, number=1):
        """ Starts over again at the new line. If number is specified,
            it will leave multiple lines.
        """
        if isinstance(number, int):
            try:
                self.page.addNewline(self.font, number)
            except ValueError:
                self.addPage()
        else:
            raise TypeError("Number of newlines must be an integer.")

    def addIndent(self):
        """ Adds a standard tab of 4 spaces.
        """
        self.page.addIndent(self.font)

    def addLine(self, x1=None, y1=None, x2=None, y2=None, cursor1=None, cursor2=None):
        if cursor1 is not None:
            if cursor2 is not None:
                pass
        else:
            cursor1 = PDFCursor(x1, y1)
            cursor2 = PDFCursor(x2, y2)

        myline = PDFLine(self.SS, self.page, self.colorscheme, cursor1, cursor2)
        myline.draw()

    def drawHorizonalLine(self):
        endcursor = self.page.cursor.copy()
        endcursor.x = endcursor.xmax

        myline = PDFLine(self.SS, self.page, self.colorscheme, self.page.cursor, endcursor)
        myline.draw()

    def drawRectangle(self, x1=None, y1=None, x2=None, y2=None, width=None, height=None, cursor1=None, cursor2=None, style='S'):
        if cursor1 is not None:
            if cursor2 is not None:
                pass
            elif width is not None and height is not None:
                dims = PDFCursor(width, height)
                cursor2 = cursor1.add(dims)
            elif x2 is not None and y2 is not None:
                cursor2 = PDFCursor(x2, y2)
            else:
                raise Exception("Rectanlge not defined")
        else:
            if x1 is not None and y1 is not None:
                cursor1 = PDFCursor(x1, y1)
                if x2 is not None and y2 is not None:
                    cursor2 = PDFCursor(x2, y2)
                elif width is not None and height is not None:
                    dims = PDFCursor(width, height)
                    cursor2 = cursor1.add(dims)
                else:
                    raise Exception("Rectanlge not defined")
            else:
                raise Exception("Rectanlge not defined")

        rect = PDFRectangle(self.SS, self.page, self.colorscheme, cursor1, cursor2, size=1, style=style)
        rect.draw()

    def addTable(self, datalist, headerlist=None, cursor=None):
        pass
Exemple #11
0
class PDFDocument(object):
    """ The Document object is the base class that
        is used to add and manage the content of the
        pdf file.

        Document maintains lists of pages and other
        resources, provides for default selections,
        which can be changed through the convinence
        methods.

        Note that page and text objects do not maintain
        fonts themselves. If they must use them (for
        calculating widths or such), they must have
        the current font passed to them.

    """
    def __init__(self, session, orientation, layout):
        """Sets up a standard default document."""
        self.session = session
        self.pages = []
        self.fonts = []
        self.fontkeys = []  # array of used fonts

        self.images = []
        self.imagekeys = []
        self.image_filter = None

        self.margins = None
        self.orientation_default = orientation
        self.layout_default = layout
        self.justification = 'left'
        self.double_spacing = None
        self.diffs = []

        self._set_defaults()

    # Set Defaults
    def _set_defaults(self):
        """Set color scheme & font to defaults."""
        self._set_color_scheme()
        self._set_default_font()
        self.add_page()  # add first page

    def _set_color_scheme(self,
                          draw_color=None,
                          fill_color=None,
                          text_color=None):
        """ Default color object is black letters
            & black lines."""
        if draw_color is None:
            draw_color = PDFColor()
            draw_color._set_type('d')
        if fill_color is None:
            fill_color = PDFColor()
            fill_color._set_type('f')
        if text_color is None:
            text_color = PDFColor()
            text_color._set_type('t')

        self.draw_color = draw_color
        self.fill_color = fill_color
        self.text_color = text_color

    def set_text_color(self, color):
        if isinstance(color, PDFColor):
            self.text_color = color
        else:
            raise ValueError("Not a PDFColor instance")

    def set_fill_color(self, color):
        if isinstance(color, PDFColor):
            self.fill_color = color
        else:
            raise ValueError("Not a PDFColor instance")

    def set_draw_color(self, color):
        if isinstance(color, PDFColor):
            self.draw_color = color
        else:
            raise ValueError("Not a PDFColor instance")

    def _set_default_font(self):
        """ Internal method to set the initial default font. Change
            the font using set_font method."""
        self.font = PDFFont(self.session)
        self.font._set_index()
        self.fonts.append(self.font)
        self.fontkeys.append(self.font.font_key)

    @staticmethod
    def get_color():
        return PDFColor()

    # Public methods, main interface
    # Pages
    def add_page(self, page=None):
        """ May generate and add a PDFPage separately, or use this to generate
            a default page."""
        if page is None:
            self.page = PDFPage(self.orientation_default, self.layout_default,
                                self.margins)
        else:
            self.page = page
        self.page._set_index(len(self.pages))
        self.pages.append(self.page)
        currentfont = self.font
        self.set_font(font=currentfont)
        self.session._reset_colors()

    def get_page(self):
        """Returns reference to current page object."""
        return self.page

    def set_margins(self, left, top=None, right=None, bottom=None):
        if isinstance(left, PDFMargin):
            self.margins = left
        else:
            self.margins = PDFMargin(left, top, right, bottom)
        if self.page is not None:
            self.page.set_margins(self.margins)

    def set_justification(self, value='left'):
        if value in ['left', 'right', 'center']:
            self.justification = value
        else:
            raise ValueError("Incorrect value for justification %s" % value)

    def add_page_numbers(self,
                         location='right',
                         font=None,
                         cursor=None,
                         color=None,
                         text1="Page %s",
                         text2=None):
        self.page_numbers = location
        self.page_numbers_font = font
        self.page_numbers_cursor = cursor
        self.page_numbers_color = color
        self.page_numbers_text1 = text1
        self.page_numbers_text2 = text2

    def change_page_orientation(self):
        self.page._change_orientation()

    # Cursor
    def get_new_cursor(self):
        """ Returns a new default cursor """
        return PDFCursor()

    def get_cursor(self):
        return self.page.cursor.copy()

    def set_cursor(self, x=None, y=None):
        if x is not None and isinstance(x, PDFCursor):
            self.page.cursor = x
        elif x is not None and y is not None:
            self.page.cursor = PDFCursor(x, y, True)
        else:
            raise Exception("Invalid cursor input")

    # Font
    def set_font(self, family=None, style='', size=None, font=None):
        """ Set the document font object, size given in points. If family, style, and/or size is given, generates
            a new Font object, checks to see if it is already in use, and selects it. """
        if font:
            testfont = font
        elif isinstance(family, PDFFont):
            testfont = family
        else:
            # If size is not defined, keep the last size.
            if size is None:
                size = self.font.font_size

            # Create a font from givens to test its key
            if family in CORE_FONTS:
                testfont = PDFFont(self.session, family, style, size)
            else:
                testfont = PDFTTFont(self.session, family, style, size)

        testkey = testfont.font_key

        if testkey in self.fontkeys:
            index = self.fontkeys.index(testkey)
            self.font = self.fonts[index]
            if size != self.font.font_size:
                self.font._set_size(size)
            if style != self.font.style:
                self.font._set_style(style)
        else:
            self.font = testfont
            self._register_new_font(self.font)

        self.font.is_set = False

        if self.page.index > 0:
            self.session._out(
                'BT /F%d %.2f Tf ET' % (self.font.index, self.font.font_size),
                self.page)
            self.font.is_set = True
        return self.font

    def get_font(self):
        """ Get the current font object. Useful for storing
            in variables, and switching between styles. """
        return self.font

    def set_font_size(self, size):
        """Convenience method for just changing font size."""
        if self.font.font_size == size:
            pass
        else:
            self.font._set_size(size)

    def get_font_size(self):
        return self.font.font_size

    def print_available_fonts(self):
        print PDFTTFont.available_fonts()

    # Writing
    def add_text(self, text, cursor=None, justification=None):
        """ Input text, short or long. Writes in order, within the defined page boundaries. Sequential add_text commands will print without
            additional whitespace. """
        if cursor is None:
            cursor = self.page.cursor

        text = re.sub("\s\s+", " ", text)

        if justification is None:
            justification = self.justification

        if '\n' in text:
            text_list = text.split('\n')
            for text in text_list:
                PDFText(self.session, self.page, text, self.font,
                        self.text_color, cursor, justification,
                        self.double_spacing)
                self.add_newline()
        else:
            PDFText(self.session, self.page, text, self.font, self.text_color,
                    cursor, justification, self.double_spacing)

    def draw_text(self, *args, **kwargs):
        self.add_text(*args, **kwargs)

    def text(self, *args, **kwargs):
        self.add_text(*args, **kwargs)

    def double_space(self, multiplier=1):
        self.double_spacing = multiplier

    def add_newline(self, number=1):
        """ Starts over again at the new line. If number is specified,
            it will leave multiple lines."""
        if isinstance(number, int):
            try:
                self.page._add_newline(self.font, number, self.double_spacing)
            except ValueError:
                self.add_page()
        else:
            raise TypeError("Number of newlines must be an integer.")

    def newline(self, *args, **kwargs):
        self.add_newline(*args, **kwargs)

    def add_indent(self, spaces=4):
        """ Adds a standard tab of 4 spaces.
        """
        self.page._add_indent(self.font, spaces)

    def indent(self, *args, **kwargs):
        self.add_indent(*args, **kwargs)

    def start_block_indent(self, px=20):
        self.px = px
        self.page.cursor.x_plus(self.px)
        self.page.cursor.xmin = self.page.cursor.x

    def end_block_indent(self):
        self.page.cursor.xmin -= self.px
        self.page.cursor.x_plus(-self.px)

    def add_list(self, *args, **kwargs):
        bullet_code = 149
        if 'bullet' in kwargs:
            if kwargs['bullet'] == 1:
                bullet_code = 149
            elif kwargs['bullet'] == 2:
                bullet_code = 186
            elif kwargs['bullet'] == 3:
                bullet_code = 150

        char = chr(bullet_code)

        if 'force' in kwargs and kwargs['force'] is True:
            saved_font = self.get_font()
            for arg in args:
                self.set_font(family='helvetica', size=saved_font.font_size)
                self.add_text(char)
                self.set_font(saved_font)
                self.add_text(' %s' % arg)
                self.add_newline(2)
        else:
            for arg in args:
                if isinstance(arg, list):
                    for item in arg:
                        self.add_text(char)
                        self.add_text(' %s' % item)
                        self.add_newline(1)
                else:
                    self.add_text(char)
                    self.add_text(' %s' % arg)
                    self.add_newline(2)

    def list(self, *args, **kwargs):
        self.add_list(*args, **kwargs)

    def add_html(self, htmltext, context=None, formats=None, cursor=None):
        if cursor is not None:
            self.set_cursor(cursor)

        if hasattr(htmltext, 'write'):
            htmltext = htmltext.read()

        PDFHtml(self, self.session, htmltext, formats, context)

    def html(self, *args, **kwargs):
        self.add_html(*args, **kwargs)

    # Shapes
    def add_line(self,
                 x1=None,
                 y1=None,
                 x2=None,
                 y2=None,
                 cursor1=None,
                 cursor2=None,
                 stroke="solid"):
        if cursor1 is not None:
            if cursor2 is not None:
                pass
            elif x2 is not None and y2 is not None:
                cursor2 = self.page.cursor.copy()
                cursor2.x = x2
                cursor2.y = y2
            else:
                raise Exception("Line not fully specified")
        elif x1 is not None and y1 is not None:
            cursor1 = self.page.cursor.copy()
            cursor1.x = x1
            cursor1.y = y1
            if cursor2 is not None:
                pass
            elif x2 is not None and y2 is not None:
                cursor2 = self.page.cursor.copy()
                cursor2.x = x2
                cursor2.y = y2
            else:
                raise Exception("Line not fully specified")
        else:
            raise Exception("Line not specified")

        myline = PDFLine(self.session, self.page, cursor1, cursor2,
                         self.draw_color, stroke)
        myline._draw()

    def draw_line(self, *args, **kwargs):
        self.add_line(*args, **kwargs)

    def line(self, *args, **kwargs):
        self.add_line(*args, **kwargs)

    def draw_horizontal_line(self):
        end_cursor = self.page.cursor.copy()
        end_cursor.x = end_cursor.xmax

        myline = PDFLine(self.session, self.page, self.page.cursor, end_cursor,
                         self.draw_color, None)
        myline._draw()

    def add_horizontal_line(self):
        self.draw_horizontal_line()

    def horizontal_line(self):
        self.draw_horizontal_line()

    def draw_rectangle(self,
                       x1=None,
                       y1=None,
                       x2=None,
                       y2=None,
                       width=None,
                       height=None,
                       cursor1=None,
                       cursor2=None,
                       style='S',
                       stroke="solid",
                       size=1):
        if cursor1 is not None:
            if cursor2 is not None:
                pass
            elif width is not None and height is not None:
                dims = PDFCursor(width, height)
                cursor2 = cursor1 + dims
            elif x2 is not None and y2 is not None:
                cursor2 = PDFCursor(x2, y2)
            else:
                raise Exception("Rectangle not defined")
        else:
            if x1 is not None and y1 is not None:
                cursor1 = PDFCursor(x1, y1)
                if x2 is not None and y2 is not None:
                    cursor2 = PDFCursor(x2, y2)
                elif width is not None and height is not None:
                    dims = PDFCursor(width, height)
                    cursor2 = cursor1 + dims
                elif cursor2 is not None:
                    pass
                else:
                    raise Exception("Rectangle not defined")
            else:
                raise Exception("Rectangle not defined")

        rectangle = PDFRectangle(self.session, self.page, cursor1, cursor2,
                                 self.draw_color, self.fill_color, style,
                                 stroke, size)

        rectangle._draw()

    def add_rectangle(self, *args, **kwargs):
        self.draw_rectangle(*args, **kwargs)

    def rectangle(self, *args, **kwargs):
        self.draw_rectangle(*args, **kwargs)

    def draw_circle(self,
                    center_cursor,
                    radius,
                    border_color=None,
                    fill_color=None,
                    style="S",
                    stroke='solid',
                    size=1):
        self.draw_ellipse(center_cursor, radius, radius, border_color,
                          fill_color, style, stroke, size)

    def draw_ellipse(self,
                     center_cursor,
                     x_radius,
                     y_radius,
                     border_color=None,
                     fill_color=None,
                     style="S",
                     stroke='solid',
                     size=1):
        radius_cursor = PDFCursor(x_radius, y_radius)
        if isinstance(border_color, PDFColor):
            border_color = border_color
        else:
            border_color = self.draw_color
        if isinstance(fill_color, PDFColor):
            fill_color = fill_color
        else:
            fill_color = self.fill_color

        circle = PDFEllipse(self.session, self.page, center_cursor,
                            radius_cursor, border_color, fill_color, style,
                            stroke, size)
        circle._draw()

    def draw_arc(self,
                 center_cursor,
                 radius,
                 starting_angle,
                 arc_angle,
                 inverted=False,
                 end_angle=None,
                 border_color=None,
                 fill_color=None,
                 style='S',
                 stroke='solid',
                 size=1):
        if isinstance(border_color, PDFColor):
            border_color = border_color
        else:
            border_color = self.draw_color
        if isinstance(fill_color, PDFColor):
            fill_color = fill_color
        else:
            fill_color = self.fill_color

        arc = PDFArc(self.session, self.page, center_cursor, radius,
                     starting_angle, arc_angle, inverted, end_angle,
                     border_color, fill_color, style, stroke, size)
        arc._draw()

    def draw_bezier_lines(self, points, color=None, style="S", stroke="solid"):
        save_draw_color = self.draw_color

        line = PDFBezier(self.session, self.page, points, color, style, stroke)

        self.set_draw_color(save_draw_color)

    # Graphs
    def add_line_graph(self,
                       data,
                       cursor,
                       width,
                       height,
                       title=None,
                       x_axis_limits=None,
                       y_axis_limits=None,
                       frequency=None,
                       axis_titles=None,
                       axis_labels=None,
                       axis_font_size=None,
                       line_colors=None,
                       background=None,
                       legend=None,
                       dots=None,
                       style="S",
                       axis=True):
        save_draw_color = self.draw_color
        save_fill_color = self.fill_color
        save_font_size = self.get_font_size()
        if axis_font_size is None:
            self.set_font_size(8)
        else:
            self.set_font_size(axis_font_size)

        graph = PDFLineGraph(self.session, self.page, cursor, data, width,
                             height, title, x_axis_limits, y_axis_limits,
                             frequency, axis_titles, axis_labels, line_colors,
                             background, legend, dots, style, axis)

        self.set_font_size(save_font_size)
        self.set_draw_color(save_draw_color)
        self.set_fill_color(save_fill_color)

    def add_xy_scatter(self,
                       data,
                       cursor,
                       width,
                       height,
                       title=None,
                       x_axis_limits=None,
                       y_axis_limits=None,
                       frequency=None,
                       axis_titles=None,
                       axis_labels=None,
                       axis_font_size=None,
                       line_colors=None,
                       background=None,
                       legend=None,
                       dots=None,
                       linear_regression=None,
                       linear_regression_equation=None):

        save_draw_color = self.draw_color
        save_fill_color = self.fill_color
        save_font_size = self.get_font_size()
        if axis_font_size is None:
            self.set_font_size(8)
        else:
            self.set_font_size(axis_font_size)

        graph = PDFXYScatter(self.session, self.page, cursor, data, width,
                             height, title, x_axis_limits, y_axis_limits,
                             frequency, axis_titles, axis_labels, line_colors,
                             background, legend, dots, linear_regression,
                             linear_regression_equation)

        self.set_font_size(save_font_size)
        self.set_draw_color(save_draw_color)
        self.set_fill_color(save_fill_color)

    def add_simple_bar_chart(self,
                             data,
                             cursor,
                             width,
                             height,
                             title=None,
                             axis_titles=None,
                             axis_font_size=None,
                             y_axis_limits=None,
                             y_axis_frequency=None,
                             bar_style="B",
                             bar_padding=0.1,
                             bar_border_colors=None,
                             bar_fill_colors=None,
                             background=None):

        save_draw_color = self.draw_color
        save_fill_color = self.fill_color
        save_font_size = self.get_font_size()

        if axis_font_size is None:
            self.set_font_size(8)
        else:
            self.set_font_size(axis_font_size)

        graph = PDFBarChart(self.session, self.page, data, cursor, width,
                            height, title, axis_titles, y_axis_limits,
                            y_axis_frequency, bar_style, bar_padding,
                            bar_border_colors, bar_fill_colors, background)

        self.set_font_size(save_font_size)
        self.set_draw_color(save_draw_color)
        self.set_fill_color(save_fill_color)

    def add_multi_bar_chart(self,
                            data,
                            cursor,
                            width,
                            height,
                            title=None,
                            axis_titles=None,
                            axis_font_size=None,
                            y_axis_limits=None,
                            y_axis_frequency=None,
                            bar_style="B",
                            bar_padding=0.1,
                            bar_border_colors=None,
                            bar_fill_colors=None,
                            background=None,
                            legend=None):
        save_draw_color = self.draw_color
        save_fill_color = self.fill_color
        save_font_size = self.get_font_size()

        if axis_font_size is None:
            self.set_font_size(8)
        else:
            self.set_font_size(axis_font_size)

        graph = PDFMultiBarChart(self.session, self.page, data, cursor, width,
                                 height, title, axis_titles, y_axis_limits,
                                 y_axis_frequency, bar_style, bar_padding,
                                 bar_border_colors, bar_fill_colors,
                                 background, legend)

        self.set_font_size(save_font_size)
        self.set_draw_color(save_draw_color)
        self.set_fill_color(save_fill_color)

    def add_pie_chart(self,
                      data,
                      cursor,
                      width,
                      height,
                      title=None,
                      data_type="raw",
                      fill_colors=None,
                      labels=False,
                      background=None,
                      legend=None):
        """ Data type may be "raw" or "percent" """
        save_draw_color = self.draw_color
        save_fill_color = self.fill_color

        chart = PDFPieChart(self.session, self.page, data, cursor, width,
                            height, title, data_type, fill_colors, labels,
                            background, legend)

        self.set_draw_color(save_draw_color)
        self.set_fill_color(save_fill_color)

    # Tables
    def add_table(self, rows, columns, cursor=None):
        if cursor is None:
            cursor = self.page.cursor

        table = PDFTable(self.session, self.page, rows, columns, cursor,
                         self.font)

        return table

    def draw_table(self, table):
        if isinstance(table, PDFTable):
            table._draw()
            self.page.cursor = table.cursor
        else:
            raise Exception("Invalid Table")

    def add_cell_format(self, data=None, cell_font=None):
        if cell_font is None:
            cell_font = self.font
        if data is None:
            data = {}
        format = PDFCellFormat(data, cell_font)
        return format

    # Images
    def add_image(self, image, name=None):
        if isinstance(image, PDFImage):  # It's an image object
            myimage = self._get_image(image.name)
            if not myimage:
                self._register_new_image(image)
            myimage = image

        elif isinstance(image, str):  # Name or path
            image_string = image
            if image_string.find('.') == -1:  # Not a path, treat as name
                myimage = self._get_image(image_string)
                if not myimage:
                    raise Exception('Not a proper image path')
            else:  # Is a path
                if not name:  # But it doesn't have a name specified.
                    name = os.path.splitext(image_string)[0]  # Specify it
                myimage = self._get_image(name)
                if not myimage:  # New image
                    extension = os.path.splitext(image_string)[1]
                    if extension == '.png':
                        myimage = PDFPNG(self.session, image_string, name)
                    elif extension == '.jpg':
                        myimage = PDFJPG(self.session, image_string, name)
                    else:
                        raise Exception("Image format %s not supported" %
                                        extension)
                    self._register_new_image(myimage)

        return myimage

    def draw_image(self, image, cursor=None, width=None, height=None):
        if isinstance(image, PDFImage):  # It's an image object
            myimage = self._get_image(image.name)
        else:
            try:
                myimage = self._get_image(image)
            except:
                raise Exception("%s is an invalid image. Add it first.")

        if not cursor:
            imagecursor = self.page.cursor
        else:
            imagecursor = cursor
        myimage._set_cursor(imagecursor)
        myimage._set_size(width, height)
        myimage._draw(self.page)

        # If a cursor was not specified, place at the end
        if not cursor:
            self.page.cursor = myimage.cursor

    def set_background_image(self, image):
        background_cursor = PDFCursor(0, 0)
        background_cursor.set_bounds(xmin=0,
                                     ymin=0,
                                     xmax=self.page.width,
                                     ymax=self.page.height,
                                     ymaxmax=self.page.height)
        myimage = self.add_image(image)
        self.draw_image(myimage, background_cursor, width=self.page.width)

    # Private methods for outputting document
    def _get_image(self, image_name):
        if image_name in self.imagekeys:
            index = self.imagekeys.index(image_name)
            myimage = self.images[index]
            return myimage
        else:
            return False

    def _output_pages(self):
        """ Called by the PDFLite object to prompt creating
            the page objects."""
        if not self.orientation_changes:
            self._get_orientation_changes()

        # Page
        for page in self.pages:
            obj = self.session._add_object()
            self.session._out('<</Type /Page')
            self.session._out('/Parent 1 0 R')
            if self.orientation_changes:
                self.session._out('/MediaBox [0 0 %.2f %.2f]' %
                                  (page.width, page.height))
            self.session._out('/Resources 2 0 R')
            self.session._out(
                '/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>')
            self.session._out('/Contents %s 0 R>>' % (obj.id + 1))
            self.session._out('endobj')

            # Page content
            self.session._add_object()
            if self.session.compression is True:
                textfilter = ' /Filter /FlateDecode '
                page._compress()
            else:
                textfilter = ''
            self.session._out('<<%s/Length %s >>' %
                              (textfilter, len(page.buffer)))
            self.session._put_stream(page.buffer)
            self.session._out('endobj')

    def _set_page_numbers(self):
        if hasattr(self, 'page_numbers'):
            for page in self.pages:
                self._add_page_number(page, len(self.pages))

    def _add_page_number(self, page, number_of_pages):
        if self.page_numbers_font is not None:
            fs = 'BT /F%d %.2f Tf ET' % (self.page_numbers_font.index,
                                         self.page_numbers_font.font_size)
            self.session._out(fs, page)

        if self.page_numbers_color is not None:
            self.page_numbers_color._set_type('t')
            self.session._out(self.page_numbers_color._get_color_string(),
                              page)

        text_string = self.page_numbers_text1 % (page.index + 1)
        if self.page_numbers_text2 is not None:
            text_string += self.page_numbers_text2 % number_of_pages

        if self.page_numbers_cursor is None:
            sw = self.font._string_width(text_string)
            y = page.page_size[1]
            y = y - self.page.margin.bottom

            if self.page_numbers == 'left':
                x = self.page.margin.left
            elif self.page_numbers == 'right':
                x = page.page_size[0] - page.margin.right - sw
            else:
                x = page.page_size[0] / 2 - int(sw / 2.0)
            cursor = PDFCursor(x, y)
        else:
            cursor = self.page_numbers_cursor

        text_string = '(%s)' % text_string
        s = 'BT %.2f %.2f Td %s Tj ET' % (cursor.x, cursor.y_prime,
                                          text_string)
        self.session._out(s, page)

    def _get_orientation_changes(self):
        """ Returns a list of the pages that have
            orientation changes."""
        self.orientation_changes = []
        for page in self.pages:
            if page.orientation_change is True:
                self.orientation_changes.append(page.index)
            else:
                pass
        return self.orientation_changes

    def _register_new_font(self, font):
        font._set_index(len(self.fonts) + 1)
        self.fonts.append(self.font)
        self.fontkeys.append(self.font.font_key)
        if hasattr(font, 'diffs') and font.diffs is not None:
            try:
                index_of_diff = self.diffs.index(font.diffs)
            except AttributeError:
                index_of_diff = len(self.diffs) + 1
                self.diffs.append(font.diffs)
            font.diff_number = index_of_diff

    def _output_fonts(self):
        """ Called by the PDFLite object to prompt creating
            the font objects."""
        self.session._save_object_number()
        self._output_encoding_diffs()
        self._output_font_files()

        for font in self.fonts:
            obj = self.session._add_object()
            font._set_number(obj.id)
            font._output()

    def _output_font_files(self):
        for font in self.fonts:
            if hasattr(font, 'file'):
                font.output_file()

    def _output_encoding_diffs(self):
        if self.diffs:
            for _ in self.diffs:
                self.session._add_object()
                self.session._out(
                    '<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [%s]>>'
                )
                self.session._out('endobj')

    def _register_new_image(self, image):
        image._set_index(len(self.images) + 1)
        self.images.append(image)
        self.imagekeys.append(image.name)

    def _output_images(self):
        """ Creates reference images, that can be
            drawn throughout the document."""
        for image in self.images:
            obj = self.session._add_object()
            image._set_number(obj.id)
            image._output()