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
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)
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
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):
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()