예제 #1
0
    def test_get_size_simple(self):
        res = get_size('12pt')
        self.assertEqual(res, 12.00)

        # Memoized...
        res = get_size('12pt')
        self.assertEqual(res, 12.00)
예제 #2
0
 def test_get_size_for_none_str(self):
     res = get_size("none")
     self.assertEqual(res, 0.0)
     res = get_size("0")
     self.assertEqual(res, 0.0)
     res = get_size("auto")  # Really?
     self.assertEqual(res, 0.0)
예제 #3
0
파일: tables.py 프로젝트: n3h3m/html2pdf
def _height(value=None):
    if value is None:
        return None
    value = str(value)
    if value.endswith("%"):
        return value
    return get_size(value)
예제 #4
0
파일: tags.py 프로젝트: n3h3m/html2pdf
 def start(self, c):
     if self.attr["color"] is not None:
         c.frag.textColor = get_color(self.attr["color"])
     if self.attr["face"] is not None:
         c.frag.fontName = c.get_font_name(self.attr["face"])
     if self.attr["size"] is not None:
         size = get_size(self.attr["size"], c.frag.fontSize, c.baseFontSize)
         c.frag.fontSize = max(size, 1.0)
예제 #5
0
파일: context.py 프로젝트: n3h3m/html2pdf
    def add_paragraph(self, force=False):

        force = (force or self.force)
        self.force = False

        # Cleanup the trail
        try:
            rfragList = reversed(self.fragList)
        except:
            # For Python 2.3 compatibility
            rfragList = copy.copy(self.fragList)
            rfragList.reverse()

        # Find maximum lead
        maxLeading = 0
        #fontSize = 0
        for frag in self.fragList:
            leading = get_size(frag.leadingSource,
                               frag.fontSize) + frag.leadingSpace
            maxLeading = max(leading, frag.fontSize + frag.leadingSpace,
                             maxLeading)
            frag.leading = leading

        if force or (self.text.strip() and self.fragList):

            # Update paragraph style by style of first fragment
            first = self.fragBlock
            style = self.to_paragraph_style(first)
            # style.leading = first.leading + first.leadingSpace
            if first.leadingSpace:
                style.leading = maxLeading
            else:
                style.leading = get_size(first.leadingSource,
                                         first.fontSize) + first.leadingSpace

            bulletText = copy.copy(first.bulletText)
            first.bulletText = None

            # Add paragraph to story
            if force or len(self.fragAnchor + self.fragList) > 0:

                # We need this empty fragment to work around problems in
                # Reportlab paragraphs regarding backGround etc.
                if self.fragList:
                    self.fragList.append(self.fragList[-1].clone(text=''))
                else:
                    blank = self.frag.clone()
                    blank.fontName = "Helvetica"
                    blank.text = ''
                    self.fragList.append(blank)

                self.dump_paragraph(self.fragAnchor + self.fragList, style)
                para = PmlParagraph(self.text,
                                    style,
                                    frags=self.fragAnchor + self.fragList,
                                    bulletText=bulletText)

                para.outline = first.outline
                para.outlineLevel = first.outlineLevel
                para.outlineOpen = first.outlineOpen
                para.keepWithNext = first.keepWithNext
                para.autoLeading = "max"

                if self.image:
                    para = PmlParagraphAndImage(para,
                                                self.image,
                                                side=self.imageData.get(
                                                    "align", "left"))

                self.add_story(para)

            self.fragAnchor = []
            first.bulletText = None

        # Reset data

        self.image = None
        self.imageData = {}

        self.clear_fragment()
