Repository: kyamagu/skia-python (language: python)

Issue:
How to underline and strikethrough fonts？
How to underline and strikethrough fonts? Could you indicate me which function or class  I can refer to?
pre-release CI cache management
There is a small recurring issue just before release - because we run `bash scripts/build_Linux.sh` at the end of building the cache, the skia directory is patched with some of our patches. This is fine under normal pulls/commits. But on release, (that's just tagging and build on the same commit), the fact that the cache is patched confused the first line `git config --global core.compression 0 && cd skia && patch -p1 -i ../patch/skia-m128-minimize-download.patch && python tools/git-sync-deps && patch -p1 -R -i ../patch/skia-m128-minimize-download.patch` . I think the first step also needs `if: ${{ steps.cache-skia.outputs.cache-hit != 'true' }}`.

There is a easy work-around: clear the CI cache after the merge, before the tagging.


Discussion / hints:
These two seem to be part of SkPaint in skia which was removed 7 years ago https://github.com/mono/SkiaSharp/issues/391 . I checked there only one reference to these in current skia-python code, which is in the FontMetrics class. The Font class has a getMetrics method which gets it, but I don't think it supports setting. I.e. it looks like with current skia-python, you can try getting at the metrics of your current font of choice, then draw those lines at the returned positions... though I'd be happy to be corrected if you can set underlinewidth/strikeoutwidth to non-zero and get it to work that way.

In current upstream skia code, it seems that the skparagraph module has a text declaration class and a setter for this. So I'll open a new issue for binding skparagraph if one doesn't exist yet.
There seems to be a custom typeface builder class which allows one to change the metrics of a supplied typeface.
https://github.com/google/skia/blob/b7ac9da3e5ac34baf5da70b3e37cdbfd898ed54b/include/utils/SkCustomTypeface.h#L34


Skia-python currently doesn't touch that, I think. Though it is designated SK_API, it is also not documented...
Thanks. I will close this issue.
The custom typeface builder class seems stable (SK_API) so we might add that, besides the bigger skparagraph project.
This is a somewhat difficult to fix issue, not because it is hard, but because it requires experimenting with making releases. Probably ideal use of my own playground for this sort of problems: https://github.com/HinTak/skia-m1xx-python/ . 

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ac293ce1..f104578c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,7 +24,8 @@ jobs:
             skia
           key: linux-aarch64-skia-${{ github.sha }}-3rd-party
       - name: Pre-fetch skia deps
-        run: git config --global core.compression 0 && cd skia && patch -p1 -i ../patch/skia-m128-minimize-download.patch && python tools/git-sync-deps && patch -p1 -R -i ../patch/skia-m128-minimize-download.patch
+        if: ${{ steps.cache-skia.outputs.cache-hit != 'true' }}
+        run: git config --global core.compression 0 && cd skia && patch -p1 -i ../patch/skia-m130-minimize-download.patch && python tools/git-sync-deps && patch -p1 -R -i ../patch/skia-m130-minimize-download.patch
       - name: Set up QEMU
         uses: docker/setup-qemu-action@v3
       - name: Build skia 3rd-Party
@@ -121,7 +122,7 @@ jobs:
           CIBW_BUILD: "${{ matrix.cp }}-*"
           CIBW_SKIP: "*musllinux*"
           CIBW_ARCHS: ${{ matrix.arch }}
-          CIBW_ENVIRONMENT_MACOS: TARGET_ARCH=${{ matrix.arch }} MACOSX_DEPLOYMENT_TARGET=10.13
+          CIBW_ENVIRONMENT_MACOS: TARGET_ARCH=${{ matrix.arch }} MACOSX_DEPLOYMENT_TARGET=11.0
           CIBW_BEFORE_ALL: bash scripts/build_${{ runner.os }}.sh
           CIBW_BEFORE_BUILD: pip install pybind11 numpy
           CIBW_TEST_REQUIRES: pytest pillow glfw
diff --git a/README.md b/README.md
index cc7a5d9b..37b2bfa8 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,8 @@ https://kyamagu.github.io/skia-python
   [README.m117](relnotes/README.m117.md), [README.m118](relnotes/README.m118.md), [README.m119](relnotes/README.m119.md),
   [README.m120](relnotes/README.m120.md), [README.m121](relnotes/README.m121.md), [README.m122](relnotes/README.m122.md),
   [README.m123](relnotes/README.m123.md), [README.m124](relnotes/README.m124.md), [README.m125](relnotes/README.m125.md),
-  [README.m126](relnotes/README.m126.md), [README.m127](relnotes/README.m127.md), [README.m128](relnotes/README.m128.md).
+  [README.m126](relnotes/README.m126.md), [README.m127](relnotes/README.m127.md), [README.m128](relnotes/README.m128.md),
+  [README.m129](relnotes/README.m129.md), [README.m130](relnotes/README.m130.md).
 
 ## Contributing
 
diff --git a/patch/skia-m129-minimize-download.patch b/patch/skia-m129-minimize-download.patch
new file mode 100644
index 00000000..ad81d42d
--- /dev/null
+++ b/patch/skia-m129-minimize-download.patch
@@ -0,0 +1,69 @@
+diff --git a/DEPS b/DEPS
+index 56af0ea..ebb198d 100644
+--- a/DEPS
++++ b/DEPS
+@@ -28,52 +28,18 @@ vars = {
+ #     ./tools/git-sync-deps
+ deps = {
+   "buildtools"                                   : "https://chromium.googlesource.com/chromium/src/buildtools.git@b138e6ce86ae843c42a1a08f37903207bebcca75",
+-  "third_party/externals/angle2"                 : "https://chromium.googlesource.com/angle/angle.git@579a58552fa6beed56f3e32d9b822575d1946b06",
+-  "third_party/externals/brotli"                 : "https://skia.googlesource.com/external/github.com/google/brotli.git@6d03dfbedda1615c4cba1211f8d81735575209c8",
+-  "third_party/externals/d3d12allocator"         : "https://skia.googlesource.com/external/github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator.git@169895d529dfce00390a20e69c2f516066fe7a3b",
+-  # Dawn requires jinja2 and markupsafe for the code generator, tint for SPIRV compilation, and abseil for string formatting.
+-  # When the Dawn revision is updated these should be updated from the Dawn DEPS as well.
+-  "third_party/externals/dawn"                   : "https://dawn.googlesource.com/dawn.git@db1fa936ad0a58846f179c81cdf60f55267099b9",
+-  "third_party/externals/jinja2"                 : "https://chromium.googlesource.com/chromium/src/third_party/jinja2@e2d024354e11cc6b041b0cff032d73f0c7e43a07",
+-  "third_party/externals/markupsafe"             : "https://chromium.googlesource.com/chromium/src/third_party/markupsafe@0bad08bb207bbfc1d6f3bbc82b9242b0c50e5794",
+-  "third_party/externals/abseil-cpp"             : "https://skia.googlesource.com/external/github.com/abseil/abseil-cpp.git@65a55c2ba891f6d2492477707f4a2e327a0b40dc",
+   "third_party/externals/dng_sdk"                : "https://android.googlesource.com/platform/external/dng_sdk.git@c8d0c9b1d16bfda56f15165d39e0ffa360a11123",
+-  "third_party/externals/egl-registry"           : "https://skia.googlesource.com/external/github.com/KhronosGroup/EGL-Registry@b055c9b483e70ecd57b3cf7204db21f5a06f9ffe",
+-  "third_party/externals/emsdk"                  : "https://skia.googlesource.com/external/github.com/emscripten-core/emsdk.git@a896e3d066448b3530dbcaa48869fafefd738f57",
+   "third_party/externals/expat"                  : "https://chromium.googlesource.com/external/github.com/libexpat/libexpat.git@441f98d02deafd9b090aea568282b28f66a50e36",
+   "third_party/externals/freetype"               : "https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@73720c7c9958e87b3d134a7574d1720ad2d24442",
+   "third_party/externals/harfbuzz"               : "https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@b74a7ecc93e283d059df51ee4f46961a782bcdb8",
+-  "third_party/externals/highway"                : "https://chromium.googlesource.com/external/github.com/google/highway.git@424360251cdcfc314cfc528f53c872ecd63af0f0",
+   "third_party/externals/icu"                    : "https://chromium.googlesource.com/chromium/deps/icu.git@364118a1d9da24bb5b770ac3d762ac144d6da5a4",
+-  "third_party/externals/icu4x"                  : "https://chromium.googlesource.com/external/github.com/unicode-org/icu4x.git@bcf4f7198d4dc5f3127e84a6ca657c88e7d07a13",
+-  "third_party/externals/imgui"                  : "https://skia.googlesource.com/external/github.com/ocornut/imgui.git@55d35d8387c15bf0cfd71861df67af8cfbda7456",
+-  "third_party/externals/libavif"                : "https://skia.googlesource.com/external/github.com/AOMediaCodec/libavif.git@55aab4ac0607ab651055d354d64c4615cf3d8000",
+-  "third_party/externals/libgav1"                : "https://chromium.googlesource.com/codecs/libgav1.git@5cf722e659014ebaf2f573a6dd935116d36eadf1",
+-  "third_party/externals/libgrapheme"            : "https://skia.googlesource.com/external/github.com/FRIGN/libgrapheme/@c0cab63c5300fa12284194fbef57aa2ed62a94c0",
+   "third_party/externals/libjpeg-turbo"          : "https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@ccfbe1c82a3b6dbe8647ceb36a3f9ee711fba3cf",
+-  "third_party/externals/libjxl"                 : "https://chromium.googlesource.com/external/gitlab.com/wg1/jpeg-xl.git@a205468bc5d3a353fb15dae2398a101dff52f2d3",
+   "third_party/externals/libpng"                 : "https://skia.googlesource.com/third_party/libpng.git@ed217e3e601d8e462f7fd1e04bed43ac42212429",
+   "third_party/externals/libwebp"                : "https://chromium.googlesource.com/webm/libwebp.git@845d5476a866141ba35ac133f856fa62f0b7445f",
+-  "third_party/externals/libyuv"                 : "https://chromium.googlesource.com/libyuv/libyuv.git@d248929c059ff7629a85333699717d7a677d8d96",
+-  "third_party/externals/microhttpd"             : "https://android.googlesource.com/platform/external/libmicrohttpd@748945ec6f1c67b7efc934ab0808e1d32f2fb98d",
+-  "third_party/externals/oboe"                   : "https://chromium.googlesource.com/external/github.com/google/oboe.git@b02a12d1dd821118763debec6b83d00a8a0ee419",
+-  "third_party/externals/opengl-registry"        : "https://skia.googlesource.com/external/github.com/KhronosGroup/OpenGL-Registry@14b80ebeab022b2c78f84a573f01028c96075553",
+-  "third_party/externals/perfetto"               : "https://android.googlesource.com/platform/external/perfetto@93885509be1c9240bc55fa515ceb34811e54a394",
+   "third_party/externals/piex"                   : "https://android.googlesource.com/platform/external/piex.git@bb217acdca1cc0c16b704669dd6f91a1b509c406",
+-  "third_party/externals/swiftshader"            : "https://swiftshader.googlesource.com/SwiftShader@65157d32945d9a75fc9a657e878a1b2f61342f03",
+   "third_party/externals/vulkanmemoryallocator"  : "https://chromium.googlesource.com/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator@a6bfc237255a6bac1513f7c1ebde6d8aed6b5191",
+-  # vulkan-deps is a meta-repo containing several interdependent Khronos Vulkan repositories.
+-  # When the vulkan-deps revision is updated, those repos (spirv-*, vulkan-*) should be updated as well.
+   "third_party/externals/vulkan-deps"            : "https://chromium.googlesource.com/vulkan-deps@b038f07b8faf4d057b1a38fdf90e9dfd7238ac16",
+-  "third_party/externals/spirv-cross"            : "https://chromium.googlesource.com/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3",
+   "third_party/externals/spirv-headers"          : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Headers.git@1b75a4ae0b4289014b4c369301dc925c366f78a6",
+-  "third_party/externals/spirv-tools"            : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Tools.git@87fcbaf1bc8346469e178711eff27cfd20aa1960",
+-  "third_party/externals/vello"                  : "https://skia.googlesource.com/external/github.com/linebender/vello.git@3ee3bea02164c5a816fe6c16ef4e3a810edb7620",
+-  "third_party/externals/vulkan-headers"         : "https://chromium.googlesource.com/external/github.com/KhronosGroup/Vulkan-Headers@d205aff40b4e15d4c568523ee6a26f85138126d9",
+-  "third_party/externals/vulkan-tools"           : "https://chromium.googlesource.com/external/github.com/KhronosGroup/Vulkan-Tools@494d32f2da0bd8a782d88fdaa98b9e1967148d1b",
+-  "third_party/externals/vulkan-utility-libraries": "https://chromium.googlesource.com/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@9b6e18888be3ac761a3f71594c3949f8dc862ccc",
+-  "third_party/externals/unicodetools"           : "https://chromium.googlesource.com/external/github.com/unicode-org/unicodetools@66a3fa9dbdca3b67053a483d130564eabc5fe095",
+-  #"third_party/externals/v8"                     : "https://chromium.googlesource.com/v8/v8.git@5f1ae66d5634e43563b2d25ea652dfb94c31a3b4",
+   "third_party/externals/wuffs"                  : "https://skia.googlesource.com/external/github.com/google/wuffs-mirror-release-c.git@e3f919ccfe3ef542cfc983a82146070258fb57f8",
+   "third_party/externals/zlib"                   : "https://chromium.googlesource.com/chromium/src/third_party/zlib@646b7f569718921d7d4b5b8e22572ff6c76f2596",
+ 
+diff --git a/bin/activate-emsdk b/bin/activate-emsdk
+index 687ca9f..7167d8d 100755
+--- a/bin/activate-emsdk
++++ b/bin/activate-emsdk
+@@ -17,6 +17,7 @@ EMSDK_PATH = os.path.join(EMSDK_ROOT, 'emsdk.py')
+ EMSDK_VERSION = '3.1.44'
+ 
+ def main():
++    return
+     if sysconfig.get_platform() in ['linux-aarch64', 'linux-arm64']:
+         # This platform cannot install emsdk at the provided version. See
+         # https://github.com/emscripten-core/emsdk/blob/main/emscripten-releases-tags.json#L5
diff --git a/patch/skia-m130-minimize-download.patch b/patch/skia-m130-minimize-download.patch
new file mode 100644
index 00000000..263b2985
--- /dev/null
+++ b/patch/skia-m130-minimize-download.patch
@@ -0,0 +1,69 @@
+diff --git a/DEPS b/DEPS
+index c04853d..e2dee70 100644
+--- a/DEPS
++++ b/DEPS
+@@ -31,52 +31,18 @@ vars = {
+ #     ./tools/git-sync-deps
+ deps = {
+   "buildtools"                                   : "https://chromium.googlesource.com/chromium/src/buildtools.git@b138e6ce86ae843c42a1a08f37903207bebcca75",
+-  "third_party/externals/angle2"                 : "https://chromium.googlesource.com/angle/angle.git@f6d9b179eb8303fdbb954099da89c2e106b04edb",
+-  "third_party/externals/brotli"                 : "https://skia.googlesource.com/external/github.com/google/brotli.git@6d03dfbedda1615c4cba1211f8d81735575209c8",
+-  "third_party/externals/d3d12allocator"         : "https://skia.googlesource.com/external/github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator.git@169895d529dfce00390a20e69c2f516066fe7a3b",
+-  # Dawn requires jinja2 and markupsafe for the code generator, tint for SPIRV compilation, and abseil for string formatting.
+-  # When the Dawn revision is updated these should be updated from the Dawn DEPS as well.
+-  "third_party/externals/dawn"                   : "https://dawn.googlesource.com/dawn.git@2e8afd5a4962750ee1d3cdf1663596b697044d5c",
+-  "third_party/externals/jinja2"                 : "https://chromium.googlesource.com/chromium/src/third_party/jinja2@e2d024354e11cc6b041b0cff032d73f0c7e43a07",
+-  "third_party/externals/markupsafe"             : "https://chromium.googlesource.com/chromium/src/third_party/markupsafe@0bad08bb207bbfc1d6f3bbc82b9242b0c50e5794",
+-  "third_party/externals/abseil-cpp"             : "https://skia.googlesource.com/external/github.com/abseil/abseil-cpp.git@65a55c2ba891f6d2492477707f4a2e327a0b40dc",
+   "third_party/externals/dng_sdk"                : "https://android.googlesource.com/platform/external/dng_sdk.git@c8d0c9b1d16bfda56f15165d39e0ffa360a11123",
+-  "third_party/externals/egl-registry"           : "https://skia.googlesource.com/external/github.com/KhronosGroup/EGL-Registry@b055c9b483e70ecd57b3cf7204db21f5a06f9ffe",
+-  "third_party/externals/emsdk"                  : "https://skia.googlesource.com/external/github.com/emscripten-core/emsdk.git@a896e3d066448b3530dbcaa48869fafefd738f57",
+   "third_party/externals/expat"                  : "https://chromium.googlesource.com/external/github.com/libexpat/libexpat.git@624da0f593bb8d7e146b9f42b06d8e6c80d032a3",
+   "third_party/externals/freetype"               : "https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@83af801b552111e37d9466a887e1783a0fb5f196",
+   "third_party/externals/harfbuzz"               : "https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@a070f9ebbe88dc71b248af9731dd49ec93f4e6e6",
+-  "third_party/externals/highway"                : "https://chromium.googlesource.com/external/github.com/google/highway.git@424360251cdcfc314cfc528f53c872ecd63af0f0",
+   "third_party/externals/icu"                    : "https://chromium.googlesource.com/chromium/deps/icu.git@364118a1d9da24bb5b770ac3d762ac144d6da5a4",
+-  "third_party/externals/icu4x"                  : "https://chromium.googlesource.com/external/github.com/unicode-org/icu4x.git@bcf4f7198d4dc5f3127e84a6ca657c88e7d07a13",
+-  "third_party/externals/imgui"                  : "https://skia.googlesource.com/external/github.com/ocornut/imgui.git@55d35d8387c15bf0cfd71861df67af8cfbda7456",
+-  "third_party/externals/libavif"                : "https://skia.googlesource.com/external/github.com/AOMediaCodec/libavif.git@55aab4ac0607ab651055d354d64c4615cf3d8000",
+-  "third_party/externals/libgav1"                : "https://chromium.googlesource.com/codecs/libgav1.git@5cf722e659014ebaf2f573a6dd935116d36eadf1",
+-  "third_party/externals/libgrapheme"            : "https://skia.googlesource.com/external/github.com/FRIGN/libgrapheme/@c0cab63c5300fa12284194fbef57aa2ed62a94c0",
+   "third_party/externals/libjpeg-turbo"          : "https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@ccfbe1c82a3b6dbe8647ceb36a3f9ee711fba3cf",
+-  "third_party/externals/libjxl"                 : "https://chromium.googlesource.com/external/gitlab.com/wg1/jpeg-xl.git@a205468bc5d3a353fb15dae2398a101dff52f2d3",
+   "third_party/externals/libpng"                 : "https://skia.googlesource.com/third_party/libpng.git@ed217e3e601d8e462f7fd1e04bed43ac42212429",
+   "third_party/externals/libwebp"                : "https://chromium.googlesource.com/webm/libwebp.git@845d5476a866141ba35ac133f856fa62f0b7445f",
+-  "third_party/externals/libyuv"                 : "https://chromium.googlesource.com/libyuv/libyuv.git@d248929c059ff7629a85333699717d7a677d8d96",
+-  "third_party/externals/microhttpd"             : "https://android.googlesource.com/platform/external/libmicrohttpd@748945ec6f1c67b7efc934ab0808e1d32f2fb98d",
+-  "third_party/externals/oboe"                   : "https://chromium.googlesource.com/external/github.com/google/oboe.git@b02a12d1dd821118763debec6b83d00a8a0ee419",
+-  "third_party/externals/opengl-registry"        : "https://skia.googlesource.com/external/github.com/KhronosGroup/OpenGL-Registry@14b80ebeab022b2c78f84a573f01028c96075553",
+-  "third_party/externals/perfetto"               : "https://android.googlesource.com/platform/external/perfetto@93885509be1c9240bc55fa515ceb34811e54a394",
+   "third_party/externals/piex"                   : "https://android.googlesource.com/platform/external/piex.git@bb217acdca1cc0c16b704669dd6f91a1b509c406",
+-  "third_party/externals/swiftshader"            : "https://swiftshader.googlesource.com/SwiftShader@2afc8c97882a5c66abf5f26670ae420d2e30adc3",
+   "third_party/externals/vulkanmemoryallocator"  : "https://chromium.googlesource.com/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator@a6bfc237255a6bac1513f7c1ebde6d8aed6b5191",
+-  # vulkan-deps is a meta-repo containing several interdependent Khronos Vulkan repositories.
+-  # When the vulkan-deps revision is updated, those repos (spirv-*, vulkan-*) should be updated as well.
+   "third_party/externals/vulkan-deps"            : "https://chromium.googlesource.com/vulkan-deps@4a69b8e2bcddf68b543487c2d5e2b34bf5076338",
+-  "third_party/externals/spirv-cross"            : "https://chromium.googlesource.com/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3",
+   "third_party/externals/spirv-headers"          : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Headers.git@2a9b6f951c7d6b04b6c21fe1bf3f475b68b84801",
+-  "third_party/externals/spirv-tools"            : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Tools.git@a2c9c2387e97ac47ec3b1f508dd1f9f354e023cc",
+-  "third_party/externals/vello"                  : "https://skia.googlesource.com/external/github.com/linebender/vello.git@3ee3bea02164c5a816fe6c16ef4e3a810edb7620",
+-  "third_party/externals/vulkan-headers"         : "https://chromium.googlesource.com/external/github.com/KhronosGroup/Vulkan-Headers@c6391a7b8cd57e79ce6b6c832c8e3043c4d9967b",
+-  "third_party/externals/vulkan-tools"           : "https://chromium.googlesource.com/external/github.com/KhronosGroup/Vulkan-Tools@4c63e845962ff3b197855f3ae4907a47d0863f5a",
+-  "third_party/externals/vulkan-utility-libraries": "https://chromium.googlesource.com/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@eb62fb38a538cde41b1180ec182ba923455a49ee",
+-  "third_party/externals/unicodetools"           : "https://chromium.googlesource.com/external/github.com/unicode-org/unicodetools@66a3fa9dbdca3b67053a483d130564eabc5fe095",
+-  #"third_party/externals/v8"                     : "https://chromium.googlesource.com/v8/v8.git@5f1ae66d5634e43563b2d25ea652dfb94c31a3b4",
+   "third_party/externals/wuffs"                  : "https://skia.googlesource.com/external/github.com/google/wuffs-mirror-release-c.git@e3f919ccfe3ef542cfc983a82146070258fb57f8",
+   "third_party/externals/zlib"                   : "https://chromium.googlesource.com/chromium/src/third_party/zlib@646b7f569718921d7d4b5b8e22572ff6c76f2596",
+ 
+diff --git a/bin/activate-emsdk b/bin/activate-emsdk
+index 687ca9f..7167d8d 100755
+--- a/bin/activate-emsdk
++++ b/bin/activate-emsdk
+@@ -17,6 +17,7 @@ EMSDK_PATH = os.path.join(EMSDK_ROOT, 'emsdk.py')
+ EMSDK_VERSION = '3.1.44'
+ 
+ def main():
++    return
+     if sysconfig.get_platform() in ['linux-aarch64', 'linux-arm64']:
+         # This platform cannot install emsdk at the provided version. See
+         # https://github.com/emscripten-core/emsdk/blob/main/emscripten-releases-tags.json#L5
diff --git a/relnotes/README.m129.md b/relnotes/README.m129.md
new file mode 100644
index 00000000..7717a755
--- /dev/null
+++ b/relnotes/README.m129.md
@@ -0,0 +1,2 @@
+No user-visible change between m128 and m129
+(Small Update against upstream clean-up of GPU-related header files)
diff --git a/relnotes/README.m130.md b/relnotes/README.m130.md
new file mode 100644
index 00000000..fa8bc7ba
--- /dev/null
+++ b/relnotes/README.m130.md
@@ -0,0 +1,35 @@
+Since m129:
+
+* Upstream removed `SkColorFilter::filterColor`, so `ColorFilter.filterColor` is now emulated.
+
+
+Since m128 (last beta release):
+
+* We now build for Mac OS 11.0 (instead of 10.13; github CI warning)
+
+* We now bind uptream's experimental `skparagraph` module and the `SkParagraph` class,
+  to provide multi-line text paragraph layout.
+  The functionality is under the `skia.textlayout` namespace, as `Paragraph`, etc.
+
+  * There is a `FontMgr.OneFontMgr()` method which takes a font file or data, which returns
+    a font manager having knowledge of exactly one font. `FontMgr.New_Custom_Empty()` has been
+    overloaded as an alias to this, too.
+
+  * The `SkUnicode` class is now available under python as `skia.Unicode`.
+    The constructor is known to fail on windows - It is likely that downloading
+    a `icudtl.dat` file, renaming from the versioned data-bin-{l,b}.zip in
+    https://github.com/unicode-org/icu/releases, is needed. Windows users please report
+    success/failure on this.
+
+  * There are two examples `shape_text.py` (a python port of upstream's example), and
+    `skparagraph-example.py` hosted [elsewhere](https://github.com/HinTak/skia-python-examples/).
+
+  * Note that the entire `skparagraph` module is still experimental and subjected to change.
+    Font choices on Linux are sensitive to LANG and FC_LANG, and you may need to set/unset
+    them for desired outcome.
+    See [filed issue upstream](https://issues.skia.org/361963992) for details and updates.
+
+* Improved preview of default arguments in function signatures
+
+* Some parametric tests involving `skia.SurfaceProps` removed during the m87->m116 changes
+  are re-added.
diff --git a/scripts/build_Linux.sh b/scripts/build_Linux.sh
index eb04a210..8fc6efca 100644
--- a/scripts/build_Linux.sh
+++ b/scripts/build_Linux.sh
@@ -60,7 +60,7 @@ git clone https://gn.googlesource.com/gn && \
 
 # Build skia
 cd skia && \
-    patch -p1 < ../patch/skia-m128-minimize-download.patch && \
+    patch -p1 < ../patch/skia-m130-minimize-download.patch && \
     patch -p1 < ../patch/skia-m123-colrv1-freetype.diff && \
     python3 tools/git-sync-deps && \
     cp -f ../gn/out/gn bin/gn && \
diff --git a/scripts/build_Windows.sh b/scripts/build_Windows.sh
index b36fd733..ce694e46 100644
--- a/scripts/build_Windows.sh
+++ b/scripts/build_Windows.sh
@@ -4,7 +4,7 @@ export PATH="${PWD}/depot_tools:$PATH"
 
 # Build skia
 cd skia && \
-    patch -p1 < ../patch/skia-m128-minimize-download.patch && \
+    patch -p1 < ../patch/skia-m130-minimize-download.patch && \
     patch -p1 < ../patch/skia-m123-colrv1-freetype.diff && \
     python tools/git-sync-deps && \
     bin/gn gen out/Release --args='
diff --git a/scripts/build_macOS.sh b/scripts/build_macOS.sh
index 2c9afa38..666d1fdb 100644
--- a/scripts/build_macOS.sh
+++ b/scripts/build_macOS.sh
@@ -22,7 +22,7 @@ function apply_patch {
 }
 
 cd skia && \
-    patch -p1 < ../patch/skia-m128-minimize-download.patch && \
+    patch -p1 < ../patch/skia-m130-minimize-download.patch && \
     patch -p1 < ../patch/skia-m123-colrv1-freetype.diff && \
     python3 tools/git-sync-deps && \
     bin/gn gen out/Release --args="
diff --git a/setup.py b/setup.py
index 9bcd5797..e4494234 100644
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,7 @@
     pass
 
 NAME = 'skia-python'
-__version__ = '128.0b9'
+__version__ = '130.0b10'
 
 SKIA_PATH = os.getenv('SKIA_PATH', 'skia')
 SKIA_OUT_PATH = os.getenv(
@@ -33,7 +33,7 @@
     ]
     EXTRA_OBJECTS = list(
     ) + [os.path.join(SKIA_OUT_PATH, 'svg.lib'), os.path.join(SKIA_OUT_PATH, 'skresources.lib'), os.path.join(SKIA_OUT_PATH, 'skia.lib'),
-         os.path.join(SKIA_OUT_PATH, 'skshaper.lib'),
+         os.path.join(SKIA_OUT_PATH, 'skparagraph.lib'), os.path.join(SKIA_OUT_PATH, 'skshaper.lib'),
          os.path.join(SKIA_OUT_PATH, 'skunicode_icu.lib'), os.path.join(SKIA_OUT_PATH, 'skunicode_core.lib')]
     EXTRA_COMPILE_ARGS = [
         '/std:c++17',  # c++20 fails.
@@ -66,17 +66,17 @@
     ]
     EXTRA_OBJECTS = list(
     ) + [os.path.join(SKIA_OUT_PATH, 'libsvg.a'), os.path.join(SKIA_OUT_PATH, 'libskia.a'),
-         os.path.join(SKIA_OUT_PATH, 'libskshaper.a'),
+         os.path.join(SKIA_OUT_PATH, 'libskparagraph.a'), os.path.join(SKIA_OUT_PATH, 'libskshaper.a'),
          os.path.join(SKIA_OUT_PATH, 'libskunicode_icu.a'), os.path.join(SKIA_OUT_PATH, 'libskunicode_core.a')]
     EXTRA_COMPILE_ARGS = [
         '-std=c++17',
         '-stdlib=libc++',
-        '-mmacosx-version-min=10.13',
+        '-mmacosx-version-min=11.0',
         '-fvisibility=hidden',
     ]
     EXTRA_LINK_ARGS = [
         '-stdlib=libc++',
-        '-mmacosx-version-min=10.13',
+        '-mmacosx-version-min=11.0',
         '-dead_strip',
         '-framework',
         'AppKit',
@@ -100,7 +100,7 @@
     ]
     EXTRA_OBJECTS = list(
     ) + [os.path.join(SKIA_OUT_PATH, 'libsvg.a'), os.path.join(SKIA_OUT_PATH, 'libskresources.a'), os.path.join(SKIA_OUT_PATH, 'libskia.a'),
-         os.path.join(SKIA_OUT_PATH, 'libskshaper.a'),
+         os.path.join(SKIA_OUT_PATH, 'libskparagraph.a'), os.path.join(SKIA_OUT_PATH, 'libskshaper.a'),
          os.path.join(SKIA_OUT_PATH, 'libskunicode_icu.a'), os.path.join(SKIA_OUT_PATH, 'libskunicode_core.a')]
     EXTRA_COMPILE_ARGS = [
         '-std=c++17',
diff --git a/skia b/skia
index 5f2abc61..0f81dc8b 160000
--- a/skia
+++ b/skia
@@ -1,1 +1,1 @@
-Subproject commit 5f2abc61e21e9668fa48221d2e5e8c6f09bd929b
+Subproject commit 0f81dc8b2e43c060972a8d8d229b346694a674f8
diff --git a/src/skia/Bitmap.cpp b/src/skia/Bitmap.cpp
index 1fd481e0..1f11020d 100644
--- a/src/skia/Bitmap.cpp
+++ b/src/skia/Bitmap.cpp
@@ -1028,8 +1028,10 @@ bitmap
     .def("makeShader",
         py::overload_cast<SkTileMode, SkTileMode, const SkSamplingOptions&, const SkMatrix*>(
             &SkBitmap::makeShader, py::const_),
-        py::arg("tmx") = SkTileMode::kClamp,
-        py::arg("tmy") = SkTileMode::kClamp, py::arg("sampling") = SkSamplingOptions(),  py::arg("localMatrix") = nullptr)
+        py::arg_v("tmx", SkTileMode::kClamp, "skia.TileMode.kClamp"),
+        py::arg_v("tmy", SkTileMode::kClamp, "skia.TileMode.kClamp"),
+        py::arg_v("sampling", SkSamplingOptions(), "skia.SamplingOptions()"),
+        py::arg("localMatrix") = nullptr)
     ;
 
 
diff --git a/src/skia/Canvas.cpp b/src/skia/Canvas.cpp
index 7c1c2f16..b9fc369a 100644
--- a/src/skia/Canvas.cpp
+++ b/src/skia/Canvas.cpp
@@ -294,8 +294,9 @@ canvas
         :alphaType: alpha type of the array
         :colorSpace: range of colors; may be nullptr
         )docstring",
-        py::arg("array"), py::arg("colorType") = kN32_SkColorType,
-        py::arg("alphaType") = kUnpremul_SkAlphaType,
+        py::arg("array"),
+        py::arg_v("colorType", kN32_SkColorType, "skia.ColorType.kN32_ColorType"),
+        py::arg_v("alphaType", kUnpremul_SkAlphaType, "skia.AlphaType.kUnpremul_AlphaType"),
         py::arg("colorSpace") = nullptr, py::arg("surfaceProps") = nullptr)
     .def(py::init<int, int, const SkSurfaceProps*>(),
         R"docstring(
@@ -361,8 +362,8 @@ canvas
         :return: numpy.ndarray
         )docstring",
         py::arg("srcX") = 0, py::arg("srcY") = 0,
-        py::arg("colorType") = kUnknown_SkColorType,
-        py::arg("alphaType") = kUnpremul_SkAlphaType,
+        py::arg_v("colorType", kUnknown_SkColorType, "skia.ColorType.kUnknown_ColorType"),
+        py::arg_v("alphaType", kUnpremul_SkAlphaType, "skia.AlphaType.kUnpremul_AlphaType"),
         py::arg("colorSpace") = nullptr)
     .def("imageInfo", &SkCanvas::imageInfo,
         R"docstring(
@@ -1169,7 +1170,8 @@ canvas
         :param skia.Region deviceRgn: :py:class:`Region` to combine with clip
         :param skia.ClipOp op: :py:class:`ClipOp` to apply to clip
         )docstring",
-        py::arg("deviceRgn"), py::arg("op") = SkClipOp::kIntersect)
+        py::arg("deviceRgn"),
+        py::arg_v("op", SkClipOp::kIntersect, "skia.ClipOp.kIntersect"))
     .def("quickReject",
         py::overload_cast<const SkRect&>(&SkCanvas::quickReject, py::const_),
         R"docstring(
@@ -1271,7 +1273,8 @@ canvas
         :param skia.BlendMode mode: :py:class:`BlendMode` used to combine source
             color and destination
         )docstring",
-        py::arg("color"), py::arg("mode") = SkBlendMode::kSrcOver)
+        py::arg("color"),
+        py::arg_v("mode", SkBlendMode::kSrcOver, "skia.BlendMode.kSrcOver"))
     .def("drawColor",
         py::overload_cast<const SkColor4f&, SkBlendMode>(&SkCanvas::drawColor),
         R"docstring(
@@ -1283,7 +1286,8 @@ canvas
         :param skia.BlendMode mode: :py:class:`BlendMode` used to combine source
             color and destination
         )docstring",
-        py::arg("color"), py::arg("mode") = SkBlendMode::kSrcOver)
+        py::arg("color"),
+        py::arg_v("mode", SkBlendMode::kSrcOver, "skia.BlendMode.kSrcOver"))
     .def("clear",
         py::overload_cast<SkColor>(&SkCanvas::clear),
         R"docstring(
@@ -1433,7 +1437,7 @@ canvas
         :y1: end of line segment on y-axis
         :paint: stroke, blend, color, and so on, used to draw
         )docstring",
