Exemplo n.º 1
0
    def __init__(self, file_object, page_width, page_height, left_margin,
                 top_margin, right_margin, bottom_margin, width, height,
                 errors=print, debug=print, compress=True,
                 mark_links=False):
        QPaintEngine.__init__(self, self.FEATURES)
        self.file_object = file_object
        self.compress, self.mark_links = compress, mark_links
        self.page_height, self.page_width = page_height, page_width
        self.left_margin, self.top_margin = left_margin, top_margin
        self.right_margin, self.bottom_margin = right_margin, bottom_margin
        self.pixel_width, self.pixel_height = width, height
        # Setup a co-ordinate transform that allows us to use co-ords
        # from Qt's pixel based co-ordinate system with its origin at the top
        # left corner. PDF's co-ordinate system is based on pts and has its
        # origin in the bottom left corner. We also have to implement the page
        # margins. Therefore, we need to translate, scale and reflect about the
        # x-axis.
        dy = self.page_height - self.top_margin
        dx = self.left_margin
        sx =  (self.page_width - self.left_margin -
                            self.right_margin) / self.pixel_width
        sy =  (self.page_height - self.top_margin -
                            self.bottom_margin) / self.pixel_height

        self.pdf_system = QTransform(sx, 0, 0, -sy, dx, dy)
        self.graphics = Graphics(self.pixel_width, self.pixel_height)
        self.errors_occurred = False
        self.errors, self.debug = errors, debug
        self.fonts = {}
        self.current_page_num = 1
        self.current_page_inited = False
        self.qt_hack, err = plugins['qt_hack']
        if err:
            raise RuntimeError('Failed to load qt_hack with err: %s'%err)
Exemplo n.º 2
0
 def __init__(self, file_object, page_width, page_height, left_margin,
              top_margin, right_margin, bottom_margin, width, height,
              errors=print, debug=print, compress=True,
              mark_links=False, opts=None, page_margins=(0, 0, 0, 0)):
     QPaintEngine.__init__(self, self.FEATURES)
     self.file_object = file_object
     self.compress, self.mark_links = compress, mark_links
     self.page_height, self.page_width = page_height, page_width
     self.left_margin, self.top_margin = left_margin, top_margin
     self.right_margin, self.bottom_margin = right_margin, bottom_margin
     self.pixel_width, self.pixel_height = width, height
     self.pdf_system = self.create_transform()
     self.graphics = Graphics(self.pixel_width, self.pixel_height)
     self.errors_occurred = False
     self.errors, self.debug = errors, debug
     self.fonts = {}
     self.current_page_num = 1
     self.current_page_inited = False
     self.content_written_to_current_page = False
     self.qt_hack, err = plugins['qt_hack']
     self.has_footers = opts is not None and (opts.pdf_page_numbers or opts.pdf_footer_template is not None)
     self.has_headers = opts is not None and opts.pdf_header_template is not None
     ml, mr, mt, mb = page_margins
     self.header_height = mt
     self.footer_height = mb
     if err:
         raise RuntimeError('Failed to load qt_hack with err: %s'%err)
Exemplo n.º 3
0
    def __init__(self, file_object, page_width, page_height, left_margin,
                 top_margin, right_margin, bottom_margin, width, height,
                 errors=print, debug=print, compress=True,
                 mark_links=False):
        QPaintEngine.__init__(self, self.FEATURES)
        self.file_object = file_object
        self.compress, self.mark_links = compress, mark_links
        self.page_height, self.page_width = page_height, page_width
        self.left_margin, self.top_margin = left_margin, top_margin
        self.right_margin, self.bottom_margin = right_margin, bottom_margin
        self.pixel_width, self.pixel_height = width, height
        # Setup a co-ordinate transform that allows us to use co-ords
        # from Qt's pixel based co-ordinate system with its origin at the top
        # left corner. PDF's co-ordinate system is based on pts and has its
        # origin in the bottom left corner. We also have to implement the page
        # margins. Therefore, we need to translate, scale and reflect about the
        # x-axis.
        dy = self.page_height - self.top_margin
        dx = self.left_margin
        sx =  (self.page_width - self.left_margin -
                            self.right_margin) / self.pixel_width
        sy =  (self.page_height - self.top_margin -
                            self.bottom_margin) / self.pixel_height

        self.pdf_system = QTransform(sx, 0, 0, -sy, dx, dy)
        self.graphics = Graphics(self.pixel_width, self.pixel_height)
        self.errors_occurred = False
        self.errors, self.debug = errors, debug
        self.fonts = {}
        self.current_page_num = 1
        self.current_page_inited = False
        self.content_written_to_current_page = False
        self.qt_hack, err = plugins['qt_hack']
        if err:
            raise RuntimeError('Failed to load qt_hack with err: %s'%err)
Exemplo n.º 4
0
 def __init__(self,
              file_object,
              page_width,
              page_height,
              left_margin,
              top_margin,
              right_margin,
              bottom_margin,
              width,
              height,
              errors=print,
              debug=print,
              compress=True,
              mark_links=False,
              opts=None,
              page_margins=(0, 0, 0, 0)):
     QPaintEngine.__init__(self, self.FEATURES)
     self.file_object = file_object
     self.compress, self.mark_links = compress, mark_links
     self.page_height, self.page_width = page_height, page_width
     self.left_margin, self.top_margin = left_margin, top_margin
     self.right_margin, self.bottom_margin = right_margin, bottom_margin
     self.pixel_width, self.pixel_height = width, height
     self.pdf_system = self.create_transform()
     self.graphics = Graphics(self.pixel_width, self.pixel_height)
     self.errors_occurred = False
     self.errors, self.debug = errors, debug
     self.fonts = {}
     self.current_page_num = 1
     self.current_page_inited = False
     self.content_written_to_current_page = False
     self.qt_hack, err = plugins['qt_hack']
     self.has_footers = opts is not None and (
         opts.pdf_page_numbers or opts.pdf_footer_template is not None)
     self.has_headers = opts is not None and opts.pdf_header_template is not None
     ml, mr, mt, mb = page_margins
     self.header_height = mt
     self.footer_height = mb
     if err:
         raise RuntimeError('Failed to load qt_hack with err: %s' % err)