예제 #6
0
파일: context.py 프로젝트: n3h3m/html2pdf
    def at_page(self, name, pseudopage, declarations):
        c = self.c
        data = {}
        name = name or "body"
        page_border = None

        if declarations:
            result = self.ruleset([self.selector('*')], declarations)

            if declarations:
                try:
                    data = result[0].values()[0]
                except Exception:
                    data = result[0].popitem()[1]
                page_border = data.get("-pdf-frame-border", None)

        if name in c.templateList:
            log.warn(
                self.c.warning("template '%s' has already been defined", name))

        if "-pdf-page-size" in data:
            c.pageSize = xhtml2pdf.default.PML_PAGESIZES.get(
                str(data["-pdf-page-size"]).lower(), c.pageSize)

        is_landscape = False
        if "size" in data:
            size = data["size"]
            if type(size) is not ListType:
                size = [size]
            size_list = []
            for value in size:
                valueStr = str(value).lower()
                if isinstance(value, tuple):
                    size_list.append(get_size(value))
                elif valueStr == "landscape":
                    is_landscape = True
                elif valueStr == "portrait":
                    is_landscape = False
                elif valueStr in xhtml2pdf.default.PML_PAGESIZES:
                    c.pageSize = xhtml2pdf.default.PML_PAGESIZES[valueStr]
                else:
                    log.warn(c.warning("Unknown size value for @page"))

            if len(size_list) == 2:
                c.pageSize = tuple(size_list)
            if is_landscape:
                c.pageSize = landscape(c.pageSize)

        padding_top = self._get_from_data(data, 'padding-top', 0, get_size)
        padding_left = self._get_from_data(data, 'padding-left', 0, get_size)
        padding_right = self._get_from_data(data, 'padding-right', 0, get_size)
        padding_bottom = self._get_from_data(data, 'padding-bottom', 0,
                                             get_size)
        border_color = self._get_from_data(
            data, ('border-top-color', 'border-bottom-color',
                   'border-left-color', 'border-right-color'), None, get_color)
        border_width = self._get_from_data(
            data, ('border-top-width', 'border-bottom-width',
                   'border-left-width', 'border-right-width'), 0, get_size)

        for prop in ("margin-top", "margin-left", "margin-right",
                     "margin-bottom", "top", "left", "right", "bottom",
                     "width", "height"):
            if prop in data:
                c.frameList.append(
                    self._pisa_add_frame(name,
                                         data,
                                         first=True,
                                         border=page_border,
                                         size=c.pageSize))
                break

        # Frames have to be calculated after we know the pagesize
        frame_list = []
        static_list = []
        for fname, static, border, x, y, w, h, fdata in c.frameList:
            fpadding_top = self._get_from_data(fdata, 'padding-top',
                                               padding_top, get_size)
            fpadding_left = self._get_from_data(fdata, 'padding-left',
                                                padding_left, get_size)
            fpadding_right = self._get_from_data(fdata, 'padding-right',
                                                 padding_right, get_size)
            fpadding_bottom = self._get_from_data(fdata, 'padding-bottom',
                                                  padding_bottom, get_size)
            fborder_color = self._get_from_data(
                fdata, ('border-top-color', 'border-bottom-color',
                        'border-left-color', 'border-right-color'),
                border_color, get_color)
            fborder_width = self._get_from_data(
                fdata, ('border-top-width', 'border-bottom-width',
                        'border-left-width', 'border-right-width'),
                border_width, get_size)

            if border or page_border:
                frame_border = ShowBoundaryValue()
            else:
                frame_border = ShowBoundaryValue(color=fborder_color,
                                                 width=fborder_width)

            #fix frame sizing problem.
            if static:
                x, y, w, h = get_frame_dimensions(fdata, c.pageSize[0],
                                                  c.pageSize[1])
            x, y, w, h = get_coordinates(x, y, w, h, c.pageSize)
            if w <= 0 or h <= 0:
                log.warn(
                    self.c.warning(
                        "Negative width or height of frame. Check @frame definitions."
                    ))

            frame = Frame(x,
                          y,
                          w,
                          h,
                          id=fname,
                          leftPadding=fpadding_left,
                          rightPadding=fpadding_right,
                          bottomPadding=fpadding_bottom,
                          topPadding=fpadding_top,
                          showBoundary=frame_border)

            if static:
                frame.pisaStaticStory = []
                c.frameStatic[static] = [frame] + c.frameStatic.get(static, [])
                static_list.append(frame)
            else:
                frame_list.append(frame)

        background = data.get("background-image", None)
        if background:
            #should be relative to the css file
            background = self.c.get_file(background,
                                         relative=self.c.cssParser.rootPath)

        if not frame_list:
            log.warn(
                c.warning(
                    "missing explicit frame definition for content or just static frames"
                ))
            fname, static, border, x, y, w, h, data = self._pisa_add_frame(
                name, data, first=True, border=page_border, size=c.pageSize)
            x, y, w, h = get_coordinates(x, y, w, h, c.pageSize)
            if w <= 0 or h <= 0:
                log.warn(
                    c.warning(
                        "Negative width or height of frame. Check @page definitions."
                    ))

            if border or page_border:
                frame_border = ShowBoundaryValue()
            else:
                frame_border = ShowBoundaryValue(color=border_color,
                                                 width=border_width)

            frame_list.append(
                Frame(x,
                      y,
                      w,
                      h,
                      id=fname,
                      leftPadding=padding_left,
                      rightPadding=padding_right,
                      bottomPadding=padding_bottom,
                      topPadding=padding_top,
                      showBoundary=frame_border))

        pt = PmlPageTemplate(
            id=name,
            frames=frame_list,
            pagesize=c.pageSize,
        )
        pt.pisaStaticList = static_list
        pt.pisaBackground = background
        pt.pisaBackgroundList = c.pisaBackgroundList

        if is_landscape:
            pt.pageorientation = pt.LANDSCAPE

        c.templateList[name] = pt
        c.template = None
        c.frameList = []
        c.frameStaticList = []

        return {}, {}
