def _get_font(self): _font = AppKit.NSFont.fontWithName_size_(self._fontName, self.fontSize) if _font == None: ff = self._fallbackFontName or _FALLBACKFONT warnings.warn( "font: %s is not installed, back to the fallback font: %s" % (self._fontName, ff)) self._fontName = ff _font = AppKit.NSFont.fontWithName_size_(ff, self.fontSize) coreTextfeatures = [] for featureTag, value in self.openTypeFeatures.items(): if not value: featureTag = "%s_off" % featureTag if featureTag in openType.featureMap: feature = openType.featureMap[featureTag] coreTextfeatures.append(feature) fontDescriptor = _font.fontDescriptor() fontAttributes = { CoreText.NSFontFeatureSettingsAttribute: coreTextfeatures, } if self._fallbackFontName: fontAttributes[CoreText.NSFontCascadeListAttribute] = [ AppKit.NSFontDescriptor.fontDescriptorWithName_size_( self._fallbackFontName, self.fontSize) ] fontDescriptor = fontDescriptor.fontDescriptorByAddingAttributes_( fontAttributes) _font = AppKit.NSFont.fontWithDescriptor_size_(fontDescriptor, self.fontSize) return _font
def appendGlyph(self, *glyphNames): """ Appends a glyph by his glyph name using the current `font`. """ # use a non breaking space as replacement character baseString = unichr(0x00A0) font = None if self._font: font = AppKit.NSFont.fontWithName_size_(self._font, self._fontSize) if font is None: warnings.warn( "font: %s is not installed, back to the fallback font: %s" % (self._font, _FALLBACKFONT)) font = AppKit.NSFont.fontWithName_size_(_FALLBACKFONT, self._fontSize) fallbackFont = self._fallbackFont self._fallbackFont = None for glyphName in glyphNames: glyph = font.glyphWithName_(glyphName) if glyph: self.append(baseString) glyphInfo = AppKit.NSGlyphInfo.glyphInfoWithGlyph_forFont_baseString_( glyph, font, baseString) self._attributedString.addAttribute_value_range_( AppKit.NSGlyphInfoAttributeName, glyphInfo, (len(self) - 1, 1)) else: warnings.warn("font %s has no glyph with the name %s" % (font.fontName(), glyphName)) self._fallbackFont = fallbackFont
def _pen_width(self): i = int(round(value * 300 / 72)) if i > 10: i = 10 max_pt = (i + 0.4 ) * 72 / 300 warnings.warn("Stroke widths > %0.2f are not supported in the HPGL context." % max_pt) self._hpglData.write(["PW%i;" % i])
def _get_font(self): _font = AppKit.NSFont.fontWithName_size_(self._fontName, self.fontSize) if _font == None: warnings.warn("font: %s is not installed, back to the falllback font: %s" % (self._fontName, self._backupFont)) self._fontName = self._backupFont _font = AppKit.NSFont.fontWithName_size_(self._backupFont, self.fontSize) return _font
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 text(self, txt, font=_FALLBACKFONT, fontSize=10, offset=None, box=None): """ Draws a `txt` with a `font` and `fontSize` at an `offset` in the bezier path. Optionally `txt` can be a `FormattedString` and be drawn inside a `box`, a tuple of (x, y, width, height). """ try: txt = txt.decode("utf-8") except: pass if isinstance(txt, FormattedString): attributedString = txt.getNSObject() else: fontName = font font = AppKit.NSFont.fontWithName_size_(font, fontSize) if font is None: warnings.warn("font: %s is not installed, back to the fallback font: %s" % (fontName, _FALLBACKFONT)) font = AppKit.NSFont.fontWithName_size_(_FALLBACKFONT, fontSize) attributes = { AppKit.NSFontAttributeName: font } attributedString = AppKit.NSAttributedString.alloc().initWithString_attributes_(txt, attributes) w, h = attributedString.size() setter = CoreText.CTFramesetterCreateWithAttributedString(attributedString) path = Quartz.CGPathCreateMutable() if offset: x, y = offset else: x = y = 0 if box: bx, by, w, h = box x += bx y += by Quartz.CGPathAddRect(path, None, Quartz.CGRectMake(0, 0, w, h)) else: Quartz.CGPathAddRect(path, None, Quartz.CGRectMake(0, -h, w*2, h*2)) box = CoreText.CTFramesetterCreateFrame(setter, (0, 0), path, None) ctLines = CoreText.CTFrameGetLines(box) origins = CoreText.CTFrameGetLineOrigins(box, (0, len(ctLines)), None) if origins and box is not None: x -= origins[-1][0] y -= origins[-1][1] for i, (originX, originY) in enumerate(origins): ctLine = ctLines[i] ctRuns = CoreText.CTLineGetGlyphRuns(ctLine) for ctRun in ctRuns: attributes = CoreText.CTRunGetAttributes(ctRun) font = attributes.get(AppKit.NSFontAttributeName) glyphCount = CoreText.CTRunGetGlyphCount(ctRun) for i in range(glyphCount): glyph = CoreText.CTRunGetGlyphs(ctRun, (i, 1), None)[0] ax, ay = CoreText.CTRunGetPositions(ctRun, (i, 1), None)[0] if glyph: self._path.moveToPoint_((x+originX+ax, y+originY+ay)) self._path.appendBezierPathWithGlyph_inFont_(glyph, font) self.optimizePath()
def _get_font(self): _font = AppKit.NSFont.fontWithName_size_(self._fontName, self.fontSize) if _font == None: warnings.warn( "font: %s is not installed, back to the fallback font: %s" % (self._fontName, self._backupFont)) self._fontName = self._backupFont _font = AppKit.NSFont.fontWithName_size_(self._backupFont, self.fontSize) return _font
def fontLeading(self): """ Returns the current font leading, based on the current `font` and `fontSize`. """ font = AppKit.NSFont.fontWithName_size_(self._font, self._fontSize) if font is None: ff = self._fallbackFont or _FALLBACKFONT warnings.warn("font: %s is not installed, back to the fallback font: %s" % (self._font, ff)) font = AppKit.NSFont.fontWithName_size_(ff, self._fontSize) return font.leading()
def text(self, txt, font=_FALLBACKFONT, fontSize=10, offset=None): """ Draws a `txt` with a `font` and `fontSize` at an `offset` in the bezier path. Optionally `txt` can be a `FormattedString`. """ if isinstance(txt, FormattedString): attributedString = txt.getNSObject() else: fontName = font font = AppKit.NSFont.fontWithName_size_(font, fontSize) if font is None: warnings.warn( "font: %s is not installed, back to the fallback font: %s" % (fontName, _FALLBACKFONT)) font = AppKit.NSFont.fontWithName_size_( _FALLBACKFONT, fontSize) attributes = {AppKit.NSFontAttributeName: font} attributedString = AppKit.NSAttributedString.alloc( ).initWithString_attributes_(txt, attributes) w, h = attributedString.size() setter = CoreText.CTFramesetterCreateWithAttributedString( attributedString) path = Quartz.CGPathCreateMutable() Quartz.CGPathAddRect(path, None, Quartz.CGRectMake(0, -h, w * 2, h * 2)) box = CoreText.CTFramesetterCreateFrame(setter, (0, 0), path, None) ctLines = CoreText.CTFrameGetLines(box) origins = CoreText.CTFrameGetLineOrigins(box, (0, len(ctLines)), None) if offset: x, y = offset else: x = y = 0 if origins: x -= origins[-1][0] y -= origins[-1][1] for i, (originX, originY) in enumerate(origins): ctLine = ctLines[i] # path around a bug somewhere # create a new CTLine from a substring with the same range... rng = CoreText.CTLineGetStringRange(ctLine) txtLine = attributedString.attributedSubstringFromRange_(rng) ctLine = CoreText.CTLineCreateWithAttributedString(txtLine) ctRuns = CoreText.CTLineGetGlyphRuns(ctLine) self._path.moveToPoint_((x + originX, y + originY)) for ctRun in ctRuns: glyphs = CoreText.CTRunGetGlyphs( ctRun, CoreText.CTRunGetStringRange(ctRun), None) attributes = CoreText.CTRunGetAttributes(ctRun) font = attributes.get(AppKit.NSFontAttributeName) glyphs = [g for g in glyphs if g != 0] self._path.appendBezierPathWithGlyphs_count_inFont_( glyphs, len(glyphs), font)
def fontLeading(self): """ Returns the current font leading, based on the current `font` and `fontSize`. """ font = AppKit.NSFont.fontWithName_size_(self._font, self._fontSize) if font is None: ff = self._fallbackFont or _FALLBACKFONT warnings.warn( "font: %s is not installed, back to the fallback font: %s" % (self._font, ff)) font = AppKit.NSFont.fontWithName_size_(ff, self._fontSize) return font.leading()
def _writeDataToFile(self, data, path, options): frameRate = round(1.0 / self._frameDurations[0], 3) frameDurations = set(self._frameDurations) if len(frameDurations) > 1: warnings.warn("Exporting to mp4 doesn't support varying frame durations, only the first value was used.") options["multipage"] = True codec = options.get("ffmpegCodec", "libx264") tempDir = tempfile.mkdtemp(suffix=".mp4tmp") try: super(MP4Context, self)._writeDataToFile(data, os.path.join(tempDir, "frame.png"), options) generateMP4(os.path.join(tempDir, "frame_%d.png"), path, frameRate, codec) finally: shutil.rmtree(tempDir)
def fontLineHeight(self): """ Returns the current line height, based on the current `font` and `fontSize`. If a `lineHeight` is set, this value will be returned. """ if self._lineHeight is not None: return self._lineHeight font = AppKit.NSFont.fontWithName_size_(self._font, self._fontSize) if font is None: ff = self._fallbackFont or _FALLBACKFONT warnings.warn("font: %s is not installed, back to the fallback font: %s" % (self._font, ff)) font = AppKit.NSFont.fontWithName_size_(ff, self._fontSize) return font.defaultLineHeightForFont()
def fontLineHeight(self): """ Returns the current line height, based on the current `font` and `fontSize`. If a `lineHeight` is set, this value will be returned. """ if self._lineHeight is not None: return self._lineHeight font = AppKit.NSFont.fontWithName_size_(self._font, self._fontSize) if font is None: ff = self._fallbackFont or _FALLBACKFONT warnings.warn( "font: %s is not installed, back to the fallback font: %s" % (self._font, ff)) font = AppKit.NSFont.fontWithName_size_(ff, self._fontSize) return font.defaultLineHeightForFont()
def text(self, txt, font=_FALLBACKFONT, fontSize=10, offset=None): """ Draws a `txt` with a `font` and `fontSize` at an `offset` in the bezier path. Optionally `txt` can be a `FormattedString`. """ if isinstance(txt, FormattedString): attributedString = txt.getNSObject() else: fontName = font font = AppKit.NSFont.fontWithName_size_(font, fontSize) if font is None: warnings.warn("font: %s is not installed, back to the fallback font: %s" % (fontName, _FALLBACKFONT)) font = AppKit.NSFont.fontWithName_size_(_FALLBACKFONT, fontSize) attributes = { AppKit.NSFontAttributeName : font } attributedString = AppKit.NSAttributedString.alloc().initWithString_attributes_(txt, attributes) w, h = attributedString.size() setter = CoreText.CTFramesetterCreateWithAttributedString(attributedString) path = Quartz.CGPathCreateMutable() Quartz.CGPathAddRect(path, None, Quartz.CGRectMake(0, -h, w*2, h*2)) box = CoreText.CTFramesetterCreateFrame(setter, (0, 0), path, None) ctLines = CoreText.CTFrameGetLines(box) origins = CoreText.CTFrameGetLineOrigins(box, (0, len(ctLines)), None) if offset: x, y = offset else: x = y = 0 if origins: x -= origins[-1][0] y -= origins[-1][1] for i, (originX, originY) in enumerate(origins): ctLine = ctLines[i] # path around a bug somewhere # create a new CTLine from a substring with the same range... rng = CoreText.CTLineGetStringRange(ctLine) txtLine = attributedString.attributedSubstringFromRange_(rng) ctLine = CoreText.CTLineCreateWithAttributedString(txtLine) ctRuns = CoreText.CTLineGetGlyphRuns(ctLine) self._path.moveToPoint_((x+originX, y+originY)) for ctRun in ctRuns: glyphs = CoreText.CTRunGetGlyphs(ctRun, CoreText.CTRunGetStringRange(ctRun), None) attributes = CoreText.CTRunGetAttributes(ctRun) font = attributes.get(AppKit.NSFontAttributeName) glyphs = [g for g in glyphs if g != 0] self._path.appendBezierPathWithGlyphs_count_inFont_(glyphs, len(glyphs), font)
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)
def _get_font(self): _font = AppKit.NSFont.fontWithName_size_(self._fontName, self.fontSize) if _font == None: warnings.warn("font: %s is not installed, back to the fallback font: %s" % (self._fontName, _FALLBACKFONT)) self._fontName = _FALLBACKFONT _font = AppKit.NSFont.fontWithName_size_(_FALLBACKFONT, self.fontSize) coreTextfeatures = [] for featureTag, value in self.openTypeFeatures.items(): if not value: featureTag = "%s_off" % featureTag if featureTag in openType.featureMap: feature = openType.featureMap[featureTag] coreTextfeatures.append(feature) fontDescriptor = _font.fontDescriptor() fontDescriptor = fontDescriptor.fontDescriptorByAddingAttributes_({CoreText.NSFontFeatureSettingsAttribute : coreTextfeatures}) _font = AppKit.NSFont.fontWithDescriptor_size_(fontDescriptor, self.fontSize) return _font
def getFontVariationAttributes(font, fontVariations): coreTextFontVariations = dict() if fontVariations: existingAxes = getVariationAxesForFont(font) for axis, value in fontVariations.items(): if axis in existingAxes: existinsAxis = existingAxes[axis] # clip variation value within the min max value if value < existinsAxis["minValue"]: value = existinsAxis["minValue"] if value > existinsAxis["maxValue"]: value = existinsAxis["maxValue"] coreTextFontVariations[convertVariationTagToInt(axis)] = value else: warnings.warn("variation axis '%s' not available for '%s'" % (axis, font.fontName())) return coreTextFontVariations
def _writeDataToFile(self, data, path, options): if os.path.exists(path): os.remove(path) warnings.warn("export to '.mov' is deprecated, use '.mp4' instead.") movie, error = QTKit.QTMovie.alloc().initToWritableFile_error_(path, None) if error: raise DrawBotError("Could not create a quick time movie, %s" % error.localizedDescription()) pdfDocument = Quartz.PDFDocument.alloc().initWithData_(data) for index in range(pdfDocument.pageCount()): pool = AppKit.NSAutoreleasePool.alloc().init() try: frameLength, frameScale = self._frameDurationData[index] duration = QTKit.QTMakeTime(frameLength, frameScale) page = pdfDocument.pageAtIndex_(index) image = AppKit.NSImage.alloc().initWithData_(page.dataRepresentation()) movie.addImage_forDuration_withAttributes_(image, duration, self._saveMovieAttributes) finally: del pool movie.updateMovieFile()
def _get_font(self): _font = AppKit.NSFont.fontWithName_size_(self._fontName, self.fontSize) if _font is None: ff = self._fallbackFontName or _FALLBACKFONT warnings.warn("font: %s is not installed, back to the fallback font: %s" % (self._fontName, ff)) self._fontName = ff _font = AppKit.NSFont.fontWithName_size_(ff, self.fontSize) coreTextfeatures = [] for featureTag, value in self.openTypeFeatures.items(): if not value: featureTag = "%s_off" % featureTag if featureTag in openType.featureMap: feature = openType.featureMap[featureTag] coreTextfeatures.append(feature) fontDescriptor = _font.fontDescriptor() fontAttributes = { CoreText.NSFontFeatureSettingsAttribute: coreTextfeatures, } if self._fallbackFontName: fontAttributes[CoreText.NSFontCascadeListAttribute] = [AppKit.NSFontDescriptor.fontDescriptorWithName_size_(self._fallbackFontName, self.fontSize)] fontDescriptor = fontDescriptor.fontDescriptorByAddingAttributes_(fontAttributes) _font = AppKit.NSFont.fontWithDescriptor_size_(fontDescriptor, self.fontSize) return _font
def appendGlyph(self, *glyphNames): """ Appends a glyph by his glyph name using the current `font`. """ # use a non breaking space as replacement character baseString = unichr(0x00A0) font = None if self._font: font = AppKit.NSFont.fontWithName_size_(self._font, self._fontSize) if font is None: warnings.warn("font: %s is not installed, back to the fallback font: %s" % (self._font, _FALLBACKFONT)) font = AppKit.NSFont.fontWithName_size_(_FALLBACKFONT, self._fontSize) fallbackFont = self._fallbackFont self._fallbackFont = None for glyphName in glyphNames: glyph = font.glyphWithName_(glyphName) if glyph: self.append(baseString) glyphInfo = AppKit.NSGlyphInfo.glyphInfoWithGlyph_forFont_baseString_(glyph, font , baseString) self._attributedString.addAttribute_value_range_(AppKit.NSGlyphInfoAttributeName, glyphInfo, (len(self)-1, 1)) else: warnings.warn("font %s has no glyph with the name %s" % (font.fontName(), glyphName)) self._fallbackFont = fallbackFont
def cmykShadow(self, offset, blur, color): warnings.warn("cmykShadow 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 cmykStroke(self, c, m, y, k, a=1): warnings.warn("cmykStroke 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")
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()
def append(self, txt, font=None, fallbackFont=None, fontSize=None, fill=None, cmykFill=None, stroke=None, cmykStroke=None, strokeWidth=None, align=None, lineHeight=None, openTypeFeatures=None): """ Add `txt` to the formatted string with some additional text formatting attributes: * `font`: the font to be used for the given text * `fallbackFont`: the fallback font * `fontSize`: the font size to be used for the given text * `fill`: the fill color to be used for the given text * `cmykFill`: the cmyk fill color to be used for the given text * `stroke`: the stroke color to be used for the given text * `cmykStroke`: the cmyk stroke color to be used for the given text * `strokeWidth`: the strokeWidth to be used for the given text * `align`: the alignment to be used for the given text * `lineHeight`: the lineHeight to be used for the given text * `openTypeFeatures`: enable OpenType features All formatting attributes follow the same notation as other similar DrawBot methods. A color is a tuple of `(r, g, b, alpha)`, and a cmykColor is a tuple of `(c, m, y, k, alpha)`. Text can also be added with `formattedString += "hello"`. It will append the text with the current settings of the formatted string. """ if font is None: font = self._font else: self._font = font if fallbackFont is None: fallbackFont = self._fallbackFont else: self._fallbackFont = fallbackFont if fontSize is None: fontSize = self._fontSize else: self._fontSize = fontSize if fill is None and cmykFill is None: fill = self._fill cmykFill = self._cmykFill elif fill is not None: self._fill = fill self._cmykFill = None elif cmykFill is not None: self._cmykFill = cmykFill self._fill = None if stroke is None and cmykStroke is None: stroke = self._stroke cmykStroke = self._cmykStroke elif stroke is not None: self._stroke = stroke self._cmykStroke = None elif cmykStroke is not None: self._cmykStroke = cmykStroke self._stroke = None if strokeWidth is None: strokeWidth = self._strokeWidth else: self._strokeWidth = strokeWidth if align is None: align = self._align else: self._align = align if lineHeight is None: lineHeight = self._lineHeight else: self._lineHeight = lineHeight if openTypeFeatures is None: openTypeFeatures = self._openTypeFeatures else: self._openTypeFeatures = openTypeFeatures if isinstance(txt, FormattedString): self._attributedString.appendAttributedString_(txt.getNSObject()) return attributes = {} if font: fontName = font font = AppKit.NSFont.fontWithName_size_(fontName, fontSize) if font is None: ff = fallbackFont if ff is None: ff = _FALLBACKFONT warnings.warn( "font: %s is not installed, back to the fallback font: %s" % (fontName, ff)) font = AppKit.NSFont.fontWithName_size_(ff, fontSize) coreTextfeatures = [] for featureTag, value in openTypeFeatures.items(): if not value: featureTag = "%s_off" % featureTag if featureTag in openType.featureMap: feature = openType.featureMap[featureTag] coreTextfeatures.append(feature) fontDescriptor = font.fontDescriptor() fontAttributes = { CoreText.NSFontFeatureSettingsAttribute: coreTextfeatures, } if fallbackFont: fontAttributes[CoreText.NSFontCascadeListAttribute] = [ AppKit.NSFontDescriptor.fontDescriptorWithName_size_( fallbackFont, fontSize) ] fontDescriptor = fontDescriptor.fontDescriptorByAddingAttributes_( fontAttributes) font = AppKit.NSFont.fontWithDescriptor_size_( fontDescriptor, fontSize) attributes[AppKit.NSFontAttributeName] = font elif fontSize: font = AppKit.NSFont.fontWithName_size_(_FALLBACKFONT, fontSize) attributes[AppKit.NSFontAttributeName] = font if fill or cmykFill: if fill: fillColor = self._colorClass.getColor(fill).getNSObject() elif cmykFill: fillColor = self._cmykColorClass.getColor( cmykFill).getNSObject() attributes[AppKit.NSForegroundColorAttributeName] = fillColor if stroke or cmykStroke: if stroke: strokeColor = self._colorClass.getColor(stroke).getNSObject() elif cmykStroke: strokeColor = self._cmykColorClass.getColor( cmykStroke).getNSObject() attributes[AppKit.NSStrokeColorAttributeName] = strokeColor attributes[AppKit.NSStrokeWidthAttributeName] = -abs(strokeWidth) para = AppKit.NSMutableParagraphStyle.alloc().init() if align: para.setAlignment_(self._textAlignMap[align]) if lineHeight: #para.setLineSpacing_(lineHeight) para.setMaximumLineHeight_(lineHeight) para.setMinimumLineHeight_(lineHeight) attributes[AppKit.NSParagraphStyleAttributeName] = para txt = AppKit.NSAttributedString.alloc().initWithString_attributes_( txt, attributes) self._attributedString.appendAttributedString_(txt)
def openTypeFeatures(self, *args, **features): warnings.warn("openTypeFeatures is not supported in a svg context")
def _textBox(self, rawTxt, box, align): path, (x, y) = self._getPathForFrameSetter(box) canDoGradients = True if align == "justified": warnings.warn("justified text is not supported in a svg context") attrString = self.attributedString(rawTxt, align=align) if self._state.hyphenation: attrString = self.hyphenateAttributedString(attrString, path) txt = attrString.string() setter = CoreText.CTFramesetterCreateWithAttributedString(attrString) box = CoreText.CTFramesetterCreateFrame(setter, (0, 0), path, None) self._svgBeginClipPath() defaultData = self._svgDrawingAttributes() data = { "text-anchor": "start", "transform": self._svgTransform(self._state.transformMatrix.translate(x, y + self.height).scale(1, -1)) } if self._state.shadow is not None: data["filter"] = "url(#%s_flipped)" % self._state.shadow.tagID if isinstance(rawTxt, FormattedString): if rawTxt.svgID: data["id"] = rawTxt.svgID if rawTxt.svgClass: data["class"] = rawTxt.svgClass if rawTxt.svgLink: self._svgContext.begintag("a", **{"xlink:href": rawTxt.svgLink}) self._svgContext.newline() 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: stringRange = CoreText.CTRunGetStringRange(ctRun) attributes = CoreText.CTRunGetAttributes(ctRun) font = attributes.get(AppKit.NSFontAttributeName) fontDescriptor = font.fontDescriptor() fillColor = attributes.get(AppKit.NSForegroundColorAttributeName) strokeColor = attributes.get(AppKit.NSStrokeColorAttributeName) strokeWidth = attributes.get(AppKit.NSStrokeWidthAttributeName, self._state.strokeWidth) baselineShift = attributes.get(AppKit.NSBaselineOffsetAttributeName, 0) openTypeFeatures = attributes.get("drawbot.openTypeFeatures") underline = attributes.get(AppKit.NSUnderlineStyleAttributeName) url = attributes.get(AppKit.NSLinkAttributeName) fontName = font.fontName() fontSize = font.pointSize() fontFallbacks = [fallbackFont.postscriptName() for fallbackFont in fontDescriptor.get(CoreText.NSFontCascadeListAttribute, [])] fontNames = ", ".join([fontName] + fontFallbacks) style = dict() spanData = dict(defaultData) fill = self._colorClass(fillColor).svgColor() if fill: c, a = fill spanData["fill"] = c if a != 1: spanData["fill-opacity"] = a stroke = self._colorClass(strokeColor).svgColor() if stroke: c, a = stroke spanData["stroke"] = c if a != 1: spanData["stroke-opacity"] = a spanData["stroke-width"] = formatNumber(abs(strokeWidth) * .5) spanData["font-family"] = fontNames spanData["font-size"] = formatNumber(fontSize) if openTypeFeatures: style["font-feature-settings"] = self._svgStyleOpenTypeFeatures(openTypeFeatures) if canDoGradients and self._state.gradient is not None: spanData["fill"] = "url(#%s_flipped)" % self._state.gradient.tagID if underline is not None: style["text-decoration"] = "underline" underlineStyle = self._svgUnderlineStylesMap.get(underline) if underlineStyle: style["text-decoration-style"] = underlineStyle if style: spanData["style"] = self._svgStyle(**style) self._save() runTxt = txt.substringWithRange_((stringRange.location, stringRange.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"] = formatNumber(originX + runX) spanData["y"] = formatNumber(self.height - originY - runY + baselineShift) if url is not None: self._svgContext.begintag("a", href=url.absoluteString()) self._svgContext.newline() self._svgContext.begintag("tspan", **spanData) self._svgContext.newline() self._svgContext.write(runTxt) self._svgContext.newline() self._svgContext.endtag("tspan") self._svgContext.newline() if url is not None: self._svgContext.endtag("a") self._svgContext.newline() self._restore() self._svgContext.endtag("text") self._svgContext.newline() if isinstance(rawTxt, FormattedString) and rawTxt.svgLink: self._svgContext.endtag("a") self._svgContext.newline() self._svgEndClipPath()
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)
def _textBox(self, txt, box, align): path, (x, y) = self._getPathForFrameSetter(box) canDoGradients = True if align == "justified": warnings.warn("justified text is not supported in a svg context") attrString = self.attributedString(txt, align=align) if self._state.hyphenation: attrString = self.hyphenateAttributedString(attrString, path) txt = attrString.string() setter = CoreText.CTFramesetterCreateWithAttributedString(attrString) box = CoreText.CTFramesetterCreateFrame(setter, (0, 0), path, None) self._svgBeginClipPath() defaultData = self._svgDrawingAttributes() data = { "text-anchor": "start", "transform": self._svgTransform(self._state.transformMatrix.translate(x, y + self.height).scale(1, -1)) } 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: stringRange = CoreText.CTRunGetStringRange(ctRun) attributes = CoreText.CTRunGetAttributes(ctRun) font = attributes.get(AppKit.NSFontAttributeName) fontAttributes = font.fontDescriptor().fontAttributes() fillColor = attributes.get(AppKit.NSForegroundColorAttributeName) strokeColor = attributes.get(AppKit.NSStrokeColorAttributeName) strokeWidth = attributes.get(AppKit.NSStrokeWidthAttributeName, self._state.strokeWidth) baselineShift = attributes.get(AppKit.NSBaselineOffsetAttributeName, 0) openTypeFeatures = fontAttributes.get(CoreText.NSFontFeatureSettingsAttribute) fontName = font.fontName() fontSize = font.pointSize() spanData = dict(defaultData) fill = self._colorClass(fillColor).svgColor() if fill: c, a = fill spanData["fill"] = c if a != 1: spanData["fill-opacity"] = a stroke = self._colorClass(strokeColor).svgColor() if stroke: c, a = stroke spanData["stroke"] = c if a != 1: spanData["stroke-opacity"] = a spanData["stroke-width"] = formatNumber(abs(strokeWidth) * .5) spanData["font-family"] = fontName spanData["font-size"] = formatNumber(fontSize) if openTypeFeatures: featureTags = getFeatureTagsForFontAttributes(openTypeFeatures) spanData["style"] = self._svgStyle(**{ "font-feature-settings": self._svgStyleOpenTypeFeatures(featureTags) } ) if canDoGradients and self._state.gradient is not None: spanData["fill"] = "url(#%s_flipped)" % self._state.gradient.tagID self._save() runTxt = txt.substringWithRange_((stringRange.location, stringRange.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"] = formatNumber(originX + runX) spanData["y"] = formatNumber(self.height - originY - runY + baselineShift) 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()
def append(self, txt, font=None, fallbackFont=None, fontSize=None, fill=None, cmykFill=None, stroke=None, cmykStroke=None, strokeWidth=None, align=None, lineHeight=None, openTypeFeatures=None): """ Add `txt` to the formatted string with some additional text formatting attributes: * `font`: the font to be used for the given text * `fallbackFont`: the fallback font * `fontSize`: the font size to be used for the given text * `fill`: the fill color to be used for the given text * `cmykFill`: the cmyk fill color to be used for the given text * `stroke`: the stroke color to be used for the given text * `cmykStroke`: the cmyk stroke color to be used for the given text * `strokeWidth`: the strokeWidth to be used for the given text * `align`: the alignment to be used for the given text * `lineHeight`: the lineHeight to be used for the given text * `openTypeFeatures`: enable OpenType features All formatting attributes follow the same notation as other similar DrawBot methods. A color is a tuple of `(r, g, b, alpha)`, and a cmykColor is a tuple of `(c, m, y, k, alpha)`. Text can also be added with `formattedString += "hello"`. It will append the text with the current settings of the formatted string. """ if font is None: font = self._font else: self._font = font if fallbackFont is None: fallbackFont = self._fallbackFont else: self._fallbackFont = fallbackFont if fontSize is None: fontSize = self._fontSize else: self._fontSize = fontSize if fill is None and cmykFill is None: fill = self._fill cmykFill = self._cmykFill elif fill is not None: self._fill = fill self._cmykFill = None elif cmykFill is not None: self._cmykFill = cmykFill self._fill = None if stroke is None and cmykStroke is None: stroke = self._stroke cmykStroke = self._cmykStroke elif stroke is not None: self._stroke = stroke self._cmykStroke = None elif cmykStroke is not None: self._cmykStroke = cmykStroke self._stroke = None if strokeWidth is None: strokeWidth = self._strokeWidth else: self._strokeWidth = strokeWidth if align is None: align = self._align else: self._align = align if lineHeight is None: lineHeight = self._lineHeight else: self._lineHeight = lineHeight if openTypeFeatures is None: openTypeFeatures = self._openTypeFeatures else: self._openTypeFeatures = openTypeFeatures if isinstance(txt, FormattedString): self._attributedString.appendAttributedString_(txt.getNSObject()) return attributes = {} if font: fontName = font font = AppKit.NSFont.fontWithName_size_(fontName, fontSize) if font is None: ff = fallbackFont if ff is None: ff = _FALLBACKFONT warnings.warn("font: %s is not installed, back to the fallback font: %s" % (fontName, ff)) font = AppKit.NSFont.fontWithName_size_(ff, fontSize) coreTextfeatures = [] for featureTag, value in openTypeFeatures.items(): if not value: featureTag = "%s_off" % featureTag if featureTag in openType.featureMap: feature = openType.featureMap[featureTag] coreTextfeatures.append(feature) fontDescriptor = font.fontDescriptor() fontAttributes = { CoreText.NSFontFeatureSettingsAttribute : coreTextfeatures, } if fallbackFont: fontAttributes[CoreText.NSFontCascadeListAttribute] = [AppKit.NSFontDescriptor.fontDescriptorWithName_size_(fallbackFont, fontSize)] fontDescriptor = fontDescriptor.fontDescriptorByAddingAttributes_(fontAttributes) font = AppKit.NSFont.fontWithDescriptor_size_(fontDescriptor, fontSize) attributes[AppKit.NSFontAttributeName] = font elif fontSize: font = AppKit.NSFont.fontWithName_size_(_FALLBACKFONT, fontSize) attributes[AppKit.NSFontAttributeName] = font if fill or cmykFill: if fill: fillColor = self._colorClass.getColor(fill).getNSObject() elif cmykFill: fillColor = self._cmykColorClass.getColor(cmykFill).getNSObject() attributes[AppKit.NSForegroundColorAttributeName] = fillColor if stroke or cmykStroke: if stroke: strokeColor = self._colorClass.getColor(stroke).getNSObject() elif cmykStroke: strokeColor = self._cmykColorClass.getColor(cmykStroke).getNSObject() attributes[AppKit.NSStrokeColorAttributeName] = strokeColor attributes[AppKit.NSStrokeWidthAttributeName] = -abs(strokeWidth) para = AppKit.NSMutableParagraphStyle.alloc().init() if align: para.setAlignment_(self._textAlignMap[align]) if lineHeight: #para.setLineSpacing_(lineHeight) para.setMaximumLineHeight_(lineHeight) para.setMinimumLineHeight_(lineHeight) attributes[AppKit.NSParagraphStyleAttributeName] = para txt = AppKit.NSAttributedString.alloc().initWithString_attributes_(txt, attributes) self._attributedString.appendAttributedString_(txt)