Exemplo n.º 5
0
class PdfEngine(QPaintEngine):

    FEATURES = QPaintEngine.AllFeatures & ~(
        QPaintEngine.PorterDuff | QPaintEngine.PerspectiveTransform
        | QPaintEngine.ObjectBoundingModeGradients
        | QPaintEngine.RadialGradientFill | QPaintEngine.ConicalGradientFill
    )  # noqa

    def __init__(self,
                 file_object,
                 page_width,
                 page_height,
                 left_margin,
                 top_margin,
                 right_margin,
                 bottom_margin,
                 width,
                 height,
                 errors=print,
                 debug=print,
                 compress=True,
                 mark_links=False,
                 opts=None,
                 page_margins=(0, 0, 0, 0)):
        QPaintEngine.__init__(self, self.FEATURES)
        self.file_object = file_object
        self.compress, self.mark_links = compress, mark_links
        self.page_height, self.page_width = page_height, page_width
        self.left_margin, self.top_margin = left_margin, top_margin
        self.right_margin, self.bottom_margin = right_margin, bottom_margin
        self.pixel_width, self.pixel_height = width, height
        self.pdf_system = self.create_transform()
        self.graphics = Graphics(self.pixel_width, self.pixel_height)
        self.errors_occurred = False
        self.errors, self.debug = errors, debug
        self.fonts = {}
        self.current_page_num = 1
        self.current_page_inited = False
        self.content_written_to_current_page = False
        self.qt_hack, err = plugins['qt_hack']
        self.has_footers = opts is not None and (
            opts.pdf_page_numbers or opts.pdf_footer_template is not None)
        self.has_headers = opts is not None and opts.pdf_header_template is not None
        ml, mr, mt, mb = page_margins
        self.header_height = mt
        self.footer_height = mb
        if err:
            raise RuntimeError('Failed to load qt_hack with err: %s' % err)

    def create_transform(self,
                         left_margin=None,
                         top_margin=None,
                         right_margin=None,
                         bottom_margin=None):
        # Setup a co-ordinate transform that allows us to use co-ords
        # from Qt's pixel based co-ordinate system with its origin at the top
        # left corner. PDF's co-ordinate system is based on pts and has its
        # origin in the bottom left corner. We also have to implement the page
        # margins. Therefore, we need to translate, scale and reflect about the
        # x-axis.
        left_margin = self.left_margin if left_margin is None else left_margin
        top_margin = self.top_margin if top_margin is None else top_margin
        right_margin = self.right_margin if right_margin is None else right_margin
        bottom_margin = self.bottom_margin if bottom_margin is None else bottom_margin
        dy = self.page_height - top_margin
        dx = left_margin
        sx = (self.page_width - left_margin - right_margin) / self.pixel_width
        sy = (self.page_height - top_margin -
              bottom_margin) / self.pixel_height
        return QTransform(sx, 0, 0, -sy, dx, dy)

    def apply_graphics_state(self):
        self.graphics(self.pdf_system, self.painter())

    def resolve_fill(self, rect):
        self.graphics.resolve_fill(rect, self.pdf_system,
                                   self.painter().transform())

    @property
    def do_fill(self):
        return self.graphics.current_state.do_fill

    @property
    def do_stroke(self):
        return self.graphics.current_state.do_stroke

    def init_page(self, custom_margins=None):
        self.content_written_to_current_page = False
        if custom_margins is None:
            self.pdf.transform(self.pdf_system)
        else:
            self.pdf.transform(self.create_transform(*custom_margins))
        self.pdf.apply_fill(
            color=(1, 1,
                   1))  # QPainter has a default background brush of white
        self.graphics.reset()
        self.pdf.save_stack()
        self.current_page_inited = True

    def begin(self, device):
        if not hasattr(self, 'pdf'):
            try:
                self.pdf = PDFStream(self.file_object,
                                     (self.page_width, self.page_height),
                                     compress=self.compress,
                                     mark_links=self.mark_links,
                                     debug=self.debug)
                self.graphics.begin(self.pdf)
            except:
                self.errors(traceback.format_exc())
                self.errors_occurred = True
                return False
        return True

    def end_page(self, is_last_page=False):
        if self.current_page_inited:
            self.pdf.restore_stack()
            drop_page = is_last_page and not self.content_written_to_current_page
            self.pdf.end_page(drop_page=drop_page)
            self.current_page_inited = False
            self.current_page_num += 0 if drop_page else 1
        return self.content_written_to_current_page

    def end(self):
        try:
            self.end_page()
            self.pdf.end()
        except:
            self.errors(traceback.format_exc())
            self.errors_occurred = True
            return False
        finally:
            self.pdf = self.file_object = None
        return True

    def type(self):
        return QPaintEngine.Pdf

    def add_image(self, img, cache_key):
        if img.isNull():
            return
        return self.pdf.add_image(img, cache_key)

    @store_error
    def drawTiledPixmap(self, rect, pixmap, point):
        self.content_written_to_current_page = 'drawTiledPixmap'
        self.apply_graphics_state()
        brush = QBrush(pixmap)
        bl = rect.topLeft()
        color, opacity, pattern, do_fill = self.graphics.convert_brush(
            brush, bl - point, 1.0, self.pdf_system,
            self.painter().transform())
        self.pdf.save_stack()
        self.pdf.apply_fill(color, pattern)
        self.pdf.draw_rect(bl.x(),
                           bl.y(),
                           rect.width(),
                           rect.height(),
                           stroke=False,
                           fill=True)
        self.pdf.restore_stack()

    @store_error
    def drawPixmap(self, rect, pixmap, source_rect):
        self.content_written_to_current_page = 'drawPixmap'
        self.apply_graphics_state()
        source_rect = source_rect.toRect()
        pixmap = (pixmap if source_rect == pixmap.rect() else
                  pixmap.copy(source_rect))
        image = pixmap.toImage()
        ref = self.add_image(image, pixmap.cacheKey())
        if ref is not None:
            self.pdf.draw_image(rect.x(), rect.y(), rect.width(),
                                rect.height(), ref)

    @store_error
    def drawImage(self, rect, image, source_rect, flags=Qt.AutoColor):
        self.content_written_to_current_page = 'drawImage'
        self.apply_graphics_state()
        source_rect = source_rect.toRect()
        image = (image
                 if source_rect == image.rect() else image.copy(source_rect))
        ref = self.add_image(image, image.cacheKey())
        if ref is not None:
            self.pdf.draw_image(rect.x(), rect.y(), rect.width(),
                                rect.height(), ref)

    @store_error
    def updateState(self, state):
        self.graphics.update_state(state, self.painter())

    @store_error
    def drawPath(self, path):
        self.content_written_to_current_page = 'drawPath'
        self.apply_graphics_state()
        p = convert_path(path)
        fill_rule = {
            Qt.OddEvenFill: 'evenodd',
            Qt.WindingFill: 'winding'
        }[path.fillRule()]
        self.pdf.draw_path(p,
                           stroke=self.do_stroke,
                           fill=self.do_fill,
                           fill_rule=fill_rule)

    @store_error
    def drawPoints(self, points):
        self.content_written_to_current_page = 'drawPoints'
        self.apply_graphics_state()
        p = Path()
        for point in points:
            p.move_to(point.x(), point.y())
            p.line_to(point.x(), point.y() + 0.001)
        self.pdf.draw_path(p, stroke=self.do_stroke, fill=False)

    @store_error
    def drawRects(self, rects):
        self.apply_graphics_state()
        with self.graphics:
            for rect in rects:
                self.resolve_fill(rect)
                bl = rect.topLeft()
                if self.do_stroke or self.do_fill:
                    self.content_written_to_current_page = 'drawRects'
                self.pdf.draw_rect(bl.x(),
                                   bl.y(),
                                   rect.width(),
                                   rect.height(),
                                   stroke=self.do_stroke,
                                   fill=self.do_fill)

    def create_sfnt(self, text_item):
        get_table = partial(self.qt_hack.get_sfnt_table, text_item)
        try:
            ans = Font(Sfnt(get_table))
        except UnsupportedFont as e:
            raise UnsupportedFont(
                'The font %s is not a valid sfnt. Error: %s' %
                (text_item.font().family(), e))
        glyph_map = self.qt_hack.get_glyph_map(text_item)
        gm = {}
        ans.ignore_glyphs = set()
        for uc, glyph_id in enumerate(glyph_map):
            if glyph_id not in gm:
                gm[glyph_id] = unichr(uc)
                if uc in (0xad, 0x200b):
                    ans.ignore_glyphs.add(glyph_id)
        ans.full_glyph_map = gm
        return ans

    @store_error
    def drawTextItem(self, point, text_item):
        # return super(PdfEngine, self).drawTextItem(point, text_item)
        self.apply_graphics_state()
        gi = GlyphInfo(*self.qt_hack.get_glyphs(point, text_item))
        if not gi.indices:
            return
        metrics = self.fonts.get(gi.name)
        if metrics is None:
            from calibre.utils.fonts.utils import get_all_font_names
            try:
                names = get_all_font_names(gi.name, True)
                names = ' '.join('%s=%s' % (k, names[k])
                                 for k in sorted(names))
            except Exception:
                names = 'Unknown'
            self.debug('Loading font: %s' % names)
            try:
                self.fonts[gi.name] = metrics = self.create_sfnt(text_item)
            except UnsupportedFont:
                self.debug(
                    'Failed to load font: %s, drawing text as outlines...' %
                    names)
                return super(PdfEngine, self).drawTextItem(point, text_item)
        indices, positions = [], []
        ignore_glyphs = metrics.ignore_glyphs
        for glyph_id, gpos in zip(gi.indices, gi.positions):
            if glyph_id not in ignore_glyphs:
                indices.append(glyph_id), positions.append(gpos)
        for glyph_id in indices:
            try:
                metrics.glyph_map[glyph_id] = metrics.full_glyph_map[glyph_id]
            except (KeyError, ValueError):
                pass
        glyphs = []
        last_x = last_y = 0
        for glyph_index, (x, y) in zip(indices, positions):
            glyphs.append((x - last_x, last_y - y, glyph_index))
            last_x, last_y = x, y

        if not self.content_written_to_current_page:
            dy = self.graphics.current_state.transform.dy()
            ypositions = [y + dy for x, y in positions]
            miny = min(ypositions or (0, ))
            maxy = max(ypositions or (self.pixel_height, ))
            page_top = self.header_height if self.has_headers else 0
            page_bottom = self.pixel_height - (self.footer_height
                                               if self.has_footers else 0)
            if page_top <= miny <= page_bottom or page_top <= maxy <= page_bottom:
                self.content_written_to_current_page = 'drawTextItem'
            else:
                self.debug(
                    'Text in header/footer: miny=%s maxy=%s page_top=%s page_bottom=%s'
                    % (miny, maxy, page_top, page_bottom))
        self.pdf.draw_glyph_run([gi.stretch, 0, 0, -1, 0, 0], gi.size, metrics,
                                glyphs)

    @store_error
    def drawPolygon(self, points, mode):
        self.content_written_to_current_page = 'drawPolygon'
        self.apply_graphics_state()
        if not points:
            return
        p = Path()
        p.move_to(points[0].x(), points[0].y())
        for point in points[1:]:
            p.line_to(point.x(), point.y())
        p.close()
        fill_rule = {
            self.OddEvenMode: 'evenodd',
            self.WindingMode: 'winding'
        }.get(mode, 'evenodd')
        self.pdf.draw_path(p,
                           stroke=True,
                           fill_rule=fill_rule,
                           fill=(mode in (self.OddEvenMode, self.WindingMode,
                                          self.ConvexMode)))

    def set_metadata(self, *args, **kwargs):
        self.pdf.set_metadata(*args, **kwargs)

    def add_outline(self, toc):
        self.pdf.links.add_outline(toc)

    def add_links(self, current_item, start_page, links, anchors):
        for pos in anchors.itervalues():
            pos['left'], pos['top'] = self.pdf_system.map(
                pos['left'], pos['top'])
        for link in links:
            pos = link[1]
            llx = pos['left']
            lly = pos['top'] + pos['height']
            urx = pos['left'] + pos['width']
            ury = pos['top']
            llx, lly = self.pdf_system.map(llx, lly)
            urx, ury = self.pdf_system.map(urx, ury)
            link[1] = pos['column'] + start_page
            link.append((llx, lly, urx, ury))
        self.pdf.links.add(current_item, start_page, links, anchors)
