diff --git a/drawBot/drawBotDrawingTools.py b/drawBot/drawBotDrawingTools.py index 885478cb..c24130b2 100644 --- a/drawBot/drawBotDrawingTools.py +++ b/drawBot/drawBotDrawingTools.py @@ -1,12 +1,15 @@ +from __future__ import annotations + import math import os import random from collections import namedtuple from contextlib import contextmanager -from typing import Any +from typing import TYPE_CHECKING, Any, Literal import AppKit # type: ignore import CoreText # type: ignore +import PIL # type: ignore import Quartz # type: ignore from .aliases import ( @@ -44,6 +47,9 @@ warnings, ) +if TYPE_CHECKING: + from .drawBotPageDrawingTools import DrawBotPage + def _getmodulecontents(module, names=None): d = {} @@ -160,7 +166,7 @@ def _copy(self): new._tempInstalledFonts = dict(self._tempInstalledFonts) return new - def newDrawing(self): + def newDrawing(self) -> None: """ Reset the drawing stack to the clean and empty stack. @@ -181,7 +187,7 @@ def newDrawing(self): """ self._reset() - def endDrawing(self): + def endDrawing(self) -> None: """ Explicitly tell drawBot the drawing is done. This is advised when using drawBot as a standalone module. @@ -257,7 +263,7 @@ def pageCount(self) -> int: # size and pages - def size(self, width: float | str, height: float | None = None): + def size(self, width: float | str, height: float | None = None) -> None: """ Set the width and height of the canvas. Without calling `size()` the default drawing board is 1000 by 1000 points. @@ -299,7 +305,7 @@ def size(self, width: float | str, height: float | None = None): else: raise DrawBotError("Can't use 'size()' after drawing has begun. Try to move it to the top of your script.") - def newPage(self, width: str | float | None = None, height: float | None = None): + def newPage(self, width: str | float | None = None, height: float | None = None) -> None: """ Create a new canvas to draw in. This will act like a page in a pdf or a frame in a mov. @@ -338,7 +344,7 @@ def newPage(self, width: str | float | None = None, height: float | None = None) self._dummyContext = DummyContext() self._addInstruction("newPage", width, height) - def pages(self): + def pages(self) -> tuple[DrawBotPage, ...]: """ Return all pages. @@ -390,7 +396,9 @@ def pages(self): break return tuple(DrawBotPage(instructionSet) for instructionSet in instructions) - def saveImage(self, path: SomePath, *args, **options: dict[str, Any]): + def saveImage( + self, path: SomePath, *args: Any, **options: Any + ) -> list[AppKit.NSImage | PIL.ImageFile.ImageFile] | None: """ Save or export the canvas to a specified format. The `path` argument is a single destination path to save the current drawing actions. @@ -476,7 +484,7 @@ def saveImage(self, path: SomePath, *args, **options: dict[str, Any]): supportedOptions="\n ".join(getContextOptionsDocs()), ) - def printImage(self, pdf=None): + def printImage(self, pdf=None) -> None: """ Export the canvas to a printing dialog, ready to print. @@ -498,7 +506,7 @@ def printImage(self, pdf=None): else: context.printImage(pdf) - def pdfImage(self): + def pdfImage(self) -> Quartz.PDFDocument | None: """ Return the image as a pdf document object. """ @@ -510,7 +518,7 @@ def pdfImage(self): # graphics state - def save(self): + def save(self) -> None: """ DrawBot strongly recommends to use `savedState()` in a `with` statement instead. @@ -522,7 +530,7 @@ def save(self): self._requiresNewFirstPage = True self._addInstruction("save") - def restore(self): + def restore(self) -> None: """ DrawBot strongly recommends to use `savedState()` in a `with` statement instead. @@ -565,7 +573,7 @@ def savedState(self): # basic shapes - def rect(self, x: float, y: float, w: float, h: float): + def rect(self, x: float, y: float, w: float, h: float) -> None: """ Draw a rectangle from position x, y with the given width and height. @@ -578,7 +586,7 @@ def rect(self, x: float, y: float, w: float, h: float): self._requiresNewFirstPage = True self._addInstruction("rect", x, y, w, h) - def oval(self, x: float, y: float, w: float, h: float): + def oval(self, x: float, y: float, w: float, h: float) -> None: """ Draw an oval from position x, y with the given width and height. @@ -593,14 +601,14 @@ def oval(self, x: float, y: float, w: float, h: float): # path - def newPath(self): + def newPath(self) -> None: """ Create a new path. """ self._requiresNewFirstPage = True self._addInstruction("newPath") - def moveTo(self, xy: Point): + def moveTo(self, xy: Point) -> None: """ Move to a point `x`, `y`. """ @@ -608,7 +616,7 @@ def moveTo(self, xy: Point): self._requiresNewFirstPage = True self._addInstruction("moveTo", (x, y)) - def lineTo(self, xy: Point): + def lineTo(self, xy: Point) -> None: """ Line to a point `x`, `y`. """ @@ -616,7 +624,7 @@ def lineTo(self, xy: Point): self._requiresNewFirstPage = True self._addInstruction("lineTo", (x, y)) - def curveTo(self, xy1: Point, xy2: Point, xy3: Point): + def curveTo(self, xy1: Point, xy2: Point, xy3: Point) -> None: """ Curve to a point `x3`, `y3`. With given bezier handles `x1`, `y1` and `x2`, `y2`. @@ -627,7 +635,7 @@ def curveTo(self, xy1: Point, xy2: Point, xy3: Point): self._requiresNewFirstPage = True self._addInstruction("curveTo", (x1, y1), (x2, y2), (x3, y3)) - def qCurveTo(self, *points: Point): + def qCurveTo(self, *points: Point) -> None: """ Quadratic curve with a given set of off curves to a on curve. """ @@ -641,14 +649,14 @@ def arc( startAngle: float, endAngle: float, clockwise: bool, - ): + ) -> None: """ Arc with `center` and a given `radius`, from `startAngle` to `endAngle`, going clockwise if `clockwise` is True and counter clockwise if `clockwise` is False. """ self._requiresNewFirstPage = True self._addInstruction("arc", center, radius, startAngle, endAngle, clockwise) - def arcTo(self, xy1: Point, xy2: Point, radius: float): + def arcTo(self, xy1: Point, xy2: Point, radius: float) -> None: """ Arc from one point to an other point with a given `radius`. @@ -688,14 +696,14 @@ def drawPt(pos, r=5): self._requiresNewFirstPage = True self._addInstruction("arcTo", (x1, y1), (x2, y2), radius) - def closePath(self): + def closePath(self) -> None: """ Close the path. """ self._requiresNewFirstPage = True self._addInstruction("closePath") - def drawPath(self, path: BezierPath | None = None): + def drawPath(self, path: BezierPath | None = None) -> None: """ Draw the current path, or draw the provided path. @@ -724,7 +732,7 @@ def drawPath(self, path: BezierPath | None = None): self._requiresNewFirstPage = True self._addInstruction("drawPath", path) - def clipPath(self, path=None): + def clipPath(self, path=None) -> None: """ Use the given path as a clipping path, or the current path if no path was given. @@ -756,7 +764,7 @@ def clipPath(self, path=None): self._requiresNewFirstPage = True self._addInstruction("clipPath", path) - def line(self, point1: Point, point2: Point): + def line(self, point1: Point, point2: Point) -> None: """ Draws a line between two given points. @@ -771,7 +779,7 @@ def line(self, point1: Point, point2: Point): path.line(point1, point2) self.drawPath(path) - def polygon(self, *points: Point, **kwargs: bool): + def polygon(self, *points: Point, **kwargs: bool) -> None: """ Draws a polygon with n-amount of points. Optionally a `close` argument can be provided to open or close the path. @@ -788,7 +796,7 @@ def polygon(self, *points: Point, **kwargs: bool): # color - def colorSpace(self, colorSpace): + def colorSpace(self, colorSpace) -> None: """ Set the color space. Options are `genericRGB`, `adobeRGB1998`, `sRGB`, `genericGray`, `genericGamma22Gray`. @@ -826,7 +834,7 @@ def listColorSpaces(self) -> list[str]: """ return sorted(self._dummyContext._colorSpaceMap.keys()) - def blendMode(self, operation: str): + def blendMode(self, operation: str) -> None: """ Set a blend mode. @@ -862,7 +870,7 @@ def fill( g: float | None = None, b: float | None = None, alpha: float = 1, - ): + ) -> None: """ Sets the fill color with a `red`, `green`, `blue` and `alpha` value. Each argument must a value float between 0 and 1. @@ -902,7 +910,7 @@ def stroke( g: float | None = None, b: float | None = None, alpha: float = 1, - ): + ) -> None: """ Sets the stroke color with a `red`, `green`, `blue` and `alpha` value. Each argument must a value float between 0 and 1. @@ -947,7 +955,7 @@ def cmykFill( y: float | None = None, k: float | None = None, alpha: float = 1, - ): + ) -> None: """ Set a fill using a CMYK color before drawing a shape. This is handy if the file is intended for print. @@ -978,7 +986,7 @@ def cmykStroke( y: float | None = None, k: float | None = None, alpha: float = 1, - ): + ) -> None: """ Set a stroke using a CMYK color before drawing a shape. This is handy if the file is intended for print. @@ -1006,7 +1014,7 @@ def cmykStroke( self._requiresNewFirstPage = True self._addInstruction("cmykStroke", c, m, y, k, alpha) - def opacity(self, value: float): + def opacity(self, value: float) -> None: """ Sets the current opacity value. The `value` argument must be a value between 0.0 and 1.0. @@ -1031,7 +1039,7 @@ def shadow( offset: Point, blur: float | None = None, color: tuple[float, ...] | None = None, - ): + ) -> None: """ Adds a shadow with an `offset` (x, y), `blur` and a `color`. The `color` argument must be a tuple similarly as `fill`. @@ -1056,7 +1064,7 @@ def cmykShadow( offset: Point, blur: float | None = None, color: tuple[float, ...] | None = None, - ): + ) -> None: """ Adds a cmyk shadow with an `offset` (x, y), `blur` and a `color`. The `color` argument must be a tuple similarly as `cmykFill`. @@ -1113,7 +1121,7 @@ def cmykLinearGradient( endPoint: Point | None = None, colors: list[CMYKColorTuple] | None = None, locations=None, - ): + ) -> None: """ A cmyk linear gradient fill with: @@ -1147,7 +1155,7 @@ def radialGradient( locations: list[float] | None = None, startRadius: float = 0, endRadius: float = 100, - ): + ) -> None: """ A radial gradient fill with: @@ -1175,7 +1183,15 @@ def radialGradient( rect(10, 10, 980, 980) """ self._requiresNewFirstPage = True - self._addInstruction("radialGradient", startPoint, endPoint, colors, locations, startRadius, endRadius) + self._addInstruction( + "radialGradient", + startPoint, + endPoint, + colors, + locations, + startRadius, + endRadius, + ) def cmykRadialGradient( self, @@ -1185,7 +1201,7 @@ def cmykRadialGradient( locations: list[float] | None = None, startRadius: float = 0, endRadius: float = 100, - ): + ) -> None: """ A cmyk radial gradient fill with: @@ -1213,11 +1229,19 @@ def cmykRadialGradient( rect(10, 10, 980, 980) """ self._requiresNewFirstPage = True - self._addInstruction("cmykRadialGradient", startPoint, endPoint, colors, locations, startRadius, endRadius) + self._addInstruction( + "cmykRadialGradient", + startPoint, + endPoint, + colors, + locations, + startRadius, + endRadius, + ) # path drawing behavoir - def strokeWidth(self, value: float): + def strokeWidth(self, value: float) -> None: """ Sets stroke width. @@ -1239,7 +1263,7 @@ def strokeWidth(self, value: float): self._requiresNewFirstPage = True self._addInstruction("strokeWidth", value) - def miterLimit(self, value: float): + def miterLimit(self, value: float) -> None: """ Set a miter limit. Used on corner points. @@ -1270,7 +1294,7 @@ def miterLimit(self, value: float): self._requiresNewFirstPage = True self._addInstruction("miterLimit", value) - def lineJoin(self, value: str): + def lineJoin(self, value: Literal["miter", "round", "bevel"]): """ Set a line join. @@ -1313,7 +1337,7 @@ def lineJoin(self, value: str): self._requiresNewFirstPage = True self._addInstruction("lineJoin", value) - def lineCap(self, value: str): + def lineCap(self, value: Literal["butt", "square", "round"]): """ Set a line cap. @@ -1393,7 +1417,7 @@ def lineDash(self, value: float | None, *values: float, offset: float = 0): # transform - def transform(self, matrix: TransformTuple, center: Point = (0, 0)): + def transform(self, matrix: TransformTuple, center: Point = (0, 0)) -> None: """ Transform the canvas with a transformation matrix. """ @@ -1402,13 +1426,13 @@ def transform(self, matrix: TransformTuple, center: Point = (0, 0)): matrix = transformationAtCenter(matrix, center) self._addInstruction("transform", matrix) - def translate(self, x: float = 0, y: float = 0): + def translate(self, x: float = 0, y: float = 0) -> None: """ Translate the canvas with a given offset. """ self.transform((1, 0, 0, 1, x, y)) - def rotate(self, angle: float, center: Point = (0, 0)): + def rotate(self, angle: float, center: Point = (0, 0)) -> None: """ Rotate the canvas around the `center` point (which is the origin by default) with a given angle in degrees. """ @@ -1417,7 +1441,7 @@ def rotate(self, angle: float, center: Point = (0, 0)): s = math.sin(angle) self.transform((c, s, -s, c, 0, 0), center) - def scale(self, x: float = 1, y: float | None = None, center: Point = (0, 0)): + def scale(self, x: float = 1, y: float | None = None, center: Point = (0, 0)) -> None: """ Scale the canvas with a given `x` (horizontal scale) and `y` (vertical scale). @@ -1429,7 +1453,7 @@ def scale(self, x: float = 1, y: float | None = None, center: Point = (0, 0)): y = x self.transform((x, 0, 0, y, 0, 0), center) - def skew(self, angle1: float, angle2: float = 0, center: Point = (0, 0)): + def skew(self, angle1: float, angle2: float = 0, center: Point = (0, 0)) -> None: """ Skew the canvas with given `angle1` and `angle2`. @@ -1443,7 +1467,12 @@ def skew(self, angle1: float, angle2: float = 0, center: Point = (0, 0)): # text - def font(self, fontNameOrPath: SomePath, fontSize: float | None = None, fontNumber: int = 0): + def font( + self, + fontNameOrPath: SomePath, + fontSize: float | None = None, + fontNumber: int = 0, + ) -> str | None: """ Set a font with the name of the font. If a font path is given the font will be installed and used directly. @@ -1465,7 +1494,7 @@ def font(self, fontNameOrPath: SomePath, fontSize: float | None = None, fontNumb self._addInstruction("font", fontNameOrPath, fontSize, fontNumber) return getFontName(font) - def fallbackFont(self, fontNameOrPath: SomePath, fontNumber: int = 0): + def fallbackFont(self, fontNameOrPath: SomePath, fontNumber: int = 0) -> str | None: """ Set a fallback font, this is used whenever a glyph is not available in the current font. @@ -1480,7 +1509,7 @@ def fallbackFont(self, fontNameOrPath: SomePath, fontNumber: int = 0): self._addInstruction("fallbackFont", fontNameOrPath, fontNumber) return getFontName(dummyFont) - def fontSize(self, fontSize: float): + def fontSize(self, fontSize: float) -> None: """ Set the font size in points. The default `fontSize` is 10pt. @@ -1508,7 +1537,7 @@ def lineHeight(self, value): self._dummyContext.lineHeight(value) self._addInstruction("lineHeight", value) - def tracking(self, value: float): + def tracking(self, value: float) -> None: """ Set the tracking between characters. It adds an absolute number of points between the characters. @@ -1530,14 +1559,14 @@ def tracking(self, value: float): self._dummyContext.tracking(value) self._addInstruction("tracking", value) - def baselineShift(self, value): + def baselineShift(self, value) -> None: """ Set the shift of the baseline. """ self._dummyContext.baselineShift(value) self._addInstruction("baselineShift", value) - def underline(self, value: str): + def underline(self, value: Literal["single", "thick", "double"] | None) -> None: """ Set the underline value. Underline must be `single`, `thick`, `double` or `None`. @@ -1551,7 +1580,7 @@ def underline(self, value: str): self._dummyContext.underline(value) self._addInstruction("underline", value) - def strikethrough(self, value: str): + def strikethrough(self, value: Literal["single", "thick", "double"] | None) -> None: """ Set the strikethrough value. Underline must be `single`, `thick`, `double` or `None`. @@ -1566,7 +1595,7 @@ def strikethrough(self, value: str): self._dummyContext.strikethrough(value) self._addInstruction("strikethrough", value) - def url(self, value: str): + def url(self, value: str) -> None: """ Set the url value for text. @@ -1579,7 +1608,7 @@ def url(self, value: str): self._dummyContext.url(value) self._addInstruction("url", value) - def hyphenation(self, value: bool): + def hyphenation(self, value: bool) -> None: """ Set hyphenation, `True` or `False`. @@ -1597,7 +1626,7 @@ def hyphenation(self, value: bool): self._checkLanguageHyphenation() self._addInstruction("hyphenation", value) - def tabs(self, *tabs: tuple[float, str]): + def tabs(self, *tabs: tuple[float, str]) -> None: r""" Set tabs, tuples of (`float`, `alignment`) Aligment can be `"left"`, `"center"`, `"right"` or any other character. @@ -1624,7 +1653,7 @@ def tabs(self, *tabs: tuple[float, str]): self._dummyContext.tabs(*tabs) self._addInstruction("tabs", *tabs) - def language(self, language): + def language(self, language) -> None: """ Set the preferred language as language tag or None to use the default language. A language tag might be a [iso639-2 or iso639-1](https://www.loc.gov/standards/iso639-2/php/English_list.php) @@ -1674,7 +1703,7 @@ def _checkLanguageHyphenation(self): if not CoreText.CFStringIsHyphenationAvailableForLocale(locale): warnings.warn(f"Language '{language}' has no hyphenation available.") - def writingDirection(self, direction: str | None): + def writingDirection(self, direction: Literal["LTR", "RTL"] | None) -> None: """ Set the writing direction: `None`, `'LTR'` or `'RTL'`. @@ -1734,7 +1763,9 @@ def listOpenTypeFeatures(self, fontNameOrPath: SomePath | None = None) -> list[s listOpenTypeFeatures.__doc__ = FormattedString.listOpenTypeFeatures.__doc__ - def fontVariations(self, *args: None, **axes: float | bool): + def fontVariations( + self, *args: None, **axes: float + ) -> dict[str, float]: # FIXME why was bool there? also, why *args? """ Pick a variation by axes values. @@ -1766,7 +1797,7 @@ def listFontVariations(self, fontNameOrPath: SomePath | None = None) -> dict[str listFontVariations.__doc__ = FormattedString.listFontVariations.__doc__ - def fontNamedInstance(self, name: str, fontNameOrPath: SomePath | None = None): + def fontNamedInstance(self, name: str, fontNameOrPath: SomePath | None = None) -> None: """ Set a font with `name` of a named instance. The `name` of the named instance must be listed in `listNamedInstances()`, @@ -1798,7 +1829,12 @@ def textProperties(self) -> dict[str, Any]: # drawing text - def text(self, txt: FormattedString | str, position: Point, align: str | None = None): + def text( + self, + txt: FormattedString | str, + position: Point, + align: Literal["left", "center", "right"] | None = None, + ) -> None: """ Draw a text at a provided position. @@ -1824,13 +1860,21 @@ def text(self, txt: FormattedString | str, position: Point, align: str | None = raise DrawBotError("align must be left, right, center") attributedString = self._dummyContext.attributedString(txt, align=align) for subTxt, box in makeTextBoxes( - attributedString, (x, y), align=align, plainText=not isinstance(txt, FormattedString) + attributedString, + (x, y), + align=align, + plainText=not isinstance(txt, FormattedString), ): if isinstance(txt, FormattedString): subTxt.copyContextProperties(txt) self.textBox(subTxt, box, align=align) - def textOverflow(self, txt: FormattedString | str, box: BoundingBox, align: str | None = None): + def textOverflow( + self, + txt: FormattedString | str, + box: BoundingBox | BezierPath, + align: Literal["left", "center", "right", "justified"] | None = None, + ) -> FormattedString | str | None: """ Returns the overflowed text without drawing the text. @@ -1854,7 +1898,12 @@ def textOverflow(self, txt: FormattedString | str, box: BoundingBox, align: str raise DrawBotError("align must be %s" % (", ".join(self._dummyContext._textAlignMap.keys()))) return self._dummyContext.clippedText(txt, box, align) - def textBox(self, txt: FormattedString | str, box: BoundingBox, align: str | None = None): + def textBox( + self, + txt: FormattedString | str, + box: BoundingBox, + align: Literal["left", "center", "right", "justified"] | None = None, + ) -> str | FormattedString | None: """ Draw a text in a provided rectangle. @@ -1992,7 +2041,12 @@ def textBox(self, txt: FormattedString | str, box: BoundingBox, align: str | Non self._addInstruction("textBox", txt, box, align) return self._dummyContext.clippedText(txt, box, align) - def textBoxBaselines(self, txt: FormattedString | str, box: BoundingBox, align: str | None = None): + def textBoxBaselines( + self, + txt: FormattedString | str, + box: BoundingBox, + align: Literal["left", "center", "right", "justified"] | None = None, + ) -> list[tuple[float, float]]: """ Returns a list of `x, y` coordinates indicating the start of each line @@ -2013,7 +2067,9 @@ def textBoxBaselines(self, txt: FormattedString | str, box: BoundingBox, align: origins = CoreText.CTFrameGetLineOrigins(box, (0, len(ctLines)), None) return [(x + o.x, y + o.y) for o in origins] - def textBoxCharacterBounds(self, txt: FormattedString | str, box: BoundingBox, align: str | None = None): + def textBoxCharacterBounds( + self, txt: FormattedString | str, box: BoundingBox, align: str | None = None + ) -> list[tuple[BoundingBox | BezierPath, float, FormattedString | str]]: """ Returns a list of typesetted bounding boxes `((x, y, w, h), baseLineOffset, formattedSubString)`. @@ -2027,7 +2083,7 @@ def textBoxCharacterBounds(self, txt: FormattedString | str, box: BoundingBox, a CharactersBounds = namedtuple("CharactersBounds", ["bounds", "baselineOffset", "formattedSubString"]) - bounds = list() + bounds = list[tuple[BoundingBox | BezierPath, float, FormattedString | str]]() path, (x, y) = self._dummyContext._getPathForFrameSetter(box) attrString = self._dummyContext.attributedString(txt) setter = CoreText.CTFramesetterCreateWithAttributedString(attrString) @@ -2043,7 +2099,12 @@ def textBoxCharacterBounds(self, txt: FormattedString | str, box: BoundingBox, a runW, runH, ascent, descent = CoreText.CTRunGetTypographicBounds(ctRun, (0, 0), None, None, None) bounds.append( CharactersBounds( - (x + originX + runPos.x, y + originY + runPos.y - ascent, runW, runH + ascent), + ( + x + originX + runPos.x, + y + originY + runPos.y - ascent, + runW, + runH + ascent, + ), ascent, txt[runRange.location : runRange.location + runRange.length], ) @@ -2057,7 +2118,7 @@ def image( position: Point, alpha: float = 1, pageNumber: int | None = None, - ): + ) -> None: """ Add an image from a `path` with an `offset` and an `alpha` value. This accepts most common file types like pdf, jpg, png, tiff and gif. `NSImage` objects are accepted too. @@ -2203,9 +2264,14 @@ def imagePixelColor( if color is None: return None color = color.colorUsingColorSpaceName_("NSCalibratedRGBColorSpace") - return color.redComponent(), color.greenComponent(), color.blueComponent(), color.alphaComponent() + return ( + color.redComponent(), + color.greenComponent(), + color.blueComponent(), + color.alphaComponent(), + ) - def imageResolution(self, path: SomePath | AppKit.NSImage) -> int: + def imageResolution(self, path: SomePath | AppKit.NSImage) -> float: """ Return the image resolution for a given image. Supports pdf, jpg, png, tiff and gif file formats. `NSImage` objects are supported too. """ @@ -2257,7 +2323,7 @@ def numberOfPages(self, path: SomePath) -> int | None: # mov - def frameDuration(self, seconds: float): + def frameDuration(self, seconds: float) -> None: """ When exporting to `mov` or `gif` each frame can have duration set in `seconds`. @@ -2378,7 +2444,7 @@ def textSize( align: str | None = None, width: float | None = None, height: float | None = None, - ): + ) -> tuple[float, float]: """ Returns the size of a text with the current settings, like `font`, `fontSize` and `lineHeight` as a tuple (width, height). @@ -2458,7 +2524,7 @@ def installFont(self, path: SomePath) -> str: warnings.warn("install font: %s" % error) return psName - def uninstallFont(self, path: SomePath): + def uninstallFont(self, path: SomePath) -> None: """ Uninstall a font with a given path. @@ -2549,7 +2615,7 @@ def fontLineHeight(self) -> float: """ return self._dummyContext._state.text.fontLineHeight() - def Variable(self, variables, workSpace, continuous=True): + def Variable(self, variables, workSpace, continuous=True) -> None: """ Build small UI for variables in a script.