예제 #1
0
def textSearch(self, txt, box, search, align):
    canHyphenate = True
    if isinstance(box, self._bezierPathClass):
        canHyphenate = False
        path = box._getCGPath()
        (x, y), (w, h) = CoreText.CGPathGetPathBoundingBox(path)
    else:
        x, y, w, h = box
        path = CoreText.CGPathCreateMutable()
        CoreText.CGPathAddRect(path, None, CoreText.CGRectMake(x, y, w, h))

    canDoGradients = True
    attrString = self.attributedString(txt, align=align)
    if canHyphenate and self._state.hyphenation:
        attrString = self.hyphenateAttributedString(attrString, w)

    txt = attrString.string()
    searchRE = re.compile(search)
    locations = []
    for found in searchRE.finditer(txt):
        locations.append((found.start(), found.end()))

    setter = CoreText.CTFramesetterCreateWithAttributedString(attrString)
    box = CoreText.CTFramesetterCreateFrame(setter, (0, 0), path, None)

    ctLines = CoreText.CTFrameGetLines(box)
    origins = CoreText.CTFrameGetLineOrigins(box, (0, len(ctLines)), None)

    rectangles = []
    for startLocation, endLocation in locations:
        minx = miny = maxx = maxy = None
        for i, (originX, originY) in enumerate(origins):
            ctLine = ctLines[i]
            bounds = CoreText.CTLineGetImageBounds(ctLine, None)
            if bounds.size.width == 0:
                continue
            _, ascent, descent, leading = CoreText.CTLineGetTypographicBounds(
                ctLine, None, None, None)
            height = ascent + descent
            lineRange = CoreText.CTLineGetStringRange(ctLine)
            miny = maxy = originY
            if AppKit.NSLocationInRange(startLocation, lineRange):
                minx, _ = CoreText.CTLineGetOffsetForStringIndex(
                    ctLine, startLocation, None)

            if AppKit.NSLocationInRange(endLocation, lineRange):
                maxx, _ = CoreText.CTLineGetOffsetForStringIndex(
                    ctLine, endLocation, None)
                rectangles.append(
                    (x + minx, y + miny - descent, maxx - minx, height))

            if minx and maxx is None:
                rectangles.append((x + minx, y + miny - descent,
                                   bounds.size.width - minx, height))
                minx = 0

    return rectangles
예제 #2
0
 def _getPathForFrameSetter(self, box):
     if isinstance(box, BezierPath):
         path = box._getCGPath()
         (x, y), (w, h) = CoreText.CGPathGetPathBoundingBox(path)
     else:
         x, y, w, h = box
         path = CoreText.CGPathCreateMutable()
         CoreText.CGPathAddRect(path, None, CoreText.CGRectMake(x, y, w, h))
     return path, (x, y)
예제 #3
0
def getTextPositionSearch(bs, w, h, search, xTextAlign=LEFT, hyphenation=True):
    u"""
    """
    bc = BaseContext()
    path = CoreText.CGPathCreateMutable()
    CoreText.CGPathAddRect(path, None, CoreText.CGRectMake(0, 0, w, h))

    attrString = bc.attributedString(bs, align=xTextAlign)
    if hyphenation and bc._state.hyphenation:
        attrString = bc.hyphenateAttributedString(attrString, w)

    txt = attrString.string()
    searchRE = re.compile(search)
    locations = []
    for found in searchRE.finditer(txt):
        locations.append((found.start(), found.end()))

    setter = CoreText.CTFramesetterCreateWithAttributedString(attrString)
    box = CoreText.CTFramesetterCreateFrame(setter, (0, 0), path, None)

    ctLines = CoreText.CTFrameGetLines(box)
    origins = CoreText.CTFrameGetLineOrigins(box, (0, len(ctLines)), None)

    rectangles = []
    for startLocation, endLocation in locations:
        minx = miny = maxx = maxy = None
        for i, (originX, originY) in enumerate(origins):
            ctLine = ctLines[i]
            bounds = CoreText.CTLineGetImageBounds(ctLine, None)
            if bounds.size.width == 0:
                continue
            _, ascent, descent, leading = CoreText.CTLineGetTypographicBounds(
                ctLine, None, None, None)
            height = ascent + descent
            lineRange = CoreText.CTLineGetStringRange(ctLine)
            miny = maxy = originY
            if AppKit.NSLocationInRange(startLocation, lineRange):
                minx, _ = CoreText.CTLineGetOffsetForStringIndex(
                    ctLine, startLocation, None)

            if AppKit.NSLocationInRange(endLocation, lineRange):
                maxx, _ = CoreText.CTLineGetOffsetForStringIndex(
                    ctLine, endLocation, None)
                rectangles.append(
                    (ctLine, (minx, miny - descent, maxx - minx, height)))

            if minx and maxx is None:
                rectangles.append((ctLine, (minx, miny - descent,
                                            bounds.size.width - minx, height)))
                minx = 0

    return rectangles
