Python实现字体字形修改与矢量数据提取

一、字体处理基础

在Python中处理字体文件主要涉及以下几个关键库:

  • fontTools:专业的字体文件操作库
  • freetype-py:FreeType的Python绑定
  • matplotlib:用于可视化字体轮廓
  • numpy:处理矢量数据
图片[1]_Python实现字体字形修改与矢量数据提取_知途无界

首先安装必要的库:

pip install fonttools freetype-py matplotlib numpy

二、提取字体矢量数据

1. 使用fontTools提取字形轮廓

from fontTools.ttLib import TTFont
from fontTools.pens.basePen import BasePen
import matplotlib.pyplot as plt
import numpy as np

def extract_glyph_vectors(font_path, char='A'):
    # 加载字体文件
    font = TTFont(font_path)

    # 获取字形轮廓
    glyph_set = font.getGlyphSet()
    glyph = glyph_set[char]

    # 自定义Pen来收集坐标点
    class VectorPen(BasePen):
        def __init__(self, glyphSet):
            BasePen.__init__(self, glyphSet)
            self.points = []
            self.types = []

        def _moveTo(self, pt):
            self.points.append(pt)
            self.types.append('M')  # MoveTo

        def _lineTo(self, pt):
            self.points.append(pt)
            self.types.append('L')  # LineTo

        def _curveToOne(self, pt1, pt2, pt3):
            self.points.extend([pt1, pt2, pt3])
            self.types.extend(['C', 'C', 'C'])  # CurveTo

    pen = VectorPen(glyph_set)
    glyph.draw(pen)

    return np.array(pen.points), pen.types

# 示例使用
points, types = extract_glyph_vectors('arial.ttf', 'B')
print(f"提取到{len(points)}个坐标点")

2. 使用freetype-py提取字形轮廓

import freetype
import numpy as np

def get_glyph_outline(font_path, char='A', size=64):
    # 加载字体
    face = freetype.Face(font_path)
    face.set_char_size(size * 64)

    # 加载字形
    face.load_char(char, freetype.FT_LOAD_DEFAULT | freetype.FT_LOAD_NO_BITMAP)

    # 获取轮廓
    outline = face.glyph.outline
    points = np.array(outline.points, dtype=[('x', 'f4'), ('y', 'f4')])
    tags = outline.tags

    return points, tags

# 示例使用
points, tags = get_glyph_outline('arial.ttf', 'C')
print(f"提取到{len(points)}个轮廓点")

三、可视化字体轮廓

def plot_glyph(points, types):
    plt.figure(figsize=(10, 10))

    # 绘制轮廓
    start_idx = 0
    for i, (point, typ) in enumerate(zip(points, types)):
        if typ == 'M':
            start_idx = i
        elif typ == 'L':
            plt.plot([points[i-1][0], point[0]], 
                    [points[i-1][1], point[1]], 'b-')
        elif typ == 'C':
            if i+2 < len(points) and types[i+1] == 'C' and types[i+2] == 'C':
                # 三次贝塞尔曲线
                p0 = points[i-1] if i > 0 else points[0]
                p1, p2, p3 = points[i], points[i+1], points[i+2]
                t = np.linspace(0, 1, 20)
                x = (1-t)**3*p0[0] + 3*(1-t)**2*t*p1[0] + 3*(1-t)*t**2*p2[0] + t**3*p3[0]
                y = (1-t)**3*p0[1] + 3*(1-t)**2*t*p1[1] + 3*(1-t)*t**2*p2[1] + t**3*p3[1]
                plt.plot(x, y, 'r-')

    plt.axis('equal')
    plt.gca().invert_yaxis()  # 字体坐标系Y轴向下
    plt.title('Glyph Outline')
    plt.show()

# 示例使用
points, types = extract_glyph_vectors('arial.ttf', 'S')
plot_glyph(points, types)

四、修改字体字形

1. 简单变形(缩放、旋转)

def transform_glyph(points, scale=1.0, angle=0):
    # 转换为numpy数组便于计算
    pts = np.array(points)

    # 缩放
    if scale != 1.0:
        pts *= scale

    # 旋转(角度制)
    if angle != 0:
        rad = np.deg2rad(angle)
        rot = np.array([[np.cos(rad), -np.sin(rad)],
                       [np.sin(rad), np.cos(rad)]])
        pts = np.dot(pts, rot)

    return pts.tolist()