예제 #7
0
파일: context.py 프로젝트: n3h3m/html2pdf
    def __init__(self, path, debug=0, capacity=-1):
        self.fontList = copy.copy(xhtml2pdf.default.DEFAULT_FONT)
        self.path = []
        self.capacity = capacity

        self.node = None
        self.toc = PmlTableOfContents()
        self.story = []
        self.indexing_story = None
        self.text = []
        self.log = []
        self.err = 0
        self.warn = 0
        self.text = u""
        self.uidctr = 0
        self.multiBuild = False

        self.pageSize = A4
        self.template = None
        self.templateList = {}

        self.frameList = []
        self.frameStatic = {}
        self.frameStaticList = []
        self.pisaBackgroundList = []

        self.keepInFrameIndex = None

        self.baseFontSize = get_size("12pt")

        self.anchorFrag = []
        self.anchorName = []

        self.tableData = None

        self.frag = self.fragBlock = get_paragraph_fragment(
            ParagraphStyle('default%d' % self.uid()))
        self.fragList = []
        self.fragAnchor = []
        self.fragStack = []
        self.fragStrip = True

        self.listCounter = 0

        self.cssText = ""
        self.cssDefaultText = ""

        self.image = None
        self.imageData = {}
        self.force = False

        self.path_callback = None  # External callback function for path calculations

        # Store path to document
        self.pathDocument = path or "__dummy__"
        parts = urlparse.urlparse(self.pathDocument)
        if not parts.scheme:
            self.pathDocument = os.path.abspath(self.pathDocument)
        self.pathDirectory = get_dir_name(self.pathDocument)

        self.meta = dict(
            author="",
            title="",
            subject="",
            keywords="",
            pagesize=A4,
        )
        self.CSSBuilder = None
        self.CSSParser = None
예제 #8
0
 def test_get_size_for_float(self):
     res = get_size(12.00)
     self.assertEqual(res, 12.00)
예제 #9
0
 def test_get_size_for_tuple(self):
     # TODO: This is a really strange case. Probably should not work this way.
     res = get_size(("12", ".12"))
     self.assertEqual(res, 12.12)