예제 #4
0
 def textSize(self, txt, align, width, height):
     attrString = self.attributedString(txt, align)
     if width is None:
         w, h = attrString.size()
     else:
         if width is None:
             width = CoreText.CGFLOAT_MAX
         if height is None:
             height = CoreText.CGFLOAT_MAX
         if self._state.hyphenation:
             path = CoreText.CGPathCreateMutable()
             CoreText.CGPathAddRect(path, None, CoreText.CGRectMake(0, 0, width, height))
             attrString = self.hyphenateAttributedString(attrString, path)
         setter = CoreText.CTFramesetterCreateWithAttributedString(attrString)
         (w, h), _ = CoreText.CTFramesetterSuggestFrameSizeWithConstraints(setter, (0, 0), None, (width, height), None)
     return w, h
        mutString.replaceOccurrencesOfString_withString_options_range_(
            unichr(self._softHypen), "", AppKit.NSLiteralSearch,
            (0, mutString.length()))
        return attrString

    def clippedText(self, txt, (x, y, w, h), align):
        attrString = self.attributedString(txt, align=align)
        if self._state.text.hyphenation:
            hyphenIndexes = [
                i for i, c in enumerate(attrString.string()) if c == "-"
            ]
            attrString = self.hyphenateAttributedString(attrString, w)
        setter = CoreText.CTFramesetterCreateWithAttributedString(attrString)
        path = CoreText.CGPathCreateMutable()
        CoreText.CGPathAddRect(path, None, CoreText.CGRectMake(x, y, w, h))
        box = CoreText.CTFramesetterCreateFrame(setter, (0, 0), path, None)
        visibleRange = CoreText.CTFrameGetVisibleStringRange(box)
        clip = visibleRange.length
        if self._state.text.hyphenation:
            subString = attrString.string()[:clip]
            for i in hyphenIndexes:
                if i < clip:
                    clip += 1
                else:
                    break
            clip -= subString.count("-")

        return txt[clip:]

    def textSize(self, txt, align):