-        py::arg("x0"), py::arg("y0"), py::arg("x1"), py::arg("y1`"),
+        py::arg("x0"), py::arg("y0"), py::arg("x1"), py::arg("y1"),
         py::arg("paint"))
     .def("drawLine",
         py::overload_cast<SkPoint, SkPoint, const SkPaint&>(
@@ -1673,7 +1677,7 @@ canvas
             nullptr
         )docstring",
         py::arg("image"), py::arg("left"), py::arg("top"),
-        py::arg("options") = SkSamplingOptions(),
+        py::arg_v("options", SkSamplingOptions(), "skia.SamplingOptions()"),
         py::arg("paint") = nullptr)
     // .def("drawImage",
     //     py::overload_cast<const sk_sp<SkImage>&, SkScalar, SkScalar,
@@ -1722,9 +1726,10 @@ canvas
         :constraint: filter strictly within src or draw faster
         )docstring",
         py::arg("image"), py::arg("src"), py::arg("dst"),
-        py::arg("options") = SkSamplingOptions(),
-        py::arg("paint") = nullptr, py::arg("constraint") =
-            SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint)
+        py::arg_v("options", SkSamplingOptions(), "skia.SamplingOptions()"),
+        py::arg("paint") = nullptr,
+        py::arg_v("constraint", SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint,
+            "skia.Canvas.SrcRectConstraint.kStrict_SrcRectConstraint"))
     .def("drawImageRect",
         py::overload_cast<const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&,
             const SkPaint*, SkCanvas::SrcRectConstraint>(
@@ -1767,9 +1772,10 @@ canvas
         :constraint: filter strictly within isrc or draw faster
         )docstring",
         py::arg("image"), py::arg("isrc"), py::arg("dst"),
-        py::arg("options") = SkSamplingOptions(),
-        py::arg("paint") = nullptr, py::arg("constraint") =
-            SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint)
+        py::arg_v("options", SkSamplingOptions(), "skia.SamplingOptions()"),
+        py::arg("paint") = nullptr,
+        py::arg_v("constraint", SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint,
+            "skia.Canvas.SrcRectConstraint.kStrict_SrcRectConstraint"))
     .def("drawImageRect",
         py::overload_cast<const SkImage*, const SkRect&, const SkSamplingOptions&, const SkPaint*>(
             &SkCanvas::drawImageRect),
@@ -1801,7 +1807,9 @@ canvas
             nullptr
         :constraint: filter strictly within src or draw faster
         )docstring",
