You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python缩放SVG矢量图,实现Inkscape手写字体自动化生成?

SVG字符路径的缩放与平移自动化实现

针对你从Remarkable2导出的手写字符SVG,需要将其批量缩放并平移到目标位置的需求,以下是具体的数学逻辑和Python实现方案:

核心变换逻辑

SVG坐标系以左上角为原点,Y轴向下延伸。要将原始字符的 bounding box(X:167.996, Y:86.660, W:32.623, H:38.221)映射到目标区域(X:126, Y:338, W:504, H:620),需按以下步骤计算坐标变换:

  1. 计算缩放因子:分别基于宽度和高度计算缩放比例

    • 宽度缩放因子:scale_x = 目标宽度 / 原始宽度 = 504 / 32.623 ≈ 15.45
    • 高度缩放因子:scale_y = 目标高度 / 原始高度 = 620 / 38.221 ≈ 16.22
  2. 计算坐标平移:先将原始字符的左上角对齐到原点,缩放后再平移到目标位置。对任意原始坐标点(x, y),变换公式为:

    • 变换后X坐标:x' = (x - 原始X) * scale_x + 目标X
    • 变换后Y坐标:y' = (y - 原始Y) * scale_y + 目标Y

Python代码实现

使用内置的xml.etree.ElementTree解析SVG,处理路径的d属性,同时兼容绝对/相对坐标指令:

import xml.etree.ElementTree as ET
import re

def transform_path_d(d_str, orig_x, orig_y, orig_w, orig_h, dest_x, dest_y, dest_w, dest_h):
    # 计算缩放比例
    scale_x = dest_w / orig_w
    scale_y = dest_h / orig_h
    
    # 拆分路径指令与坐标
    pattern = re.compile(r'([a-zA-Z])([^a-zA-Z]*)')
    path_parts = pattern.findall(d_str)
    
    transformed_parts = []
    current_abs_x = 0
    current_abs_y = 0
    
    for cmd, coord_str in path_parts:
        # 提取坐标数值
        coords = list(map(float, re.findall(r'-?\d+\.?\d*', coord_str)))
        new_coords = []
        
        # 按坐标对处理(适配M/L/C等指令)
        for i in range(0, len(coords), 2):
            x = coords[i]
            y = coords[i+1] if i+1 < len(coords) else 0
            
            if cmd.isupper():
                # 绝对坐标直接变换
                trans_x = (x - orig_x) * scale_x + dest_x
                trans_y = (y - orig_y) * scale_y + dest_y
                current_abs_x, current_abs_y = trans_x, trans_y
            else:
                # 相对坐标先转绝对,变换后再转回相对
                abs_x = current_abs_x + x
                abs_y = current_abs_y + y
                trans_abs_x = (abs_x - orig_x) * scale_x + dest_x
                trans_abs_y = (abs_y - orig_y) * scale_y + dest_y
                trans_x = trans_abs_x - current_abs_x
                trans_y = trans_abs_y - current_abs_y
                current_abs_x, current_abs_y = trans_abs_x, trans_abs_y
            
            new_coords.append(f"{trans_x:.3f}")  # 保留3位小数精简路径
            new_coords.append(f"{trans_y:.3f}")
        
        transformed_parts.append(f"{cmd} {' '.join(new_coords)}")
    
    return ' '.join(transformed_parts)

def process_single_svg(input_svg, output_svg, orig_bbox, dest_bbox):
    # 解析原始与目标bounding box参数
    orig_x, orig_y, orig_w, orig_h = orig_bbox
    dest_x, dest_y, dest_w, dest_h = dest_bbox
    
    tree = ET.parse(input_svg)
    root = tree.getroot()
    ns = {'svg': 'http://www.w3.org/2000/svg'}  # SVG命名空间
    
    # 遍历所有路径(假设已移除图层,路径在根目录)
    for path in root.findall('.//svg:path', ns):
        orig_d = path.get('d')
        if orig_d:
            new_d = transform_path_d(orig_d, orig_x, orig_y, orig_w, orig_h, dest_x, dest_y, dest_w, dest_h)
            path.set('d', new_d)
    
    # 保存变换后的SVG
    tree.write(output_svg, encoding='utf-8', xml_declaration=True)

# 示例:处理字母A的SVG
process_single_svg(
    input_svg='a_original.svg',
    output_svg='a_transformed.svg',
    orig_bbox=(167.996, 86.660, 32.623, 38.221),
    dest_bbox=(126, 338, 504, 620)
)

批量处理优化

如果每个字符的原始bounding box不同,可以用svgpathtools自动获取每个SVG的路径边界,无需手动输入参数:

from svgpathtools import svg2paths

def get_svg_bbox(svg_path):
    paths, _ = svg2paths(svg_path)
    min_x, min_y, max_x, max_y = paths[0].bbox()
    return (min_x, min_y, max_x - min_x, max_y - min_y)

# 批量处理示例(遍历所有字符SVG)
import os

input_dir = 'raw_chars'
output_dir = 'transformed_chars'
os.makedirs(output_dir, exist_ok=True)
dest_bbox = (126, 338, 504, 620)

for filename in os.listdir(input_dir):
    if filename.endswith('.svg'):
        input_path = os.path.join(input_dir, filename)
        output_path = os.path.join(output_dir, filename)
        orig_bbox = get_svg_bbox(input_path)
        process_single_svg(input_path, output_path, orig_bbox, dest_bbox)

注意事项

  • 命名空间处理:SVG文件通常包含xmlns属性,必须在查找路径时指定命名空间,否则无法匹配<path>标签
  • 特殊指令支持:上述代码适配了大部分常用路径指令(M/L/C/Q等),若遇到单坐标指令(H/V/h/v),需额外补充逻辑处理
  • 精度控制:通过:.3f保留三位小数,避免路径字符串过于冗长,同时保证视觉精度

内容的提问来源于stack exchange,提问作者Joshua Ritchie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 22:50:29