예제 #10
0
def CSS2Frag(c, kw, isBlock):
    # COLORS
    if "color" in c.cssAttr:
        c.frag.textColor = get_color(c.cssAttr["color"])
    if "background-color" in c.cssAttr:
        c.frag.backColor = get_color(c.cssAttr["background-color"])
        # FONT SIZE, STYLE, WEIGHT
    if "font-family" in c.cssAttr:
        c.frag.fontName = c.get_font_name(c.cssAttr["font-family"])
    if "font-size" in c.cssAttr:
        # XXX inherit
        c.frag.fontSize = max(get_size("".join(c.cssAttr["font-size"]), c.frag.fontSize, c.baseFontSize), 1.0)
    if "line-height" in c.cssAttr:
        leading = "".join(c.cssAttr["line-height"])
        c.frag.leading = get_size(leading, c.frag.fontSize)
        c.frag.leadingSource = leading
    else:
        c.frag.leading = get_size(c.frag.leadingSource, c.frag.fontSize)
    if "letter-spacing" in c.cssAttr:
        c.frag.letterSpacing = c.cssAttr["letter-spacing"]
    if "-pdf-line-spacing" in c.cssAttr:
        c.frag.leadingSpace = get_size("".join(c.cssAttr["-pdf-line-spacing"]))
        # print "line-spacing", c.cssAttr["-pdf-line-spacing"], c.frag.leading
    if "font-weight" in c.cssAttr:
        value = lower(c.cssAttr["font-weight"])
        if value in ("bold", "bolder", "500", "600", "700", "800", "900"):
            c.frag.bold = 1
        else:
            c.frag.bold = 0
    for value in to_list(c.cssAttr.get("text-decoration", "")):
        if "underline" in value:
            c.frag.underline = 1
        if "line-through" in value:
            c.frag.strike = 1
        if "none" in value:
            c.frag.underline = 0
            c.frag.strike = 0
    if "font-style" in c.cssAttr:
        value = lower(c.cssAttr["font-style"])
        if value in ("italic", "oblique"):
            c.frag.italic = 1
        else:
            c.frag.italic = 0
    if "white-space" in c.cssAttr:
        # normal | pre | nowrap
        c.frag.whiteSpace = str(c.cssAttr["white-space"]).lower()
        # ALIGN & VALIGN
    if "text-align" in c.cssAttr:
        c.frag.alignment = get_alignment(c.cssAttr["text-align"])
    if "vertical-align" in c.cssAttr:
        c.frag.vAlign = c.cssAttr["vertical-align"]
        # HEIGHT & WIDTH
    if "height" in c.cssAttr:
        try:
            c.frag.height = "".join(to_list(c.cssAttr["height"]))  # XXX Relative is not correct!
        except TypeError:
            # sequence item 0: expected string, tuple found
            c.frag.height = "".join(to_list(c.cssAttr["height"][0]))
        if c.frag.height in ("auto",):
            c.frag.height = None
    if "width" in c.cssAttr:
        try:
            c.frag.width = "".join(to_list(c.cssAttr["width"]))  # XXX Relative is not correct!
        except TypeError:
            c.frag.width = "".join(to_list(c.cssAttr["width"][0]))
        if c.frag.width in ("auto",):
            c.frag.width = None
        # ZOOM
    if "zoom" in c.cssAttr:
        zoom = "".join(to_list(c.cssAttr["zoom"]))  # XXX Relative is not correct!
        if zoom.endswith("%"):
            zoom = float(zoom[: - 1]) / 100.0
        c.frag.zoom = float(zoom)
        # MARGINS & LIST INDENT, STYLE
    if isBlock:
        if "margin-top" in c.cssAttr:
            c.frag.spaceBefore = get_size(c.cssAttr["margin-top"], c.frag.fontSize)
        if "margin-bottom" in c.cssAttr:
            c.frag.spaceAfter = get_size(c.cssAttr["margin-bottom"], c.frag.fontSize)
        if "margin-left" in c.cssAttr:
            c.frag.bulletIndent = kw["margin-left"]  # For lists
            kw["margin-left"] += get_size(c.cssAttr["margin-left"], c.frag.fontSize)
            c.frag.leftIndent = kw["margin-left"]
        if "margin-right" in c.cssAttr:
            kw["margin-right"] += get_size(c.cssAttr["margin-right"], c.frag.fontSize)
            c.frag.rightIndent = kw["margin-right"]
        if "text-indent" in c.cssAttr:
            c.frag.firstLineIndent = get_size(c.cssAttr["text-indent"], c.frag.fontSize)
        if "list-style-type" in c.cssAttr:
            c.frag.listStyleType = str(c.cssAttr["list-style-type"]).lower()
        if "list-style-image" in c.cssAttr:
            c.frag.listStyleImage = c.get_file(c.cssAttr["list-style-image"])
        # PADDINGS
    if isBlock:
        if "padding-top" in c.cssAttr:
            c.frag.paddingTop = get_size(c.cssAttr["padding-top"], c.frag.fontSize)
        if "padding-bottom" in c.cssAttr:
            c.frag.paddingBottom = get_size(c.cssAttr["padding-bottom"], c.frag.fontSize)
        if "padding-left" in c.cssAttr:
            c.frag.paddingLeft = get_size(c.cssAttr["padding-left"], c.frag.fontSize)
        if "padding-right" in c.cssAttr:
            c.frag.paddingRight = get_size(c.cssAttr["padding-right"], c.frag.fontSize)
        # BORDERS
    if isBlock:
        if "border-top-width" in c.cssAttr:
            c.frag.borderTopWidth = get_size(c.cssAttr["border-top-width"], c.frag.fontSize)
        if "border-bottom-width" in c.cssAttr:
            c.frag.borderBottomWidth = get_size(c.cssAttr["border-bottom-width"], c.frag.fontSize)
        if "border-left-width" in c.cssAttr:
            c.frag.borderLeftWidth = get_size(c.cssAttr["border-left-width"], c.frag.fontSize)
        if "border-right-width" in c.cssAttr:
            c.frag.borderRightWidth = get_size(c.cssAttr["border-right-width"], c.frag.fontSize)
        if "border-top-style" in c.cssAttr:
            c.frag.borderTopStyle = c.cssAttr["border-top-style"]
        if "border-bottom-style" in c.cssAttr:
            c.frag.borderBottomStyle = c.cssAttr["border-bottom-style"]
        if "border-left-style" in c.cssAttr:
            c.frag.borderLeftStyle = c.cssAttr["border-left-style"]
        if "border-right-style" in c.cssAttr:
            c.frag.borderRightStyle = c.cssAttr["border-right-style"]
        if "border-top-color" in c.cssAttr:
            c.frag.borderTopColor = get_color(c.cssAttr["border-top-color"])
        if "border-bottom-color" in c.cssAttr:
            c.frag.borderBottomColor = get_color(c.cssAttr["border-bottom-color"])
        if "border-left-color" in c.cssAttr:
            c.frag.borderLeftColor = get_color(c.cssAttr["border-left-color"])
        if "border-right-color" in c.cssAttr:
            c.frag.borderRightColor = get_color(c.cssAttr["border-right-color"])
