# draw-image **Repository Path**: 3433/draw-image ## Basic Information - **Project Name**: draw-image - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-09 - **Last Updated**: 2026-07-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Python 配置化画图工具 通过 `config.json` 描述画布、层级、样式和输出文件,然后用命令行生成图片。 ## 安装依赖 ```bash python3 -m pip install -r requirements.txt ``` ## 使用方式 ```bash python3 draw.py config.example.json ``` 也可以把配置里的相对输出路径统一放到指定目录: ```bash python3 draw.py config.example.json --out-dir ./dist ``` 临时开启测试模式: ```bash python3 draw.py config.example.json --debug --debug-interval 100 ``` ## 顶层配置 推荐结构: ```json { "vars": {}, "defaults": {}, "templates": {}, "scenes": {}, "outputs": [] } ``` 兼容最外层直接写数组的老式结构,数组会被当作 `outputs`。 每个输出项推荐直接使用 `children` 定义根层级: ```json { "output": "./dist/example.png", "width": 1000, "height": 700, "background": "#ffffff", "children": [] } ``` 旧版 `contents` 仍然兼容,它表示一个显式根节点。普通节点里如果写了 `contents`,也会被当成 `children` 处理。 ## 场景复用和输出补丁 多个输出结构相同、图片和文字不同时,可以在 `scenes` 中定义一次完整节点树。每个输出通过 `scene` 引用,并用独立的 `vars` 提供图片路径等内容: ```json { "scenes": { "front": { "id": "root", "type": "layout", "children": [ { "id": "background", "type": "image", "src": "$src", "width": 961, "height": 1636 }, { "id": "title", "type": "text", "x": 480, "y": 238, "text": "$title" }, { "id": "subtitle", "type": "text", "x": 480, "y": 334, "text": "$subtitle" } ] } }, "outputs": [ { "output": "./dist/front-a.png", "width": 961, "height": 1636, "scene": "front", "vars": { "src": "./assets/a.png", "title": "标题 A", "subtitle": "副标题 A" } }, { "output": "./dist/front-b.png", "width": 961, "height": 1636, "scene": "front", "vars": { "src": "./assets/b.png", "title": "标题 B", "subtitle": "副标题 B" }, "patches": [ { "target": "title", "set": { "y": 260, "fill": "255,255,255,255" } }, { "target": "subtitle", "remove": true }, { "target": "title", "after": { "id": "seal", "type": "rect", "x": 700, "y": 200, "width": 60, "height": 110, "fill": "#a64326" } } ] } ] } ``` 场景内用于补丁定位的 `id` 必须唯一。`patches` 按数组顺序执行,支持: | 操作 | 说明 | | --- | --- | | `set` | 递归合并修改目标节点属性 | | `remove` | 删除目标节点 | | `replace` | 完整替换目标节点 | | `before`、`after` | 在目标节点前后插入节点 | | `appendChildren`、`prependChildren` | 在目标节点的子节点末尾或开头插入 | 每个补丁必须且只能包含一个操作。插入类操作既可以配置一个节点,也可以配置节点数组。`patches` 也可以直接用于带有 `children` 或 `contents` 的普通输出。 ## 通用节点属性 | 属性 | 说明 | | --- | --- | | `type` | 节点类型 | | `x`, `y` | 节点坐标 | | `coordinate` | `parent` 相对父级,`global` 相对画布,默认 `parent` | | `anchor.origin` | 坐标起点位于父级/画布的哪个位置,默认 `topLeft` | | `anchor.current` | `x/y` 对准当前节点自身的哪个位置,默认 `topLeft` | | `native` | 透传给 Pillow 绘制方法的原生参数 | | `visible` | 是否绘制,默认 `true` | | `opacity` | 透明度,范围 `0-1` | | `blur` | 高斯模糊半径,是单个 `blur` 效果的简写 | | `effects` | 按顺序执行的图层效果数组 | | `rotate` | 旋转角度,单位度 | | `zIndex` | 同级绘制顺序,越小越早绘制 | | `children` | 子节点 | | `extends` | 继承 `templates` 里的模板 | ## 坐标和锚点 默认情况下,`x/y` 表示节点左上角位置: ```json { "type": "rect", "x": 100, "y": 100, "width": 200, "height": 120 } ``` 如果希望 `x/y` 表示中心点,可以设置 `anchor`: ```json { "type": "rect", "x": 500, "y": 300, "anchor": { "current": "center" }, "width": 200, "height": 120 } ``` 如果希望从父级或画布的中心、右下角等位置开始计算 `x/y`,可以设置 `anchor.origin`: ```json { "type": "text", "coordinate": "global", "anchor": { "origin": "bottomRight", "current": "bottomRight" }, "x": -40, "y": -40, "text": "右下角水印" } ``` 这里的含义是:先把全局画布的右下角作为坐标起点,再用 `x/y` 向左上偏移 40 像素,最后让文本自身的右下角对齐这个点。 ## 逐字排版 需要精确控制中文横排、纵排和字间距时,使用项目级逐字排版,不需要把变量内容改成数组: ```json { "vars": { "venue": "惠居云", "seal": "归家" }, "outputs": [ { "output": "./dist/text.png", "width": 800, "height": 600, "children": [ { "type": "text", "x": 400, "y": 120, "anchor": { "current": "center" }, "text": "$venue", "writingMode": "horizontal", "letterSpacing": 50 }, { "type": "text", "x": 650, "y": 260, "anchor": { "current": "center" }, "text": "$seal", "writingMode": "vertical", "letterSpacing": 0 } ] } ] } ``` `letterSpacing` 是相邻字符之间额外增加的像素距离,可以为负数。`lineSpacing` 在横排时控制行距,在纵排时控制多列之间的距离。 使用 `glyphOverrides` 可以按索引微调单字,换行符不计入索引: ```json { "type": "text", "text": "$seal", "writingMode": "vertical", "letterSpacing": 0, "glyphOverrides": [ { "index": 0, "offsetX": -2, "rotate": -3 }, { "index": 1, "offsetX": 2, "offsetY": 4, "fontSize": 54, "fill": "255,239,210,246" } ] } ``` 单字支持覆盖 `char`、`offsetX`、`offsetY`、`rotate`、`fontSize`、`fontPath`、`fill`、`stroke`、`strokeWidth`、`opacity` 和 `visible`。整段文字的真实包围盒会包含单字偏移与旋转,仍可正常使用 `anchor.current`。 ## Pillow 原生参数 节点可以通过 `native` 配置 Pillow 原生参数。项目自身的层级定位属性仍然放在节点外层,避免与 Pillow 同名参数冲突: ```json { "type": "text", "x": 480, "y": 300, "text": "纵向文字", "fontSize": 48, "native": { "direction": "ttb", "anchor": "mm", "language": "zh-CN" } } ``` 配置 `text.native.anchor` 后,`x/y` 会作为 Pillow 原生锚点坐标。它不能和项目定位用的 `anchor.current` 同时配置,但仍然可以使用 `anchor.origin`: ```json { "type": "text", "x": 0, "y": 0, "anchor": { "origin": "center" }, "native": { "anchor": "mm" }, "text": "画布中心" } ``` 当前支持的原生参数: | 节点类型 | `native` 参数 | | --- | --- | | `text` | `anchor`、`direction`、`features`、`language`、`embedded_color`、`spacing`、`align` | | `rect` | `corners`,依次控制左上、右上、右下、左下圆角 | | `line` | `joint`,可配置为 `curve` | 其他节点暂时没有 Pillow 独有且适合 JSON 表达的参数。未知参数会直接报告配置错误,不会静默忽略。`direction: "ttb"` 等高级文字排版能力需要当前 Pillow、字体和排版引擎共同支持。 逐字字距或单字微调应优先使用 `writingMode`、`letterSpacing` 和 `glyphOverrides`。项目级逐字排版不能与 `text.native.anchor` 或 `text.native.direction` 同时使用。 ## 图层效果 推荐使用 `effects` 数组配置效果,数组顺序就是执行顺序: ```json { "type": "ellipse", "x": 180, "y": 100, "width": 640, "height": 360, "fill": "255,243,210,84", "effects": [ { "type": "blur", "radius": 38 }, { "type": "blend", "mode": "softLight" } ] } ``` 支持的效果: | 效果 | 属性 | 说明 | | --- | --- | --- | | `blur` | `radius` | 对当前节点及其子节点执行高斯模糊 | | `shadow` | `x`、`y`、`blur`、`color` | 根据当前图层透明轮廓生成下层阴影 | | `blend` | `mode` | 设置当前节点与已经绘制内容的混合模式 | | `clip` | `enabled` | 把节点及其子节点裁剪在当前节点几何区域内 | `blend.mode` 支持 `normal`、`multiply`、`screen`、`overlay`、`softLight`、`darken` 和 `lighten`。 简单模糊也可以直接写 `"blur": 38`。如果同时配置 `blur` 和 `effects`,简写的 `blur` 会最先执行。 `anchor.origin` 和 `anchor.current` 都可以只配置其中一个: ```json { "anchor": { "origin": "center" } } ``` 支持的锚点:`topLeft`、`top`、`topCenter`、`topRight`、`left`、`centerLeft`、`center`、`right`、`centerRight`、`bottomLeft`、`bottom`、`bottomCenter`、`bottomRight`。 也可以用比例自定义: ```json { "anchor": { "origin": { "x": 0.5, "y": 0.5 }, "current": { "x": 0.5, "y": 0.5 } } } ``` 旧版 `anchor: "center"` 仍然兼容,表示 `anchor.current`。 ## 测试模式 测试模式会在最终图片上叠加网格和标尺,方便微调元素坐标。 可以在全局或单个输出文件里配置: ```json { "debug": { "enabled": true, "grid": true, "ruler": true, "interval": 100, "subInterval": 20, "gridColor": "#1677ff33", "subGridColor": "#64748b22", "rulerColor": "#111827cc", "labelColor": "#111827", "rulerSize": 24 } } ``` 也可以命令行临时开启,不改配置文件: ```bash python3 draw.py config.example.json --debug --debug-interval 50 ``` ## JSON Schema 自动补全 项目已提供 [draw.schema.json](./draw.schema.json)。有两种用法: 1. 在每个配置文件顶部写 `$schema`: ```json { "$schema": "./draw.schema.json", "outputs": [] } ``` 2. 使用项目里的 [.vscode/settings.json](./.vscode/settings.json) 自动匹配多个配置文件: ```json { "json.schemas": [ { "fileMatch": ["/config*.json", "/*.draw.json", "/configs/*.json"], "url": "./draw.schema.json" } ] } ``` 默认会匹配: | 文件 | 说明 | | --- | --- | | `config.json`、`config.example.json`、`config.xxx.json` | 常规配置文件 | | `*.draw.json` | 推荐用于多配置文件命名 | | `configs/*.json` | 配置目录下的文件 | ## 支持的节点类型 | type | 说明 | | --- | --- | | `layout` | 容器,可设置背景和子节点 | | `rect` | 矩形 / 圆角矩形 | | `circle` | 圆形 | | `ellipse` | 椭圆 | | `arc` | 圆弧 / 椭圆弧 | | `line` | 直线 | | `text` | 文本,支持中文字体 | | `image` | 图片,支持裁剪和缩放 | | `polygon` | 多边形 | ## 圆弧节点 `arc` 使用 `x/y/width/height` 定义外接矩形,用 `start/end` 定义角度范围: ```json { "type": "arc", "x": 100, "y": 100, "width": 200, "height": 120, "start": 0, "end": 180, "stroke": "22,119,255,255", "strokeWidth": 6 } ``` 角度规则沿用 Pillow:`0` 度在 3 点钟方向,图片坐标系里角度顺时针增加。 ## 图片节点 ```json { "type": "image", "src": "./avatar.png", "x": 100, "y": 100, "width": 300, "height": 300, "fit": "cover", "source": { "x": 20, "y": 30, "width": 400, "height": 400 } } ``` `fit` 支持: | fit | 说明 | | --- | --- | | `fill` | 拉伸到目标宽高 | | `contain` | 完整显示,可能留白 | | `cover` | 铺满区域,可能裁剪 | | `none` | 使用原始尺寸 | ## 变量和模板 变量支持 `$name` 和 `${name}`: ```json { "vars": { "primary": "#1677ff" } } ``` 模板支持用 `extends` 继承,节点自身属性会覆盖模板属性。 ## 颜色写法 颜色字段如 `background`、`fill`、`stroke`、`gridColor` 等支持多种写法: ```json { "fill": "#1677ff", "stroke": "255,255,255,255", "background": [245, 247, 251, 255] } ``` 其中 `"255,255,255"` 会按不透明 RGB 处理,等价于 `"255,255,255,255"`。