by roundabout, Saturday, 22 November 2025, 14:22:51 (1763821371), pushed by roundabout, Saturday, 22 November 2025, 14:22:54 (1763821374)
Author identity: vlad <vlad.muntoiu@gmail.com>
95bb25105c4915f391d501f302b4e1c9d3704f71
.idea/workspace.xml
@@ -4,8 +4,8 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="b2c629ea-d173-4caf-b306-cbeaee617270" name="Changes" comment="Add a dark theme">
<change afterPath="$PROJECT_DIR$/articles/Do not get Samsung.md" afterDir="false" />
<list default="true" id="b2c629ea-d173-4caf-b306-cbeaee617270" name="Changes" comment="Add Samsung telescreens">
<change afterPath="$PROJECT_DIR$/articles/Inkscape boolean operations in extension.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
@@ -24,26 +24,26 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"Python.generate.executor": "Run",
"Python.main.executor": "Debug",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "master",
"junie.onboarding.icon.badge.shown": "true",
"last_opened_file_path": "/home/vlad/blog/static/photos",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "reference.settingsdialog.IDE.editor.colors.HTML",
"to.speed.mode.migration.done": "true",
"vue.rearranger.settings.migration": "true"
<component name="PropertiesComponent">{
"keyToString": {
"Python.generate.executor": "Run",
"Python.main.executor": "Debug",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "master",
"junie.onboarding.icon.badge.shown": "true",
"last_opened_file_path": "/home/vlad/blog/static/photos",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "reference.settingsdialog.IDE.editor.colors.HTML",
"to.speed.mode.migration.done": "true",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
}</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/static/photos" />
@@ -86,8 +86,8 @@
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-js-predefined-d6986cc7102b-e03c56caf84a-JavaScript-PY-252.23892.515" />
<option value="bundled-python-sdk-7e47963ff851-f0eec537fc84-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-252.23892.515" />
<option value="bundled-js-predefined-d6986cc7102b-3aa1da707db6-JavaScript-PY-252.27397.106" />
<option value="bundled-python-sdk-4e2b1448bda8-9a97661f3031-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-252.27397.106" />
</set>
</attachedChunks>
</component>
@@ -131,7 +131,8 @@
<workItem from="1742578470374" duration="5000" />
<workItem from="1746351256945" duration="9039000" />
<workItem from="1746449599308" duration="36000" />
<workItem from="1755156520743" duration="3060000" />
<workItem from="1755156520743" duration="3485000" />
<workItem from="1763820661504" duration="496000" />
</task>
<task id="LOCAL-00001" summary="Blog">
<option name="closed" value="true" />
@@ -429,7 +430,15 @@
<option name="project" value="LOCAL" />
<updated>1746360413779</updated>
</task>
<option name="localTasksCounter" value="38" />
<task id="LOCAL-00038" summary="Add Samsung telescreens">
<option name="closed" value="true" />
<created>1755164515179</created>
<option name="number" value="00038" />
<option name="presentableId" value="LOCAL-00038" />
<option name="project" value="LOCAL" />
<updated>1755164515179</updated>
</task>
<option name="localTasksCounter" value="39" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -447,7 +456,6 @@
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="Roundabout article" />
<MESSAGE value="Add source link" />
<MESSAGE value="Article" />
<MESSAGE value="Add dates" />
@@ -472,10 +480,11 @@
<MESSAGE value="Add a new post" />
<MESSAGE value="Add Chromium full-page screenshot" />
<MESSAGE value="Add a dark theme" />
<option name="LAST_COMMIT_MESSAGE" value="Add a dark theme" />
<MESSAGE value="Add Samsung telescreens" />
<option name="LAST_COMMIT_MESSAGE" value="Add Samsung telescreens" />
</component>
<component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/blog$generate.coverage" NAME="generate Coverage Results" MODIFIED="1755162909719" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/blog$generate.coverage" NAME="generate Coverage Results" MODIFIED="1755164499496" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/blog$main.coverage" NAME="main Coverage Results" MODIFIED="1734783549604" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
</component>
</project>
articles/Inkscape boolean operations in extension.md
@@ -0,0 +1,67 @@
---
title: Using boolean operations in an Inkscape extension
topics: ["inkscape", "python", "tip"]
DATE: 2025-11-22
---
I've wasted 3 days doing this, so I will at least publish my findings on the web
so you don't have to waste another 3 days. I wasn't able to find a good example
online and the docs are not well-made (not saying that they should be a
priority), so figuring it out does require some code research.
The `inkex` module provides lots of things, but boolean operations aren't one of
them. Thus, you will have to call Inkscape yourself; luckily, `inkex` does
provide that.
Even though they are listed as arguments, no special processing is done to the
values, which are still strings.
I leave a minimal example:
~~~python
import inkex
import io
class UnionTwoPaths(inkex.EffectExtension):
def effect(self):
selected = list(self.svg.selection)
if len(selected) != 2:
inkex.errormsg("Select exactly two paths!")
return
# Make a string of the IDs
ids = ",".join([elem.get_id() for elem in selected])
# Call Inkscape to make a union
union_svg_bytes = inkex.command.inkscape_command(
self.document,
select=ids,
actions="path-union"
)
# Send the drawing back
self.document = inkex.load_svg(union_svg_bytes)
if __name__ == "__main__":
UnionTwoPaths().run()
~~~
~~~xml
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Union of two paths</name>
<id>org.inkscape.boolean.union_two_paths</id>
<effect>
<!-- Enable only on path objects (could use 'all' if preferred) -->
<object-type>path</object-type>
<effects-menu>
<submenu name="Generate from Path"/>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">union_two_paths.py</command>
</script>
</inkscape-extension>
~~~