-        py::arg("image"), py::arg("dst"), py::arg("options") = SkSamplingOptions(), py::arg("paint") = nullptr)
+        py::arg("image"), py::arg("dst"),
+        py::arg_v("options", SkSamplingOptions(), "skia.SamplingOptions()"),
+        py::arg("paint") = nullptr)
     // .def("drawImageRect",
     //     py::overload_cast<const sk_sp<SkImage>&, const SkRect&, const SkRect&,
     //         const SkPaint*, SkCanvas::SrcRectConstraint>(
@@ -1942,8 +1950,9 @@ canvas
         :constraint: filter strictly within src or draw faster
         )docstring",
         py::arg("bitmap"), py::arg("src"), py::arg("dst"),
-        py::arg("paint") = nullptr, py::arg("constraint") =
-            SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint)
+        py::arg("paint") = nullptr,
+        py::arg_v("constraint", SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint,
+            "skia.Canvas.SrcRectConstraint.kStrict_SrcRectConstraint"))
     .def("drawBitmapRect",
         [] (SkCanvas &self, const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
             const SkPaint* paint,
@@ -1982,8 +1991,9 @@ canvas
         :constraint: filter strictly within isrc or draw faster
         )docstring",
         py::arg("bitmap"), py::arg("isrc"), py::arg("dst"),
-        py::arg("paint") = nullptr, py::arg("constraint") =
-            SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint)
+        py::arg("paint") = nullptr,
+        py::arg_v("constraint", SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint,
+            "skia.Canvas.SrcRectConstraint.kStrict_SrcRectConstraint"))
     .def("drawBitmapRect",
         [] (SkCanvas &self, const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
             SkCanvas::SrcRectConstraint constraint) {
@@ -2021,8 +2031,8 @@ canvas
         :constraint: filter strictly within bitmap or draw faster
         )docstring",
         py::arg("bitmap"), py::arg("dst"), py::arg("paint") = nullptr,
-        py::arg("constraint") =
-            SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint)
+        py::arg_v("constraint", SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint,
+            "skia.Canvas.SrcRectConstraint.kStrict_SrcRectConstraint"))
     // .def("drawImageLattice", &SkCanvas::drawImageLattice,
     //     "Draws SkImage image stretched proportionally to fit into SkRect dst.")
     // .def("experimental_DrawEdgeAAQuad",
@@ -2207,7 +2217,7 @@ canvas
             :py:class:`Vertices` texture
         )docstring",
         py::arg("vertices"), py::arg("paint"),
-        py::arg("mode") = SkBlendMode::kModulate)
+        py::arg_v("mode", SkBlendMode::kModulate, "skia.BlendMode.kModulate"))
     // .def("drawVertices",
     //     py::overload_cast<const SkVertices*, const SkPaint&>(
     //         &SkCanvas::drawVertices),
@@ -2337,7 +2347,9 @@ canvas
             `None`
         )docstring",
         py::arg("atlas"), py::arg("xform"), py::arg("tex"), py::arg("colors"),
-        py::arg("mode"), py::arg("options") = SkSamplingOptions(), py::arg("cullRect") = nullptr,
+        py::arg("mode"),
+        py::arg_v("options", SkSamplingOptions(), "skia.SamplingOptions()"),
+        py::arg("cullRect") = nullptr,
         py::arg("paint") = nullptr)
     // .def("drawAtlas",
     //     py::overload_cast<const sk_sp<SkImage>&, const SkRSXform[],
diff --git a/src/skia/ColorFilter.cpp b/src/skia/ColorFilter.cpp
index 795e493c..0002087f 100644
--- a/src/skia/ColorFilter.cpp
+++ b/src/skia/ColorFilter.cpp
@@ -100,7 +100,13 @@ colorfilter
         Override in subclasses to return custom flags.
         )docstring")
 */
