예제 #1
0
class LineChartProperties(PropHolder):
    _attrMap = AttrMap(
        strokeWidth=AttrMapValue(isNumber, desc='Width of a line.'),
        strokeColor=AttrMapValue(isColorOrNone,
                                 desc='Color of a line or border.'),
        fillColor=AttrMapValue(isColorOrNone, desc='fill color of a bar.'),
        strokeDashArray=AttrMapValue(isListOfNumbersOrNone,
                                     desc='Dash array of a line.'),
        symbol=AttrMapValue(NoneOr(isSymbol),
                            desc='Widget placed at data points.',
                            advancedUsage=1),
        shader=AttrMapValue(None, desc='Shader Class.', advancedUsage=1),
        filler=AttrMapValue(None, desc='Filler Class.', advancedUsage=1),
        name=AttrMapValue(isStringOrNone, desc='Name of the line.'),
        lineStyle=AttrMapValue(NoneOr(OneOf('line', 'joinedLine', 'bar')),
                               desc="What kind of plot this line is",
                               advancedUsage=1),
        barWidth=AttrMapValue(
            isNumberOrNone,
            desc="Percentage of available width to be used for a bar",
            advancedUsage=1),
        inFill=AttrMapValue(isBoolean,
                            desc='If true flood fill to x axis',
                            advancedUsage=1),
    )
예제 #2
0
class SubColProperty(PropHolder):
    dividerLines = 0
    _attrMap = AttrMap(
        minWidth=AttrMapValue(isNumber, desc="minimum width for this subcol"),
        rpad=AttrMapValue(isNumber, desc="right padding for this subcol"),
        align=AttrMapValue(OneOf('left', 'right', 'center', 'centre',
                                 'numeric'),
                           desc='alignment in subCol'),
        fontName=AttrMapValue(isString, desc="Font name of the strings"),
        fontSize=AttrMapValue(isNumber, desc="Font size of the strings"),
        leading=AttrMapValue(isNumber, desc="leading for the strings"),
        fillColor=AttrMapValue(isColorOrNone, desc="fontColor"),
        underlines=AttrMapValue(EitherOr((NoneOr(isInstanceOf(Line)),
                                          SequenceOf(isInstanceOf(Line),
                                                     emptyOK=0,
                                                     lo=0,
                                                     hi=0x7fffffff))),
                                desc="underline definitions"),
        overlines=AttrMapValue(EitherOr((NoneOr(isInstanceOf(Line)),
                                         SequenceOf(isInstanceOf(Line),
                                                    emptyOK=0,
                                                    lo=0,
                                                    hi=0x7fffffff))),
                               desc="overline definitions"),
    )
예제 #3
0
class LineChartProperties(PropHolder):
    _attrMap = AttrMap(
        strokeWidth = AttrMapValue(isNumber, desc='Width of a line.'),
        strokeColor = AttrMapValue(isColorOrNone, desc='Color of a line.'),
        strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array of a line.'),
        symbol = AttrMapValue(NoneOr(isSymbol), desc='Widget placed at data points.'),
        shader = AttrMapValue(None, desc='Shader Class.'),
        filler = AttrMapValue(None, desc='Filler Class.'),
        name = AttrMapValue(isStringOrNone, desc='Name of the line.'),
        )
예제 #4
0
class WedgeProperties(PropHolder):
    """This holds descriptive information about the wedges in a pie chart.

    It is not to be confused with the 'wedge itself'; this just holds
    a recipe for how to format one, and does not allow you to hack the
    angles.  It can format a genuine Wedge object for you with its
    format method.
    """
    _attrMap = AttrMap(
        strokeWidth = AttrMapValue(isNumber),
        fillColor = AttrMapValue(isColorOrNone),
        strokeColor = AttrMapValue(isColorOrNone),
        strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
        popout = AttrMapValue(isNumber),
        fontName = AttrMapValue(isString),
        fontSize = AttrMapValue(isNumber),
        fontColor = AttrMapValue(isColorOrNone),
        labelRadius = AttrMapValue(isNumber),
        label_dx = AttrMapValue(isNumber),
        label_dy = AttrMapValue(isNumber),
        label_angle = AttrMapValue(isNumber),
        label_boxAnchor = AttrMapValue(isBoxAnchor),
        label_boxStrokeColor = AttrMapValue(isColorOrNone),
        label_boxStrokeWidth = AttrMapValue(isNumber),
        label_boxFillColor = AttrMapValue(isColorOrNone),
        label_strokeColor = AttrMapValue(isColorOrNone),
        label_strokeWidth = AttrMapValue(isNumber),
        label_text = AttrMapValue(isStringOrNone),
        label_leading = AttrMapValue(isNumberOrNone),
        label_width = AttrMapValue(isNumberOrNone),
        label_maxWidth = AttrMapValue(isNumberOrNone),
        label_height = AttrMapValue(isNumberOrNone),
        label_textAnchor = AttrMapValue(isTextAnchor),
        label_visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
        label_topPadding = AttrMapValue(isNumber,'padding at top of box'),
        label_leftPadding = AttrMapValue(isNumber,'padding at left of box'),
        label_rightPadding = AttrMapValue(isNumber,'padding at right of box'),
        label_bottomPadding = AttrMapValue(isNumber,'padding at bottom of box'),
        label_pointer_strokeColor = AttrMapValue(isColorOrNone,desc='Color of indicator line'),
        label_pointer_strokeWidth = AttrMapValue(isNumber,desc='StrokeWidth of indicator line'),
        label_pointer_elbowLength = AttrMapValue(isNumber,desc='length of final indicator line segment'),
        label_pointer_edgePad = AttrMapValue(isNumber,desc='pad between pointer label and box'),
        label_pointer_piePad = AttrMapValue(isNumber,desc='pad between pointer label and pie'),
        swatchMarker = AttrMapValue(NoneOr(isSymbol), desc="None or makeMarker('Diamond') ..."),
        visible = AttrMapValue(isBoolean,'set to false to skip displaying'),
        )

    def __init__(self):
        self.strokeWidth = 0
        self.fillColor = None
        self.strokeColor = STATE_DEFAULTS["strokeColor"]
        self.strokeDashArray = STATE_DEFAULTS["strokeDashArray"]
        self.popout = 0
        self.fontName = STATE_DEFAULTS["fontName"]
        self.fontSize = STATE_DEFAULTS["fontSize"]
        self.fontColor = STATE_DEFAULTS["fillColor"]
        self.labelRadius = 1.2
        self.label_dx = self.label_dy = self.label_angle = 0
        self.label_text = None
        self.label_topPadding = self.label_leftPadding = self.label_rightPadding = self.label_bottomPadding = 0
        self.label_boxAnchor = 'c'
        self.label_boxStrokeColor = None    #boxStroke
        self.label_boxStrokeWidth = 0.5 #boxStrokeWidth
        self.label_boxFillColor = None
        self.label_strokeColor = None
        self.label_strokeWidth = 0.1
        self.label_leading =    self.label_width = self.label_maxWidth = self.label_height = None
        self.label_textAnchor = 'start'
        self.label_visible = 1
        self.label_pointer_strokeColor = colors.black
        self.label_pointer_strokeWidth = 0.5
        self.label_pointer_elbowLength = 3
        self.label_pointer_edgePad = 2
        self.label_pointer_piePad = 3
        self.visible = 1