Exemplo n.º 6
0
class PdfEngine(QPaintEngine):

    FEATURES = QPaintEngine.AllFeatures & ~(
        QPaintEngine.PorterDuff | QPaintEngine.PerspectiveTransform |
        QPaintEngine.ObjectBoundingModeGradients |
        QPaintEngine.RadialGradientFill |
        QPaintEngine.ConicalGradientFill
    )

    def __init__(self, file_object, page_width, page_height, left_margin,
                 top_margin, right_margin, bottom_margin, width, height,
                 errors=print, debug=print, compress=True,
                 mark_links=False, opts=None):
        QPaintEngine.__init__(self, self.FEATURES)
        self.file_object = file_object
        self.compress, self.mark_links = compress, mark_links
        self.page_height, self.page_width = page_height, page_width
        self.left_margin, self.top_margin = left_margin, top_margin
        self.right_margin, self.bottom_margin = right_margin, bottom_margin
        self.pixel_width, self.pixel_height = width, height
        # Setup a co-ordinate transform that allows us to use co-ords
        # from Qt's pixel based co-ordinate system with its origin at the top
        # left corner. PDF's co-ordinate system is based on pts and has its
        # origin in the bottom left corner. We also have to implement the page
        # margins. Therefore, we need to translate, scale and reflect about the
        # x-axis.
        dy = self.page_height - self.top_margin
        dx = self.left_margin
        sx =  (self.page_width - self.left_margin -
                            self.right_margin) / self.pixel_width
        sy =  (self.page_height - self.top_margin -
                            self.bottom_margin) / self.pixel_height

        self.pdf_system = QTransform(sx, 0, 0, -sy, dx, dy)
        self.graphics = Graphics(self.pixel_width, self.pixel_height)
        self.errors_occurred = False
        self.errors, self.debug = errors, debug
        self.fonts = {}
        self.current_page_num = 1
        self.current_page_inited = False
        self.content_written_to_current_page = False
        self.qt_hack, err = plugins['qt_hack']
        self.has_footers = opts is not None and (opts.pdf_page_numbers or opts.pdf_footer_template is not None)
        self.has_headers = opts is not None and opts.pdf_header_template is not None
        self.header_height = (opts.margin_top or 0) if opts else 0
        self.footer_height = (opts.margin_bottom) or 0 if opts else 0
        if err:
            raise RuntimeError('Failed to load qt_hack with err: %s'%err)

    def apply_graphics_state(self):
        self.graphics(self.pdf_system, self.painter())

    def resolve_fill(self, rect):
        self.graphics.resolve_fill(rect, self.pdf_system,
                                   self.painter().transform())

    @property
    def do_fill(self):
        return self.graphics.current_state.do_fill

    @property
    def do_stroke(self):
        return self.graphics.current_state.do_stroke

    def init_page(self):
        self.content_written_to_current_page = False
        self.pdf.transform(self.pdf_system)
        self.pdf.apply_fill(color=(1, 1, 1))  # QPainter has a default background brush of white
        self.graphics.reset()
        self.pdf.save_stack()
        self.current_page_inited = True

    def begin(self, device):
        if not hasattr(self, 'pdf'):
            try:
                self.pdf = PDFStream(self.file_object, (self.page_width,
                        self.page_height), compress=self.compress,
                                     mark_links=self.mark_links,
                                     debug=self.debug)
                self.graphics.begin(self.pdf)
            except:
                self.errors(traceback.format_exc())
                self.errors_occurred = True
                return False
        return True

    def end_page(self, is_last_page=False):
        if self.current_page_inited:
            self.pdf.restore_stack()
            drop_page = is_last_page and not self.content_written_to_current_page
            self.pdf.end_page(drop_page=drop_page)
            self.current_page_inited = False
            self.current_page_num += 0 if drop_page else 1
        return self.content_written_to_current_page

    def end(self):
        try:
            self.end_page()
            self.pdf.end()
        except:
            self.errors(traceback.format_exc())
            self.errors_occurred = True
            return False
        finally:
            self.pdf = self.file_object = None
        return True

    def type(self):
        return QPaintEngine.Pdf

    def add_image(self, img, cache_key):
        if img.isNull():
            return
        return self.pdf.add_image(img, cache_key)

    @store_error
    def drawTiledPixmap(self, rect, pixmap, point):
        self.content_written_to_current_page = 'drawTiledPixmap'
        self.apply_graphics_state()
        brush = QBrush(pixmap)
        bl = rect.topLeft()
        color, opacity, pattern, do_fill = self.graphics.convert_brush(
            brush, bl-point, 1.0, self.pdf_system,
            self.painter().transform())
        self.pdf.save_stack()
        self.pdf.apply_fill(color, pattern)
        self.pdf.draw_rect(bl.x(), bl.y(), rect.width(), rect.height(),
                            stroke=False, fill=True)
        self.pdf.restore_stack()

    @store_error
    def drawPixmap(self, rect, pixmap, source_rect):
        self.content_written_to_current_page = 'drawPixmap'
        self.apply_graphics_state()
        source_rect = source_rect.toRect()
        pixmap = (pixmap if source_rect == pixmap.rect() else
                  pixmap.copy(source_rect))
        image = pixmap.toImage()
        ref = self.add_image(image, pixmap.cacheKey())
        if ref is not None:
            self.pdf.draw_image(rect.x(), rect.y(), rect.width(),
                                rect.height(), ref)

    @store_error
    def drawImage(self, rect, image, source_rect, flags=Qt.AutoColor):
        self.content_written_to_current_page = 'drawImage'
        self.apply_graphics_state()
        source_rect = source_rect.toRect()
        image = (image if source_rect == image.rect() else
                 image.copy(source_rect))
        ref = self.add_image(image, image.cacheKey())
        if ref is not None:
            self.pdf.draw_image(rect.x(), rect.y(), rect.width(),
                                rect.height(), ref)

    @store_error
    def updateState(self, state):
        self.graphics.update_state(state, self.painter())

    @store_error
    def drawPath(self, path):
        self.content_written_to_current_page = 'drawPath'
        self.apply_graphics_state()
        p = convert_path(path)
        fill_rule = {Qt.OddEvenFill:'evenodd',
                    Qt.WindingFill:'winding'}[path.fillRule()]
        self.pdf.draw_path(p, stroke=self.do_stroke,
                                fill=self.do_fill, fill_rule=fill_rule)

    @store_error
    def drawPoints(self, points):
        self.content_written_to_current_page = 'drawPoints'
        self.apply_graphics_state()
        p = Path()
        for point in points:
            p.move_to(point.x(), point.y())
            p.line_to(point.x(), point.y() + 0.001)
        self.pdf.draw_path(p, stroke=self.do_stroke, fill=False)

    @store_error
    def drawRects(self, rects):
        self.apply_graphics_state()
        with self.graphics:
            for rect in rects:
                self.resolve_fill(rect)
                bl = rect.topLeft()
                if self.do_stroke or self.do_fill:
                    self.content_written_to_current_page = 'drawRects'
                self.pdf.draw_rect(bl.x(), bl.y(), rect.width(), rect.height(),
                                stroke=self.do_stroke, fill=self.do_fill)

    def create_sfnt(self, text_item):
        get_table = partial(self.qt_hack.get_sfnt_table, text_item)
        try:
            ans = Font(Sfnt(get_table))
        except UnsupportedFont as e:
            raise UnsupportedFont('The font %s is not a valid sfnt. Error: %s'%(
                text_item.font().family(), e))
        glyph_map = self.qt_hack.get_glyph_map(text_item)
        gm = {}
        for uc, glyph_id in enumerate(glyph_map):
            if glyph_id not in gm:
                gm[glyph_id] = unichr(uc)
        ans.full_glyph_map = gm
        return ans

    @store_error
    def drawTextItem(self, point, text_item):
        # return super(PdfEngine, self).drawTextItem(point, text_item)
        self.apply_graphics_state()
        gi = GlyphInfo(*self.qt_hack.get_glyphs(point, text_item))
        if not gi.indices:
            return
        metrics = self.fonts.get(gi.name)
        if metrics is None:
            from calibre.utils.fonts.utils import get_all_font_names
            try:
                names = get_all_font_names(gi.name, True)
                names = ' '.join('%s=%s'%(k, names[k]) for k in sorted(names))
            except Exception:
                names = 'Unknown'
            self.debug('Loading font: %s' % names)
            try:
                self.fonts[gi.name] = metrics = self.create_sfnt(text_item)
            except UnsupportedFont:
                return super(PdfEngine, self).drawTextItem(point, text_item)
        for glyph_id in gi.indices:
            try:
                metrics.glyph_map[glyph_id] = metrics.full_glyph_map[glyph_id]
            except (KeyError, ValueError):
                pass
        glyphs = []
        last_x = last_y = 0
        for glyph_index, (x, y) in zip(gi.indices, gi.positions):
            glyphs.append((x-last_x, last_y - y, glyph_index))
            last_x, last_y = x, y

        if not self.content_written_to_current_page:
            dy = self.graphics.current_state.transform.dy()
            ypositions = [y + dy for x, y in gi.positions]
            miny = min(ypositions or (0,))
            maxy = max(ypositions or (self.pixel_height,))
            page_top = self.header_height if self.has_headers else 0
            page_bottom = self.pixel_height - (self.footer_height if self.has_footers else 0)
            if page_top <= miny <= page_bottom or page_top <= maxy <= page_bottom:
                self.content_written_to_current_page = 'drawTextItem'
            else:
                self.debug('Text in header/footer: miny=%s maxy=%s page_top=%s page_bottom=%s'% (
                    miny, maxy, page_top, page_bottom))
        self.pdf.draw_glyph_run([gi.stretch, 0, 0, -1, 0, 0], gi.size, metrics,
                                glyphs)

    @store_error
    def drawPolygon(self, points, mode):
        self.content_written_to_current_page = 'drawPolygon'
        self.apply_graphics_state()
        if not points:
            return
        p = Path()
        p.move_to(points[0].x(), points[0].y())
        for point in points[1:]:
            p.line_to(point.x(), point.y())
        p.close()
        fill_rule = {self.OddEvenMode:'evenodd',
                    self.WindingMode:'winding'}.get(mode, 'evenodd')
        self.pdf.draw_path(p, stroke=True, fill_rule=fill_rule,
            fill=(mode in (self.OddEvenMode, self.WindingMode, self.ConvexMode)))

    def set_metadata(self, *args, **kwargs):
        self.pdf.set_metadata(*args, **kwargs)

    def add_outline(self, toc):
        self.pdf.links.add_outline(toc)

    def add_links(self, current_item, start_page, links, anchors):
        for pos in anchors.itervalues():
            pos['left'], pos['top'] = self.pdf_system.map(pos['left'], pos['top'])
        for link in links:
            pos = link[1]
            llx = pos['left']
            lly = pos['top'] + pos['height']
            urx = pos['left'] + pos['width']
            ury = pos['top']
            llx, lly = self.pdf_system.map(llx, lly)
            urx, ury = self.pdf_system.map(urx, ury)
            link[1] = pos['column'] + start_page
            link.append((llx, lly, urx, ury))
        self.pdf.links.add(current_item, start_page, links, anchors)