예제 #11
0
 def test_get_size_for_none(self):
     res = get_size(None, relative='TOKEN')
     self.assertEqual(res, 'TOKEN')
예제 #12
0
 def test_get_size_for_pc(self):
     res = get_size("1pc")
     self.assertEqual(res, 12.00)
예제 #13
0
파일: tags.py 프로젝트: n3h3m/html2pdf
    def start(self, c):
        attr = self.attr
        if attr.src and (not attr.src.not_found()):

            try:
                align = attr.align or c.frag.vAlign or "baseline"
                width = c.frag.width
                height = c.frag.height

                if attr.width:
                    width = attr.width * dpi96
                if attr.height:
                    height = attr.height * dpi96

                img = PmlImage(attr.src.get_data(), width=None, height=None)

                img.pisaZoom = c.frag.zoom

                img.drawHeight *= dpi96
                img.drawWidth *= dpi96

                if (width is None) and (height is not None):
                    factor = get_size(height) / img.drawHeight
                    img.drawWidth *= factor
                    img.drawHeight = get_size(height)
                elif (height is None) and (width is not None):
                    factor = get_size(width) / img.drawWidth
                    img.drawHeight *= factor
                    img.drawWidth = get_size(width)
                elif (width is not None) and (height is not None):
                    img.drawWidth = get_size(width)
                    img.drawHeight = get_size(height)

                img.drawWidth *= img.pisaZoom
                img.drawHeight *= img.pisaZoom

                img.spaceBefore = c.frag.spaceBefore
                img.spaceAfter = c.frag.spaceAfter

                # print "image", id(img), img.drawWidth, img.drawHeight
                '''
                TODO:

                - Apply styles
                - vspace etc.
                - Borders
                - Test inside tables
                '''

                c.force = True
                if align in ["left", "right"]:

                    c.image = img
                    c.imageData = dict(align=align)

                else:

                    # Important! Make sure that cbDefn is not inherited by other
                    # fragments because of a bug in Reportlab!
                    # afrag = c.frag.clone()

                    valign = align
                    if valign in ["texttop"]:
                        valign = "top"
                    elif valign in ["absmiddle"]:
                        valign = "middle"
                    elif valign in ["absbottom", "baseline"]:
                        valign = "bottom"

                    afrag = c.frag.clone()
                    afrag.text = ""
                    afrag.fontName = "Helvetica"  # Fix for a nasty bug!!!
                    afrag.cbDefn = ABag(
                        kind="img",
                        image=img,  # .getImage(), # XXX Inline?
                        valign=valign,
                        fontName="Helvetica",
                        fontSize=img.drawHeight,
                        width=img.drawWidth,
                        height=img.drawHeight)

                    c.fragList.append(afrag)
                    c.fontSize = img.drawHeight

            except Exception:  # TODO: Kill catch-all
                log.warn(c.warning("Error in handling image"), exc_info=1)
        else:
            log.warn(c.warning("Need a valid file name!"))