-    .def("filterColor", &SkColorFilter::filterColor, py::arg("color"))
+    .def("filterColor",
+        [] (const SkColorFilter& colorFilter, SkColor c) -> SkColor const {
+            // This is mostly meaningless. We should phase-out this call entirely.
+            SkColorSpace* cs = nullptr;
+            return colorFilter.filterColor4f(SkColor4f::FromColor(c), cs, cs).toSkColor();
+        },
+        py::arg("color"))
     .def("filterColor4f", &SkColorFilter::filterColor4f,
         R"docstring(
         Converts the src color (in src colorspace), into the dst colorspace,
diff --git a/src/skia/Font.cpp b/src/skia/Font.cpp
index dcbb9a50..307ff100 100644
--- a/src/skia/Font.cpp
+++ b/src/skia/Font.cpp
@@ -94,6 +94,108 @@ sk_sp<SkTypeface> SkTypeface_MakeFromName(
 
 }  // namespace
 
+namespace {
+
+/* class OneFontStyleSet and OneFontMgr verbatim from
+   skia's chrome/m128:example/external_client/src/shape_text.cpp
+ */
+class OneFontStyleSet : public SkFontStyleSet {
+ public:
+  explicit OneFontStyleSet(sk_sp<SkTypeface> face) : face_(face) {}
+
+ protected:
+  int count() override { return 1; }
+  void getStyle(int, SkFontStyle* out_style, SkString*) override {
+    *out_style = SkFontStyle();
+  }
+  sk_sp<SkTypeface> createTypeface(int index) override { return face_; }
+  sk_sp<SkTypeface> matchStyle(const SkFontStyle&) override { return face_; }
+
+ private:
+  sk_sp<SkTypeface> face_;
+};
+
+class OneFontMgr : public SkFontMgr {
+ public:
+  explicit OneFontMgr(sk_sp<SkTypeface> face)
+      : face_(face), style_set_(sk_make_sp<OneFontStyleSet>(face)) {}
+
+ protected:
+  int onCountFamilies() const override { return 1; }
+  void onGetFamilyName(int index, SkString* familyName) const override {
+    *familyName = SkString("the-only-font-I-have");
+  }
+  sk_sp<SkFontStyleSet> onCreateStyleSet(int index) const override {
+    return style_set_;
+  }
+  sk_sp<SkFontStyleSet> onMatchFamily(const char[]) const override {
+    return style_set_;
+  }
+
+  sk_sp<SkTypeface> onMatchFamilyStyle(const char[],
+                                       const SkFontStyle&) const override {
+    return face_;
+  }
+  sk_sp<SkTypeface> onMatchFamilyStyleCharacter(
+      const char familyName[], const SkFontStyle& style, const char* bcp47[],
+      int bcp47Count, SkUnichar character) const override {
+    return face_;
+  }
+  sk_sp<SkTypeface> onLegacyMakeTypeface(const char[],
+                                         SkFontStyle) const override {
+    return face_;
+  }
+
+  sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int) const override {
+    std::abort();
+    return nullptr;
+  }
+  sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>,
+                                          int) const override {
+    std::abort();
+    return nullptr;
+  }
+  sk_sp<SkTypeface> onMakeFromStreamArgs(
+      std::unique_ptr<SkStreamAsset>, const SkFontArguments&) const override {
+    std::abort();
+    return nullptr;
+  }
+  sk_sp<SkTypeface> onMakeFromFile(const char[], int) const override {
+    std::abort();
+    return nullptr;
+  }
+
+ private:
+  sk_sp<SkTypeface> face_;
+  sk_sp<SkFontStyleSet> style_set_;
+};
+
+/* Adapted from skia's chrome/m128:example/external_client/src/shape_text.cpp */
+
+/* Forward declaration */
+sk_sp<SkFontMgr> OneFontMgr_New_Custom_Empty(sk_sp<SkData> font_data);
+
+sk_sp<SkFontMgr> OneFontMgr_New_Custom_Empty(char* argv1) {
+  SkFILEStream input(argv1);
+  if (!input.isValid()) {
+    printf("Cannot open input file %s\n", argv1);
+    return nullptr;
+  }
+  sk_sp<SkData> font_data = SkData::MakeFromStream(&input, input.getLength());
+  return OneFontMgr_New_Custom_Empty(font_data);
+}
+
+sk_sp<SkFontMgr> OneFontMgr_New_Custom_Empty(sk_sp<SkData> font_data) {
+  sk_sp<SkFontMgr> mgr = SkFontMgr_New_Custom_Empty();
+  sk_sp<SkTypeface> face = mgr->makeFromData(font_data);
+  if (!face) {
+    printf("input font stream was not parsable by Freetype\n");
+    return nullptr;
+  }
+  return sk_make_sp<OneFontMgr>(face);
+}
+
+}  // namespace
 
 void initFont(py::module &m) {
 // FontStyle
@@ -415,8 +517,8 @@ typeface
         Returns a unique signature to a stream, sufficient to reconstruct a
         typeface referencing the same font when Deserialize is called.
         )docstring",
-        py::arg("behavior") =
-            SkTypeface::SerializeBehavior::kIncludeDataIfLocal)
+        py::arg_v("behavior", SkTypeface::SerializeBehavior::kIncludeDataIfLocal,
+                  "skia.Typeface.SerializeBehavior.kIncludeDataIfLocal"))
     .def("unicharsToGlyphs",
         [] (const SkTypeface& typeface, const std::vector<SkUnichar>& chars) {
             std::vector<SkGlyphID> glyphs(chars.size());
@@ -707,6 +809,10 @@ py::class_<SkFontMgr, sk_sp<SkFontMgr>, SkRefCnt>(m, "FontMgr",
     )docstring")
     .def(py::init([] () { return SkFontMgr_RefDefault(); }))
     .def_static("New_Custom_Empty", &SkFontMgr_New_Custom_Empty)
+    .def_static("New_Custom_Empty", py::overload_cast<char*>(&OneFontMgr_New_Custom_Empty),
+        py::arg("filename"))
+    .def_static("New_Custom_Empty", py::overload_cast<sk_sp<SkData>>(&OneFontMgr_New_Custom_Empty),
+        py::arg("data"))
     .def("__getitem__", &SkFontMgr_getFamilyName, py::arg("index"))
     .def("__len__", &SkFontMgr::countFamilies)
     .def("countFamilies", &SkFontMgr::countFamilies)
@@ -816,6 +922,9 @@ py::class_<SkFontMgr, sk_sp<SkFontMgr>, SkRefCnt>(m, "FontMgr",
         )docstring")
     ;
 
+// This has the side-effect of letting "skia.FontMgr.OneFontMgr()" work.
+m.attr("FontMgr").attr("OneFontMgr") = m.attr("FontMgr").attr("New_Custom_Empty");
+
 // Font
 py::enum_<SkFontHinting>(m, "FontHinting")
     .value("kNone", SkFontHinting::kNone)
@@ -1223,7 +1332,8 @@ font
         :param skia.TextEncoding encoding: text encoding
         :return: glyphs represented by text
         )docstring",
-        py::arg("text"), py::arg("encoding") = SkTextEncoding::kUTF8)
+        py::arg("text"),
+        py::arg_v("encoding", SkTextEncoding::kUTF8, "skia.TextEncoding.kUTF8"))
     .def("unicharToGlyph", &SkFont::unicharToGlyph,
         R"docstring(
         Returns glyph index for Unicode character.
@@ -1256,7 +1366,8 @@ font
         :param str text: character storage encoded with :py:class:`TextEncoding`
         :param skia.TextEncoding encoding: text encoding
         )docstring",
-        py::arg("text"), py::arg("encoding") = SkTextEncoding::kUTF8)
+        py::arg("text"),
+        py::arg_v("encoding", SkTextEncoding::kUTF8, "skia.TextEncoding.kUTF8"))
     .def("measureText",
         [] (const SkFont& font, const std::string& text,
             SkTextEncoding encoding, SkRect* bounds, const SkPaint* paint) {
@@ -1277,7 +1388,8 @@ font
         :param skia.Paint paint: optional; may be nullptr
         :return: the advance width of text
         )docstring",
-        py::arg("text"), py::arg("encoding") = SkTextEncoding::kUTF8,
+        py::arg("text"),
+        py::arg_v("encoding", SkTextEncoding::kUTF8, "skia.TextEncoding.kUTF8"),
         py::arg("bounds") = nullptr, py::arg("paint") = nullptr)
     .def("getWidths",
         [] (const SkFont& font, const std::vector<SkGlyphID>& glyphs) {
diff --git a/src/skia/GrContext.cpp b/src/skia/GrContext.cpp
index 62901bec..caaf7dd4 100644
--- a/src/skia/GrContext.cpp
+++ b/src/skia/GrContext.cpp
@@ -8,6 +8,7 @@
 #include <include/gpu/gl/GrGLInterface.h>
 #include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
 #include <include/gpu/ganesh/gl/GrGLDirectContext.h>
+#include <include/gpu/vk/GrVkTypes.h>
 #include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
 #include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
 #include <include/gpu/ganesh/vk/GrVkDirectContext.h>
@@ -575,7 +576,8 @@ py::class_<GrRecordingContext, sk_sp<GrRecordingContext>, GrImageContext>(
 
         The caller should check that the returned format is valid.
         )docstring",
-        py::arg("colorType"), py::arg("renderable") = GrRenderable::kNo)
+        py::arg("colorType"),
+        py::arg_v("renderable", GrRenderable::kNo, "skia.GrRenderable.kNo"))
     .def("abandoned", &GrRecordingContext::abandoned,
         R"docstring(
         Reports whether the :py:class:`GrDirectContext` associated with this
@@ -858,7 +860,7 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
         to the underlying 3D API. This is equivalent to calling :py:meth:`flush`
         with a default :py:class:`GrFlushInfo` followed by :py:meth:`submit`.
         )docstring",
-        py::arg("sync") = GrSyncCpu::kNo)
+        py::arg_v("sync", GrSyncCpu::kNo, "skia.GrSyncCpu.kNo"))
     .def("flush", py::overload_cast<const GrFlushInfo&>(&GrDirectContext::flush),
         R"docstring(
         Call to ensure all drawing to the context has been flushed to underlying
@@ -914,7 +916,7 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
         If the syncCpu flag is true this function will return once the gpu has
         finished with all submitted work.
         )docstring",
-        py::arg("sync") = GrSyncCpu::kNo)
+        py::arg_v("sync", GrSyncCpu::kNo, "skia.GrSyncCpu.kNo"))
     .def("checkAsyncWorkCompletion", &GrDirectContext::checkAsyncWorkCompletion,
         R"docstring(
         Checks whether any asynchronous work is complete and if so calls related
@@ -944,7 +946,8 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
 
         The caller should check that the returned format is valid.
         )docstring",
-        py::arg("colorType"), py::arg("renderable") = GrRenderable::kNo)
+        py::arg("colorType"),
+        py::arg_v("renderable", GrRenderable::kNo, "skia.GrRenderable.kNo"))
     .def("createBackendTexture",
         py::overload_cast<int, int, const GrBackendFormat&, skgpu::Mipmapped,
             GrRenderable, GrProtected, std::string_view>(&GrDirectContext::createBackendTexture),
@@ -957,7 +960,8 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
         )docstring",
         py::arg("width"), py::arg("height"), py::arg("backendFormat"),
         py::arg("mipMapped"), py::arg("renderable"),
-        py::arg("isProtected") = GrProtected::kNo, py::arg("label") = std::string_view{})
+        py::arg_v("isProtected", GrProtected::kNo, "skia.GrProtected.kNo"),
+        py::arg("label") = std::string_view{})
     .def("createBackendTexture",
         py::overload_cast<int, int, SkColorType, skgpu::Mipmapped,
             GrRenderable, GrProtected, std::string_view>(&GrDirectContext::createBackendTexture),
@@ -971,7 +975,8 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
         )docstring",
         py::arg("width"), py::arg("height"), py::arg("colorType"),
         py::arg("mipMapped"), py::arg("renderable"),
-        py::arg("isProtected") = GrProtected::kNo, py::arg("label") = std::string_view{})
+        py::arg_v("isProtected", GrProtected::kNo, "skia.GrProtected.kNo"),
+        py::arg("label") = std::string_view{})
     .def("createBackendTexture",
         [] (GrDirectContext& context, int width, int height,
             const GrBackendFormat& backendFormat, const SkColor4f& color,
@@ -995,7 +1000,7 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
         )docstring",
         py::arg("width"), py::arg("height"), py::arg("backendFormat"),
         py::arg("color"), py::arg("mipMapped"), py::arg("renderable"),
-        py::arg("isProtected") = GrProtected::kNo)
+        py::arg_v("isProtected", GrProtected::kNo, "skia.GrProtected.kNo"))
     .def("createBackendTexture",
         [] (GrDirectContext& context, int width, int height,
             SkColorType colorType, const SkColor4f& color,
@@ -1015,7 +1020,7 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
         )docstring",
         py::arg("width"), py::arg("height"), py::arg("colorType"),
         py::arg("color"), py::arg("mipMapped"), py::arg("renderable"),
-        py::arg("isProtected") = GrProtected::kNo)
+        py::arg_v("isProtected", GrProtected::kNo, "skia.GrProtected.kNo"))
     .def("createBackendTexture",
         [] (GrDirectContext& context, const std::vector<SkPixmap>& srcData,
             GrRenderable renderable, GrProtected isProtected) {
@@ -1048,7 +1053,7 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
         be: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
         )docstring",
         py::arg("srcData"),  py::arg("renderable"),
-        py::arg("isProtected") = GrProtected::kNo)
+        py::arg_v("isProtected", GrProtected::kNo, "skia.GrProtected.kNo"))
     .def("createBackendTexture",
         [] (GrDirectContext& context, const SkPixmap& pixmap, GrRenderable renderable,
             GrProtected isProtected) {
@@ -1056,7 +1061,7 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
                 pixmap, renderable, isProtected);
         },
         py::arg("pixmap"), py::arg("renderable"),
-        py::arg("isProtected") = GrProtected::kNo)
+        py::arg_v("isProtected", GrProtected::kNo, "skia.GrProtected.kNo"))
     .def("updateBackendTexture",
         [] (GrDirectContext& context, const GrBackendTexture& texture,
             const SkColor4f& color) {
@@ -1133,7 +1138,7 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
         )docstring",
         py::arg("width"), py::arg("height"), py::arg("backendFormat"),
         py::arg("color"), py::arg("mipMapped"),
-        py::arg("isProtected") = GrProtected::kNo)
+        py::arg_v("isProtected", GrProtected::kNo, "skia.GrProtected.kNo"))
     .def("createCompressedBackendTexture",
         [] (GrDirectContext& context, int width, int height,
             SkTextureCompressionType type, const SkColor4f& color,
@@ -1142,7 +1147,8 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
                 width, height, type, color, mipMapped, isProtected);
         },
         py::arg("width"), py::arg("height"), py::arg("type"), py::arg("color"),