Exemplo n.º 7
0
class PdfEngine(QPaintEngine):

    FEATURES = QPaintEngine.AllFeatures & ~(
        QPaintEngine.PorterDuff | QPaintEngine.PerspectiveTransform
        | QPaintEngine.ObjectBoundingModeGradients
        | QPaintEngine.RadialGradientFill
        | QPaintEngine.ConicalGradientFill)

    def __init__(self,
                 file_object,
                 page_width,
                 page_height,
                 left_margin,
                 top_margin,
                 right_margin,
                 bottom_margin,
                 width,
                 height,
                 errors=print,
                 debug=print,
                 compress=True,
                 mark_links=False):
        QPaintEngine.__init__(self, self.FEATURES)
        self.file_object = file_object
        self.compress, self.mark_links = compress, mark_links
        self.page_height, self.page_width = page_height, page_width
        self.left_margin, self.top_margin = left_margin, top_margin
        self.right_margin, self.bottom_margin = right_margin, bottom_margin
        self.pixel_width, self.pixel_height = width, height
        # Setup a co-ordinate transform that allows us to use co-ords
        # from Qt's pixel based co-ordinate system with its origin at the top
        # left corner. PDF's co-ordinate system is based on pts and has its
        # origin in the bottom left corner. We also have to implement the page
        # margins. Therefore, we need to translate, scale and reflect about the
        # x-axis.
        dy = self.page_height - self.top_margin
        dx = self.left_margin
        sx = (self.page_width - self.left_margin -
              self.right_margin) / self.pixel_width
        sy = (self.page_height - self.top_margin -
              self.bottom_margin) / self.pixel_height

        self.pdf_system = QTransform(sx, 0, 0, -sy, dx, dy)
        self.graphics = Graphics(self.pixel_width, self.pixel_height)
        self.errors_occurred = False
        self.errors, self.debug = errors, debug
        self.fonts = {}
        self.current_page_num = 1
        self.current_page_inited = False
        self.qt_hack, err = plugins['qt_hack']
        if err:
            raise RuntimeError('Failed to load qt_hack with err: %s' % err)

    def apply_graphics_state(self):
        self.graphics(self.pdf_system, self.painter())

    def resolve_fill(self, rect):
        self.graphics.resolve_fill(rect, self.pdf_system,
                                   self.painter().transform())

    @property
    def do_fill(self):
        return self.graphics.current_state.do_fill

    @property
    def do_stroke(self):
        return self.graphics.current_state.do_stroke

    def init_page(self):
        self.pdf.transform(self.pdf_system)
        self.pdf.apply_fill(
            color=(1, 1,
                   1))  # QPainter has a default background brush of white
        self.graphics.reset()
        self.pdf.save_stack()
        self.current_page_inited = True

    def begin(self, device):
        if not hasattr(self, 'pdf'):
            try:
                self.pdf = PDFStream(self.file_object,
                                     (self.page_width, self.page_height),
                                     compress=self.compress,
                                     mark_links=self.mark_links,
                                     debug=self.debug)
                self.graphics.begin(self.pdf)
            except:
                self.errors(traceback.format_exc())
                self.errors_occurred = True
                return False
        return True

    def end_page(self):
        if self.current_page_inited:
            self.pdf.restore_stack()
            self.pdf.end_page()
            self.current_page_inited = False
            self.current_page_num += 1

    def end(self):
        try:
            self.end_page()
            self.pdf.end()
        except:
            self.errors(traceback.format_exc())
            self.errors_occurred = True
            return False
        finally:
            self.pdf = self.file_object = None
        return True

    def type(self):
        return QPaintEngine.Pdf

    def add_image(self, img, cache_key):
        if img.isNull():
            return
        return self.pdf.add_image(img, cache_key)

    @store_error
    def drawTiledPixmap(self, rect, pixmap, point):
        self.apply_graphics_state()
        brush = QBrush(pixmap)
        bl = rect.topLeft()
        color, opacity, pattern, do_fill = self.graphics.convert_brush(
            brush, bl - point, 1.0, self.pdf_system,
            self.painter().transform())
        self.pdf.save_stack()
        self.pdf.apply_fill(color, pattern)
        self.pdf.draw_rect(bl.x(),
                           bl.y(),
                           rect.width(),
                           rect.height(),
                           stroke=False,
                           fill=True)
        self.pdf.restore_stack()

    @store_error
    def drawPixmap(self, rect, pixmap, source_rect):
        self.apply_graphics_state()
        source_rect = source_rect.toRect()
        pixmap = (pixmap if source_rect == pixmap.rect() else
                  pixmap.copy(source_rect))
        image = pixmap.toImage()
        ref = self.add_image(image, pixmap.cacheKey())
        if ref is not None:
            self.pdf.draw_image(rect.x(), rect.y(), rect.width(),
                                rect.height(), ref)

    @store_error
    def drawImage(self, rect, image, source_rect, flags=Qt.AutoColor):
        self.apply_graphics_state()
        source_rect = source_rect.toRect()
        image = (image
                 if source_rect == image.rect() else image.copy(source_rect))
        ref = self.add_image(image, image.cacheKey())
        if ref is not None:
            self.pdf.draw_image(rect.x(), rect.y(), rect.width(),
                                rect.height(), ref)

    @store_error
    def updateState(self, state):
        self.graphics.update_state(state, self.painter())

    @store_error
    def drawPath(self, path):
        self.apply_graphics_state()
        p = convert_path(path)
        fill_rule = {
            Qt.OddEvenFill: 'evenodd',
            Qt.WindingFill: 'winding'
        }[path.fillRule()]
        self.pdf.draw_path(p,
                           stroke=self.do_stroke,
                           fill=self.do_fill,
                           fill_rule=fill_rule)

    @store_error
    def drawPoints(self, points):
        self.apply_graphics_state()
        p = Path()
        for point in points:
            p.move_to(point.x(), point.y())
            p.line_to(point.x(), point.y() + 0.001)
        self.pdf.draw_path(p, stroke=self.do_stroke, fill=False)

    @store_error
    def drawRects(self, rects):
        self.apply_graphics_state()
        with self.graphics:
            for rect in rects:
                self.resolve_fill(rect)
                bl = rect.topLeft()
                self.pdf.draw_rect(bl.x(),
                                   bl.y(),
                                   rect.width(),
                                   rect.height(),
                                   stroke=self.do_stroke,
                                   fill=self.do_fill)

    def create_sfnt(self, text_item):
        get_table = partial(self.qt_hack.get_sfnt_table, text_item)
        try:
            ans = Font(Sfnt(get_table))
        except UnsupportedFont as e:
            raise UnsupportedFont(
                'The font %s is not a valid sfnt. Error: %s' %
                (text_item.font().family(), e))
        glyph_map = self.qt_hack.get_glyph_map(text_item)
        gm = {}
        for uc, glyph_id in enumerate(glyph_map):
            if glyph_id not in gm:
                gm[glyph_id] = unichr(uc)
        ans.full_glyph_map = gm
        return ans

    @store_error
    def drawTextItem(self, point, text_item):
        # return super(PdfEngine, self).drawTextItem(point, text_item)
        self.apply_graphics_state()
        gi = self.qt_hack.get_glyphs(point, text_item)
        if not gi.indices:
            sip.delete(gi)
            return
        name = hash(bytes(gi.name))
        if name not in self.fonts:
            try:
                self.fonts[name] = self.create_sfnt(text_item)
            except UnsupportedFont:
                return super(PdfEngine, self).drawTextItem(point, text_item)
        metrics = self.fonts[name]
        for glyph_id in gi.indices:
            try:
                metrics.glyph_map[glyph_id] = metrics.full_glyph_map[glyph_id]
            except (KeyError, ValueError):
                pass
        glyphs = []
        last_x = last_y = 0
        for i, pos in enumerate(gi.positions):
            x, y = pos.x(), pos.y()
            glyphs.append((x - last_x, last_y - y, gi.indices[i]))
            last_x, last_y = x, y

        self.pdf.draw_glyph_run([gi.stretch, 0, 0, -1, 0, 0], gi.size, metrics,
                                glyphs)
        sip.delete(gi)

    @store_error
    def drawPolygon(self, points, mode):
        self.apply_graphics_state()
        if not points:
            return
        p = Path()
        p.move_to(points[0].x(), points[0].y())
        for point in points[1:]:
            p.line_to(point.x(), point.y())
        p.close()
        fill_rule = {
            self.OddEvenMode: 'evenodd',
            self.WindingMode: 'winding'
        }.get(mode, 'evenodd')
        self.pdf.draw_path(p,
                           stroke=True,
                           fill_rule=fill_rule,
                           fill=(mode in (self.OddEvenMode, self.WindingMode,
                                          self.ConvexMode)))

    def set_metadata(self, *args, **kwargs):
        self.pdf.set_metadata(*args, **kwargs)

    def add_outline(self, toc):
        self.pdf.links.add_outline(toc)

    def add_links(self, current_item, start_page, links, anchors):
        for pos in anchors.itervalues():
            pos['left'], pos['top'] = self.pdf_system.map(
                pos['left'], pos['top'])
        for link in links:
            pos = link[1]
            llx = pos['left']
            lly = pos['top'] + pos['height']
            urx = pos['left'] + pos['width']
            ury = pos['top']
            llx, lly = self.pdf_system.map(llx, lly)
            urx, ury = self.pdf_system.map(urx, ury)
            link[1] = pos['column'] + start_page
            link.append((llx, lly, urx, ury))
        self.pdf.links.add(current_item, start_page, links, anchors)