예제 #5
0
            v = A._y
        if jA:
            if flipXY:
                _v = jA._x
            else:
                _v = jA._y
            if mode == 'high':
                v = _v + jA._length
            elif mode == 'low':
                v = _v
            elif mode == 'bar':
                v = _v + val
        return v + delta


NoneOrInstanceOfLabelOffset = NoneOr(isInstanceOf(LabelOffset))


class PMVLabel(Label):
    _attrMap = AttrMap(BASE=Label, )

    def __init__(self):
        Label.__init__(self)
        self._pmv = 0

    def _getBoxAnchor(self):
        a = Label._getBoxAnchor(self)
        if self._pmv < 0:
            a = {
                'nw': 'se',
                'n': 's',
예제 #6
0
class Legend(Widget):
    """A simple legend containing rectangular swatches and strings.

    The swatches are filled rectangles whenever the respective
    color object in 'colorNamePairs' is a subclass of Color in
    reportlab.lib.colors. Otherwise the object passed instead is
    assumed to have 'x', 'y', 'width' and 'height' attributes.
    A legend then tries to set them or catches any error. This
    lets you plug-in any widget you like as a replacement for
    the default rectangular swatches.

    Strings can be nicely aligned left or right to the swatches.
    """

    _attrMap = AttrMap(
        x=AttrMapValue(isNumber,
                       desc="x-coordinate of upper-left reference point"),
        y=AttrMapValue(isNumber,
                       desc="y-coordinate of upper-left reference point"),
        deltax=AttrMapValue(isNumberOrNone,
                            desc="x-distance between neighbouring swatches"),
        deltay=AttrMapValue(isNumberOrNone,
                            desc="y-distance between neighbouring swatches"),
        dxTextSpace=AttrMapValue(
            isNumber, desc="Distance between swatch rectangle and text"),
        autoXPadding=AttrMapValue(
            isNumber,
            desc="x Padding between columns if deltax=None",
            advancedUsage=1),
        autoYPadding=AttrMapValue(isNumber,
                                  desc="y Padding between rows if deltay=None",
                                  advancedUsage=1),
        yGap=AttrMapValue(isNumber,
                          desc="Additional gap between rows",
                          advancedUsage=1),
        dx=AttrMapValue(isNumber, desc="Width of swatch rectangle"),
        dy=AttrMapValue(isNumber, desc="Height of swatch rectangle"),
        columnMaximum=AttrMapValue(isNumber,
                                   desc="Max. number of items per column"),
        alignment=AttrMapValue(
            OneOf("left", "right"),
            desc="Alignment of text with respect to swatches"),
        colorNamePairs=AttrMapValue(
            None, desc="List of color/name tuples (color can also be widget)"),
        fontName=AttrMapValue(isString, desc="Font name of the strings"),
        fontSize=AttrMapValue(isNumber, desc="Font size of the strings"),
        fillColor=AttrMapValue(isColorOrNone, desc="swatches filling color"),
        strokeColor=AttrMapValue(isColorOrNone,
                                 desc="Border color of the swatches"),
        strokeWidth=AttrMapValue(
            isNumber, desc="Width of the border color of the swatches"),
        swatchMarker=AttrMapValue(
            NoneOr(AutoOr(isSymbol)),
            desc="None, Auto() or makeMarker('Diamond') ...",
            advancedUsage=1),
        callout=AttrMapValue(None,
                             desc="a user callout(self,g,x,y,(color,text))",
                             advancedUsage=1),
        boxAnchor=AttrMapValue(isBoxAnchor,
                               'Anchor point for the legend area'),
        variColumn=AttrMapValue(
            isBoolean,
            'If true column widths may vary (default is false)',
            advancedUsage=1),
        dividerLines=AttrMapValue(
            OneOf(0, 1, 2, 3, 4, 5, 6, 7),
            'If 1 we have dividers between the rows | 2 for extra top | 4 for bottom',
            advancedUsage=1),
        dividerWidth=AttrMapValue(isNumber,
                                  desc="dividerLines width",
                                  advancedUsage=1),
        dividerColor=AttrMapValue(isColorOrNone,
                                  desc="dividerLines color",
                                  advancedUsage=1),
        dividerDashArray=AttrMapValue(isListOfNumbersOrNone,
                                      desc='Dash array for dividerLines.',
                                      advancedUsage=1),
        dividerOffsX=AttrMapValue(SequenceOf(isNumber, emptyOK=0, lo=2, hi=2),
                                  desc='divider lines X offsets',
                                  advancedUsage=1),
        dividerOffsY=AttrMapValue(isNumber,
                                  desc="dividerLines Y offset",
                                  advancedUsage=1),
        colEndCallout=AttrMapValue(
            None,
            desc="a user callout(self,g, x, xt, y,width, lWidth)",
            advancedUsage=1),
        subCols=AttrMapValue(None, desc="subColumn properties"),
        swatchCallout=AttrMapValue(
            None,
            desc="a user swatch callout(self,g,x,y,i,(col,name),swatch)",
            advancedUsage=1),
    )

    def __init__(self):
        # Upper-left reference point.
        self.x = 0
        self.y = 0

        # Alginment of text with respect to swatches.
        self.alignment = "left"

        # x- and y-distances between neighbouring swatches.
        self.deltax = 75
        self.deltay = 20
        self.autoXPadding = 5
        self.autoYPadding = 2

        # Size of swatch rectangle.
        self.dx = 10
        self.dy = 10

        # Distance between swatch rectangle and text.
        self.dxTextSpace = 10

        # Max. number of items per column.
        self.columnMaximum = 3

        # Color/name pairs.
        self.colorNamePairs = [(colors.red, "red"), (colors.blue, "blue"),
                               (colors.green, "green"), (colors.pink, "pink"),
                               (colors.yellow, "yellow")]

        # Font name and size of the labels.
        self.fontName = STATE_DEFAULTS['fontName']
        self.fontSize = STATE_DEFAULTS['fontSize']
        self.fillColor = STATE_DEFAULTS['fillColor']
        self.strokeColor = STATE_DEFAULTS['strokeColor']
        self.strokeWidth = STATE_DEFAULTS['strokeWidth']
        self.swatchMarker = None
        self.boxAnchor = 'nw'
        self.yGap = 0
        self.variColumn = 0
        self.dividerLines = 0
        self.dividerWidth = 0.5
        self.dividerDashArray = None
        self.dividerColor = colors.black
        self.dividerOffsX = (0, 0)
        self.dividerOffsY = 0
        self.colEndCallout = None
        self._init_subCols()

    def _init_subCols(self):
        sc = self.subCols = TypedPropertyCollection(SubColProperty)
        sc.rpad = 1
        sc.minWidth = 0
        sc.align = 'right'
        sc[0].align = 'left'

    def _getChartStyleName(self, chart):
        for a in 'lines', 'bars', 'slices', 'strands':
            if hasattr(chart, a): return a
        return None

    def _getChartStyle(self, chart):
        return getattr(chart, self._getChartStyleName(chart), None)

    def _getTexts(self, colorNamePairs):
        if not isAuto(colorNamePairs):
            texts = [_getStr(p[1]) for p in colorNamePairs]
        else:
            chart = getattr(colorNamePairs, 'chart',
                            getattr(colorNamePairs, 'obj', None))
            texts = [
                chart.getSeriesName(i, 'series %d' % i)
                for i in xrange(chart._seriesCount)
            ]
        return texts

    def _calculateMaxBoundaries(self, colorNamePairs):
        "Calculate the maximum width of some given strings."
        fontName = self.fontName
        fontSize = self.fontSize
        subCols = self.subCols

        M = [
            _getWidths(i, m, fontName, fontSize, subCols)
            for i, m in enumerate(self._getTexts(colorNamePairs))
        ]
        if not M:
            return [0, 0]
        n = max([len(m) for m in M])
        if self.variColumn:
            columnMaximum = self.columnMaximum
            return [
                _transMax(n, M[r:r + columnMaximum])
                for r in xrange(0, len(M), self.columnMaximum)
            ]
        else:
            return _transMax(n, M)

    def _calcHeight(self):
        dy = self.dy
        yGap = self.yGap
        thisy = upperlefty = self.y - dy
        fontSize = self.fontSize
        ascent = getFont(self.fontName).face.ascent / 1000.
        if ascent == 0: ascent = 0.718  # default (from helvetica)
        ascent *= fontSize
        leading = fontSize * 1.2
        deltay = self.deltay
        if not deltay: deltay = max(dy, leading) + self.autoYPadding
        columnCount = 0
        count = 0
        lowy = upperlefty
        lim = self.columnMaximum - 1
        for name in self._getTexts(self.colorNamePairs):
            y0 = thisy + (dy - ascent) * 0.5
            y = y0 - _getLineCount(name) * leading
            leadingMove = 2 * y0 - y - thisy
            newy = thisy - max(deltay, leadingMove) - yGap
            lowy = min(y, newy, lowy)
            if count == lim:
                count = 0
                thisy = upperlefty
                columnCount = columnCount + 1
            else:
                thisy = newy
                count = count + 1
        return upperlefty - lowy

    def _defaultSwatch(self, x, thisy, dx, dy, fillColor, strokeWidth,
                       strokeColor):
        return Rect(
            x,
            thisy,
            dx,
            dy,
            fillColor=fillColor,
            strokeColor=strokeColor,
            strokeWidth=strokeWidth,
        )

    def draw(self):
        colorNamePairs = self.colorNamePairs
        autoCP = isAuto(colorNamePairs)
        if autoCP:
            chart = getattr(colorNamePairs, 'chart',
                            getattr(colorNamePairs, 'obj', None))
            swatchMarker = None
            autoCP = Auto(obj=chart)
            n = chart._seriesCount
            chartTexts = self._getTexts(colorNamePairs)
        else:
            swatchMarker = getattr(self, 'swatchMarker', None)
            if isAuto(swatchMarker):
                chart = getattr(swatchMarker, 'chart',
                                getattr(swatchMarker, 'obj', None))
                swatchMarker = Auto(obj=chart)
            n = len(colorNamePairs)
        dx = self.dx
        dy = self.dy
        alignment = self.alignment
        columnMaximum = self.columnMaximum
        deltax = self.deltax
        deltay = self.deltay
        dxTextSpace = self.dxTextSpace
        fontName = self.fontName
        fontSize = self.fontSize
        fillColor = self.fillColor
        strokeWidth = self.strokeWidth
        strokeColor = self.strokeColor
        subCols = self.subCols
        leading = fontSize * 1.2
        yGap = self.yGap
        if not deltay:
            deltay = max(dy, leading) + self.autoYPadding
        ba = self.boxAnchor
        maxWidth = self._calculateMaxBoundaries(colorNamePairs)
        nCols = int((n + columnMaximum - 1) / (columnMaximum * 1.0))
        xW = dx + dxTextSpace + self.autoXPadding
        variColumn = self.variColumn
        if variColumn:
            width = reduce(operator.add, [m[-1]
                                          for m in maxWidth], 0) + xW * nCols
        else:
            deltax = max(maxWidth[-1] + xW, deltax)
            width = maxWidth[-1] + nCols * deltax
            maxWidth = nCols * [maxWidth]

        thisx = self.x
        thisy = self.y - self.dy
        if ba not in ('ne', 'n', 'nw', 'autoy'):
            height = self._calcHeight()
            if ba in ('e', 'c', 'w'):
                thisy += height / 2.
            else:
                thisy += height
        if ba not in ('nw', 'w', 'sw', 'autox'):
            if ba in ('n', 'c', 's'):
                thisx -= width / 2
            else:
                thisx -= width
        upperlefty = thisy

        g = Group()

        ascent = getFont(fontName).face.ascent / 1000.
        if ascent == 0: ascent = 0.718  # default (from helvetica)
        ascent *= fontSize  # normalize

        lim = columnMaximum - 1
        callout = getattr(self, 'callout', None)
        scallout = getattr(self, 'swatchCallout', None)
        dividerLines = self.dividerLines
        if dividerLines:
            dividerWidth = self.dividerWidth
            dividerColor = self.dividerColor
            dividerDashArray = self.dividerDashArray
            dividerOffsX = self.dividerOffsX
            dividerOffsY = self.dividerOffsY

        for i in xrange(n):
            if autoCP:
                col = autoCP
                col.index = i
                name = chartTexts[i]
            else:
                col, name = colorNamePairs[i]
                if isAuto(swatchMarker):
                    col = swatchMarker
                    col.index = i
                if isAuto(name):
                    name = getattr(swatchMarker, 'chart',
                                   getattr(swatchMarker, 'obj',
                                           None)).getSeriesName(
                                               i, 'series %d' % i)
            T = _getLines(name)
            S = []
            aS = S.append
            j = int(i / (columnMaximum * 1.0))
            jOffs = maxWidth[j]

            # thisy+dy/2 = y+leading/2
            y = y0 = thisy + (dy - ascent) * 0.5

            if callout: callout(self, g, thisx, y, (col, name))
            if alignment == "left":
                x = thisx
                xn = thisx + jOffs[-1] + dxTextSpace
            elif alignment == "right":
                x = thisx + dx + dxTextSpace
                xn = thisx
            else:
                raise ValueError, "bad alignment"
            if not isSeqType(name):
                T = [T]
            yd = y
            for k, lines in enumerate(T):
                y = y0
                kk = k * 2
                x1 = x + jOffs[kk]
                x2 = x + jOffs[kk + 1]
                sc = subCols[k, i]
                anchor = sc.align
                fN = getattr(sc, 'fontName', fontName)
                fS = getattr(sc, 'fontSize', fontSize)
                fC = getattr(sc, 'fillColor', fillColor)
                fL = getattr(sc, 'leading', 1.2 * fontSize)
                if fN == fontName:
                    fA = (ascent * fS) / fontSize
                else:
                    fA = getFont(fontName).face.ascent / 1000.
                    if fA == 0: fA = 0.718
                    fA *= fS
                if anchor == 'left':
                    anchor = 'start'
                    xoffs = x1
                elif anchor == 'right':
                    anchor = 'end'
                    xoffs = x2
                elif anchor == 'numeric':
                    xoffs = x2
                else:
                    anchor = 'middle'
                    xoffs = 0.5 * (x1 + x2)
                for t in lines:
                    aS(
                        String(xoffs,
                               y,
                               t,
                               fontName=fN,
                               fontSize=fS,
                               fillColor=fC,
                               textAnchor=anchor))
                    y -= fL
                yd = min(yd, y)
                y += fL
                for iy, a in ((y - max(fL - fA, 0), 'underlines'),
                              (y + fA, 'overlines')):
                    il = getattr(sc, a, None)
                    if il:
                        if not isinstance(il, (tuple, list)): il = (il, )
                        for l in il:
                            l = copy.copy(l)
                            l.y1 += iy
                            l.y2 += iy
                            l.x1 += x1
                            l.x2 += x2
                            aS(l)
            x = xn
            y = yd
            leadingMove = 2 * y0 - y - thisy

            if dividerLines:
                xd = thisx + dx + dxTextSpace + jOffs[-1] + dividerOffsX[1]
                yd = thisy + dy * 0.5 + dividerOffsY
                if ((dividerLines & 1) and i % columnMaximum) or (
                    (dividerLines & 2) and not i % columnMaximum):
                    g.add(
                        Line(thisx + dividerOffsX[0],
                             yd,
                             xd,
                             yd,
                             strokeColor=dividerColor,
                             strokeWidth=dividerWidth,
                             strokeDashArray=dividerDashArray))

                if (dividerLines & 4) and (i % columnMaximum == lim
                                           or i == (n - 1)):
                    yd -= max(deltay, leadingMove) + yGap
                    g.add(
                        Line(thisx + dividerOffsX[0],
                             yd,
                             xd,
                             yd,
                             strokeColor=dividerColor,
                             strokeWidth=dividerWidth,
                             strokeDashArray=dividerDashArray))

            # Make a 'normal' color swatch...
            if isAuto(col):
                chart = getattr(col, 'chart', getattr(col, 'obj', None))
                c = chart.makeSwatchSample(getattr(col, 'index', i), x, thisy,
                                           dx, dy)
            elif isinstance(col, colors.Color):
                if isSymbol(swatchMarker):
                    c = uSymbol2Symbol(swatchMarker, x + dx / 2.,
                                       thisy + dy / 2., col)
                else:
                    c = self._defaultSwatch(x,
                                            thisy,
                                            dx,
                                            dy,
                                            fillColor=col,
                                            strokeWidth=strokeWidth,
                                            strokeColor=strokeColor)
            elif col is not None:
                try:
                    c = copy.deepcopy(col)
                    c.x = x
                    c.y = thisy
                    c.width = dx
                    c.height = dy
                except:
                    c = None
            else:
                c = None

            if c:
                g.add(c)
                if scallout: scallout(self, g, thisx, y0, i, (col, name), c)

            for s in S:
                g.add(s)
            if self.colEndCallout and (i % columnMaximum == lim
                                       or i == (n - 1)):
                if alignment == "left":
                    xt = thisx
                else:
                    xt = thisx + dx + dxTextSpace
                yd = thisy + dy * 0.5 + dividerOffsY - (
                    max(deltay, leadingMove) + yGap)
                self.colEndCallout(self, g, thisx, xt, yd, jOffs[-1],
                                   jOffs[-1] + dx + dxTextSpace)

            if i % columnMaximum == lim:
                if variColumn:
                    thisx += jOffs[-1] + xW
                else:
                    thisx = thisx + deltax
                thisy = upperlefty
            else:
                thisy = thisy - max(deltay, leadingMove) - yGap

        return g

    def demo(self):
        "Make sample legend."

        d = Drawing(200, 100)

        legend = Legend()
        legend.alignment = 'left'
        legend.x = 0
        legend.y = 100
        legend.dxTextSpace = 5
        items = 'red green blue yellow pink black white'.split()
        items = map(lambda i: (getattr(colors, i), i), items)
        legend.colorNamePairs = items

        d.add(legend, 'legend')

        return d
예제 #7
0
class Legend(Widget):
    """A simple legend containing rectangular swatches and strings.

    The swatches are filled rectangles whenever the respective
    color object in 'colorNamePairs' is a subclass of Color in
    reportlab.lib.colors. Otherwise the object passed instead is
    assumed to have 'x', 'y', 'width' and 'height' attributes.
    A legend then tries to set them or catches any error. This
    lets you plug-in any widget you like as a replacement for
    the default rectangular swatches.

    Strings can be nicely aligned left or right to the swatches.
    """

    _attrMap = AttrMap(
        x = AttrMapValue(isNumber, desc="x-coordinate of upper-left reference point"),
        y = AttrMapValue(isNumber, desc="y-coordinate of upper-left reference point"),
        deltax = AttrMapValue(isNumberOrNone, desc="x-distance between neighbouring swatches"),
        deltay = AttrMapValue(isNumberOrNone, desc="y-distance between neighbouring swatches"),
        dxTextSpace = AttrMapValue(isNumber, desc="Distance between swatch rectangle and text"),
        autoXPadding = AttrMapValue(isNumber, desc="x Padding between columns if deltax=None"),
        autoYPadding = AttrMapValue(isNumber, desc="y Padding between rows if deltay=None"),
        yGap = AttrMapValue(isNumber, desc="Additional gap between rows"),
        dx = AttrMapValue(isNumber, desc="Width of swatch rectangle"),
        dy = AttrMapValue(isNumber, desc="Height of swatch rectangle"),
        columnMaximum = AttrMapValue(isNumber, desc="Max. number of items per column"),
        alignment = AttrMapValue(OneOf("left", "right"), desc="Alignment of text with respect to swatches"),
        colorNamePairs = AttrMapValue(None, desc="List of color/name tuples (color can also be widget)"),
        fontName = AttrMapValue(isString, desc="Font name of the strings"),
        fontSize = AttrMapValue(isNumber, desc="Font size of the strings"),
        fillColor = AttrMapValue(isColorOrNone, desc=""),
        strokeColor = AttrMapValue(isColorOrNone, desc="Border color of the swatches"),
        strokeWidth = AttrMapValue(isNumber, desc="Width of the border color of the swatches"),
        swatchMarker = AttrMapValue(NoneOr(AutoOr(isSymbol)), desc="None, Auto() or makeMarker('Diamond') ..."),
        callout = AttrMapValue(None, desc="a user callout(self,g,x,y,(color,text))"),
        boxAnchor = AttrMapValue(isBoxAnchor,'Anchor point for the legend area'),
        variColumn = AttrMapValue(isBoolean,'If true column widths may vary (default is false)'),
        dividerLines = AttrMapValue(OneOf(0,1,2,3,4,5,6,7),'If 1 we have dividers between the rows | 2 for extra top | 4 for bottom'),
        dividerWidth = AttrMapValue(isNumber, desc="dividerLines width"),
        dividerColor = AttrMapValue(isColorOrNone, desc="dividerLines color"),
        dividerDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array for dividerLines.'),
        dividerOffsX = AttrMapValue(SequenceOf(isNumber,emptyOK=0,lo=2,hi=2), desc='divider lines X offsets'),
        dividerOffsY = AttrMapValue(isNumber, desc="dividerLines Y offset"),
        sepSpace = AttrMapValue(isNumber, desc="separator spacing"),
        colEndCallout = AttrMapValue(None, desc="a user callout(self,g, x, xt, y,width, lWidth)"),
       )

    def __init__(self):
        # Upper-left reference point.
        self.x = 0
        self.y = 0

        # Alginment of text with respect to swatches.
        self.alignment = "left"

        # x- and y-distances between neighbouring swatches.
        self.deltax = 75
        self.deltay = 20
        self.autoXPadding = 5
        self.autoYPadding = 2

        # Size of swatch rectangle.
        self.dx = 10
        self.dy = 10

        # Distance between swatch rectangle and text.
        self.dxTextSpace = 10

        # Max. number of items per column.
        self.columnMaximum = 3

        # Color/name pairs.
        self.colorNamePairs = [ (colors.red, "red"),
                                (colors.blue, "blue"),
                                (colors.green, "green"),
                                (colors.pink, "pink"),
                                (colors.yellow, "yellow") ]

        # Font name and size of the labels.
        self.fontName = STATE_DEFAULTS['fontName']
        self.fontSize = STATE_DEFAULTS['fontSize']
        self.fillColor = STATE_DEFAULTS['fillColor']
        self.strokeColor = STATE_DEFAULTS['strokeColor']
        self.strokeWidth = STATE_DEFAULTS['strokeWidth']
        self.swatchMarker = None
        self.boxAnchor = 'nw'
        self.yGap = 0
        self.variColumn = 0
        self.dividerLines = 0
        self.dividerWidth = 0.5
        self.dividerDashArray = None
        self.dividerColor = colors.black
        self.dividerOffsX = (0,0)
        self.dividerOffsY = 0
        self.sepSpace = 0
        self.colEndCallout = None

    def _getChartStyleName(self,chart):
        for a in 'lines', 'bars', 'slices', 'strands':
            if hasattr(chart,a): return a
        return None

    def _getChartStyle(self,chart):
        return getattr(chart,self._getChartStyleName(chart),None)
        
    def _getTexts(self,colorNamePairs):
        if not isAuto(colorNamePairs):
            texts = [_getStr(p[1]) for p in colorNamePairs]
        else:
            chart = colorNamePairs.chart
            texts = [str(chart.getSeriesName(i,'series %d' % i)) for i in xrange(chart._seriesCount)]
        return texts

    def _calculateMaxWidth(self, colorNamePairs):
        "Calculate the maximum width of some given strings."
        M = []
        a = M.append
        for t in self._getTexts(colorNamePairs):
            M.append(_getWidth(t, self.fontName, self.fontSize,self.sepSpace))
        if not M: return 0
        if self.variColumn:
            columnMaximum = self.columnMaximum
            return [max(M[r:r+columnMaximum]) for r in range(0,len(M),self.columnMaximum)]
        else:
            return max(M)

    def _calcHeight(self):
        dy = self.dy
        yGap = self.yGap
        thisy = upperlefty = self.y - dy
        fontSize = self.fontSize
        ascent=getFont(self.fontName).face.ascent/1000.
        if ascent==0: ascent=0.718 # default (from helvetica)
        ascent *= fontSize
        leading = fontSize*1.2
        deltay = self.deltay
        if not deltay: deltay = max(dy,leading)+self.autoYPadding
        columnCount = 0
        count = 0
        lowy = upperlefty
        lim = self.columnMaximum - 1
        for name in self._getTexts(self.colorNamePairs):
            y0 = thisy+(dy-ascent)*0.5
            y = y0 - _getLineCount(name)*leading
            leadingMove = 2*y0-y-thisy
            newy = thisy-max(deltay,leadingMove)-yGap
            lowy = min(y,newy,lowy)
            if count==lim:
                count = 0
                thisy = upperlefty
                columnCount = columnCount + 1
            else:
                thisy = newy
                count = count+1
        return upperlefty - lowy

    def _defaultSwatch(self,x,thisy,dx,dy,fillColor,strokeWidth,strokeColor):
        return Rect(x, thisy, dx, dy,
                    fillColor = fillColor,
                    strokeColor = strokeColor,
                    strokeWidth = strokeWidth,
                    )

    def draw(self):
        colorNamePairs = self.colorNamePairs
        autoCP = isAuto(colorNamePairs)
        if autoCP:
            chart = getattr(colorNamePairs,'chart',getattr(colorNamePairs,'obj',None))
            swatchMarker = None
            autoCP = Auto(obj=chart)
            n = chart._seriesCount
            chartTexts = self._getTexts(colorNamePairs)
        else:
            swatchMarker = getattr(self,'swatchMarker',None)
            if isAuto(swatchMarker):
                chart = getattr(swatchMarker,'chart',getattr(swatchMarker,'obj',None))
                swatchMarker = Auto(obj=chart)
            n = len(colorNamePairs)
        dx = self.dx
        dy = self.dy
        alignment = self.alignment
        columnMaximum = self.columnMaximum
        deltax = self.deltax
        deltay = self.deltay
        dxTextSpace = self.dxTextSpace
        fontName = self.fontName
        fontSize = self.fontSize
        fillColor = self.fillColor
        strokeWidth = self.strokeWidth
        strokeColor = self.strokeColor
        leading = fontSize*1.2
        yGap = self.yGap
        if not deltay:
            deltay = max(dy,leading)+self.autoYPadding
        ba = self.boxAnchor
        maxWidth = self._calculateMaxWidth(colorNamePairs)
        nCols = int((n+columnMaximum-1)/columnMaximum)
        xW = dx+dxTextSpace+self.autoXPadding
        variColumn = self.variColumn
        if variColumn:
            width = reduce(operator.add,maxWidth,0)+xW*(nCols-1)
        else:
            deltax = max(maxWidth+xW,deltax)
            width = maxWidth+(nCols-1)*deltax
            maxWidth = nCols*[maxWidth]

        thisx = self.x
        thisy = self.y - self.dy
        if ba not in ('ne','n','nw','autoy'):
            height = self._calcHeight()
            if ba in ('e','c','w'):
                thisy += height/2.
            else:
                thisy += height
        if ba not in ('nw','w','sw','autox'):
            if ba in ('n','c','s'):
                thisx -= width/2
            else:
                thisx -= width
        upperlefty = thisy

        g = Group()
        def gAdd(t,g=g,fontName=fontName,fontSize=fontSize,fillColor=fillColor):
            t.fontName = fontName
            t.fontSize = fontSize
            t.fillColor = fillColor
            return g.add(t)

        ascent=getFont(fontName).face.ascent/1000.
        if ascent==0: ascent=0.718 # default (from helvetica)
        ascent *= fontSize # normalize

        lim = columnMaximum - 1
        callout = getattr(self,'callout',None)
        dividerLines = self.dividerLines
        if dividerLines:
            dividerWidth = self.dividerWidth
            dividerColor = self.dividerColor
            dividerDashArray = self.dividerDashArray
            dividerOffsX = self.dividerOffsX
            dividerOffsY = self.dividerOffsY

        for i in xrange(n):
            if autoCP:
                col = autoCP
                col.index = i
                name = chartTexts[i]
            else:
                col, name = colorNamePairs[i]
                if isAuto(swatchMarker):
                    col = swatchMarker
                    col.index = i
                if isAuto(name):
                    name = getattr(swatchMarker,'chart',getattr(swatchMarker,'obj',None)).getSeriesName(i,'series %d' % i)
            T = _getLines(name)
            S = []
            j = int(i/columnMaximum)

            # thisy+dy/2 = y+leading/2
            y = y0 = thisy+(dy-ascent)*0.5

            if callout: callout(self,g,thisx,y,(col,name))
            if alignment == "left":
                if isSeqType(name):
                    for t in T[0]:
                        S.append(String(thisx,y,t,fontName=fontName,fontSize=fontSize,fillColor=fillColor,
                                textAnchor = "start"))
                        y -= leading
                    yd = y
                    y = y0
                    for t in T[1]:
                        S.append(String(thisx+maxWidth[j],y,t,fontName=fontName,fontSize=fontSize,fillColor=fillColor,
                                textAnchor = "end"))
                        y -= leading
                    y = min(yd,y)
                else:
                    for t in T:
                        # align text to left
                        S.append(String(thisx+maxWidth[j],y,t,fontName=fontName,fontSize=fontSize,fillColor=fillColor,
                                textAnchor = "end"))
                        y -= leading
                x = thisx+maxWidth[j]+dxTextSpace
            elif alignment == "right":
                if isSeqType(name):
                    y0 = y
                    for t in T[0]:
                        S.append(String(thisx+dx+dxTextSpace,y,t,fontName=fontName,fontSize=fontSize,fillColor=fillColor,
                                textAnchor = "start"))
                        y -= leading
                    yd = y
                    y = y0
                    for t in T[1]:
                        S.append(String(thisx+dx+dxTextSpace+maxWidth[j],y,t,fontName=fontName,fontSize=fontSize,fillColor=fillColor,
                                textAnchor = "end"))
                        y -= leading
                    y = min(yd,y)
                else:
                    for t in T:
                        # align text to right
                        S.append(String(thisx+dx+dxTextSpace,y,t,fontName=fontName,fontSize=fontSize,fillColor=fillColor,
                                textAnchor = "start"))
                        y -= leading
                x = thisx
            else:
                raise ValueError, "bad alignment"
            leadingMove = 2*y0-y-thisy

            if dividerLines:
                xd = thisx+dx+dxTextSpace+maxWidth[j]+dividerOffsX[1]
                yd = thisy+dy*0.5+dividerOffsY
                if ((dividerLines&1) and i%columnMaximum) or ((dividerLines&2) and not i%columnMaximum):
                    g.add(Line(thisx+dividerOffsX[0],yd,xd,yd,
                        strokeColor=dividerColor, strokeWidth=dividerWidth, strokeDashArray=dividerDashArray))

                if (dividerLines&4) and (i%columnMaximum==lim or i==(n-1)):
                    yd -= max(deltay,leadingMove)+yGap
                    g.add(Line(thisx+dividerOffsX[0],yd,xd,yd,
                        strokeColor=dividerColor, strokeWidth=dividerWidth, strokeDashArray=dividerDashArray))

            # Make a 'normal' color swatch...
            if isAuto(col):
                chart = getattr(col,'chart',getattr(col,'obj',None))
                g.add(chart.makeSwatchSample(getattr(col,'index',i),x,thisy,dx,dy))
            elif isinstance(col, colors.Color):
                if isSymbol(swatchMarker):
                    g.add(uSymbol2Symbol(swatchMarker,x+dx/2.,thisy+dy/2.,col))
                else:
                    g.add(self._defaultSwatch(x,thisy,dx,dy,fillColor=col,strokeWidth=strokeWidth,strokeColor=strokeColor))
            else:
                try:
                    c = copy.deepcopy(col)
                    c.x = x
                    c.y = thisy
                    c.width = dx
                    c.height = dy
                    g.add(c)
                except:
                    pass

            map(gAdd,S)
            if self.colEndCallout and (i%columnMaximum==lim or i==(n-1)):
                if alignment == "left":
                    xt = thisx
                else:
                    xt = thisx+dx+dxTextSpace
                yd = thisy+dy*0.5+dividerOffsY - (max(deltay,leadingMove)+yGap)
                self.colEndCallout(self, g, thisx, xt, yd, maxWidth[j], maxWidth[j]+dx+dxTextSpace)

            if i%columnMaximum==lim:
                if variColumn:
                    thisx += maxWidth[j]+xW
                else:
                    thisx = thisx+deltax
                thisy = upperlefty
            else:
                thisy = thisy-max(deltay,leadingMove)-yGap

        return g

    def demo(self):
        "Make sample legend."

        d = Drawing(200, 100)

        legend = Legend()
        legend.alignment = 'left'
        legend.x = 0
        legend.y = 100
        legend.dxTextSpace = 5
        items = 'red green blue yellow pink black white'.split()
        items = map(lambda i:(getattr(colors, i), i), items)
        legend.colorNamePairs = items

        d.add(legend, 'legend')

        return d
예제 #8
0
class Label(Widget):
    """A text label to attach to something else, such as a chart axis.

    This allows you to specify an offset, angle and many anchor
    properties relative to the label's origin.  It allows, for example,
    angled multiline axis labels.
    """
    # fairly straight port of Robin Becker's textbox.py to new widgets
    # framework.

    _attrMap = AttrMap(
        x = AttrMapValue(isNumber,desc=''),
        y = AttrMapValue(isNumber,desc=''),
        dx = AttrMapValue(isNumber,desc='delta x - offset'),
        dy = AttrMapValue(isNumber,desc='delta y - offset'),
        angle = AttrMapValue(isNumber,desc='angle of label: default (0), 90 is vertical, 180 is upside down, etc'),
        boxAnchor = AttrMapValue(isBoxAnchor,desc='anchoring point of the label'),
        boxStrokeColor = AttrMapValue(isColorOrNone,desc='border color of the box'),
        boxStrokeWidth = AttrMapValue(isNumber,desc='border width'),
        boxFillColor = AttrMapValue(isColorOrNone,desc='the filling color of the box'),
        boxTarget = AttrMapValue(OneOf('normal','anti','lo','hi'),desc="one of ('normal','anti','lo','hi')"),
        fillColor = AttrMapValue(isColorOrNone,desc='label text color'),
        strokeColor = AttrMapValue(isColorOrNone,desc='label text border color'),
        strokeWidth = AttrMapValue(isNumber,desc='label text border width'),
        text = AttrMapValue(isString,desc='the actual text to display'),
        fontName = AttrMapValue(isString,desc='the name of the font used'),
        fontSize = AttrMapValue(isNumber,desc='the size of the font'),
        leading = AttrMapValue(isNumberOrNone,desc=''),
        width = AttrMapValue(isNumberOrNone,desc='the width of the label'),
        maxWidth = AttrMapValue(isNumberOrNone,desc='maximum width the label can grow to'),
        height = AttrMapValue(isNumberOrNone,desc='the height of the text'),
        textAnchor = AttrMapValue(isTextAnchor,desc='the anchoring point of the text inside the label'),
        visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
        topPadding = AttrMapValue(isNumber,desc='padding at top of box'),
        leftPadding = AttrMapValue(isNumber,desc='padding at left of box'),
        rightPadding = AttrMapValue(isNumber,desc='padding at right of box'),
        bottomPadding = AttrMapValue(isNumber,desc='padding at bottom of box'),
        useAscentDescent = AttrMapValue(isBoolean,desc="If True then the font's Ascent & Descent will be used to compute default heights and baseline."),
        customDrawChanger = AttrMapValue(isNoneOrCallable,desc="An instance of CustomDrawChanger to modify the behavior at draw time", _advancedUsage=1),
        ddf = AttrMapValue(NoneOr(isSubclassOf(DirectDraw),'NoneOrDirectDraw'),desc="A DirectDrawFlowable instance", _advancedUsage=1),
        ddfKlass = AttrMapValue(NoneOr(isSubclassOf(Flowable),'NoneOrDirectDraw'),desc="A Flowable class for direct drawing (default is XPreformatted", _advancedUsage=1),
        ddfStyle = AttrMapValue(NoneOr((isSubclassOf(PropertySet),isInstanceOf(PropertySet))),desc="A style or style class for a ddfKlass or None", _advancedUsage=1),
        )

    def __init__(self,**kw):
        self._setKeywords(**kw)
        self._setKeywords(
                _text = 'Multi-Line\nString',
                boxAnchor = 'c',
                angle = 0,
                x = 0,
                y = 0,
                dx = 0,
                dy = 0,
                topPadding = 0,
                leftPadding = 0,
                rightPadding = 0,
                bottomPadding = 0,
                boxStrokeWidth = 0.5,
                boxStrokeColor = None,
                boxTarget = 'normal',
                strokeColor = None,
                boxFillColor = None,
                leading = None,
                width = None,
                maxWidth = None,
                height = None,
                fillColor = STATE_DEFAULTS['fillColor'],
                fontName = STATE_DEFAULTS['fontName'],
                fontSize = STATE_DEFAULTS['fontSize'],
                strokeWidth = 0.1,
                textAnchor = 'start',
                visible = 1,
                useAscentDescent = False,
                ddf = DirectDrawFlowable,
                ddfKlass = None,
                ddfStyle = None,
                )

    def setText(self, text):
        """Set the text property.  May contain embedded newline characters.
        Called by the containing chart or axis."""
        self._text = text


    def setOrigin(self, x, y):
        """Set the origin.  This would be the tick mark or bar top relative to
        which it is defined.  Called by the containing chart or axis."""
        self.x = x
        self.y = y


    def demo(self):
        """This shows a label positioned with its top right corner
        at the top centre of the drawing, and rotated 45 degrees."""

        d = Drawing(200, 100)

        # mark the origin of the label
        d.add(Circle(100,90, 5, fillColor=colors.green))

        lab = Label()
        lab.setOrigin(100,90)
        lab.boxAnchor = 'ne'
        lab.angle = 45
        lab.dx = 0
        lab.dy = -20
        lab.boxStrokeColor = colors.green
        lab.setText('Another\nMulti-Line\nString')
        d.add(lab)

        return d

    def _getBoxAnchor(self):
        '''hook for allowing special box anchor effects'''
        ba = self.boxAnchor
        if ba in ('autox', 'autoy'):
            angle = self.angle
            na = (int((angle%360)/45.)*45)%360
            if not (na % 90): # we have a right angle case
                da = (angle - na) % 360
                if abs(da)>5:
                    na = na + (da>0 and 45 or -45)
            ba = _A2BA[ba[-1]][na]
        return ba

    def _getBaseLineRatio(self):
        if self.useAscentDescent:
            self._ascent, self._descent = getAscentDescent(self.fontName,self.fontSize)
            self._baselineRatio = self._ascent/(self._ascent-self._descent)
        else:
            self._baselineRatio = 1/1.2

    def _computeSizeEnd(self,objH):
        self._height = self.height or (objH + self.topPadding + self.bottomPadding)
        self._ewidth = (self._width-self.leftPadding-self.rightPadding)
        self._eheight = (self._height-self.topPadding-self.bottomPadding)
        boxAnchor = self._getBoxAnchor()
        if boxAnchor in ['n','ne','nw']:
            self._top = -self.topPadding
        elif boxAnchor in ['s','sw','se']:
            self._top = self._height-self.topPadding
        else:
            self._top = 0.5*self._eheight
        self._bottom = self._top - self._eheight

        if boxAnchor in ['ne','e','se']:
            self._left = self.leftPadding - self._width
        elif boxAnchor in ['nw','w','sw']:
            self._left = self.leftPadding
        else:
            self._left = -self._ewidth*0.5
        self._right = self._left+self._ewidth

    def computeSize(self):
        # the thing will draw in its own coordinate system
        ddfKlass = getattr(self,'ddfKlass',None)
        if not ddfKlass:
            self._lineWidths = []
            self._lines = simpleSplit(self._text,self.fontName,self.fontSize,self.maxWidth)
            if not self.width:
                self._width = self.leftPadding+self.rightPadding
                if self._lines:
                    self._lineWidths = [stringWidth(line,self.fontName,self.fontSize) for line in self._lines]
                    self._width += max(self._lineWidths)
            else:
                self._width = self.width
            self._getBaseLineRatio()
            if self.leading:
                self._leading = self.leading
            elif self.useAscentDescent:
                self._leading = self._ascent - self._descent
            else:
                self._leading = self.fontSize*1.2
            objH = self._leading*len(self._lines)
        else:
            if self.ddf is None:
                raise RuntimeError('DirectDrawFlowable class is not available you need the rlextra package as well as reportlab')
            sty = dict(
                    name='xlabel-generated',
                    fontName=self.fontName,
                    fontSize=self.fontSize,
                    fillColor=self.fillColor,
                    strokeColor=self.strokeColor,
                    )

            if not self.ddfStyle:
                sty = ParagraphStyle(**sty)
            elif issubclass(self.ddfStyle,PropertySet):
                sty = self.ddfStyle(**sty)
            else:
                sty = self.ddfStyle.clone()

            self._style = sty
            self._getBaseLineRatio()
            if self.useAscentDescent:
                sty.autoLeading = True
                sty.leading = self._ascent - self._descent
            else:
                sty.leading = self.leading if self.leading else self.fontSize*1.2
            self._leading = sty.leading
            ta = self._getTextAnchor()
            aW = self.maxWidth or 0x7fffffff
            if ta!='start':
                sty.alignment = TA_LEFT
                obj = ddfKlass(self._text,style=sty)
                _, objH = obj.wrap(aW,0x7fffffff)
                aW = self.maxWidth or obj._width_max
            sty.alignment = _ta2al[ta]
            self._ddfObj = obj = ddfKlass(self._text,style=sty)
            _, objH = obj.wrap(aW,0x7fffffff)

            if not self.width:
                self._width = self.leftPadding+self.rightPadding
                self._width += obj._width_max
            else:
                self._width = self.width
        self._computeSizeEnd(objH)

    def _getTextAnchor(self):
        '''This can be overridden to allow special effects'''
        ta = self.textAnchor
        if ta=='boxauto': ta = _BA2TA[self._getBoxAnchor()]
        return ta

    def _rawDraw(self):
        _text = self._text
        self._text = _text or ''
        self.computeSize()
        self._text = _text
        g = Group()
        g.translate(self.x + self.dx, self.y + self.dy)
        g.rotate(self.angle)

        ddfKlass = getattr(self,'ddfKlass',None)
        if ddfKlass:
            x = self._left
        else:
            y = self._top - self._leading*self._baselineRatio
            textAnchor = self._getTextAnchor()
            if textAnchor == 'start':
                x = self._left
            elif textAnchor == 'middle':
                x = self._left + self._ewidth*0.5
            else:
                x = self._right

        # paint box behind text just in case they
        # fill it
        if self.boxFillColor or (self.boxStrokeColor and self.boxStrokeWidth):
            g.add(Rect( self._left-self.leftPadding,
                        self._bottom-self.bottomPadding,
                        self._width,
                        self._height,
                        strokeColor=self.boxStrokeColor,
                        strokeWidth=self.boxStrokeWidth,
                        fillColor=self.boxFillColor)
                        )

        if ddfKlass:
            g1 = Group()
            g1.translate(x,self._top-self._eheight)
            g1.add(self.ddf(self._ddfObj))
            g.add(g1)
        else:
            fillColor, fontName, fontSize = self.fillColor, self.fontName, self.fontSize
            strokeColor, strokeWidth, leading = self.strokeColor, self.strokeWidth, self._leading
            svgAttrs=getattr(self,'_svgAttrs',{})
            if strokeColor:
                for line in self._lines:
                    s = _text2Path(line, x, y, fontName, fontSize, textAnchor)
                    s.fillColor = fillColor
                    s.strokeColor = strokeColor
                    s.strokeWidth = strokeWidth
                    g.add(s)
                    y -= leading
            else:
                for line in self._lines:
                    s = String(x, y, line, _svgAttrs=svgAttrs)
                    s.textAnchor = textAnchor
                    s.fontName = fontName
                    s.fontSize = fontSize
                    s.fillColor = fillColor
                    g.add(s)
                    y -= leading

        return g

    def draw(self):
        customDrawChanger = getattr(self,'customDrawChanger',None)
        if customDrawChanger:
            customDrawChanger(True,self)
            try:
                return self._rawDraw()
            finally:
                customDrawChanger(False,self)
        else:
            return self._rawDraw()
예제 #9
0
class ISBNBarcodeWidget(Ean13BarcodeWidget):
    """
    ISBN Barcodes optionally print the EAN-5 supplemental price
    barcode (with the price in dollars or pounds). Set price to a string
    that follows the EAN-5 for ISBN spec:

        leading digit 0, 1 = GBP
                      3    = AUD
                      4    = NZD
                      5    = USD
                      6    = CAD
        next 4 digits = price between 00.00 and 99.98, i.e.:

        price='52499' # $24.99 USD
    """
    codeName = 'ISBN'
    _attrMap = AttrMap(
        BASE=Ean13BarcodeWidget,
        price=AttrMapValue(NoneOr(nDigits(5)),
                           desc='None or the price to display'),
    )

    def draw(self):
        g = Ean13BarcodeWidget.draw(self)

        price = getattr(self, 'price', None)
        if not price:
            return g

        bounds = g.getBounds()
        x = bounds[2]
        pricecode = Ean5BarcodeWidget(x=x,
                                      value=price,
                                      price=True,
                                      humanReadable=True,
                                      barHeight=self.barHeight,
                                      quiet=self.quiet)
        g.add(pricecode)
        return g

    def _add_human_readable(self, s, gAdd):
        Ean13BarcodeWidget._add_human_readable(self, s, gAdd)
        barWidth = self.barWidth
        barHeight = self.barHeight
        fontSize = self.fontSize
        textColor = self.textColor
        fontName = self.fontName
        fth = fontSize * 1.2
        y = self.y + 0.2 * fth + barHeight
        x = self._lquiet * barWidth

        isbn = 'ISBN '
        segments = [s[0:3], s[3:4], s[4:9], s[9:12], s[12]]
        isbn += '-'.join(segments)

        gAdd(
            String(x,
                   y,
                   isbn,
                   fontName=fontName,
                   fontSize=fontSize,
                   fillColor=textColor))