-        py::arg("mipMapped"), py::arg("isProtected") = GrProtected::kNo)
+        py::arg("mipMapped"),
+        py::arg_v("isProtected", GrProtected::kNo, "skia.GrProtected.kNo"))
     .def("createCompressedBackendTexture",
         [] (GrDirectContext& context, int width, int height,
             const GrBackendFormat& backendFormat, py::buffer b,
@@ -1155,7 +1161,7 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
         },
         py::arg("width"), py::arg("height"), py::arg("backendFormat"),
         py::arg("data"), py::arg("mipMapped"),
-        py::arg("isProtected") = GrProtected::kNo)
+        py::arg_v("isProtected", GrProtected::kNo, "skia.GrProtected.kNo"))
     .def("createCompressedBackendTexture",
         [] (GrDirectContext& context, int width, int height,
             SkTextureCompressionType type, py::buffer b,
@@ -1166,7 +1172,8 @@ py::class_<GrDirectContext, sk_sp<GrDirectContext>, GrRecordingContext>(m, "GrDi
                 width, height, type, info.ptr, size, mipMapped, isProtected);
         },
         py::arg("width"), py::arg("height"), py::arg("type"), py::arg("data"),
-        py::arg("mipMapped"), py::arg("isProtected") = GrProtected::kNo)
+        py::arg("mipMapped"),
+        py::arg_v("isProtected", GrProtected::kNo, "skia.GrProtected.kNo"))
     .def("setBackendTextureState",
         [] (GrDirectContext& context, const GrBackendTexture& texture,
             const skgpu::MutableTextureState& mutableState,
diff --git a/src/skia/Image.cpp b/src/skia/Image.cpp
index 0e62e0ec..13bb1c97 100644
--- a/src/skia/Image.cpp
+++ b/src/skia/Image.cpp
@@ -364,8 +364,8 @@ image
             :py:class:`Image` shares the pixel buffer without copy.
         )docstring",
         py::arg("array"), py::arg("dimensions"),
-        py::arg("colorType") = kN32_SkColorType,
-        py::arg("alphaType") = kUnpremul_SkAlphaType,
+        py::arg_v("colorType", kN32_SkColorType, "skia.ColorType.kN32_ColorType"),
+        py::arg_v("alphaType", kUnpremul_SkAlphaType, "skia.AlphaType.kUnpremul_AlphaType"),
         py::arg("colorSpace") = nullptr, py::arg("copy") = true)
     .def_static("fromarray", &ImageFromArray,
         R"docstring(
@@ -380,8 +380,9 @@ image
         :param skia.ColorSpace colorSpace: range of colors; may be nullptr
         :param bool copy: Whether to copy pixels.
         )docstring",
-        py::arg("array"), py::arg("colorType") = kN32_SkColorType,
-        py::arg("alphaType") = kUnpremul_SkAlphaType,
+        py::arg("array"),
+        py::arg_v("colorType", kN32_SkColorType, "skia.ColorType.kN32_ColorType"),
+        py::arg_v("alphaType", kUnpremul_SkAlphaType, "skia.AlphaType.kUnpremul_AlphaType"),
         py::arg("colorSpace") = nullptr, py::arg("copy") = true)
     .def("toarray", &ReadToNumpy<SkImage>,
         R"docstring(
@@ -395,8 +396,8 @@ image
         :return: numpy.ndarray
         )docstring",
         py::arg("srcX") = 0, py::arg("srcY") = 0,
-        py::arg("colorType") = kUnknown_SkColorType,
-        py::arg("alphaType") = kUnpremul_SkAlphaType,
+        py::arg_v("colorType", kUnknown_SkColorType, "skia.ColorType.kUnknown_ColorType"),
+        py::arg_v("alphaType", kUnpremul_SkAlphaType, "skia.AlphaType.kUnpremul_AlphaType"),
         py::arg("colorSpace") = nullptr)
     .def_static("open", &ImageOpen,
         R"docstring(
@@ -437,7 +438,7 @@ image
         :param int quality: encoder specific metric with 100 equaling best
         )docstring",
         py::arg("fp"),
-        py::arg("encodedImageFormat") = SkEncodedImageFormat::kPNG,
+        py::arg_v("encodedImageFormat", SkEncodedImageFormat::kPNG, "skia.EncodedImageFormat.kPNG"),
         py::arg("quality") = 100)
     .def("bitmap", &ImageToBitmap,
         R"docstring(
@@ -454,8 +455,8 @@ image
         :param colorSpace: color space of :py:class:`Bitmap`.
         :return: :py:class:`Bitmap`
         )docstring",
-        py::arg("colorType") = kUnknown_SkColorType,
-        py::arg("alphaType") = kUnknown_SkAlphaType,
+        py::arg_v("colorType", kUnknown_SkColorType, "skia.ColorType.kUnknown_ColorType"),
+        py::arg_v("alphaType", kUnknown_SkAlphaType, "skia.AlphaType.kUnknown_AlphaType"),
         py::arg("colorSpace") = nullptr)
     .def("convert", &ImageConvert,
         R"docstring(
@@ -484,8 +485,8 @@ image
         :param colorSpace: target color space.
         :return: :py:class:`Image`
         )docstring",
-        py::arg("colorType") = kUnknown_SkColorType,
-        py::arg("alphaType") = kUnknown_SkAlphaType,
+        py::arg_v("colorType", kUnknown_SkColorType, "skia.ColorType.kUnknown_ColorType"),
+        py::arg_v("alphaType", kUnknown_SkAlphaType, "skia.AlphaType.kUnknown_AlphaType"),
         py::arg("colorSpace") = nullptr)
     .def("resize", &ImageResize,
         R"docstring(
@@ -513,8 +514,9 @@ image
         :return: :py:class:`Image`
         )docstring",
         py::arg("width"), py::arg("height"),
-        py::arg("options") = SkSamplingOptions(),
-        py::arg("cachingHint") = SkImage::kAllow_CachingHint)
+        py::arg_v("options", SkSamplingOptions(), "skia.SamplingOptions()"),
+        py::arg_v("cachingHint", SkImage::kAllow_CachingHint,
+                  "skia.Image.CachingHint.kAllow_CachingHint"))
     .def("__repr__",
         [] (const SkImage& image) {
             return py::str("Image({}, {}, {}, {})").format(
@@ -658,8 +660,8 @@ image
         )docstring",
         py::arg("context"), py::arg("data"), py::arg("width"),
         py::arg("height"), py::arg("type"),
-        py::arg("mipMapped") = skgpu::Mipmapped::kNo,
-        py::arg("isProtected") = GrProtected::kNo)
+        py::arg_v("mipMapped", skgpu::Mipmapped::kNo, "skia.GrMipmapped.kNo"),
+        py::arg_v("isProtected", GrProtected::kNo, "skia.GrProtected.kNo"))
     .def_static("MakeRasterFromCompressed", &SkImages::RasterFromCompressedTextureData,
         R"docstring(
         Creates a CPU-backed :py:class:`Image` from compressed data.
@@ -790,7 +792,7 @@ image
         )docstring",
         py::arg("context"), py::arg("backendTexture"), py::arg("origin"),
         py::arg("colorType"),