예제 #14
0
 def test_get_size_for_inch(self):
     res = get_size("1in")
     self.assertEqual(res, 72.00)
예제 #15
0
 def test_get_size_for_mm(self):
     res = get_size("1mm")
     self.assertEqual(res, 2.8346456692913385)
예제 #16
0
 def test_get_size_for_cm(self):
     res = get_size("1cm")
     self.assertEqual(res, 28.346456692913385)
예제 #17
0
def pisaGetAttributes(c, tag, attributes):
    global TAGS

    attrs = {}
    if attributes:
        for k, v in attributes.items():
            try:
                attrs[str(k)] = str(v)  # XXX no Unicode! Reportlab fails with template names
            except:
                attrs[k] = v

    nattrs = {}
    if tag in TAGS:
        block, adef = TAGS[tag]
        adef["id"] = STRING
        # print block, adef
        try:
            iteritems = adef.iteritems()
        except Exception:
            iteritems = iter(adef.items())

        for k, v in iteritems:
            nattrs[k] = None
            # print k, v
            # defaults, wenn vorhanden
            if type(v) == tuple:
                if v[1] == MUST:
                    if k not in attrs:
                        log.warn(c.warning("Attribute '%s' must be set!", k))
                        nattrs[k] = None
                        continue
                nv = attrs.get(k, v[1])
                dfl = v[1]
                v = v[0]
            else:
                nv = attrs.get(k, None)
                dfl = None

            if nv is not None:
                if type(v) == list:
                    nv = nv.strip().lower()
                    if nv not in v:
                        #~ raise PML_EXCEPTION, "attribute '%s' of wrong value, allowed is one of: %s" % (k, repr(v))
                        log.warn(c.warning("Attribute '%s' of wrong value, allowed is one of: %s", k, repr(v)))
                        nv = dfl

                elif v == BOOL:
                    nv = nv.strip().lower()
                    nv = nv in ("1", "y", "yes", "true", str(k))

                elif v == SIZE:
                    try:
                        nv = get_size(nv)
                    except:
                        log.warn(c.warning("Attribute '%s' expects a size value", k))

                elif v == BOX:
                    nv = get_box(nv, c.pageSize)

                elif v == POS:
                    nv = get_position(nv, c.pageSize)

                elif v == INT:
                    nv = int(nv)

                elif v == COLOR:
                    nv = get_color(nv)

                elif v == FILE:
                    nv = c.get_file(nv)

                elif v == FONT:
                    nv = c.get_font_name(nv)

                nattrs[k] = nv

    return AttrContainer(nattrs)
