Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Deploy Docs

on:
push:
branches: [main]
paths: ['docs/**', 'mkdocs.yml']
workflow_dispatch:

permissions:
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- run: pip install mkdocs-material
- run: mkdocs build
- uses: actions/upload-pages-artifact@v3
with:
path: site
- id: deployment
uses: actions/deploy-pages@v4
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
.env
.AFFINITY_PATH

# mkdocs build output
site/

# User-specific files
*.rsuser
*.suo
Expand Down
118 changes: 13 additions & 105 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
# Affinity Plugin Loader

> [!NOTE]
> *Information on WineFix has moved, see [WineFix/](WineFix#winefix)*


A managed code plugin loader & injector hook for Affinity by Canva (Affinity v3).

APL gives you a simple method to load custom code into Affinity and perform custom patches at runtime using the Harmony library. No more patching DLL files on disk.
Expand All @@ -12,123 +8,35 @@ APL supports Windows and Linux (Wine). MacOS support is not planned at this time

<img width="1896" height="1454" alt="image" src="https://github.com/user-attachments/assets/25639c82-94e4-4650-90ef-f605549fd806" />

## Install

1. Download `affinitypluginloader-vX.X.X-amd64.zip` from the [latest release](https://github.com/noahc3/AffinityPluginLoader/releases).
2. Extract the contents of the archive into your Affinity install directory
- This is `C:/Program Files/Affinity/Affinity/` by default on Windows, or
`path/to/wineprefix/drive_c/Program Files/Affinity/Affinity/` on Linux.

That's it. On Windows, just launch AffinityHook.exe instead of Affinity.exe. On Linux, launch AffinityHook.exe
with Wine instead of Affinity.exe.

Alternatively if you want your existing shortcuts to work without any changes, you can:
> [!TIP]
> 📖 **Full documentation is available at [apl.ncuroe.dev](https://apl.ncuroe.dev)**

- Rename `Affinity.exe` to `Affinity.real.exe`
- Rename `AffinityHook.exe` to `Affinity.exe`
## Quick Start

However, doing this means updates to Affinity may not work correctly or Affinity Hook may be removed on updates.
As such it's recommended you update your existing shortcuts or create new shortcuts for `AffinityHook.exe`.
1. Download `affinitypluginloader-vX.X.X-amd64.zip` from the [latest release](https://github.com/noahc3/AffinityPluginLoader/releases).
2. Extract into your Affinity install directory.
3. Launch `AffinityHook.exe` instead of `Affinity.exe`.

For detailed installation instructions, see the [Installation Guide](https://apl.ncuroe.dev/guide/installation/).

## Developing Plugins

Plugins extend `AffinityPlugin` and override stage methods to hook into Affinity's loading pipeline. APL calls your plugin at each stage as Affinity starts up:

| Stage | Method | What's available |
|---|---|---|
| 0 – Load | `OnLoad` | Plugin discovered, settings initialized. No Affinity types yet. |
| 1 – Patch | `OnPatch` | Serif assemblies loaded. Apply Harmony patches here. |
| 2 – ServicesReady | `OnServicesReady` | Affinity's DI container and services initialized. |
| 3 – Ready | `OnReady` | Full runtime including native engine, tools, effects. |
| 4 – UiReady | `OnUiReady` | Main window loaded. Full UI tree available. |
| 5 – StartupComplete | `OnStartupComplete` | Splash hidden, app idle. Safe to show dialogs. |

Each stage method receives an `IPluginContext` with:
- `Harmony` — shared Harmony instance for patching
- `Settings` — your plugin's settings store (if you defined settings)
- `Patch(description, action)` — apply a patch with automatic deferral if dependencies aren't loaded yet
- `Log` / `LogWarning` / `LogError` — logging helpers

### Settings API

Override `DefineSettings()` to declare configuration options. APL auto-generates a preferences page in Affinity's preferences dialog using native Affinity controls.

```csharp
public override PluginSettingsDefinition DefineSettings()
{
return new PluginSettingsDefinition("myplugin")
.AddSection("General")
.AddBool("my_toggle", "Enable feature", defaultValue: true,
description: "Description shown below the toggle.")
.AddEnum("my_choice", "Pick one", new List<EnumOption>
{
new EnumOption("a", "Option A"),
new EnumOption("b", "Option B"),
})
.AddSlider("my_slider", "Amount", 0, 100, defaultValue: 50);
}
```

Supported setting types: `Bool`, `String`, `Enum`, `Slider`, `DropdownSlider`. Layout elements like `AddSection`, `AddInlineText`, `AddInlineMutedText`, and `AddInlineXaml` are also available. Settings descriptions support basic markdown formatting.

Settings are persisted as TOML in the `apl/config/` directory and can be overridden via environment variables (`APL__PLUGINID__KEY`).

### Minimal Plugin Example

```csharp
using HarmonyLib;
using AffinityPluginLoader;
using AffinityPluginLoader.Settings;

public class MyPlugin : AffinityPlugin
{
public override PluginSettingsDefinition DefineSettings()
{
return new PluginSettingsDefinition("myplugin")
.AddBool("enabled", "Enable patch", defaultValue: true);
}

public override void OnPatch(Harmony harmony, IPluginContext context)
{
if (context.Settings.GetEffectiveValue<bool>("enabled"))
{
context.Patch("My patch", h =>
{
// Use reflection to find target types, then apply Harmony patches
});
}
}
}
```

See [WineFix/](WineFix/) for a full working example with multiple patches, settings, and deferred patching.

### Building

Build scripts are provided for Windows and Linux.
Plugins extend `AffinityPlugin` and use [Harmony](https://github.com/pardeike/Harmony) to patch Affinity methods at runtime. See the [Creating a Plugin](https://apl.ncuroe.dev/dev/creating-a-plugin/) guide for the full walkthrough, including the plugin lifecycle, settings API, and build instructions.

> [!TIP]
> Developing on Linux? Use `./docker-build.sh` to build inside a Docker container with all dependencies pre-configured. See `docker/Dockerfile` for the full list of build dependencies if you prefer to build on your host system.
>
> APL fully supports building on Linux via mingw-w64 and the dotnet SDK, you'll just need to grab a Windows SDK header and library from Wine.

Use `./deploy.sh` to build and deploy directly to your Affinity install directory for testing.
## WineFix

WineFix is an APL plugin that fixes Wine-specific Affinity bugs. See [WineFix/](WineFix/) for an overview, or the [WineFix docs](https://apl.ncuroe.dev/winefix/) for full details.

## Licensing

Affinity Hook, Affinity Bootstrap, Affinity Plugin Loader, and the project solution configuration files in the root
directory of the repository are licensed under the MIT License except for the exemption noted below. You can
find a copy of the license in the LICENSE file under the directories for each respective project.
APL (AffinityHook, AffinityBootstrap, AffinityPluginLoader) is licensed under the **MIT License**. See the LICENSE file under each project directory.

> [!WARNING]
> WineFix is offered under a different license. See [WineFix#Licensing](WineFix#Licensing) for information.

> WineFix is offered under a different license. See [WineFix#Licensing](WineFix#licensing) for information.

### License Exemption

[Canva](https://github.com/canva) and it's subsidiaries are exempt from MIT licensing and may (at its option) instead license any source code authored for the Affinity Hook, Affinity Bootstrap, and Affinity Plugin Loader projects under the Zero-Clause BSD license.
[Canva](https://github.com/canva) and its subsidiaries are exempt from MIT licensing and may (at its option) instead license any source code authored for the Affinity Hook, Affinity Bootstrap, and Affinity Plugin Loader projects under the Zero-Clause BSD license.


# Credits
Expand Down
52 changes: 19 additions & 33 deletions WineFix/README.md
Original file line number Diff line number Diff line change
@@ -1,59 +1,45 @@
# WineFix

WineFix is an APL plugin which aims to patch bugs encountered when running Affinity on Linux under Wine using
runtime code patches.
WineFix is an APL plugin which patches bugs encountered when running Affinity on Linux under Wine using runtime code patches.

## Install
> [!TIP]
> 📖 **Full documentation at [apl.ncuroe.dev/winefix](https://apl.ncuroe.dev/winefix/)**

To install WineFix, download `apl-winefix-vX.X.X.zip` from the [latest release](https://github.com/noahc3/AffinityPluginLoader/releases) and extract the `apl/` directory and `d2d1.dll` file to your Affinity install directory. That is, `d2d1.dll` should be in the Affinity install directory next to your Affinity.exe, and `WineFix.dll` should be inside the `apl/plugins` folder in your Affinity directory after extraction.
## Install

## Included Patches
1. [Install APL](https://apl.ncuroe.dev/guide/installation/) first.
2. Download `winefix-vX.X.X.zip` from the [latest release](https://github.com/noahc3/AffinityPluginLoader/releases) and extract into your Affinity install directory.

As of now WineFix fixes the following bugs:
For detailed instructions, see the [WineFix Installation Guide](https://apl.ncuroe.dev/winefix/installation/).

- Fixed: Preferences fail to save on application exit
- Fixed: Vector path preview lines are drawn incorrectly and don't match the underlying stroke path
- Fixed: Color picker zoom preview displays a black image on Wayland (auto-detected; configurable)
- Fixed: Intermittent startup crash caused by parallel font enumeration (force-disabled by default)
## Included Patches

> [!WARNING]
> WineFix currently patches out the Canva sign-in dialog prompt when launching Affinity. This is temporary and the sign-in dialog prompt will be restored as soon as we have a known consistent way to fix the sign-in browser redirect and Affinity protocol handler.
- Preferences fail to save on application exit
- Vector path preview lines drawn incorrectly
- Color picker zoom preview displays a black image on Wayland
- Intermittent startup crash from parallel font enumeration
- Canva sign-in dialog patched out (temporary; pending protocol handler fix)

## Configuration

WineFix settings are configurable from Affinity's preferences dialog (under the WineFix tab) or via environment variables.

| Setting | Default | Description |
|---|---|---|
| Color picker magnifier fix | Auto | Wayland zoom preview fix. Auto enables only on Wayland/XWayland. |
| Color picker sampling mode | Native | Native uses Affinity's color space; Exact picks the literal pixel color (sRGB). |
| Force synchronous font enumeration | Enabled | Disables parallel font loading to reduce startup crashes. May increase startup time. |

Environment variable overrides use the format `APL__WINEFIX__KEY` (e.g. `APL__WINEFIX__COLOR_PICKER_MAGNIFIER_FIX=disabled`).
WineFix is configurable from Affinity's preferences dialog, TOML files, or environment variables. See the [Configuration Reference](https://apl.ncuroe.dev/winefix/configuration/) for all settings and options.

## Known Open Bugs

We are currently researching potential solutions for the following bugs:

- Accepting crash reporting causes permanent crash until prefs are cleared
- Embedded SVG document editor causes crash after being open for some amount of time
- Embedded SVG document editor crashes after being open for some time

We are open to resolving any Wine-specific bugs. Feel free to open an issue requesting a patch for any
particular bug you encounter. Just please keep in mind these bugs take time to research and develop patches for,
especially if the bug needs to be patched in native code.
We are open to resolving any Wine-specific bugs. Feel free to [open an issue](https://github.com/noahc3/AffinityPluginLoader/issues) requesting a patch.

## Licensing

WineFix is licensed under the terms of the GPLv2 except for the exclusion and exemption noted below. You can find a copy of the license in the LICENSE file.

### License Exclusion
WineFix is licensed under **GPLv2**. See the [LICENSE](LICENSE) file.

WineFix includes source code from the Wine project for d2d1.dll under `WineFix/lib/d2d1`. In accordance with the original project, the code in this directory is instead licensed under the LGPLv2.1. A copy of this license can be found at `WineFix/lib/d2d1/LICENSE`. Changes have been applied to the d2d1 source code to implement a recursive cubic bezier subdivision algorithm to correct cubic bezier rendering in Affinity, and to allow building d2d1.dll standalone from the full Wine source code repository.
The bundled `d2d1.dll` source (under `WineFix/lib/d2d1/`) is licensed under **LGPLv2.1** per upstream Wine licensing. Changes have been applied to implement a cubic bezier subdivision algorithm and to allow building d2d1.dll standalone.

### License Exemption

[Canva](https://github.com/canva) and it's subsidiaries are exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the WineFix project under the Zero-Clause BSD license.
- Due to requirements of the upstream Wine licensing, this exemption **does not apply** to the d2d1.dll implementation source code, ie. all code under `WineFix/lib/d2d1/` is excluded from this exemption.
[Canva](https://github.com/canva) and its subsidiaries are exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the WineFix project under the Zero-Clause BSD license. This exemption **does not apply** to the d2d1.dll source code under `WineFix/lib/d2d1/`.


# Credits
Expand Down
1 change: 1 addition & 0 deletions docs/CNAME
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
apl.ncuroe.dev
Loading
Loading