-        py::arg("alphaType") = SkAlphaType::kPremul_SkAlphaType,
+        py::arg_v("alphaType", SkAlphaType::kPremul_SkAlphaType, "skia.AlphaType.kPremul_AlphaType"),
         py::arg("colorSpace") = nullptr)
 /*
     .def_static("MakeFromYUVATexturesCopy",
@@ -997,7 +999,7 @@ image
         :return:                      created :py:class:`Image`, or nullptr
         )docstring",
         py::arg("context"), py::arg("pixmaps"),
-        py::arg("buildMips") = skgpu::Mipmapped::kNo,
+        py::arg_v("buildMips", skgpu::Mipmapped::kNo, "skia.GrMipmapped.kNo"),
         py::arg("limitToMaxTextureSize") = false,
         py::arg("imageColorSpace") = nullptr)
 /*
@@ -1095,7 +1097,7 @@ image
         )docstring",
         py::arg("picture"), py::arg("dimensions"), py::arg("matrix") = nullptr,
         py::arg("paint") = nullptr,
-        py::arg("bitDepth") = SkImages::BitDepth::kU8,
+        py::arg_v("bitDepth", SkImages::BitDepth::kU8, "skia.Image.BitDepth.kU8"),
         py::arg("colorSpace") = nullptr)
     .def("imageInfo", &SkImage::imageInfo,
         R"docstring(
@@ -1224,8 +1226,10 @@ image
             nullptr
         :return: :py:class:`Shader` containing :py:class:`Image`
         )docstring",
-        py::arg("tmx") = SkTileMode::kClamp,
-        py::arg("tmy") = SkTileMode::kClamp, py::arg("samplingOptions") = SkSamplingOptions(), py::arg("localMatrix") = nullptr)
+        py::arg_v("tmx", SkTileMode::kClamp, "skia.TileMode.kClamp"),
+        py::arg_v("tmy", SkTileMode::kClamp, "skia.TileMode.kClamp"),
+        py::arg_v("samplingOptions", SkSamplingOptions(), "skia.SamplingOptions()"),
+        py::arg("localMatrix") = nullptr)
     // TODO: Other makeShader overloads.
     .def("peekPixels", &SkImage::peekPixels,
         R"docstring(
@@ -1368,7 +1372,8 @@ image
         )docstring",
         py::arg("context"), py::arg("dstInfo"), py::arg("dstPixels"),
         py::arg("dstRowBytes"), py::arg("srcX") = 0, py::arg("srcY") = 0,
-        py::arg("cachingHint") = SkImage::CachingHint::kAllow_CachingHint)
+        py::arg_v("cachingHint", SkImage::CachingHint::kAllow_CachingHint,
+                  "skia.Image.CachingHint.kAllow_CachingHint"))
     .def("readPixels",
         py::overload_cast<GrDirectContext*, const SkPixmap&, int, int,
             SkImage::CachingHint>(&SkImage::readPixels, py::const_),
@@ -1416,7 +1421,8 @@ image
         :return:             true if pixels are copied to dst
         )docstring",
         py::arg("context"), py::arg("dst"), py::arg("srcX"), py::arg("srcY"),
-        py::arg("cachingHint") = SkImage::CachingHint::kAllow_CachingHint)
+        py::arg_v("cachingHint", SkImage::CachingHint::kAllow_CachingHint,
+                  "skia.Image.CachingHint.kAllow_CachingHint"))
     .def("readPixels",
         [] (const SkImage& image, const SkImageInfo& dstInfo,
             py::buffer dstPixels, size_t dstRowBytes, int srcX, int srcY,
@@ -1430,7 +1436,8 @@ image
         )docstring",
         py::arg("dstInfo"), py::arg("dstPixels"), py::arg("dstRowBytes"),
         py::arg("srcX") = 0, py::arg("srcY") = 0,
-        py::arg("cachingHint") = SkImage::kAllow_CachingHint)
+        py::arg_v("cachingHint", SkImage::kAllow_CachingHint,
+                  "skia.Image.CachingHint.kAllow_CachingHint"))
     .def("readPixels",
         py::overload_cast<const SkPixmap&, int, int, SkImage::CachingHint>(
             &SkImage::readPixels, py::const_),
@@ -1438,7 +1445,8 @@ image
         Deprecated. Use the variants that accept a `GrDirectContext`.
         )docstring",
         py::arg("dst"), py::arg("srcX"), py::arg("srcY"),
-        py::arg("cachingHint") = SkImage::CachingHint::kAllow_CachingHint)
+        py::arg_v("cachingHint", SkImage::CachingHint::kAllow_CachingHint,
+                  "skia.Image.CachingHint.kAllow_CachingHint"))
     // .def("asyncRescaleAndReadPixels", &SkImage::asyncRescaleAndReadPixels)
     // .def("asyncRescaleAndReadPixelsYUV420",
     //      &SkImage::asyncRescaleAndReadPixelsYUV420)
@@ -1486,8 +1494,9 @@ image
         :return: true if pixels are scaled to fit dst
         )docstring",
         py::arg("dst"),
-        py::arg("samplingOptions") = SkSamplingOptions(),
-        py::arg("cachingHint") = SkImage::kAllow_CachingHint)
+        py::arg_v("samplingOptions", SkSamplingOptions(), "skia.SamplingOptions()"),
+        py::arg_v("cachingHint", SkImage::kAllow_CachingHint,
+                  "skia.Image.CachingHint.kAllow_CachingHint"))
     .def("encodeToData",
         [] (SkImage& image, SkEncodedImageFormat format, int quality) {
             sk_sp<SkData> data;
@@ -1647,8 +1656,9 @@ image
             for the returned image counts against the GrDirectContext's budget.
         :return: created :py:class:`Image`, or nullptr
         )docstring",
-        py::arg("context").none(false), py::arg("mipMapped") = skgpu::Mipmapped::kNo,
-        py::arg("budgeted") = skgpu::Budgeted::kYes)
+        py::arg("context").none(false),
+        py::arg_v("mipMapped", skgpu::Mipmapped::kNo, "skia.GrMipmapped.kNo"),
+        py::arg_v("budgeted", skgpu::Budgeted::kYes, "skia.Budgeted.kYes"))
     .def("makeNonTextureImage", &SkImage::makeNonTextureImage,
         R"docstring(
         Returns raster image or lazy image.
@@ -1680,7 +1690,7 @@ image
         :param skia.Image.CachingHint cachingHint: Caching hint
         :return: raster image, or nullptr
         )docstring",
-        py::arg("cachingHint") = SkImage::kAllow_CachingHint)
+        py::arg_v("cachingHint", SkImage::kAllow_CachingHint, "skia.Image.CachingHint.kAllow_CachingHint"))
     .def("makeWithFilter",
         [] (SkImage& image, GrRecordingContext* rContext,
             const SkImageFilter* filter,
@@ -1776,7 +1786,7 @@ image
         :return: true if :py:class:`Bitmap` was created
         )docstring",
         py::arg("bitmap").none(false),
-        py::arg("legacyBitmapMode") = SkImage::kRO_LegacyBitmapMode)
+        py::arg_v("legacyBitmapMode", SkImage::kRO_LegacyBitmapMode, "skia.Image.kRO_LegacyBitmapMode"))
     .def("isLazyGenerated", &SkImage::isLazyGenerated,
         R"docstring(
         Returns true if :py:class:`Image` is backed by an image-generator or
diff --git a/src/skia/ImageFilter.cpp b/src/skia/ImageFilter.cpp
index 15d56687..a4d59ee0 100644
--- a/src/skia/ImageFilter.cpp
+++ b/src/skia/ImageFilter.cpp
@@ -482,7 +482,8 @@ py::class_<SkImageFilters>(m, "ImageFilters")
             and output.
         )docstring",
         py::arg("sigmaX"), py::arg("sigmaY"),
-        py::arg("tileMode") = SkTileMode::kDecal, py::arg("input") = nullptr,
+        py::arg_v("tileMode", SkTileMode::kDecal, "skia.TileMode.kDecal"),
+        py::arg("input") = nullptr,
         py::arg("cropRect") = nullptr)
     .def_static("ColorFilter",
         [] (const SkColorFilter& cf, const SkImageFilter* input,
@@ -623,7 +624,7 @@ py::class_<SkImageFilters>(m, "ImageFilters")
         :filterQuality: The filter quality that is used when sampling the image.
         )docstring",
         py::arg("image"), py::arg("srcRect"), py::arg("dstRect"),
-        py::arg("options") = SkSamplingOptions())
+        py::arg_v("options", SkSamplingOptions(), "skia.SamplingOptions()"))
     .def_static("Image",
         [] (const SkImage& image, const SkSamplingOptions& options) {
             return SkImageFilters::Image(CloneImage(image), options);
@@ -633,7 +634,8 @@ py::class_<SkImageFilters>(m, "ImageFilters")
 
         :image: The image that is output by the filter.
         )docstring",
-        py::arg("image"), py::arg("options") = SkSamplingOptions())
+        py::arg("image"),
+        py::arg_v("options", SkSamplingOptions(), "skia.SamplingOptions()"))
     .def_static("Magnifier",
             [] (const SkRect& srcRect, SkScalar zoomAmount, SkScalar inset,
             const SkSamplingOptions& sampling,
diff --git a/src/skia/ImageInfo.cpp b/src/skia/ImageInfo.cpp
index 14d7f9aa..784e0931 100644
--- a/src/skia/ImageInfo.cpp
+++ b/src/skia/ImageInfo.cpp
@@ -853,9 +853,9 @@ yuvainfo
         'origin').
         )docstring",
         py::arg("dimensions"), py::arg("config"), py::arg("sampling"), py::arg("yuvColorSpace"),
-        py::arg("origin") = kTopLeft_SkEncodedOrigin,
-        py::arg("sittingX") = SkYUVAInfo::Siting::kCentered,
-        py::arg("sittingY") = SkYUVAInfo::Siting::kCentered)
+        py::arg_v("origin", kTopLeft_SkEncodedOrigin, "skia.EncodedOrigin.kTopLeft_EncodedOrigin"),
+        py::arg_v("sittingX", SkYUVAInfo::Siting::kCentered, "skia.SkYUVAInfo.Siting.kCentered"),
+        py::arg_v("sittingY", SkYUVAInfo::Siting::kCentered, "skia.SkYUVAInfo.Siting.kCentered"))
     .def("planeConfig", &SkYUVAInfo::planeConfig)
     .def("subSampling", &SkYUVAInfo::subsampling)
     .def("dimensions", &SkYUVAInfo::dimensions,
diff --git a/src/skia/Matrix.cpp b/src/skia/Matrix.cpp
index c7cc8beb..a0eb9128 100644
--- a/src/skia/Matrix.cpp
+++ b/src/skia/Matrix.cpp
@@ -1453,7 +1453,8 @@ matrix
             clipping
         :return: mapped bounds
         )docstring",
-        py::arg("src"), py::arg("pc") = SkApplyPerspectiveClip::kYes)
+        py::arg("src"),
+        py::arg_v("pc", SkApplyPerspectiveClip::kYes, "skia.ApplyPerspectiveClip.kYes"))
     .def("mapRectToQuad",
         [] (const SkMatrix& matrix, const SkRect& rect) {
             std::vector<SkPoint> dst(4);
diff --git a/src/skia/Paragraph.cpp b/src/skia/Paragraph.cpp
new file mode 100644
index 00000000..85d594ee
--- /dev/null
+++ b/src/skia/Paragraph.cpp
@@ -0,0 +1,216 @@
+#include "common.h"
+#include "modules/skparagraph/include/DartTypes.h"
+#include "modules/skparagraph/include/FontCollection.h"
+#include "modules/skparagraph/include/Paragraph.h"
+#include "modules/skparagraph/include/ParagraphBuilder.h"
+#include "modules/skparagraph/include/ParagraphStyle.h"
+#include <pybind11/stl.h>
+
+using namespace skia::textlayout;
+
+void initParagraph(py::module &m) {
+
+py::class_<FontCollection, sk_sp<FontCollection>, SkRefCnt> font_collection(m, "textlayout_FontCollection");
+py::class_<ParagraphBuilder> paragraph_builder(m, "textlayout_ParagraphBuilder");
+py::class_<ParagraphStyle> paragraph_style(m, "textlayout_ParagraphStyle");
+py::class_<TextStyle> text_style(m, "textlayout_TextStyle");
+py::class_<Paragraph> paragraph(m, "textlayout_Paragraph");
+
+py::enum_<TextAlign>(m, "textlayout_TextAlign", R"docstring(
+    )docstring")
+    .value("kLeft", TextAlign::kLeft)
+    .value("kRight", TextAlign::kRight)
+    .value("kCenter", TextAlign::kCenter)
+    .value("kJustify", TextAlign::kJustify)
+    .value("kStart", TextAlign::kStart)
+    .value("kEnd", TextAlign::kEnd)
+    .export_values();
+
+py::enum_<TextDecoration>(m, "textlayout_TextDecoration", R"docstring(
+    )docstring")
+    .value("kNoDecoration", TextDecoration::kNoDecoration)
+    .value("kUnderline", TextDecoration::kUnderline)
+    .value("kOverline", TextDecoration::kOverline)
+    .value("kLineThrough", TextDecoration::kLineThrough)
+    .value("kUnderlineOverline", TextDecoration(TextDecoration::kUnderline | TextDecoration::kOverline))
+    .value("kUnderlineLineThrough", TextDecoration(TextDecoration::kUnderline | TextDecoration::kLineThrough))
+    .value("kOverlineLineThrough", TextDecoration(TextDecoration::kOverline | TextDecoration::kLineThrough))
+    .value("kUnderlineOverlineLineThrough", TextDecoration(TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough))
+    .export_values();
+
+py::enum_<TextDecorationStyle>(m, "textlayout_TextDecorationStyle", R"docstring(
+    )docstring")
+    .value("kSolid", TextDecorationStyle::kSolid)
+    .value("kDouble", TextDecorationStyle::kDouble)
+    .value("kDotted", TextDecorationStyle::kDotted)
+    .value("kDashed", TextDecorationStyle::kDashed)
+    .value("kWavy", TextDecorationStyle::kWavy)
+    .export_values();
+
+py::enum_<TextDecorationMode>(m, "textlayout_TextDecorationMode", R"docstring(
+    )docstring")
+    .value("kGaps", TextDecorationMode::kGaps)
+    .value("kThrough", TextDecorationMode::kThrough)
+    .export_values();
+
+paragraph_builder
+    .def(py::init(
+        [] (const ParagraphStyle& style,
+            sk_sp<FontCollection> fontCollection,
+            sk_sp<SkUnicode> unicode) {
+                return ParagraphBuilder::make(style, fontCollection, unicode);
+        }),
+        R"docstring(
+        )docstring",
+        py::arg("style"), py::arg("fontCollection"), py::arg("unicode"))
+    .def_static("make",
+        py::overload_cast<ParagraphStyle const&, sk_sp<FontCollection>, sk_sp<SkUnicode>>(&ParagraphBuilder::make),
+        R"docstring(
+        )docstring",
+        py::arg("style"), py::arg("fontCollection"), py::arg("unicode"))
+    .def("addText",
+        py::overload_cast<const char*>(&ParagraphBuilder::addText),
+        R"docstring(
+        )docstring",
+        py::arg("text"))
+    .def("pop", &ParagraphBuilder::pop)
+    .def("pushStyle",
+        py::overload_cast<const TextStyle&>(&ParagraphBuilder::pushStyle),
+        R"docstring(
+        )docstring",
+        py::arg("style"))
+    .def("Build", &ParagraphBuilder::Build)
+    ;
+
+paragraph_style
+    .def(py::init())
+    .def("setTextStyle",
+        py::overload_cast<const TextStyle&>(&ParagraphStyle::setTextStyle),
+        R"docstring(
+        )docstring",
+        py::arg("textstyle"))
+    .def("setTextAlign",
+        py::overload_cast<TextAlign>(&ParagraphStyle::setTextAlign),
+        R"docstring(
+        )docstring",
+        py::arg("align"))
+    ;
+
+font_collection
+    .def(py::init())
+    .def("setDefaultFontManager",
+        py::overload_cast<sk_sp<SkFontMgr>>(&FontCollection::setDefaultFontManager),
+        R"docstring(
+        )docstring",
+        py::arg("fontManager"))
+    .def("setDefaultFontManager",
+        py::overload_cast<sk_sp<SkFontMgr>, const char[]>(&FontCollection::setDefaultFontManager),
+        R"docstring(
+        )docstring",
+        py::arg("fontManager"), py::arg("defaultFamilyName"))
+    .def("setDefaultFontManager",
+        py::overload_cast<sk_sp<SkFontMgr>, const std::vector<SkString>&>(&FontCollection::setDefaultFontManager),
+        R"docstring(
+        )docstring",
+        py::arg("fontManager"), py::arg("defaultFamilyNames"))
+    ;
+
+text_style
+    .def(py::init())
+    .def("cloneForPlaceholder", &TextStyle::cloneForPlaceholder)
+    .def("setColor",
+        py::overload_cast<SkColor>(&TextStyle::setColor),
+        R"docstring(
+        )docstring",
+        py::arg("color"))
+    .def("setForegroundColor",
+        py::overload_cast<SkPaint>(&TextStyle::setForegroundColor),
+        R"docstring(
+        DEPRECATED: prefer `setForegroundPaint`
+        )docstring",
+        py::arg("paint"))
+    .def("setForegroundPaint",
+        py::overload_cast<SkPaint>(&TextStyle::setForegroundPaint),
+        R"docstring(
+        )docstring",
+        py::arg("paint"))
+    .def("setFontFamilies",
+        py::overload_cast<std::vector<SkString>>(&TextStyle::setFontFamilies),
+        R"docstring(
+        )docstring",
+        py::arg("families"))
+    .def("setFontSize",
+        py::overload_cast<SkScalar>(&TextStyle::setFontSize),
+        R"docstring(
+        )docstring",
+        py::arg("size"))
+    .def("setFontStyle",
+        py::overload_cast<SkFontStyle>(&TextStyle::setFontStyle),
+        R"docstring(
+        )docstring",
+        py::arg("fontStyle"))
+    .def("setLocale",
+        py::overload_cast<const SkString&>(&TextStyle::setLocale),
+        R"docstring(
+        )docstring",
+        py::arg("locale"))
+    .def("setDecoration",
+        py::overload_cast<TextDecoration>(&TextStyle::setDecoration),
+        R"docstring(
+        )docstring",
+        py::arg("decoration"))
+    .def("setDecorationMode",
+        py::overload_cast<TextDecorationMode>(&TextStyle::setDecorationMode),
+        R"docstring(
+        )docstring",
+        py::arg("mode"))
+    .def("setDecorationStyle",
+        py::overload_cast<TextDecorationStyle>(&TextStyle::setDecorationStyle),
+        R"docstring(
+        )docstring",
+        py::arg("style"))
+    .def("setDecorationColor",
+        py::overload_cast<SkColor>(&TextStyle::setDecorationColor),
+        R"docstring(
+        )docstring",
+        py::arg("color"))
+    .def("setDecorationThicknessMultiplier",
+        py::overload_cast<SkScalar>(&TextStyle::setDecorationThicknessMultiplier),
+        R"docstring(
+        )docstring",
+        py::arg("m"))
+    ;
+
+paragraph
+    .def_property_readonly("Width", &Paragraph::getMaxWidth)
+    .def_property_readonly("Height", &Paragraph::getHeight)
+    .def_property_readonly("MinIntrinsicWidth", &Paragraph::getMinIntrinsicWidth)
+    .def_property_readonly("MaxIntrinsicWidth", &Paragraph::getMaxIntrinsicWidth)
+    .def_property_readonly("AlphabeticBaseline", &Paragraph::getAlphabeticBaseline)
+    .def_property_readonly("IdeographicBaseline", &Paragraph::getIdeographicBaseline)
+    .def_property_readonly("LongestLine", &Paragraph::getLongestLine)
+    .def_property_readonly("ExceedMaxLines", &Paragraph::didExceedMaxLines)
+    .def("layout",
+        py::overload_cast<SkScalar>(&Paragraph::layout),
+        R"docstring(
+        )docstring",
+        py::arg("width"))
+    .def("paint",
+        py::overload_cast<SkCanvas*, SkScalar, SkScalar>(&Paragraph::paint),
+        R"docstring(
+        )docstring",
+        py::arg("canvas"), py::arg("x"), py::arg("y"))
+    ;
+
+py::object SimpleNamespace = py::module_::import("types").attr("SimpleNamespace");
+m.attr("textlayout") = SimpleNamespace();
+m.attr("textlayout").attr("FontCollection") = m.attr("textlayout_FontCollection");
+m.attr("textlayout").attr("ParagraphBuilder") = m.attr("textlayout_ParagraphBuilder");
+m.attr("textlayout").attr("ParagraphStyle") = m.attr("textlayout_ParagraphStyle");
+m.attr("textlayout").attr("Paragraph") = m.attr("textlayout_Paragraph");
+m.attr("textlayout").attr("TextStyle") = m.attr("textlayout_TextStyle");
+m.attr("textlayout").attr("TextAlign") = m.attr("textlayout_TextAlign");
+m.attr("textlayout").attr("TextDecoration") = m.attr("textlayout_TextDecoration");
+m.attr("textlayout").attr("TextDecorationStyle") = m.attr("textlayout_TextDecorationStyle");
+m.attr("textlayout").attr("TextDecorationMode") = m.attr("textlayout_TextDecorationMode");
+}
diff --git a/src/skia/Path.cpp b/src/skia/Path.cpp
index f095ce8f..0190f680 100644
--- a/src/skia/Path.cpp
+++ b/src/skia/Path.cpp
@@ -407,26 +407,29 @@ path
         py::arg("points"), py::arg("verbs"), py::arg("conicWeights"),
         py::arg("fillType"), py::arg("isVolatile") = false)
     .def_static("Rect", &SkPath::Rect,
-        py::arg("rect"), py::arg("pathDirection") = SkPathDirection::kCW,
+        py::arg("rect"),
+        py::arg_v("pathDirection", SkPathDirection::kCW, "skia.PathDirection.kCW"),
         py::arg("startIndex") = 0)
     .def_static("Oval",
         py::overload_cast<const SkRect&, SkPathDirection, unsigned>(
             &SkPath::Oval),
-        py::arg("rect"), py::arg("pathDirection") = SkPathDirection::kCW,
+        py::arg("rect"),
+        py::arg_v("pathDirection", SkPathDirection::kCW, "skia.PathDirection.kCW"),
         py::arg("startIndex") = 0)
     .def_static("Circle", &SkPath::Circle,
         py::arg("center_x"), py::arg("center_y"), py::arg("radius"),
-        py::arg("pathDirection") = SkPathDirection::kCW)
+        py::arg_v("pathDirection", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def_static("RRect",
         py::overload_cast<const SkRRect&, SkPathDirection, unsigned>(
             &SkPath::RRect),
-        py::arg("rrect"), py::arg("pathDirection") = SkPathDirection::kCW,
+        py::arg("rrect"),
+        py::arg_v("pathDirection", SkPathDirection::kCW, "skia.PathDirection.kCW"),
         py::arg("startIndex") = 0)
     .def_static("RRect",
         py::overload_cast<const SkRect&, SkScalar, SkScalar, SkPathDirection>(
             &SkPath::RRect),
         py::arg("bounds"), py::arg("rx"), py::arg("ry"),
-        py::arg("pathDirection") = SkPathDirection::kCW)
+        py::arg_v("pathDirection", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def_static("Polygon",
         [] (const std::vector<SkPoint>& points, bool isClosed,
             SkPathFillType fillType, bool isVolatile) {
@@ -434,7 +437,7 @@ path
                 points.data(), points.size(), isClosed, fillType, isVolatile);
         },
         py::arg("points"), py::arg("isClosed"),
-        py::arg("fillType") = SkPathFillType::kWinding,
+        py::arg_v("fillType", SkPathFillType::kWinding, "skia.PathFillType.kWinding"),
         py::arg("isVolatile") = false)
     .def_static("Line", &SkPath::Line, py::arg("a"), py::arg("b"))
     .def(py::init<>(), R"docstring(
@@ -1434,7 +1437,8 @@ path
         :dir: :py:class:`Path`::Direction to wind added contour
         :return: reference to :py:class:`Path`
         )docstring",
-        py::arg("rect"), py::arg("dir") = SkPathDirection::kCW)
+        py::arg("rect"),
+        py::arg_v("dir", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def("addRect",
         py::overload_cast<const SkRect&, SkPathDirection, unsigned>(
             &SkPath::addRect),
@@ -1477,7 +1481,7 @@ path
         :return: reference to :py:class:`Path`
         )docstring",
         py::arg("left"), py::arg("top"), py::arg("right"), py::arg("bottom"),
-        py::arg("dir") = SkPathDirection::kCW)
+        py::arg_v("dir", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def("addOval",
         py::overload_cast<const SkRect&, SkPathDirection>(&SkPath::addOval),
         R"docstring(
@@ -1495,7 +1499,8 @@ path
         :dir: :py:class:`~skia.PathDirection` to wind ellipse
         :return: reference to :py:class:`Path`
         )docstring",
-        py::arg("oval"), py::arg("dir") = SkPathDirection::kCW)
+        py::arg("oval"),
+        py::arg_v("dir", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def("addOval",
         py::overload_cast<const SkRect&, SkPathDirection, unsigned>(
             &SkPath::addOval),
@@ -1535,7 +1540,7 @@ path
         :rtype: :py:class:`Path`
         )docstring",
         py::arg("x"), py::arg("y"), py::arg("radius"),
-        py::arg("dir") = SkPathDirection::kCW)
+        py::arg_v("dir", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def("addArc", &SkPath::addArc,
         R"docstring(
         Appends arc to :py:class:`Path`, as the start of new contour.
@@ -1586,7 +1591,7 @@ path
         :return: reference to :py:class:`Path`
         )docstring",
         py::arg("rect"), py::arg("rx"), py::arg("ry"),
-        py::arg("dir") = SkPathDirection::kCW)
+        py::arg_v("dir", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def("addRoundRect",
         // py::overload_cast<const SkRect&, const SkScalar[], SkPathDirection>(
         //     &SkPath::addRoundRect),
@@ -1615,7 +1620,7 @@ path
         :Returns: reference to :py:class:`Path`
         )docstring",
         py::arg("rect"), py::arg("radii"),
-        py::arg("dir") = SkPathDirection::kCW)
+        py::arg_v("dir", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def("addRRect",
         py::overload_cast<const SkRRect&, SkPathDirection>(&SkPath::addRRect),
         R"docstring(
@@ -1633,7 +1638,8 @@ path
         :dir: :py:class:`Path`::Direction to wind :py:class:`RRect`
         :return: reference to :py:class:`Path`
         )docstring",
-        py::arg("rrect"), py::arg("dir") = SkPathDirection::kCW)
+        py::arg("rrect"),
+        py::arg_v("dir", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def("addRRect",
         py::overload_cast<const SkRRect&, SkPathDirection, unsigned>(
             &SkPath::addRRect),
@@ -1690,7 +1696,8 @@ path
         refe:return: rence to :py:class:`Path`
         )docstring",
         py::arg("src"), py::arg("dx"), py::arg("dy"),
-        py::arg("mode") = SkPath::kAppend_AddPathMode)
+        py::arg_v("mode", SkPath::kAppend_AddPathMode,
+                  "skia.Path.AddPathMode.kAppend_AddPathMode"))
     .def("addPath",
         py::overload_cast<const SkPath&, SkPath::AddPathMode>(&SkPath::addPath),
         R"docstring(
@@ -1708,7 +1715,9 @@ path
 
         :return: reference to :py:class:`Path`
         )docstring",
-        py::arg("src"), py::arg("mode") = SkPath::kAppend_AddPathMode)
+        py::arg("src"),
+        py::arg_v("mode", SkPath::kAppend_AddPathMode,
+                  "skia.Path.AddPathMode.kAppend_AddPathMode"))
     .def("addPath",
         py::overload_cast<const SkPath&, const SkMatrix&, SkPath::AddPathMode>(
             &SkPath::addPath),
@@ -1732,7 +1741,8 @@ path
         :return: reference to :py:class:`Path`
         )docstring",
         py::arg("src"), py::arg("matrix"),
-        py::arg("mode") = SkPath::kAppend_AddPathMode)
+        py::arg_v("mode", SkPath::kAppend_AddPathMode,
+                  "skia.Path.AddPathMode.kAppend_AddPathMode"))
     .def("reverseAddPath", &SkPath::reverseAddPath,
         R"docstring(
         Appends src to :py:class:`Path`, from back to front.
@@ -1782,7 +1792,7 @@ path
             clipping
         )docstring",
         py::arg("matrix"), py::arg("dst") = nullptr,
-        py::arg("pc") = SkApplyPerspectiveClip::kYes)
+        py::arg_v("pc", SkApplyPerspectiveClip::kYes, "skia.ApplyPerspectiveClip.kYes"))
     // .def("transform",
     //     py::overload_cast<const SkMatrix&, SkApplyPerspectiveClip>(
     //         &SkPath::transform),
@@ -2429,18 +2439,21 @@ PathBuilder
     .def("addRect",
         py::overload_cast<const SkRect&, SkPathDirection>(
             &SkPathBuilder::addRect),
-        py::arg("rect"), py::arg("pathDirection") = SkPathDirection::kCW)
+        py::arg("rect"),
+        py::arg_v("pathDirection", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def("addOval",
         py::overload_cast<const SkRect&, SkPathDirection>(
             &SkPathBuilder::addOval),
-        py::arg("rect"), py::arg("pathDirection") = SkPathDirection::kCW)
+        py::arg("rect"),
+        py::arg_v("pathDirection", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def("addRRect",
         py::overload_cast<const SkRRect&, SkPathDirection>(
             &SkPathBuilder::addRRect),
-        py::arg("rrect"), py::arg("pathDirection") = SkPathDirection::kCW)
+        py::arg("rrect"),
+        py::arg_v("pathDirection", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def("addCircle", &SkPathBuilder::addCircle,
         py::arg("center_x"), py::arg("center_y"), py::arg("radius"),
-        py::arg("pathDirection") = SkPathDirection::kCW)
+        py::arg_v("pathDirection", SkPathDirection::kCW, "skia.PathDirection.kCW"))
     .def("addPolygon",
         [] (SkPathBuilder& self, const std::vector<SkPoint>& points,
             bool isClosed) {
diff --git a/src/skia/PathEffect.cpp b/src/skia/PathEffect.cpp
index 90585560..3e6a10ba 100644
--- a/src/skia/PathEffect.cpp
+++ b/src/skia/PathEffect.cpp
@@ -452,6 +452,6 @@ trimpatheffect
         return 2 logical segments: stopT..1 and 0...startT, in this order.
         )docstring",
         py::arg("startT"), py::arg("stopT"),
-        py::arg("mode") = SkTrimPathEffect::Mode::kNormal)
+        py::arg_v("mode", SkTrimPathEffect::Mode::kNormal, "skia.TrimPathEffect.Mode.kNormal"))
     ;
 }
diff --git a/src/skia/PathMeasure.cpp b/src/skia/PathMeasure.cpp
index 93ee9732..ec4820a8 100644
--- a/src/skia/PathMeasure.cpp
+++ b/src/skia/PathMeasure.cpp
@@ -62,7 +62,8 @@ path_measure
         the result.
         )docstring",
         py::arg("distance"),
-        py::arg("flags") = SkPathMeasure::MatrixFlags::kGetPosAndTan_MatrixFlag)
+        py::arg_v("flags", SkPathMeasure::MatrixFlags::kGetPosAndTan_MatrixFlag,
+                  "skia.PathMeasure.GetPosAndTan.kGetPosAndTan_MatrixFlag"))
     .def("getPosTan",
         []  (SkPathMeasure& measure, SkScalar distance) -> py::object {
             SkPoint position;
diff --git a/src/skia/Pixmap.cpp b/src/skia/Pixmap.cpp
index f4055f3d..6be1474e 100644
--- a/src/skia/Pixmap.cpp
+++ b/src/skia/Pixmap.cpp
@@ -173,8 +173,9 @@ py::class_<SkPixmap>(m, "Pixmap",
         :alphaType: alpha type of the array
         :colorSpace: range of colors; may be nullptr
         )docstring",
-        py::arg("array"), py::arg("colorType") = kN32_SkColorType,
-        py::arg("alphaType") = kUnpremul_SkAlphaType,
+        py::arg("array"),
+        py::arg_v("colorType", kN32_SkColorType, "skia.ColorType.kN32_ColorType"),
+        py::arg_v("alphaType", kUnpremul_SkAlphaType, "skia.AlphaType.kUnpremul_AlphaType"),
         py::arg("colorSpace") = nullptr)
     .def("reset",
         py::overload_cast<>(&SkPixmap::reset),
@@ -618,7 +619,7 @@ py::class_<SkPixmap>(m, "Pixmap",
         :return: true if pixels are scaled to fit dst
         )docstring",
         py::arg("dst"),
-        py::arg("samplingOptions") = SkSamplingOptions())
+        py::arg_v("samplingOptions", SkSamplingOptions(), "skia.SamplingOptions()"))
     .def("erase",
         py::overload_cast<const SkColor4f&, const SkIRect*>(
             &SkPixmap::erase, py::const_),
diff --git a/src/skia/Shader.cpp b/src/skia/Shader.cpp
index 8a6cbfe3..e89f95b5 100644
--- a/src/skia/Shader.cpp
+++ b/src/skia/Shader.cpp
@@ -239,7 +239,8 @@ gradientshader
         :param localMatrix: Local matrix
         )docstring",
         py::arg("points"), py::arg("colors"), py::arg("positions") = nullptr,
-        py::arg("mode") = SkTileMode::kClamp, py::arg("flags") = 0,
+        py::arg_v("mode", SkTileMode::kClamp, "skia.TileMode.kClamp"),
+        py::arg("flags") = 0,
         py::arg("localMatrix") = nullptr)
     .def_static("MakeRadial",
         [] (const SkPoint& center, SkScalar radius,
@@ -271,7 +272,8 @@ gradientshader
         :param localMatrix: Local matrix
         )docstring",
         py::arg("center"), py::arg("radius"), py::arg("colors"),
-        py::arg("positions") = nullptr, py::arg("mode") = SkTileMode::kClamp,
+        py::arg("positions") = nullptr,
+        py::arg_v("mode", SkTileMode::kClamp, "skia.TileMode.kClamp"),
         py::arg("flags") = 0, py::arg("localMatrix") = nullptr)
     .def_static("MakeTwoPointConical",
         [] (const SkPoint& start, SkScalar startRadius, const SkPoint& end,
@@ -295,7 +297,8 @@ gradientshader
         )docstring",
         py::arg("start"), py::arg("startRadius"), py::arg("end"),
         py::arg("endRadius"), py::arg("colors"), py::arg("positions") = nullptr,
-        py::arg("mode") = SkTileMode::kClamp, py::arg("flags") = 0,
+        py::arg_v("mode", SkTileMode::kClamp, "skia.TileMode.kClamp"),
+        py::arg("flags") = 0,
         py::arg("localMatrix") = nullptr)
     .def_static("MakeSweep",
         [] (SkScalar cx, SkScalar cy, const std::vector<SkColor>& colors,
@@ -317,7 +320,8 @@ gradientshader
         http://dev.w3.org/html5/2dcontext/#dom-context-2d-createradialgradient
         )docstring",
         py::arg("cx"), py::arg("cy"), py::arg("colors"),
-        py::arg("positions") = nullptr, py::arg("mode") = SkTileMode::kClamp,
+        py::arg("positions") = nullptr,
+        py::arg_v("mode", SkTileMode::kClamp, "skia.TileMode.kClamp"),
         py::arg("startAngle") = 0, py::arg("endAngle") = 360,
         py::arg("flags") = 0, py::arg("localMatrix") = nullptr)
     ;
diff --git a/src/skia/Surface.cpp b/src/skia/Surface.cpp
index e9101e85..7ecaf7e7 100644
--- a/src/skia/Surface.cpp
+++ b/src/skia/Surface.cpp
@@ -235,8 +235,8 @@ surface
         :return: numpy.ndarray
         )docstring",
         py::arg("srcX") = 0, py::arg("srcY") = 0,
-        py::arg("colorType") = kUnknown_SkColorType,
-        py::arg("alphaType") = kUnpremul_SkAlphaType,
+        py::arg_v("colorType", kUnknown_SkColorType, "skia.ColorType.kUnknown_ColorType"),
+        py::arg_v("alphaType", kUnpremul_SkAlphaType, "skia.AlphaType.kUnpremul_AlphaType"),
         py::arg("colorSpace") = nullptr)
     .def(py::init(
         [] (int width, int height, const SkSurfaceProps* surfaceProps) {
@@ -271,8 +271,9 @@ surface
         :alphaType: alpha type of the array
         :colorSpace: range of colors; may be nullptr
         )docstring",
-        py::arg("array"), py::arg("colorType") = kN32_SkColorType,
-        py::arg("alphaType") = kUnpremul_SkAlphaType,
+        py::arg("array"),
+        py::arg_v("colorType", kN32_SkColorType, "skia.ColorType.kN32_ColorType"),
+        py::arg_v("alphaType", kUnpremul_SkAlphaType, "skia.AlphaType.kUnpremul_AlphaType"),
         py::arg("colorSpace") = nullptr, py::arg("surfaceProps") = nullptr)
     .def("isCompatible", &SkSurface::isCompatible,
         R"docstring(
@@ -373,7 +374,8 @@ surface
         :param mode:    Retain or discard current Content
         )docstring",
         py::arg("backendTexture"), py::arg("origin"),
-        py::arg("mode") = SkSurface::kRetain_ContentChangeMode)
+        py::arg_v("mode", SkSurface::kRetain_ContentChangeMode,
+                  "skia.Surface.ContentChangeMode.kRetain_ContentChangeMode"))
     .def("getCanvas", &SkSurface::getCanvas,
         R"docstring(
         Returns :py:class:`Canvas` that draws into :py:class:`Surface`.
@@ -757,7 +759,7 @@ surface
         with a default :py:class:`GrFlushInfo` followed by
         :py:meth:`GrContext.submit`.
         )docstring",
-        py::arg("sync") = GrSyncCpu::kNo)
+        py::arg_v("sync", GrSyncCpu::kNo, "skia.GrSyncCpu.kNo"))
     .def("flush",
         [] (SkSurface& surface, SkSurfaces::BackendSurfaceAccess access, const GrFlushInfo& info) {
             auto dContext = GrAsDirectContext(surface.recordingContext());
@@ -1182,7 +1184,8 @@ surface
         )docstring",
         py::arg("context"), py::arg("budgeted"), py::arg("imageInfo"),
         py::arg("sampleCount") = 0,
-        py::arg("surfaceOrigin") = GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin,
+        py::arg_v("surfaceOrigin", GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin,
+                  "skia.GrSurfaceOrigin.kBottomLeft_GrSurfaceOrigin"),
         py::arg("surfaceProps") = nullptr,
         py::arg("shouldCreateWithMips") = false,
         py::arg("isProtected") = false)
diff --git a/src/skia/TextBlob.cpp b/src/skia/TextBlob.cpp
index 5c3857b9..8ff1fa70 100644
--- a/src/skia/TextBlob.cpp
+++ b/src/skia/TextBlob.cpp
@@ -111,7 +111,7 @@ textblob
         :param skia.TextEncoding encoding: text encoding used in the text array
         )docstring",
         py::arg("text"), py::arg("font"), py::arg("positions") = nullptr,
-        py::arg("encoding") = SkTextEncoding::kUTF8)
+        py::arg_v("encoding", SkTextEncoding::kUTF8, "skia.TextEncoding.kUTF8"))
     .def("__iter__",
         [] (const SkTextBlob& textblob) { return SkTextBlob::Iter(textblob); },
         py::keep_alive<0, 1>())
@@ -219,7 +219,7 @@ textblob
         :return: :py:class:`TextBlob` constructed from one run
         )docstring",
         py::arg("text"), py::arg("font"),
-        py::arg("encoding") = SkTextEncoding::kUTF8)
+        py::arg_v("encoding", SkTextEncoding::kUTF8, "skia.TextEncoding.kUTF8"))
     .def_static("MakeFromString",
         [] (const std::string& string, const SkFont& font,
             SkTextEncoding encoding) {
@@ -248,7 +248,7 @@ textblob
         :return: :py:class:`TextBlob` constructed from one run
         )docstring",
         py::arg("string"), py::arg("font"),
-        py::arg("encoding") = SkTextEncoding::kUTF8)
+        py::arg_v("encoding", SkTextEncoding::kUTF8, "skia.TextEncoding.kUTF8"))
     .def_static("MakeFromShapedText",
         [] (const std::string& utf8text, const SkFont& font,
             bool leftToRight) {
@@ -303,7 +303,7 @@ textblob
         :return: new textblob or nullptr
         )docstring",
         py::arg("text"), py::arg("xpos"), py::arg("constY"), py::arg("font"),
-        py::arg("encoding") = SkTextEncoding::kUTF8)
+        py::arg_v("encoding", SkTextEncoding::kUTF8, "skia.TextEncoding.kUTF8"))
     .def_static("MakeFromPosText",
         [] (const std::string& text, const std::vector<SkPoint>& pos,
             const SkFont& font, SkTextEncoding encoding) {
@@ -331,7 +331,7 @@ textblob
         :return: new textblob or nullptr
         )docstring",
         py::arg("text"), py::arg("pos"), py::arg("font"),
-        py::arg("encoding") = SkTextEncoding::kUTF8)
+        py::arg_v("encoding", SkTextEncoding::kUTF8, "skia.TextEncoding.kUTF8"))
     .def_static("MakeFromRSXform",
         [] (const std::string& text, const std::vector<SkRSXform>& xform,
             const SkFont& font, SkTextEncoding encoding) {
@@ -343,7 +343,7 @@ textblob
                 text.c_str(), text.size(), &xform[0], font, encoding);
         },
         py::arg("text"), py::arg("xform"), py::arg("font"),
-        py::arg("encoding") = SkTextEncoding::kUTF8)
+        py::arg_v("encoding", SkTextEncoding::kUTF8, "skia.TextEncoding.kUTF8"))
     .def_static("Deserialize",
         [] (py::buffer b) {
             auto info = b.request();
@@ -420,7 +420,7 @@ textblobbuilder
         )docstring",
         py::arg("text"), py::arg("font"), py::arg("x"), py::arg("y"),
         py::arg("bounds") = nullptr,
-        py::arg("encoding") = SkTextEncoding::kUTF8)
+        py::arg_v("encoding", SkTextEncoding::kUTF8, "skia.TextEncoding.kUTF8"))
     .def("allocRunPosH",
         [] (SkTextBlobBuilder& builder, const SkFont& font,
             const std::vector<SkGlyphID>& glyphs, py::iterable xpos,
diff --git a/src/skia/Unicode.cpp b/src/skia/Unicode.cpp
new file mode 100644
index 00000000..54718d37
--- /dev/null
+++ b/src/skia/Unicode.cpp
@@ -0,0 +1,21 @@
+#include "common.h"
+#include "modules/skunicode/include/SkUnicode_icu.h"
+
+void initUnicode(py::module &m) {
+
+  py::class_<SkUnicode, sk_sp<SkUnicode>, SkRefCnt> unicode(m, "Unicode");
+
+unicode
+    .def(py::init(
+        [] (void) {
+            return SkUnicodes::ICU::Make();
+        }))
+    .def_static("ICU_Make", &SkUnicodes::ICU::Make)
+    ;
+
+// SkUnicodes::ICU is a namespace
+py::object SimpleNamespace = py::module_::import("types").attr("SimpleNamespace");
+m.attr("Unicodes") = SimpleNamespace();
+m.attr("Unicodes").attr("ICU") = SimpleNamespace();
+m.attr("Unicodes").attr("ICU").attr("Make") = m.attr("Unicode").attr("ICU_Make");
+}
diff --git a/src/skia/main.cpp b/src/skia/main.cpp
index e3cef209..8d7caca5 100644
--- a/src/skia/main.cpp
+++ b/src/skia/main.cpp
@@ -20,6 +20,7 @@ void initImage(py::module &);
 void initImageInfo(py::module &);
 void initMatrix(py::module &);
 void initPaint(py::module &);
+void initParagraph(py::module &);
 void initPath(py::module &);
 void initPathMeasure(py::module &);
 void initPicture(py::module &);
@@ -35,6 +36,7 @@ void initStream(py::module &);
 void initString(py::module &);
 void initSurface(py::module &);
 void initTextBlob(py::module &);
+void initUnicode(py::module &);
 void initVertices(py::module &);
 void initSVGDOM(py::module &);
 
@@ -67,6 +69,8 @@ PYBIND11_MODULE(skia, m) {
     initImageInfo(m);
     initImage(m);
     initPaint(m);
+    initUnicode(m); // Before Paragraph
+    initParagraph(m);
     initPath(m);
     initPathMeasure(m);
     initPicture(m);


Test patch:
diff --git a/tests/test_canvas.py b/tests/test_canvas.py
index cf46bf46..c908eb39 100644
--- a/tests/test_canvas.py
+++ b/tests/test_canvas.py
@@ -21,7 +21,10 @@ def test_Canvas_repr(canvas):
     (np.zeros((16, 16, 4), dtype=np.uint8),),
     (100, 100),
     (100, 100, None),
+    (100, 100, skia.SurfaceProps()),
     (skia.Bitmap(),),
+    (skia.Bitmap(),
+        skia.SurfaceProps()),
 ])
 def test_Canvas_init(args):
     check_canvas(skia.Canvas(*args))
@@ -31,6 +34,11 @@ def test_Canvas_imageInfo(canvas):
     assert isinstance(canvas.imageInfo(), skia.ImageInfo)
 
 
+def test_Canvas_getProps(canvas):
+    props = skia.SurfaceProps()
+    assert isinstance(canvas.getProps(props), bool)
+
+
 def test_Canvas_flush(canvas):
     canvas.flush()
 
@@ -42,6 +50,7 @@ def test_Canvas_getBaseLayerSize(canvas):
 @pytest.mark.parametrize('args', [
     tuple(),
     (None,),
+    (skia.SurfaceProps(),),
 ])
 def test_Canvas_makeSurface(canvas, args):
     assert isinstance(canvas.makeSurface(canvas.imageInfo(), *args),
@@ -472,6 +481,8 @@ def test_Canvas_getLocalToDevice(canvas):
 @pytest.mark.parametrize('args', [
     (skia.ImageInfo.MakeN32Premul(4, 4), bytearray(64)),
     (skia.ImageInfo.MakeN32Premul(4, 4), bytearray(64), 16),
+    (skia.ImageInfo.MakeN32Premul(4, 4), bytearray(64), 16,
+        skia.SurfaceProps()),
 ])
 def test_Canvas_MakeRasterDirect(args):
     check_canvas(skia.Canvas.MakeRasterDirect(*args))
diff --git a/tests/test_font.py b/tests/test_font.py
index 368ac1bd..2a220f98 100644
--- a/tests/test_font.py
+++ b/tests/test_font.py
@@ -323,6 +323,33 @@ def fontmgr():
     return skia.FontMgr()
 
 
+def test_FontMgr_New_Custom_Empty0():
+    assert isinstance(skia.FontMgr.New_Custom_Empty(), skia.FontMgr)
+
+
+def test_FontMgr_New_Custom_Empty1(ttf_path):
+    assert isinstance(skia.FontMgr.New_Custom_Empty(ttf_path), skia.FontMgr)
+
+
+def test_FontMgr_New_Custom_Empty2(ttf_path):
+    font_data = skia.Data.MakeFromFileName(ttf_path)
+    assert isinstance(skia.FontMgr.New_Custom_Empty(font_data), skia.FontMgr)
+
+
+# Strickly speaking, we do not need/want this no-arg one.
+def test_FontMgr_OneFontMgr0():
+    assert isinstance(skia.FontMgr.OneFontMgr(), skia.FontMgr)
+
+
+def test_FontMgr_OneFontMgr1(ttf_path):
+    assert isinstance(skia.FontMgr.OneFontMgr(ttf_path), skia.FontMgr)
+
+
+def test_FontMgr_OneFontMgr2(ttf_path):
+    font_data = skia.Data.MakeFromFileName(ttf_path)
+    assert isinstance(skia.FontMgr.OneFontMgr(font_data), skia.FontMgr)
+
+
 def test_FontMgr_getitem(fontmgr):
     assert isinstance(fontmgr[0], str)
 
diff --git a/tests/test_paragraph.py b/tests/test_paragraph.py
new file mode 100644
index 00000000..fb759af9
--- /dev/null
+++ b/tests/test_paragraph.py
@@ -0,0 +1,28 @@
+import skia
+import pytest
+import sys
+
+def test_FontCollection_init0():
+    assert isinstance(skia.textlayout.FontCollection(), skia.textlayout_FontCollection)
+
+def test_ParagraphStyle_init0():
+    assert isinstance(skia.textlayout.ParagraphStyle(),skia.textlayout_ParagraphStyle)
+
+def test_TextStyle_init0():
+    assert isinstance(skia.textlayout.TextStyle(), skia.textlayout_TextStyle)
+
+@pytest.fixture(scope='session')
+def paragraph_builder():
+    return skia.textlayout.ParagraphBuilder.make(skia.textlayout.ParagraphStyle(),
+                                                 skia.textlayout.FontCollection(),
+                                                 skia.Unicodes.ICU.Make())
+
+def test_ParagraphBuilder_init0(paragraph_builder):
+    assert isinstance(paragraph_builder, skia.textlayout_ParagraphBuilder)
+
+def test_Paragraph_init0(paragraph_builder):
+    if sys.platform.startswith("win"):
+        pytest.skip("Known not to work; To be investigated.")
+    paragraph_builder.addText("")
+    paragraph = paragraph_builder.Build()
+    assert isinstance(paragraph, skia.textlayout_Paragraph)
diff --git a/tests/test_unicode.py b/tests/test_unicode.py
new file mode 100644
index 00000000..9a1684e4
--- /dev/null
+++ b/tests/test_unicode.py
@@ -0,0 +1,20 @@
+import skia
+import pytest
+import sys
+
+def test_Unicodes_init0():
+    if sys.platform.startswith("win"):
+        pytest.skip("Known not to work; To be investigated.")
+    assert isinstance(skia.Unicode(), skia.Unicode)
+
+# recommended alias (upstream):
+def test_Unicodes_init1():
+    if sys.platform.startswith("win"):
+        pytest.skip("Known not to work; To be investigated.")
+    assert isinstance(skia.Unicodes.ICU.Make(), skia.Unicode)
+
+# Canonical
+def test_Unicodes_init2():
+    if sys.platform.startswith("win"):
+        pytest.skip("Known not to work; To be investigated.")
+    assert isinstance(skia.Unicode.ICU_Make(), skia.Unicode)