예제 #18
0
def pisaLoop(node, context, path=None, **kw):

    if path is None:
        path = []

    # Initialize KW
    if not kw:
        kw = {
            "margin-top": 0,
            "margin-bottom": 0,
            "margin-left": 0,
            "margin-right": 0,
        }
    else:
        kw = copy.copy(kw)

    #indent = len(path) * "  " # only used for debug print statements

    # TEXT
    if node.nodeType == Node.TEXT_NODE:
        # print indent, "#", repr(node.data) #, context.frag
        context.add_fragment(node.data)

        # context.text.append(node.value)

    # ELEMENT
    elif node.nodeType == Node.ELEMENT_NODE:

        node.tagName = node.tagName.replace(":", "").lower()

        if node.tagName in ("style", "script"):
            return

        path = copy.copy(path) + [node.tagName]

        # Prepare attributes
        attr = pisaGetAttributes(context, node.tagName, node.attributes)
        #log.debug(indent + "<%s %s>" % (node.tagName, attr) + repr(node.attributes.items())) #, path

        # Calculate styles
        context.cssAttr = CSSCollect(node, context)
        context.cssAttr = mapNonStandardAttrs(context.cssAttr, node, attr)
        context.node = node

        # Block?
        PAGE_BREAK = 1
        PAGE_BREAK_RIGHT = 2
        PAGE_BREAK_LEFT = 3

        pageBreakAfter = False
        frameBreakAfter = False
        display = lower(context.cssAttr.get("display", "inline"))
        # print indent, node.tagName, display, context.cssAttr.get("background-color", None), attr
        isBlock = (display == "block")

        if isBlock:
            context.add_paragraph()

            # Page break by CSS
            if "-pdf-next-page" in context.cssAttr:
                context.add_story(NextPageTemplate(str(context.cssAttr["-pdf-next-page"])))
            if "-pdf-page-break" in context.cssAttr:
                if str(context.cssAttr["-pdf-page-break"]).lower() == "before":
                    context.add_story(PageBreak())
            if "-pdf-frame-break" in context.cssAttr:
                if str(context.cssAttr["-pdf-frame-break"]).lower() == "before":
                    context.add_story(FrameBreak())
                if str(context.cssAttr["-pdf-frame-break"]).lower() == "after":
                    frameBreakAfter = True
            if "page-break-before" in context.cssAttr:
                if str(context.cssAttr["page-break-before"]).lower() == "always":
                    context.add_story(PageBreak())
                if str(context.cssAttr["page-break-before"]).lower() == "right":
                    context.add_story(PageBreak())
                    context.add_story(PmlRightPageBreak())
                if str(context.cssAttr["page-break-before"]).lower() == "left":
                    context.add_story(PageBreak())
                    context.add_story(PmlLeftPageBreak())
            if "page-break-after" in context.cssAttr:
                if str(context.cssAttr["page-break-after"]).lower() == "always":
                    pageBreakAfter = PAGE_BREAK
                if str(context.cssAttr["page-break-after"]).lower() == "right":
                    pageBreakAfter = PAGE_BREAK_RIGHT
                if str(context.cssAttr["page-break-after"]).lower() == "left":
                    pageBreakAfter = PAGE_BREAK_LEFT

        if display == "none":
            # print "none!"
            return

        # Translate CSS to frags

        # Save previous frag styles
        context.push_fragment()

        # Map styles to Reportlab fragment properties
        CSS2Frag(context, kw, isBlock)

        # EXTRAS
        if "-pdf-keep-with-next" in context.cssAttr:
            context.frag.keepWithNext = str_to_bool(context.cssAttr["-pdf-keep-with-next"])
        if "-pdf-outline" in context.cssAttr:
            context.frag.outline = str_to_bool(context.cssAttr["-pdf-outline"])
        if "-pdf-outline-level" in context.cssAttr:
            context.frag.outlineLevel = int(context.cssAttr["-pdf-outline-level"])
        if "-pdf-outline-open" in context.cssAttr:
            context.frag.outlineOpen = str_to_bool(context.cssAttr["-pdf-outline-open"])
        if "-pdf-word-wrap" in context.cssAttr:
            context.frag.wordWrap = context.cssAttr["-pdf-word-wrap"]

        # handle keep-in-frame
        keepInFrameMode = None
        keepInFrameMaxWidth = 0
        keepInFrameMaxHeight = 0
        if "-pdf-keep-in-frame-mode" in context.cssAttr:
            value = str(context.cssAttr["-pdf-keep-in-frame-mode"]).strip().lower()
            if value in ("shrink", "error", "overflow", "truncate"):
                keepInFrameMode = value
        if "-pdf-keep-in-frame-max-width" in context.cssAttr:
            keepInFrameMaxWidth = get_size("".join(context.cssAttr["-pdf-keep-in-frame-max-width"]))
        if "-pdf-keep-in-frame-max-height" in context.cssAttr:
            keepInFrameMaxHeight = get_size("".join(context.cssAttr["-pdf-keep-in-frame-max-height"]))

        # ignore nested keep-in-frames, tables have their own KIF handling
        keepInFrame = keepInFrameMode is not None and context.keepInFrameIndex is None
        if keepInFrame:
            # keep track of current story index, so we can wrap everythink
            # added after this point in a KeepInFrame
            context.keepInFrameIndex = len(context.story)

        # BEGIN tag
        klass = globals().get("pisaTag%s" % node.tagName.replace(":", "").upper(), None)
        obj = None

        # Static block
        elementId = attr.get("id", None)
        staticFrame = context.frameStatic.get(elementId, None)
        if staticFrame:
            context.frag.insideStaticFrame += 1
            oldStory = context.swap_story()

        # Tag specific operations
        if klass is not None:
            obj = klass(node, attr)
            obj.start(context)

        # Visit child nodes
        context.fragBlock = fragBlock = copy.copy(context.frag)
        for nnode in node.childNodes:
            pisaLoop(nnode, context, path, **kw)
        context.fragBlock = fragBlock

        # END tag
        if obj:
            obj.end(context)

        # Block?
        if isBlock:
            context.add_paragraph()

            # XXX Buggy!

            # Page break by CSS
            if pageBreakAfter:
                context.add_story(PageBreak())
                if pageBreakAfter == PAGE_BREAK_RIGHT:
                    context.add_story(PmlRightPageBreak())
                if pageBreakAfter == PAGE_BREAK_LEFT:
                    context.add_story(PmlLeftPageBreak())
            if frameBreakAfter:
                context.add_story(FrameBreak())

        if keepInFrame:
            # get all content added after start of -pdf-keep-in-frame and wrap
            # it in a KeepInFrame
            substory = context.story[context.keepInFrameIndex:]
            context.story = context.story[:context.keepInFrameIndex]
            context.story.append(
                KeepInFrame(
                    content=substory,
                    maxWidth=keepInFrameMaxWidth,
                    maxHeight=keepInFrameMaxHeight))
            context.keepInFrameIndex = None

        # Static block, END
        if staticFrame:
            context.add_paragraph()
            for frame in staticFrame:
                frame.pisaStaticStory = context.story
            context.swap_story(oldStory)
            context.frag.insideStaticFrame -= 1

        # context.debug(1, indent, "</%s>" % (node.tagName))

        # Reset frag style
        context.pull_fragment()

    # Unknown or not handled
    else:
        # context.debug(1, indent, "???", node, node.nodeType, repr(node))
        # Loop over children
        for node in node.childNodes:
            pisaLoop(node, context, path, **kw)