예제 #6
0
class SVGContext(BaseContext):

    _graphicsStateClass = SVGGraphicsState
    _shadowClass = SVGShadow
    _colorClass = SVGColor
    _gradientClass = SVGGradient

    _svgFileClass = SVGFile

    _svgTagArguments = {
        "version": "1.1",
        "xmlns": "http://www.w3.org/2000/svg",
        "xmlns:xlink": "http://www.w3.org/1999/xlink"
    }

    _svgLineJoinStylesMap = {
        AppKit.NSMiterLineJoinStyle: "miter",
        AppKit.NSRoundLineJoinStyle: "round",
        AppKit.NSBevelLineJoinStyle: "bevel"
    }

    _svgLineCapStylesMap = {
        AppKit.NSButtLineCapStyle: "butt",
        AppKit.NSSquareLineCapStyle: "square",
        AppKit.NSRoundLineCapStyle: "round",
    }

    fileExtensions = ["svg"]

    def __init__(self):
        super(SVGContext, self).__init__()
        self._pages = []

    # not supported in a svg context

    def openTypeFeatures(self, *args, **features):
        warnings.warn("openTypeFeatures is not supported in a svg context")

    def cmykFill(self, c, m, y, k, a=1):
        warnings.warn("cmykFill is not supported in a svg context")

    def cmykStroke(self, c, m, y, k, a=1):
        warnings.warn("cmykStroke is not supported in a svg context")

    def cmykLinearGradient(self,
                           startPoint=None,
                           endPoint=None,
                           colors=None,
                           locations=None):
        warnings.warn("cmykLinearGradient is not supported in a svg context")

    def cmykRadialGradient(self,
                           startPoint=None,
                           endPoint=None,
                           colors=None,
                           locations=None,
                           startRadius=0,
                           endRadius=100):
        warnings.warn("cmykRadialGradient is not supported in a svg context")

    def cmykShadow(self, offset, blur, color):
        warnings.warn("cmykShadow is not supported in a svg context")

    # svg overwrites

    def shadow(self, offset, blur, color):
        super(SVGContext, self).shadow(offset, blur, color)
        if self._state.shadow is not None:
            self._state.shadow.writeDefs(self._svgContext)

    def linearGradient(self,
                       startPoint=None,
                       endPoint=None,
                       colors=None,
                       locations=None):
        super(SVGContext, self).linearGradient(startPoint, endPoint, colors,
                                               locations)
        if self._state.gradient is not None:
            self._state.gradient.writeDefs(self._svgContext)

    def radialGradient(self,
                       startPoint=None,
                       endPoint=None,
                       colors=None,
                       locations=None,
                       startRadius=0,
                       endRadius=100):
        super(SVGContext,
              self).radialGradient(startPoint, endPoint, colors, locations,
                                   startRadius, endRadius)
        if startRadius != 0:
            warnings.warn(
                "radialGradient will clip the startRadius to '0' in a svg context."
            )
        if self._state.gradient is not None:
            self._state.gradient.writeDefs(self._svgContext)

    # svg

    def _newPage(self, width, height):
        if hasattr(self, "_svgContext"):
            self._svgContext.endtag("svg")
        self.reset()
        self.size(width, height)
        self._svgData = self._svgFileClass()
        self._pages.append(self._svgData)
        self._svgContext = XMLWriter(self._svgData, encoding="utf-8")
        self._svgContext.width = self.width
        self._svgContext.height = self.height
        self._svgContext.begintag("svg",
                                  width=self.width,
                                  height=self.height,
                                  **self._svgTagArguments)
        self._svgContext.newline()
        self._state.transformMatrix = self._state.transformMatrix.scale(
            1, -1).translate(0, -self.height)

    def _saveImage(self, path, multipage):
        if multipage is None:
            multipage = False
        self._svgContext.endtag("svg")
        fileName, fileExt = os.path.splitext(path)
        firstPage = 0
        pageCount = len(self._pages)
        pathAdd = "_1"
        if not multipage:
            firstPage = pageCount - 1
            pathAdd = ""
        for index in range(firstPage, pageCount):
            page = self._pages[index]
            svgPath = fileName + pathAdd + fileExt
            page.writeToFile(svgPath)
            pathAdd = "_%s" % (index + 2)

    def _save(self):
        pass

    def _restore(self):
        pass

    def _drawPath(self):
        if self._state.path:
            self._svgBeginClipPath()
            data = self._svgDrawingAttributes()
            data["d"] = self._svgPath(self._state.path)
            data["transform"] = self._svgTransform(self._state.transformMatrix)
            if self._state.shadow is not None:
                data["filter"] = "url(#%s)" % self._state.shadow.tagID
            if self._state.gradient is not None:
                data["fill"] = "url(#%s)" % self._state.gradient.tagID
            self._svgContext.simpletag("path", **data)
            self._svgContext.newline()
            self._svgEndClipPath()

    def _clipPath(self):
        uniqueID = self._getUniqueID()
        self._svgContext.begintag("clipPath", id=uniqueID)
        self._svgContext.newline()
        data = dict()
        data["d"] = self._svgPath(self._state.path)
        data["transform"] = self._svgTransform(self._state.transformMatrix)
        data["clip-rule"] = "evenodd"
        self._svgContext.simpletag("path", **data)
        self._svgContext.newline()
        self._svgContext.endtag("clipPath")
        self._svgContext.newline()
        self._state.clipPathID = uniqueID

    def _textBox(self, txt, (x, y, w, h), align):
        canDoGradients = not isinstance(txt, FormattedString)
        if align == "justified":
            warnings.warn("justified text is not supported in a svg context")
        attrString = self.attributedString(txt, align=align)
        if self._state.text.hyphenation:
            attrString = self.hyphenateAttributedString(attrString, w)
        txt = attrString.string()

        setter = CoreText.CTFramesetterCreateWithAttributedString(attrString)
        path = CoreText.CGPathCreateMutable()
        CoreText.CGPathAddRect(path, None, CoreText.CGRectMake(x, y, w, h))
        box = CoreText.CTFramesetterCreateFrame(setter, (0, 0), path, None)

        self._svgBeginClipPath()
        defaultData = self._svgDrawingAttributes()

        data = {"text-anchor": "start"}
        if self._state.shadow is not None:
            data["filter"] = "url(#%s_flipped)" % self._state.shadow.tagID
        self._svgContext.begintag("text", **data)
        self._svgContext.newline()

        ctLines = CoreText.CTFrameGetLines(box)
        origins = CoreText.CTFrameGetLineOrigins(box, (0, len(ctLines)), None)
        for i, (originX, originY) in enumerate(origins):
            ctLine = ctLines[i]
            # bounds = CoreText.CTLineGetImageBounds(ctLine, self._pdfContext)
            # if bounds.size.width == 0:
            #     continue
            ctRuns = CoreText.CTLineGetGlyphRuns(ctLine)
            for ctRun in ctRuns:
                attributes = CoreText.CTRunGetAttributes(ctRun)
                font = attributes.get(AppKit.NSFontAttributeName)
                fillColor = attributes.get(
                    AppKit.NSForegroundColorAttributeName)
                strokeColor = attributes.get(AppKit.NSStrokeColorAttributeName)
                strokeWidth = attributes.get(AppKit.NSStrokeWidthAttributeName,
                                             self._state.strokeWidth)

                fontName = font.fontName()
                fontSize = font.pointSize()

                spanData = dict(defaultData)
                spanData["fill"] = self._colorClass(fillColor).svgColor()
                spanData["stroke"] = self._colorClass(strokeColor).svgColor()
                spanData["stroke-width"] = strokeWidth
                spanData["font-family"] = fontName
                spanData["font-size"] = fontSize

                if canDoGradients and self._state.gradient is not None:
                    spanData[
                        "fill"] = "url(#%s_flipped)" % self._state.gradient.tagID

                self._save()

                r = CoreText.CTRunGetStringRange(ctRun)
                runTxt = txt.substringWithRange_((r.location, r.length))
                while runTxt and runTxt[-1] == " ":
                    runTxt = runTxt[:-1]
                runTxt = runTxt.replace("\n", "")
                runTxt = runTxt.encode("utf-8")

                runPos = CoreText.CTRunGetPositions(ctRun, (0, 1), None)
                runX = runY = 0
                if runPos:
                    runX = runPos[0].x
                    runY = runPos[0].y

                spanData["x"] = originX + runX + x
                spanData["y"] = self.height - y - originY - runY
                self._svgContext.begintag("tspan", **spanData)
                self._svgContext.newline()
                self._svgContext.write(runTxt)
                self._svgContext.newline()
                self._svgContext.endtag("tspan")
                self._svgContext.newline()
                self._restore()

        self._svgContext.endtag("text")
        self._svgContext.newline()
        self._svgEndClipPath()