如何在CGContext中正确绘制垂直文本?
垂直排版NSAttributedString到CGContext的实现方案
问题核心
直接旋转水平文本无法激活垂直OpenType特性,也无法处理中文等脚本的直立字形,需要通过TextKit实现原生垂直排版并绘制到CGContext(PDF上下文)。
关键修正点及代码实现
1. 修正TextKit组件关联与布局触发
原代码的组件关联顺序和布局触发逻辑存在问题,需调整确保布局生成:
let attrString = NSAttributedString(string: "Hello 你好!") // 自定义垂直文本容器 class VerticalTextContainer: NSTextContainer { override var layoutOrientation: NSLayoutManager.TextLayoutOrientation { .vertical } } // 初始化文本容器(设置300x300的排版区域) let textContainer = VerticalTextContainer(size: CGSize(width: 300, height: 300)) textContainer.lineBreakMode = .byWordWrapping // 确保文本在垂直容器内自动换行 // 初始化布局管理器并关联文本容器 let textLayoutManager = NSTextLayoutManager() textLayoutManager.textContainer = textContainer // 初始化内容存储,先关联布局管理器再设置字符串 let textContentStorage = NSTextContentStorage() textContentStorage.addTextLayoutManager(textLayoutManager) textContentStorage.attributedString = attrString // 主动触发布局生成(必须调用,否则不会生成布局片段) textLayoutManager.ensureLayout(for: textContentStorage.documentRange)
2. 正确的CGContext绘制逻辑
修正坐标转换和布局片段绘制方式,确保内容显示:
func drawVerticalText(in cgContext: CGContext, canvasSize: CGSize) { // 保存上下文状态,避免影响后续绘制 cgContext.saveGState() // 适配PDF上下文的坐标系统(原点从左下角转为左上角) cgContext.translateBy(x: 0, y: canvasSize.height) cgContext.scaleBy(x: 1, y: -1) // 设置文本填充色 cgContext.setFillColor(NSColor.black.cgColor) // 遍历所有布局片段并绘制 textLayoutManager.enumerateTextLayoutFragments(from: nil, options: .ensuresLayout) { fragment, stop in // 绘制原点对应文本容器的位置,可根据需求调整偏移(示例为左上角对齐) let drawOrigin = CGPoint(x: 0, y: 0) fragment.draw(at: drawOrigin, in: cgContext) return true } // 恢复上下文状态 cgContext.restoreGState() }
注意事项
- 字体支持:确保使用的字体支持垂直排版特性(如PingFang SC、Noto Sans SC等),否则中文等脚本可能无法正确显示直立字形。
- 布局范围:如果需要绘制部分文本,可修改
ensureLayout(for:)的参数为指定范围,避免不必要的布局计算。 - 坐标偏移:若需要将文本绘制在画布的指定位置,可调整
drawOrigin的坐标值,比如要偏移(100,100),则设置为CGPoint(x:100, y:100)。
内容的提问来源于stack exchange,提问作者Peter Nowell
相关产品推荐
相关产品推荐

