diff --git a/docs/content/text/textProperties.rst b/docs/content/text/textProperties.rst
index 58413445..d0e2ca3f 100644
--- a/docs/content/text/textProperties.rst
+++ b/docs/content/text/textProperties.rst
@@ -10,9 +10,9 @@ Text Properties
.. autofunction:: drawBot.lineHeight
.. autofunction:: drawBot.tracking
.. autofunction:: drawBot.baselineShift
-.. autofunction:: drawBot.openTypeFeatures(frac=True, case=True, ...)
+.. autofunction:: drawBot.openTypeFeatures(frac=True, case=True, ..., resetFeatures=False)
.. autofunction:: drawBot.listOpenTypeFeatures
-.. autofunction:: drawBot.fontVariations(wdth=0.6, wght=0.1, ...)
+.. autofunction:: drawBot.fontVariations(wdth=0.6, wght=0.1, ..., resetVariations=False)
.. autofunction:: drawBot.listFontVariations
.. autofunction:: drawBot.fontNamedInstance
.. autofunction:: drawBot.listNamedInstances
diff --git a/drawBot/context/baseContext.py b/drawBot/context/baseContext.py
index 63d9d52f..d0c86e43 100644
--- a/drawBot/context/baseContext.py
+++ b/drawBot/context/baseContext.py
@@ -264,13 +264,13 @@ def _curveToOne(self, pt1, pt2, pt3):
"""
self._path.curveToPoint_controlPoint1_controlPoint2_(pt3, pt1, pt2)
- def closePath(self):
+ def closePath(self) -> None:
"""
Close the path.
"""
self._path.closePath()
- def beginPath(self, identifier=None):
+ def beginPath(self, identifier: str | None = None) -> None:
"""
Begin using the path as a so called point pen and start a new subpath.
"""
@@ -285,9 +285,9 @@ def addPoint(
segmentType: str | None = None,
smooth: bool = False,
name: str | None = None,
- identifier=None,
- **kwargs,
- ):
+ identifier: str | None = None,
+ **kwargs: Any,
+ ) -> None:
"""
Use the path as a point pen and add a point to the current subpath. `beginPath` must
have been called prior to adding points with `addPoint` calls.
@@ -303,7 +303,7 @@ def addPoint(
**kwargs,
)
- def endPath(self):
+ def endPath(self) -> None:
"""
End the current subpath. Calling this method has two distinct meanings depending
on the context:
@@ -603,7 +603,7 @@ def controlPointBounds(self) -> BoundingBox | None:
(x, y), (w, h) = self._path.controlPointBounds()
return x, y, x + w, y + h
- def optimizePath(self):
+ def optimizePath(self) -> None:
count = self._path.elementCount()
if not count or self._path.elementAtIndex_(count - 1) != AppKit.NSMoveToBezierPathElement:
return
@@ -630,13 +630,13 @@ def copy(self) -> Self:
new.copyContextProperties(self)
return new
- def reverse(self):
+ def reverse(self) -> None:
"""
Reverse the path direction
"""
self._path = self._path.bezierPathByReversingPath()
- def appendPath(self, otherPath: Self):
+ def appendPath(self, otherPath: Self) -> None:
"""
Append a path.
"""
@@ -653,13 +653,13 @@ def __iadd__(self, other: Self) -> Self:
# transformations
- def translate(self, x: float = 0, y: float = 0):
+ def translate(self, x: float = 0, y: float = 0) -> None:
"""
Translate the path 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 path around the `center` point (which is the origin by default) with a given angle in degrees.
"""
@@ -668,7 +668,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 path with a given `x` (horizontal scale) and `y` (vertical scale).
@@ -680,7 +680,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 path with given `angle1` and `angle2`.
@@ -692,7 +692,7 @@ def skew(self, angle1: float, angle2: float = 0, center: Point = (0, 0)):
angle2 = math.radians(angle2)
self.transform((1, math.tan(angle2), math.tan(angle1), 1, 0, 0), center)
- def transform(self, transformMatrix: TransformTuple, center: Point = (0, 0)):
+ def transform(self, transformMatrix: TransformTuple, center: Point = (0, 0)) -> None:
"""
Transform a path with a transform matrix (xy, xx, yy, yx, x, y).
"""
@@ -1638,7 +1638,7 @@ def font(
font = getNSFontFromNameOrPath(fontNameOrPath, fontSize or 10, fontNumber)
return getFontName(font)
- def fontNumber(self, fontNumber: int):
+ def fontNumber(self, fontNumber: int) -> None:
self._fontNumber = fontNumber
def fallbackFont(self, fontNameOrPath: SomePath, fontNumber: int = 0) -> str | None:
@@ -1656,17 +1656,17 @@ def fallbackFont(self, fontNameOrPath: SomePath, fontNumber: int = 0) -> str | N
self._fallbackFontNumber = fontNumber
return fontName
- def fallbackFontNumber(self, fontNumber: int):
+ def fallbackFontNumber(self, fontNumber: int) -> None:
self._fallbackFontNumber = fontNumber
- def fontSize(self, fontSize: float):
+ def fontSize(self, fontSize: float) -> None:
"""
Set the font size in points.
The default `fontSize` is 10pt.
"""
self._fontSize = fontSize
- def fill(self, r: float | None = None, g: float | None = None, b: float | None = None, alpha: float = 1):
+ def fill(self, r: float | None = None, 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.
@@ -1678,7 +1678,7 @@ def fill(self, r: float | None = None, g: float | None = None, b: float | None =
self._fill = fill
self._cmykFill = None
- def stroke(self, r: float | None = None, g: float | None = None, b: float | None = None, alpha: float = 1):
+ def stroke(self, r: float | None = None, 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.
@@ -1697,7 +1697,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.
@@ -1730,39 +1730,39 @@ def cmykStroke(
self._cmykStroke = cmykStroke
self._stroke = None
- def strokeWidth(self, strokeWidth: float):
+ def strokeWidth(self, strokeWidth: float) -> None:
"""
Sets stroke width.
"""
self._strokeWidth = strokeWidth
- def align(self, align: str):
+ def align(self, align: str) -> None:
"""
Sets the text alignment.
Possible `align` values are: `left`, `center` and `right`.
"""
self._align = align
- def lineHeight(self, lineHeight: float):
+ def lineHeight(self, lineHeight: float) -> None:
"""
Set the line height.
"""
self._lineHeight = lineHeight
- def tracking(self, tracking: float):
+ def tracking(self, tracking: float) -> None:
"""
Set the tracking between characters. It adds an absolute number of
points between the characters.
"""
self._tracking = tracking
- def baselineShift(self, baselineShift: float):
+ def baselineShift(self, baselineShift: float) -> None:
"""
Set the shift of the baseline.
"""
self._baselineShift = baselineShift
- def underline(self, underline: str | None):
+ def underline(self, underline: str | None) -> None:
"""
Set the underline value.
Underline must be `single`, `thick`, `double` or `None`.
@@ -1771,7 +1771,7 @@ def underline(self, underline: str | None):
raise DrawBotError("underline must be %s" % (", ".join(sorted(self._textUnderlineMap.keys()))))
self._underline = underline
- def strikethrough(self, strikethrough: str | None):
+ def strikethrough(self, strikethrough: str | None) -> None:
"""
Set the strikethrough value.
Strikethrough must be `single`, `thick`, `double` or `None`.
@@ -1780,16 +1780,17 @@ def strikethrough(self, strikethrough: str | None):
raise DrawBotError("strikethrough must be %s" % (", ".join(sorted(self._textstrikethroughMap.keys()))))
self._strikethrough = strikethrough
- def url(self, url: str | None):
+ def url(self, url: str | None) -> None:
"""
set the url value.
url must be a string or `None`
"""
self._url = url
- def openTypeFeatures(self, *args: None, **features: bool) -> dict[str, bool]:
+ def openTypeFeatures(self, *, resetFeatures: bool = False, **features: bool) -> dict[str, bool]:
"""
Enable OpenType features and return the current openType features settings.
+ You can reset the default values with `openTypeFeatures(resetFeatures=True)`
If no arguments are given `openTypeFeatures()` will just return the current openType features settings.
@@ -1811,19 +1812,9 @@ def openTypeFeatures(self, *args: None, **features: bool) -> dict[str, bool]:
# draw the formatted string
text(t, (10, 80))
"""
- if args and features:
- raise DrawBotError("Can't combine positional arguments and keyword arguments")
- if args:
- if len(args) != 1:
- raise DrawBotError("There can only be one positional argument")
- if args[0] is not None:
- raise DrawBotError("First positional argument can only be None")
- warnings.warn("openTypeFeatures(None) is deprecated, use openTypeFeatures(resetFeatures=True) instead.")
+ if resetFeatures:
self._openTypeFeatures.clear()
- else:
- if features.pop("resetFeatures", False):
- self._openTypeFeatures.clear()
- self._openTypeFeatures.update(features)
+ self._openTypeFeatures.update(features)
return dict(self._openTypeFeatures)
def listOpenTypeFeatures(self, fontNameOrPath: SomePath | None = None, fontNumber: int = 0) -> list[str]:
@@ -1837,25 +1828,16 @@ def listOpenTypeFeatures(self, fontNameOrPath: SomePath | None = None, fontNumbe
font = getNSFontFromNameOrPath(fontNameOrPath, 10, fontNumber)
return openType.getFeatureTagsForFont(font)
- def fontVariations(self, *args: None, **axes: float | bool) -> dict[str, float]:
+ def fontVariations(self, *, resetVariations: bool = False, **axes: float) -> dict[str, float]:
"""
Pick a variation by axes values and return the current font variations settings.
+ You can reset the default values with `fontVariations(resetVariations=True)`
If no arguments are given `fontVariations()` will just return the current font variations settings.
"""
- if args and axes:
- raise DrawBotError("Can't combine positional arguments and keyword arguments")
- if args:
- if len(args) != 1:
- raise DrawBotError("There can only be one positional argument")
- if args[0] is not None:
- raise DrawBotError("First positional argument can only be None")
- warnings.warn("fontVariations(None) is deprecated, use fontVariations(resetVariations=True) instead.")
+ if resetVariations:
self._fontVariations.clear()
- else:
- if axes.pop("resetVariations", False):
- self._fontVariations.clear()
- self._fontVariations.update(axes)
+ self._fontVariations.update(axes)
defaultVariations = self.listFontVariations()
currentVariation = {axis: data["defaultValue"] for axis, data in defaultVariations.items()}
currentVariation.update(self._fontVariations)
@@ -1908,7 +1890,7 @@ def listNamedInstances(self, fontNameOrPath: SomePath | None = None, fontNumber:
font = getNSFontFromNameOrPath(fontNameOrPath, 10, fontNumber)
return variation.getNamedInstancesForFont(font)
- def tabs(self, tab: tuple[float, str] | None, *tabs: tuple[float, str]):
+ def tabs(self, tab: tuple[float, str] | None, *tabs: tuple[float, str]) -> None:
"""
Set tabs,tuples of (`float`, `alignment`)
Aligment can be `"left"`, `"center"`, `"right"` or any other character.
@@ -1932,7 +1914,7 @@ def tabs(self, tab: tuple[float, str] | None, *tabs: tuple[float, str]):
combinedTabs.extend(tabs)
self._tabs = combinedTabs
- def indent(self, indent: float):
+ def indent(self, indent: float) -> None:
"""
Set indent of text left of the paragraph.
@@ -2005,7 +1987,7 @@ def indent(self, indent: float):
"""
self._indent = indent
- def tailIndent(self, indent: float):
+ def tailIndent(self, indent: float) -> None:
"""
Set indent of text right of the paragraph.
@@ -2014,19 +1996,19 @@ def tailIndent(self, indent: float):
"""
self._tailIndent = indent
- def firstLineIndent(self, indent: float):
+ def firstLineIndent(self, indent: float) -> None:
"""
Set indent of the text only for the first line.
"""
self._firstLineIndent = indent
- def paragraphTopSpacing(self, value: float):
+ def paragraphTopSpacing(self, value: float) -> None:
"""
set paragraph spacing at the top.
"""
self._paragraphTopSpacing = value
- def paragraphBottomSpacing(self, value: float):
+ def paragraphBottomSpacing(self, value: float) -> None:
"""
set paragraph spacing at the bottom.
"""
@@ -2729,11 +2711,11 @@ def language(self, language):
def writingDirection(self, direction):
self._state.text.writingDirection(direction)
- def openTypeFeatures(self, *args: None, **features: dict[str, bool]) -> dict[str, bool]:
- return self._state.text.openTypeFeatures(*args, **features)
+ def openTypeFeatures(self, *, resetFeatures: bool = False, **features: bool) -> dict[str, bool]:
+ return self._state.text.openTypeFeatures(resetFeatures=resetFeatures, **features)
- def fontVariations(self, *args, **axes):
- return self._state.text.fontVariations(*args, **axes)
+ def fontVariations(self, *, resetVariations: bool = False, **axes: float) -> dict[str, float]:
+ return self._state.text.fontVariations(resetVariations=resetVariations, **axes)
def fontNamedInstance(self, name, fontNameOrPath):
self._state.text.fontNamedInstance(name, fontNameOrPath)
diff --git a/drawBot/context/tools/imageObject.py b/drawBot/context/tools/imageObject.py
index 1e3fefca..e71f3caf 100644
--- a/drawBot/context/tools/imageObject.py
+++ b/drawBot/context/tools/imageObject.py
@@ -1,15 +1,11 @@
-import AppKit # type: ignore
-import Quartz # type: ignore
-from math import radians
import os
from math import radians
-from typing import Any
-
-import AppKit
from typing import Self
+import AppKit # type: ignore
+import Quartz # type: ignore
+
from drawBot.aliases import BoundingBox, Point, RGBAColorTuple, Size, SomePath, TransformTuple
-from drawBot.context.baseContext import FormattedString
from drawBot.context.imageContext import _makeBitmapImageRep
from drawBot.misc import DrawBotError, optimizePath
diff --git a/drawBot/drawBotDrawingTools.py b/drawBot/drawBotDrawingTools.py
index 6cc2651a..a6b289a3 100644
--- a/drawBot/drawBotDrawingTools.py
+++ b/drawBot/drawBotDrawingTools.py
@@ -1,26 +1,16 @@
+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 Quartz # type: ignore
-from .aliases import (
- BoundingBox,
- CMYKColor,
- CMYKColorTuple,
- Point,
- RGBAColorTuple,
- RGBColor,
- RGBColorTuple,
- Size,
- SomePath,
- TransformTuple,
-)
from .context import getContextForFileExt, getContextOptionsDocs, getFileExtensions
from .context.baseContext import (
BezierPath,
@@ -45,6 +35,23 @@
warnings,
)
+if TYPE_CHECKING:
+ from collections.abc import Generator
+
+ from .aliases import (
+ BoundingBox,
+ CMYKColor,
+ CMYKColorTuple,
+ Point,
+ RGBAColorTuple,
+ RGBColor,
+ RGBColorTuple,
+ Size,
+ SomePath,
+ TransformTuple,
+ )
+ from .drawBotPageDrawingTools import DrawBotPage
+
def _getmodulecontents(module, names=None):
d = {}
@@ -161,7 +168,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.
@@ -182,7 +189,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.
@@ -191,7 +198,7 @@ def endDrawing(self):
gifTools.clearExplodedGifCache()
@contextmanager
- def drawing(self):
+ def drawing(self) -> Generator[None, None, None]:
"""
Reset and clean the drawing stack in a `with` statement.
@@ -258,7 +265,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.
@@ -300,7 +307,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.
@@ -339,7 +346,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.
@@ -391,7 +398,7 @@ 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):
"""
Save or export the canvas to a specified format.
The `path` argument is a single destination path to save the current drawing actions.
@@ -477,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.
@@ -499,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.
"""
@@ -511,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.
@@ -523,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.
@@ -536,7 +543,7 @@ def restore(self):
self._addInstruction("restore")
@contextmanager
- def savedState(self):
+ def savedState(self) -> Generator[None, None, None]:
"""
Save and restore the current graphics state in a `with` statement.
@@ -566,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.
@@ -579,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.
@@ -594,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`.
"""
@@ -609,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`.
"""
@@ -617,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`.
@@ -628,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.
"""
@@ -642,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`.
@@ -689,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.
@@ -725,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.
@@ -757,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.
@@ -772,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.
@@ -789,7 +796,7 @@ def polygon(self, *points: Point, **kwargs: bool):
# color
- def colorSpace(self, colorSpace):
+ def colorSpace(self, colorSpace: str | None) -> None:
"""
Set the color space.
Options are `genericRGB`, `adobeRGB1998`, `sRGB`, `genericGray`, `genericGamma22Gray`.
@@ -827,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.
@@ -863,10 +870,10 @@ 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.
+ Each argument must be a value float between 0 and 1.
.. downloadcode:: fill.py
@@ -903,10 +910,10 @@ 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.
+ Each argument must be a value float between 0 and 1.
.. downloadcode:: stroke.py
@@ -948,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.
@@ -979,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.
@@ -1007,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.
@@ -1032,11 +1039,11 @@ 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`.
- The `offset`and `blur` argument will be drawn independent of the current context transformations.
+ The `offset` and `blur` argument will be drawn independent of the current context transformations.
.. downloadcode:: shadow.py
@@ -1057,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`.
@@ -1082,7 +1089,7 @@ def linearGradient(
endPoint: Point | None = None,
colors: list[RGBColor | RGBColorTuple] | None = None,
locations: list[float] | None = None,
- ):
+ ) -> None:
"""
A linear gradient fill with:
@@ -1114,7 +1121,7 @@ def cmykLinearGradient(
endPoint: Point | None = None,
colors: list[CMYKColorTuple] | None = None,
locations=None,
- ):
+ ) -> None:
"""
A cmyk linear gradient fill with:
@@ -1148,13 +1155,13 @@ def radialGradient(
locations: list[float] | None = None,
startRadius: float = 0,
endRadius: float = 100,
- ):
+ ) -> None:
"""
A radial gradient fill with:
* `startPoint` as (x, y)
* `endPoint` as (x, y)
- * `colors` as a list of colors, described similary as `fill`
+ * `colors` as a list of colors, described similarly as `fill`
* `locations` of each color as a list of floats. (optionally)
* `startRadius` radius around the startPoint in degrees (optionally)
* `endRadius` radius around the endPoint in degrees (optionally)
@@ -1176,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,
@@ -1186,13 +1201,13 @@ def cmykRadialGradient(
locations: list[float] | None = None,
startRadius: float = 0,
endRadius: float = 100,
- ):
+ ) -> None:
"""
A cmyk radial gradient fill with:
* `startPoint` as (x, y)
* `endPoint` as (x, y)
- * `colors` as a list of colors, described similary as `cmykFill`
+ * `colors` as a list of colors, described similarly as `cmykFill`
* `locations` of each color as a list of floats. (optionally)
* `startRadius` radius around the startPoint in degrees (optionally)
* `endRadius` radius around the endPoint in degrees (optionally)
@@ -1214,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.
@@ -1240,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.
@@ -1271,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"]) -> None:
"""
Set a line join.
@@ -1314,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"]) -> None:
"""
Set a line cap.
@@ -1324,7 +1347,7 @@ def lineCap(self, value: str):
# set stroke color to black
stroke(0)
- # set a strok width
+ # set a stroke width
strokeWidth(50)
# translate the canvas
translate(150, 50)
@@ -1338,7 +1361,7 @@ def lineCap(self, value: str):
lineCap("square")
# draw a line
line((0, 200), (0, 800))
- # translate the canvase
+ # translate the canvas
translate(300, 0)
# set a line cap style
lineCap("round")
@@ -1348,10 +1371,10 @@ def lineCap(self, value: str):
self._requiresNewFirstPage = True
self._addInstruction("lineCap", value)
- def lineDash(self, value: float | None, *values: float, offset: float = 0):
+ def lineDash(self, value: float | None, *values: float, offset: float = 0) -> None:
"""
- Set a line dash with any given amount of lenghts.
- Uneven lenghts will have a visible stroke, even lenghts will be invisible.
+ Set a line dash with any given amount of lengths.
+ Uneven lengths will have a visible stroke, even lengths will be invisible.
optionally an `offset` can be given to set the offset of the first dash.
@@ -1359,7 +1382,7 @@ def lineDash(self, value: float | None, *values: float, offset: float = 0):
# set stroke color to black
stroke(0)
- # set a strok width
+ # set a stroke width
strokeWidth(50)
# translate the canvas
translate(150, 50)
@@ -1379,7 +1402,7 @@ def lineDash(self, value: float | None, *values: float, offset: float = 0):
lineDash(2, 10, 5, 5, offset=2)
# draw a line
line((0, 200), (0, 800))
- # translate the canvase
+ # translate the canvas
translate(200, 0)
# reset the line dash
lineDash(None)
@@ -1394,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.
"""
@@ -1403,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.
"""
@@ -1418,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).
@@ -1430,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`.
@@ -1444,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.
@@ -1466,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.
@@ -1481,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.
@@ -1493,7 +1521,7 @@ def fontSize(self, fontSize: float):
self._dummyContext.fontSize(fontSize)
self._addInstruction("fontSize", fontSize)
- def lineHeight(self, value):
+ def lineHeight(self, value: float) -> None:
"""
Set the line height.
@@ -1509,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.
@@ -1531,14 +1559,14 @@ def tracking(self, value: float):
self._dummyContext.tracking(value)
self._addInstruction("tracking", value)
- def baselineShift(self, value):
+ def baselineShift(self, value: float) -> 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`.
@@ -1552,10 +1580,10 @@ 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`.
+ Strikethrough must be `single`, `thick`, `double` or `None`.
.. downloadcode:: strikethrough.py
@@ -1567,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.
@@ -1580,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`.
@@ -1598,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.
@@ -1625,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: str | None) -> 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)
@@ -1651,7 +1679,7 @@ def language(self, language):
language("nl")
# shift up a bit
translate(500, 0)
- # darw the text again with a language set
+ # draw the text again with a language set
textBox(word, box)
"""
self._dummyContext.language(language)
@@ -1675,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'`.
@@ -1694,7 +1722,7 @@ def writingDirection(self, direction: str | None):
self._dummyContext.writingDirection(direction)
self._addInstruction("writingDirection", direction)
- def openTypeFeatures(self, *args: bool | None, **features: bool) -> dict[str, bool]:
+ def openTypeFeatures(self, *, resetFeatures: bool = False, **features: bool) -> dict[str, bool]:
"""
Enable OpenType features.
@@ -1726,8 +1754,8 @@ def openTypeFeatures(self, *args: bool | None, **features: bool) -> dict[str, bo
# the same string again, back to default features
text(someTxt, (100, 70))
"""
- result = self._dummyContext.openTypeFeatures(*args, **features)
- self._addInstruction("openTypeFeatures", *args, **features)
+ result = self._dummyContext.openTypeFeatures(resetFeatures=resetFeatures, **features)
+ self._addInstruction("openTypeFeatures", resetFeatures=resetFeatures, **features)
return result
def listOpenTypeFeatures(self, fontNameOrPath: SomePath | None = None) -> list[str]:
@@ -1735,13 +1763,13 @@ 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, *, resetVariations: bool = False, **axes: float) -> dict[str, float]:
"""
Pick a variation by axes values.
.. downloadcode:: fontVariations.py
- size(1000, 500)
+ size(1000, 600)
# pick a font
font("Skia")
# pick a font size
@@ -1752,14 +1780,17 @@ def fontVariations(self, *args: None, **axes: float | bool):
# pick a variation from the current font
fontVariations(wght=.6)
# draw text!!
- text("Hello Q", (100, 100))
+ text("Hello Q", (100, 40))
# pick a variation from the current font
fontVariations(wght=3, wdth=1.2)
# draw text!!
- text("Hello Q", (100, 300))
+ text("Hello Q", (100, 220))
+ # reset defaults
+ fontVariations(resetVariations=True)
+ text("Hello Q", (100, 420))
"""
- result = self._dummyContext.fontVariations(*args, **axes)
- self._addInstruction("fontVariations", *args, **axes)
+ result = self._dummyContext.fontVariations(resetVariations=resetVariations, **axes)
+ self._addInstruction("fontVariations", resetVariations=resetVariations, **axes)
return result
def listFontVariations(self, fontNameOrPath: SomePath | None = None) -> dict[str, dict]:
@@ -1767,10 +1798,10 @@ 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()`,
+ The `name` of the named instance must be listed in `listNamedInstances()`.
Optionally a `fontNameOrPath` can be given. If a font path is given that `fontNameOrPath` will be set.
@@ -1799,7 +1830,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.
@@ -1825,13 +1861,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.
@@ -1855,7 +1899,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.
@@ -1876,12 +1925,12 @@ def textBox(self, txt: FormattedString | str, box: BoundingBox, align: str | Non
fill(1, 0, 0)
# draw a rectangle with variables from above
rect(x, y, w, h)
- # set a diferent fill
+ # set a different fill
fill(1)
# set a font size
fontSize(200)
# draw text in a text box
- # with varibales from above
+ # with variables from above
overflow = textBox("hallo, this text is a bit to long",
(x, y, w, h), align="center")
# a text box returns text overflow
@@ -1993,7 +2042,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
@@ -2014,7 +2068,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)`.
@@ -2028,7 +2084,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 = newFramesetterWithAttributedString(attrString)
@@ -2044,7 +2100,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],
)
@@ -2058,14 +2119,14 @@ 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.
+ Add an image from a `path` with a `position` and an `alpha` value.
This accepts most common file types like pdf, jpg, png, tiff and gif. `NSImage` objects are accepted too.
Optionally an `alpha` can be provided, which is a value between 0 and 1.
- Optionally a `pageNumber` can be provided when the path referes to a multi page pdf file.
+ Optionally a `pageNumber` can be provided when the path refers to a multi page pdf file.
.. downloadcode:: image.py
@@ -2204,9 +2265,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.
"""
@@ -2258,7 +2324,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`.
@@ -2271,7 +2337,7 @@ def frameDuration(self, seconds: float):
fps = 30
# duration of the movie
seconds = 3
- # calculate the lenght of a single frame
+ # calculate the length of a single frame
duration = 1 / fps
# calculate the amount of frames needed
totalFrames = seconds * fps
@@ -2308,7 +2374,7 @@ def frameDuration(self, seconds: float):
# pdf links
- def linkURL(self, url: str, xywh: BoundingBox):
+ def linkURL(self, url: str, xywh: BoundingBox) -> None:
"""
Add a clickable rectangle for an external url link.
@@ -2318,7 +2384,7 @@ def linkURL(self, url: str, xywh: BoundingBox):
self._requiresNewFirstPage = True
self._addInstruction("linkURL", url, (x, y, w, h))
- def linkDestination(self, name: str, xy: Point):
+ def linkDestination(self, name: str, xy: Point) -> None:
"""
Add a destination point for a link within a PDF.
Setup a clickable retangle with `linkRect(name, (x, y, w, h))` with the same name.
@@ -2329,7 +2395,7 @@ def linkDestination(self, name: str, xy: Point):
self._requiresNewFirstPage = True
self._addInstruction("linkDestination", name, (x, y))
- def linkRect(self, name: str, xywh: BoundingBox):
+ def linkRect(self, name: str, xywh: BoundingBox) -> None:
"""
Add a clickable rectangle for a link within a PDF.
Use `linkDestination(name, (x, y))` with the same name to set the destination of the clickable rectangle.
@@ -2379,13 +2445,13 @@ 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).
Optionally a `width` constrain or `height` constrain can be provided
- to calculate the lenght or width of text with the given constrain.
+ to calculate the length or width of text with the given constrain.
"""
if not isinstance(txt, (str, FormattedString)):
raise TypeError("expected 'str' or 'FormattedString', got '%s'" % type(txt).__name__)
@@ -2420,7 +2486,7 @@ def installFont(self, path: SomePath) -> str:
The postscript font name can be used to set the font as the active font.
Fonts are installed only for the current process.
- Fonts will not be accesible outside the scope of drawBot.
+ Fonts will not be accessible outside the scope of drawBot.
All installed fonts will automatically be uninstalled when the script is done.
@@ -2459,7 +2525,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.
@@ -2550,7 +2616,7 @@ def fontLineHeight(self) -> float:
"""
return self._dummyContext._state.text.fontLineHeight()
- def Variable(self, variables, workSpace, continuous=True):
+ def Variable(self, variables: list[dict[str, Any]], workSpace: dict[str, Any], continuous: bool = True) -> None:
"""
Build small UI for variables in a script.
diff --git a/tests/data/example_fontVariations.png b/tests/data/example_fontVariations.png
index 9151b02b..a0899751 100644
Binary files a/tests/data/example_fontVariations.png and b/tests/data/example_fontVariations.png differ
diff --git a/tests/data/expected_booleanOperations.svg b/tests/data/expected_booleanOperations.svg
index ebc3c6c1..b488b04f 100644
--- a/tests/data/expected_booleanOperations.svg
+++ b/tests/data/expected_booleanOperations.svg
@@ -2,9 +2,9 @@
\ No newline at end of file
diff --git a/tests/data/expected_booleanOperations2.svg b/tests/data/expected_booleanOperations2.svg
index ebc3c6c1..b488b04f 100644
--- a/tests/data/expected_booleanOperations2.svg
+++ b/tests/data/expected_booleanOperations2.svg
@@ -2,9 +2,9 @@
\ No newline at end of file
diff --git a/tests/data/expected_fontVariations.pdf b/tests/data/expected_fontVariations.pdf
index 6507d29f..4c1134bd 100644
Binary files a/tests/data/expected_fontVariations.pdf and b/tests/data/expected_fontVariations.pdf differ
diff --git a/tests/data/expected_fontVariations.png b/tests/data/expected_fontVariations.png
old mode 100755
new mode 100644
index d76529e8..a0899751
Binary files a/tests/data/expected_fontVariations.png and b/tests/data/expected_fontVariations.png differ
diff --git a/tests/data/expected_fontVariations.txt b/tests/data/expected_fontVariations.txt
index 25d99ced..5d031d1b 100644
--- a/tests/data/expected_fontVariations.txt
+++ b/tests/data/expected_fontVariations.txt
@@ -1,3 +1,2 @@
-*** DrawBot warning: fontVariations(None) is deprecated, use fontVariations(resetVariations=True) instead. ***
wdth [('defaultValue', 1.0), ('maxValue', 1.3), ('minValue', 0.62), ('name', 'Width')]
wght [('defaultValue', 1.0), ('maxValue', 3.2), ('minValue', 0.48), ('name', 'Weight')]
diff --git a/tests/data/expected_fontVariations2.txt b/tests/data/expected_fontVariations2.txt
index 33186fd0..e69de29b 100644
--- a/tests/data/expected_fontVariations2.txt
+++ b/tests/data/expected_fontVariations2.txt
@@ -1 +0,0 @@
-*** DrawBot warning: fontVariations(None) is deprecated, use fontVariations(resetVariations=True) instead. ***
diff --git a/tests/data/expected_openTypeFeatures.txt b/tests/data/expected_openTypeFeatures.txt
index 7743d242..704e3be9 100644
--- a/tests/data/expected_openTypeFeatures.txt
+++ b/tests/data/expected_openTypeFeatures.txt
@@ -1,2 +1 @@
['dlig', 'liga', 'lnum', 'onum', 'pnum', 'titl', 'tnum']
-*** DrawBot warning: openTypeFeatures(None) is deprecated, use openTypeFeatures(resetFeatures=True) instead. ***
diff --git a/tests/data/expected_openTypeFeatures2.txt b/tests/data/expected_openTypeFeatures2.txt
index e9a529c9..e69de29b 100644
--- a/tests/data/expected_openTypeFeatures2.txt
+++ b/tests/data/expected_openTypeFeatures2.txt
@@ -1 +0,0 @@
-*** DrawBot warning: openTypeFeatures(None) is deprecated, use openTypeFeatures(resetFeatures=True) instead. ***
diff --git a/tests/data/expected_removeOverlap.svg b/tests/data/expected_removeOverlap.svg
index c70777dd..e13f3286 100644
--- a/tests/data/expected_removeOverlap.svg
+++ b/tests/data/expected_removeOverlap.svg
@@ -1,5 +1,5 @@
\ No newline at end of file
diff --git a/tests/data/fontVariations.png b/tests/data/fontVariations.png
deleted file mode 100755
index d76529e8..00000000
Binary files a/tests/data/fontVariations.png and /dev/null differ
diff --git a/tests/drawBotScripts/fontVariations.py b/tests/drawBotScripts/fontVariations.py
index 64be8ed1..c9e46375 100644
--- a/tests/drawBotScripts/fontVariations.py
+++ b/tests/drawBotScripts/fontVariations.py
@@ -1,33 +1,30 @@
import drawBot
-drawBot.size(200, 200)
-
+drawBot.newPage(1000, 600)
+# pick a font
drawBot.font("Skia")
-drawBot.fontSize(30)
-
-drawBot.fontVariations(None)
-
+# pick a font size
+drawBot.fontSize(200)
+# list all axis from the current font
variations = drawBot.listFontVariations()
for axisTag in sorted(variations):
data = variations[axisTag]
- # we're rounding the values so we don't trip over small differences between OSes
- data["defaultValue"] = round(float(data["defaultValue"]), 3) # we need floats to make sure that 1 becomes 1.0
+ # round values to avoid small per-OS float differences
+ data["defaultValue"] = round(float(data["defaultValue"]), 3)
data["minValue"] = round(float(data["minValue"]), 3)
data["maxValue"] = round(float(data["maxValue"]), 3)
data["name"] = str(data["name"])
print(axisTag, [(k, data[k]) for k in sorted(data)])
-
-drawBot.text("Hello Q", (20, 170))
+# pick a variation from the current font
drawBot.fontVariations(wght=0.6)
-drawBot.text("Hello Q", (20, 140))
-drawBot.fontVariations(wght=2.4)
-drawBot.text("Hello Q", (20, 110))
-
-drawBot.fontVariations(wdth=1.29)
-drawBot.text("Hello Q", (20, 80))
-
-drawBot.fontVariations(wdth=0.6, resetVariations=True)
-drawBot.text("Hello Q", (20, 50))
-
-drawBot.fontVariations(wght=2.8, resetVariations=False)
-drawBot.text("Hello Q", (20, 20))
+# draw text!!
+drawBot.text("Hello Q", (100, 40))
+# pick a variation from the current font
+drawBot.fontVariations(wght=3, wdth=1.2)
+# draw text!!
+drawBot.text("Hello Q", (100, 220))
+# reset defaults
+drawBot.fontVariations(resetVariations=True)
+drawBot.text("Hello Q", (100, 420))
+
+drawBot.save()
diff --git a/tests/drawBotScripts/fontVariations2.py b/tests/drawBotScripts/fontVariations2.py
index fce770f0..3589be72 100644
--- a/tests/drawBotScripts/fontVariations2.py
+++ b/tests/drawBotScripts/fontVariations2.py
@@ -7,7 +7,7 @@
s.font("Skia")
s.fontSize(30)
s.lineHeight(30)
-s.fontVariations(None)
+s.fontVariations(resetVariations=True)
s.append("Hello Q\n")
s.fontVariations(wght=0.6)
diff --git a/tests/drawBotScripts/openTypeFeatures.py b/tests/drawBotScripts/openTypeFeatures.py
index 8a5a8e77..6ae1c8ec 100644
--- a/tests/drawBotScripts/openTypeFeatures.py
+++ b/tests/drawBotScripts/openTypeFeatures.py
@@ -10,7 +10,7 @@
drawBot.text("Hoefler Fact #123", (20, 170))
-drawBot.openTypeFeatures(None)
+drawBot.openTypeFeatures(resetFeatures=True)
drawBot.openTypeFeatures(dlig=True)
drawBot.text("Hoefler Fact #123", (20, 140))
diff --git a/tests/drawBotScripts/openTypeFeatures2.py b/tests/drawBotScripts/openTypeFeatures2.py
index 2814fd0f..397722e5 100644
--- a/tests/drawBotScripts/openTypeFeatures2.py
+++ b/tests/drawBotScripts/openTypeFeatures2.py
@@ -8,7 +8,7 @@
s.append("Hoefler Fact #123\n")
-s.openTypeFeatures(None)
+s.openTypeFeatures(resetFeatures=True)
s.openTypeFeatures(dlig=True)
s.append("Hoefler Fact #123\n")