# 示例:放大1.5倍并旋转30度
modified_points = transform_glyph(points, scale=1.5, angle=30)
plot_glyph(modified_points, types)

2. 更复杂的变形(正弦波变形)

def sin_warp_glyph(points, amplitude=10, frequency=0.1):
    pts = np.array(points)

    # 对x坐标应用正弦变形
    x = pts[:, 0]
    y = pts[:, 1]
    y += amplitude * np.sin(frequency * x)

    return pts.tolist()

# 示例:正弦波变形
warped_points = sin_warp_glyph(points, amplitude=15, frequency=0.05)
plot_glyph(warped_points, types)

五、创建新字体文件

from fontTools.ttLib import TTFont
from fontTools.pens.recordingPen import RecordingPen
from fontTools.pens.ttGlyphPen import TTGlyphPen

def create_modified_font(original_font_path, output_path, modifications):
    # 加载原始字体
    font = TTFont(original_font_path)

    # 获取字形集
    glyph_set = font.getGlyphSet()

    # 创建新字体
    new_font = TTFont()
    new_font.setGlyphOrder(font.getGlyphOrder())

    # 复制基本表
    for table in ['head', 'hhea', 'maxp', 'hmtx', 'cmap', 'name', 'OS/2']:
        new_font[table] = font[table]

    # 创建glyf表
    glyf = new_font['glyf'] = font['glyf'].__class__()

    # 修改每个字形
    for glyph_name in font.getGlyphOrder():
        # 获取原始字形
        glyph = glyph_set[glyph_name]

        # 创建记录笔
        pen = RecordingPen()
        glyph.draw(pen)

        # 应用修改
        if glyph_name in modifications:
            modified_pen = modifications[glyph_name](pen)
            pen = modified_pen

        # 创建新的字形
        tt_pen = TTGlyphPen(glyph_set)
        pen.replay(tt_pen)
        glyf[glyph_name] = tt_pen.glyph()

    # 保存新字体
    new_font.save(output_path)
    print(f"新字体已保存到: {output_path}")

# 示例:创建一个将'A'放大的字体
def modify_A(pen):
    # 这里可以实现具体的修改逻辑
    # 这是一个简单的示例,实际应用需要更复杂的处理
    class ModifiedPen(RecordingPen):
        def __init__(self, original_pen):
            super().__init__()
            self.original_pen = original_pen

        def replay(self, target_pen):
            # 这里可以修改坐标点
            for operation in self.original_pen.value:
                cmd, args = operation
                if cmd == 'lineTo':
                    # 放大1.5倍
                    args = [(x*1.5, y*1.5) for x, y in args]
                target_pen.__getattribute__(cmd)(*args)
    return ModifiedPen(pen)

# 使用示例
modifications = {'A': modify_A}
create_modified_font('arial.ttf', 'modified_arial.ttf', modifications)

六、高级应用:自动调整字形

def auto_adjust_glyph(points, types, adjustment_func):
    """
    自动调整字形轮廓
    :param points: 原始点集
    :param types: 点类型
    :param adjustment_func: 调整函数,接收(x,y)返回新(x,y)
    :return: 调整后的点集
    """
    adjusted_points = []
    for point in points:
        x, y = point
        new_x, new_y = adjustment_func(x, y)
        adjusted_points.append((new_x, new_y))
    return adjusted_points

# 示例:使字形更圆润
def make_rounder(x, y):
    # 简单的圆形变形
    r = np.sqrt(x**2 + y**2)
    theta = np.arctan2(y, x)
    new_r = r * (1 + 0.1 * np.sin(3 * theta))  # 添加三次谐波
    return new_r * np.cos(theta), new_r * np.sin(theta)

# 使用示例
points, types = extract_glyph_vectors('arial.ttf', 'B')
adjusted_points = auto_adjust_glyph(points, types, make_rounder)
plot_glyph(adjusted_points, types)

七、注意事项

  1. 字体授权:修改字体前请确保您有该字体的修改权限
  2. 轮廓完整性:修改时要注意保持轮廓的闭合性和正确方向
  3. 控制点处理:贝塞尔曲线的控制点需要特殊处理
  4. 字距调整:修改字形后可能需要调整字距(hmtx表)
  5. 字体Hinting:修改后的字体可能需要重新设置Hinting

通过以上方法,您可以实现从简单的字形调整到复杂的矢量变形等各种字体处理操作。这些技术可以应用于字体设计、艺术创作、品牌定制等多种场景。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞47 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容