Exemplo n.º 8
0
class PdfEngine(QPaintEngine):

    FEATURES = QPaintEngine.AllFeatures & ~(
        QPaintEngine.PorterDuff
        | QPaintEngine.PerspectiveTransform
        | QPaintEngine.ObjectBoundingModeGradients
        | QPaintEngine.RadialGradientFill
        | QPaintEngine.ConicalGradientFill
    )

    def __init__(
        self,
        file_object,
        page_width,
        page_height,
        left_margin,
        top_margin,
        right_margin,
        bottom_margin,
        width,
        height,
        errors=print,
        debug=print,
        compress=True,
        mark_links=False,
    ):
        QPaintEngine.__init__(self, self.FEATURES)
        self.file_object = file_object
        self.compress, self.mark_links = compress, mark_links
        self.page_height, self.page_width = page_height, page_width
        self.left_margin, self.top_margin = left_margin, top_margin
        self.right_margin, self.bottom_margin = right_margin, bottom_margin
        self.pixel_width, self.pixel_height = width, height
        # Setup a co-ordinate transform that allows us to use co-ords
        # from Qt's pixel based co-ordinate system with its origin at the top
        # left corner. PDF's co-ordinate system is based on pts and has its
        # origin in the bottom left corner. We also have to implement the page
        # margins. Therefore, we need to translate, scale and reflect about the
        # x-axis.
        dy = self.page_height - self.top_margin
        dx = self.left_margin
        sx = (self.page_width - self.left_margin - self.right_margin) / self.pixel_width
        sy = (self.page_height - self.top_margin - self.bottom_margin) / self.pixel_height

        self.pdf_system = QTransform(sx, 0, 0, -sy, dx, dy)
        self.graphics = Graphics(self.pixel_width, self.pixel_height)
        self.errors_occurred = False
        self.errors, self.debug = errors, debug
        self.fonts = {}
        self.current_page_num = 1
        self.current_page_inited = False
        self.qt_hack, err = plugins["qt_hack"]
        if err:
            raise RuntimeError("Failed to load qt_hack with err: %s" % err)

    def apply_graphics_state(self):
        self.graphics(self.pdf_system, self.painter())

    def resolve_fill(self, rect):
        self.graphics.resolve_fill(rect, self.pdf_system, self.painter().transform())

    @property
    def do_fill(self):
        return self.graphics.current_state.do_fill

    @property
    def do_stroke(self):
        return self.graphics.current_state.do_stroke

    def init_page(self):
        self.pdf.transform(self.pdf_system)
        self.pdf.apply_fill(color=(1, 1, 1))  # QPainter has a default background brush of white
        self.graphics.reset()
        self.pdf.save_stack()
        self.current_page_inited = True

    def begin(self, device):
        if not hasattr(self, "pdf"):
            try:
                self.pdf = PDFStream(
                    self.file_object,
                    (self.page_width, self.page_height),
                    compress=self.compress,
                    mark_links=self.mark_links,
                    debug=self.debug,
                )
                self.graphics.begin(self.pdf)
            except:
                self.errors(traceback.format_exc())
                self.errors_occurred = True
                return False
        return True

    def end_page(self):
        if self.current_page_inited:
            self.pdf.restore_stack()
            self.pdf.end_page()
            self.current_page_inited = False
            self.current_page_num += 1

    def end(self):
        try:
            self.end_page()
            self.pdf.end()
        except:
            self.errors(traceback.format_exc())
            self.errors_occurred = True
            return False
        finally:
            self.pdf = self.file_object = None
        return True

    def type(self):
        return QPaintEngine.Pdf

    def add_image(self, img, cache_key):
        if img.isNull():
            return
        return self.pdf.add_image(img, cache_key)

    @store_error
    def drawTiledPixmap(self, rect, pixmap, point):
        self.apply_graphics_state()
        brush = QBrush(pixmap)
        bl = rect.topLeft()
        color, opacity, pattern, do_fill = self.graphics.convert_brush(
            brush, bl - point, 1.0, self.pdf_system, self.painter().transform()
        )
        self.pdf.save_stack()
        self.pdf.apply_fill(color, pattern)
        self.pdf.draw_rect(bl.x(), bl.y(), rect.width(), rect.height(), stroke=False, fill=True)
        self.pdf.restore_stack()

    @store_error
    def drawPixmap(self, rect, pixmap, source_rect):
        self.apply_graphics_state()
        source_rect = source_rect.toRect()
        pixmap = pixmap if source_rect == pixmap.rect() else pixmap.copy(source_rect)
        image = pixmap.toImage()
        ref = self.add_image(image, pixmap.cacheKey())
        if ref is not None:
            self.pdf.draw_image(rect.x(), rect.y(), rect.width(), rect.height(), ref)

    @store_error
    def drawImage(self, rect, image, source_rect, flags=Qt.AutoColor):
        self.apply_graphics_state()
        source_rect = source_rect.toRect()
        image = image if source_rect == image.rect() else image.copy(source_rect)
        ref = self.add_image(image, image.cacheKey())
        if ref is not None:
            self.pdf.draw_image(rect.x(), rect.y(), rect.width(), rect.height(), ref)

    @store_error
    def updateState(self, state):
        self.graphics.update_state(state, self.painter())

    @store_error
    def drawPath(self, path):
        self.apply_graphics_state()
        p = convert_path(path)
        fill_rule = {Qt.OddEvenFill: "evenodd", Qt.WindingFill: "winding"}[path.fillRule()]
        self.pdf.draw_path(p, stroke=self.do_stroke, fill=self.do_fill, fill_rule=fill_rule)

    @store_error
    def drawPoints(self, points):
        self.apply_graphics_state()
        p = Path()
        for point in points:
            p.move_to(point.x(), point.y())
            p.line_to(point.x(), point.y() + 0.001)
        self.pdf.draw_path(p, stroke=self.do_stroke, fill=False)

    @store_error
    def drawRects(self, rects):
        self.apply_graphics_state()
        with self.graphics:
            for rect in rects:
                self.resolve_fill(rect)
                bl = rect.topLeft()
                self.pdf.draw_rect(
                    bl.x(), bl.y(), rect.width(), rect.height(), stroke=self.do_stroke, fill=self.do_fill
                )

    def create_sfnt(self, text_item):
        get_table = partial(self.qt_hack.get_sfnt_table, text_item)
        try:
            ans = Font(Sfnt(get_table))
        except UnsupportedFont as e:
            raise UnsupportedFont("The font %s is not a valid sfnt. Error: %s" % (text_item.font().family(), e))
        glyph_map = self.qt_hack.get_glyph_map(text_item)
        gm = {}
        for uc, glyph_id in enumerate(glyph_map):
            if glyph_id not in gm:
                gm[glyph_id] = unichr(uc)
        ans.full_glyph_map = gm
        return ans

    @store_error
    def drawTextItem(self, point, text_item):
        # return super(PdfEngine, self).drawTextItem(point, text_item)
        self.apply_graphics_state()
        gi = self.qt_hack.get_glyphs(point, text_item)
        if not gi.indices:
            sip.delete(gi)
            return
        name = hash(bytes(gi.name))
        if name not in self.fonts:
            try:
                self.fonts[name] = self.create_sfnt(text_item)
            except UnsupportedFont:
                return super(PdfEngine, self).drawTextItem(point, text_item)
        metrics = self.fonts[name]
        for glyph_id in gi.indices:
            try:
                metrics.glyph_map[glyph_id] = metrics.full_glyph_map[glyph_id]
            except (KeyError, ValueError):
                pass
        glyphs = []
        last_x = last_y = 0
        for i, pos in enumerate(gi.positions):
            x, y = pos.x(), pos.y()
            glyphs.append((x - last_x, last_y - y, gi.indices[i]))
            last_x, last_y = x, y

        self.pdf.draw_glyph_run([gi.stretch, 0, 0, -1, 0, 0], gi.size, metrics, glyphs)
        sip.delete(gi)

    @store_error
    def drawPolygon(self, points, mode):
        self.apply_graphics_state()
        if not points:
            return
        p = Path()
        p.move_to(points[0].x(), points[0].y())
        for point in points[1:]:
            p.line_to(point.x(), point.y())
        p.close()
        fill_rule = {self.OddEvenMode: "evenodd", self.WindingMode: "winding"}.get(mode, "evenodd")
        self.pdf.draw_path(
            p, stroke=True, fill_rule=fill_rule, fill=(mode in (self.OddEvenMode, self.WindingMode, self.ConvexMode))
        )

    def set_metadata(self, *args, **kwargs):
        self.pdf.set_metadata(*args, **kwargs)

    def add_outline(self, toc):
        self.pdf.links.add_outline(toc)

    def add_links(self, current_item, start_page, links, anchors):
        for pos in anchors.itervalues():
            pos["left"], pos["top"] = self.pdf_system.map(pos["left"], pos["top"])
        for link in links:
            pos = link[1]
            llx = pos["left"]
            lly = pos["top"] + pos["height"]
            urx = pos["left"] + pos["width"]
            ury = pos["top"]
            llx, lly = self.pdf_system.map(llx, lly)
            urx, ury = self.pdf_system.map(urx, ury)
            link[1] = pos["column"] + start_page
            link.append((llx, lly, urx, ury))
        self.pdf.links.add(current_item, start_page, links, anchors)