diff --git a/assets/webui/index.html b/assets/webui/index.html
index 75587aa..a3b9f46 100644
--- a/assets/webui/index.html
+++ b/assets/webui/index.html
@@ -227,19 +227,19 @@
渐变
图案
-
+
@@ -788,7 +788,7 @@
diff --git a/assets/webui/ofdgo.css b/assets/webui/ofdgo.css
index 45f0616..cded1e5 100644
--- a/assets/webui/ofdgo.css
+++ b/assets/webui/ofdgo.css
@@ -1214,7 +1214,7 @@ button:not(:disabled):hover {
}
.info-custom-row input,
-.gradient-stop-row input,
+.gradient-stop-row input[type="number"],
.structure-attribute > input,
.form-row input[type="text"],
.form-row input[type="password"],
@@ -1260,8 +1260,8 @@ button:not(:disabled):hover {
.info-custom-row {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) var(--control-height);
- gap: 6px;
- margin: 6px 0;
+ gap: 8px;
+ margin: 8px 0;
}
#infoCustomRows > :first-child {
@@ -1271,8 +1271,8 @@ button:not(:disabled):hover {
.gradient-stop-row {
display: grid;
grid-template-columns: 44px minmax(0, 1fr) minmax(0, 1fr) var(--control-height);
- gap: 6px;
- margin: 6px 0;
+ gap: 8px;
+ margin: 8px 0;
align-items: center;
}
@@ -1280,9 +1280,27 @@ button:not(:disabled):hover {
font-size: 12px;
}
+.gradient-stop-head,
+#gradientStops {
+ overflow-y: auto;
+ scrollbar-gutter: stable;
+}
+
#gradientStops {
max-height: 180px;
- overflow-y: auto;
+ margin-bottom: 8px;
+}
+
+#gradientStops > :first-child {
+ margin-top: 0;
+}
+
+#gradientStops > :last-child {
+ margin-bottom: 0;
+}
+
+.gradient-stop-row .color-input {
+ width: 100%;
}
#infoCustomRows :focus-visible,
diff --git a/assets/webui/ofdgo.js b/assets/webui/ofdgo.js
index aa3643d..27b17fb 100644
--- a/assets/webui/ofdgo.js
+++ b/assets/webui/ofdgo.js
@@ -887,7 +887,7 @@ function addCustomDataRow(field = {Name: "", Value: ""}, sourceIndex) {
}
const remove = document.createElement("button");
remove.type = "button";
- remove.className = "small-button";
+ remove.className = "icon-button";
remove.textContent = "×";
remove.title = "删除字段";
remove.setAttribute("aria-label", "删除字段");
@@ -2045,6 +2045,7 @@ function addGradientStop(position, color, transparency, source) {
for (const [type, value, label] of [["color", color, "色标颜色"], ["number", position, "色标位置"], ["number", transparency, "色标透明度"]]) {
const input = document.createElement("input");
input.type = type;
+ if (type === "color") input.className = "color-input";
input.value = value;
input.disabled = el.gradientKind.value === "keep";
input.setAttribute("aria-label", label);
@@ -2054,7 +2055,7 @@ function addGradientStop(position, color, transparency, source) {
}
const remove = document.createElement("button");
remove.type = "button";
- remove.className = "small-button";
+ remove.className = "icon-button";
remove.textContent = "×";
remove.title = "删除色标";
remove.setAttribute("aria-label", "删除色标");
@@ -5619,8 +5620,8 @@ function renderStyleResourceSettings(kind) {
}
settingsField(content, "斜体", "Italic", value.Italic, "checkbox");
settingsSelect(content, "端点", "Cap", [["", "继承"], ["Butt", "平头"], ["Round", "圆头"], ["Square", "方头"]], value.Cap || "");
- settingsSelect(content, "连接", "Join", [["", "继承"], ["Miter", "尖角"], ["Round", "圆角"], ["Bevel", "斜角"]], value.Join || "");
- settingsField(content, "虚线", "DashPattern", value.DashPattern || "");
+ settingsSelect(content, "转角", "Join", [["", "继承"], ["Miter", "尖角"], ["Round", "圆角"], ["Bevel", "斜角"]], value.Join || "");
+ settingsField(content, "间隔", "DashPattern", value.DashPattern || "").title = "虚线间隔";
settingsField(content, "偏移", "DashOffset", value.DashOffset ?? "", "number", "mm");
for (const [key, label] of [["FillColor", "填充"], ["StrokeColor", "描边"]]) {
settingsSelect(content, label, key + "Mode", [["keep", "原有"], ["inherit", "继承"], ["solid", "颜色"]], "keep");
@@ -5641,7 +5642,7 @@ function renderStyleResourceSettings(kind) {
if (kind === "ColorSpace") {
settingsSelect(binding, "应用", "PaintTarget", [["fill", "填充"], ["stroke", "描边"]], "fill");
settingsSelect(binding, "方式", "PaintMode", [["value", "分量"], ["index", "索引"]], "value");
- settingsField(binding, "颜色值", "PaintValue", value.Type === "CMYK" ? `0 0 0 ${2 ** (value.BitsPerComponent || 8) - 1}` : value.Type === "GRAY" ? "0" : "0 0 0");
+ settingsField(binding, "色值", "PaintValue", value.Type === "CMYK" ? `0 0 0 ${2 ** (value.BitsPerComponent || 8) - 1}` : value.Type === "GRAY" ? "0" : "0 0 0");
}
const apply = settingsButton("应用", () => applyStyleResource(kind), !objects.value, "Apply", group.actions);
const unbind = kind === "DrawParam" ? settingsButton("解绑", () => applyStyleResource(kind, true), !objects.value, "Unbind", group.actions) : null;
@@ -5784,7 +5785,8 @@ function renderTemplateSettings() {
settingsElements.Content.append(source);
}
settingsPages(source, "SourcePage", settings.page);
- document.querySelector("#settingsSourcePage").closest("label").firstElementChild.textContent = "来源页";
+ document.querySelector("#settingsSourcePage").closest("label").firstElementChild.textContent = "来源";
+ document.querySelector("#settingsSourcePage").setAttribute("aria-label", "来源页");
const write = async (operation, order = 0) => {
if (operation === "replace" && !window.confirm("替换模板内容?当前文档中引用此模板的页面将同步更新。")) return;
const selected = settings.selected;
@@ -6626,7 +6628,7 @@ function updateSignPlacement() {
input.disabled = !stamp;
input.required = stamp;
}
- document.querySelector('label[for="signX"]').textContent = el.signPlacement.value === "seam" ? "右侧内缩" : "横坐标";
+ document.querySelector('label[for="signX"]').textContent = el.signPlacement.value === "seam" ? "右侧内缩" : "横向";
el.signX.setAttribute("aria-label", el.signPlacement.value === "seam" ? "右侧内缩(毫米)" : "横坐标(毫米)");
}
@@ -7354,7 +7356,7 @@ function createFlowShell(page) {
surface.className = "page-surface";
const placeholder = document.createElement("div");
placeholder.className = "page-placeholder";
- placeholder.textContent = `第 ${page.index + 1} 页`;
+ placeholder.textContent = `第${page.index + 1}页`;
shell.append(surface, placeholder);
layoutPageShell(shell, page);
return shell;
@@ -8728,7 +8730,7 @@ function renderSearchResults() {
button.dataset.matchIndex = String(i);
const page = document.createElement("span");
page.className = "search-page";
- page.textContent = `第 ${match.page + 1} 页`;
+ page.textContent = `第${match.page + 1}页`;
const text = document.createElement("span");
const mark = document.createElement("mark");
mark.textContent = match.text;
@@ -8922,7 +8924,7 @@ function createOutlineList(outlines, path = []) {
const page = document.createElement("span");
page.className = "outline-page";
page.textContent = String(outline.page);
- page.setAttribute("aria-label", `第 ${outline.page} 页`);
+ page.setAttribute("aria-label", `第${outline.page}页`);
link.append(page);
}
if (outline.actionCount || outline.page || state.editing) {
@@ -9168,7 +9170,7 @@ function createThumbnail(page) {
const label = document.createElement("span");
label.className = "thumb-label";
- label.textContent = `第 ${page.index + 1} 页`;
+ label.textContent = `第${page.index + 1}页`;
const size = document.createElement("span");
size.className = "thumb-size";
diff --git a/internal/webui/webui.go b/internal/webui/webui.go
index 72a6711..64aedf8 100644
--- a/internal/webui/webui.go
+++ b/internal/webui/webui.go
@@ -39,7 +39,7 @@ type OpenOptions struct {
// FontFile 字体文件
type FontFile = ofdgo.FontFile
-// FontInfo OFD字体信息
+// FontInfo 文档字体信息
type FontInfo = ofdgo.FontInfo
// Session WebUI文档会话
diff --git a/ofdgo_drawparam.go b/ofdgo_drawparam.go
index 1916ccc..c13aa4a 100644
--- a/ofdgo_drawparam.go
+++ b/ofdgo_drawparam.go
@@ -17,7 +17,7 @@ package ofdgo
// defaultPathLineWidth 为默认描边宽度,单位为毫米
const defaultPathLineWidth = 0.353
-// defaultMiterLimit 为默认尖角限制
+// defaultMiterLimit 为默认尖角截断值
const defaultMiterLimit = 3.528
// mergeGraphicObjectAlpha 合并图形对象透明度
diff --git a/ofdgo_editor_color.go b/ofdgo_editor_color.go
index f5301d0..9769052 100644
--- a/ofdgo_editor_color.go
+++ b/ofdgo_editor_color.go
@@ -109,7 +109,7 @@ func (e *Editor) AddColorSpace(space ColorSpace, profile []byte) (string, error)
// GradientStops 解析当前文档的渐变分段,保留顺序并合并透明度
// 入参: segments 渐变分段, alpha 外层透明度
-// 返回: []ColorStop 后端无关的色标, error 颜色解析错误
+// 返回: []ColorStop 后端无关的渐变色标, error 颜色解析错误
func (e *Editor) GradientStops(segments []ShdSegment, alpha *int) ([]ColorStop, error) {
positions := gradientPositions(segments)
stops := make([]ColorStop, len(segments))
diff --git a/ofdgo_editor_composite_insert.go b/ofdgo_editor_composite_insert.go
index 8e03cef..e56547f 100644
--- a/ofdgo_editor_composite_insert.go
+++ b/ofdgo_editor_composite_insert.go
@@ -175,7 +175,7 @@ func (e *Editor) wrapCompositePath(renderer *Renderer, node *editorCompositeNode
return e.vectorInstance(renderer, []*editorCompositeNode{node})
}
-// isolateCompositeStyle 固定对象的绘制参数,避免目标容器改变颜色、虚线和线帽
+// isolateCompositeStyle 固定对象的绘制参数,避免目标容器改变颜色、虚线和端点样式
// 入参: node 页面坐标中的对象节点
// 返回: error 错误信息
func (e *Editor) isolateCompositeStyle(node *editorCompositeNode) error {
diff --git a/ofdgo_editor_resource_style.go b/ofdgo_editor_resource_style.go
index 498ed12..1c93302 100644
--- a/ofdgo_editor_resource_style.go
+++ b/ofdgo_editor_resource_style.go
@@ -443,7 +443,7 @@ func resourceColorNode(node *editorXML) bool {
// validateResourceColors 校验显式及默认颜色空间的既有颜色,防止位深或调色板更新使引用失效
// 入参: ctx 取消上下文, catalog 当前资源目录, space 替换后的颜色空间
-// 返回: error 不兼容的颜色值或调色板索引
+// 返回: error 不兼容的色值或调色板索引
func validateResourceColors(ctx context.Context, catalog *resourceCatalog, space ColorSpace) error {
doc, err := catalog.scan.reader.DocContext(ctx)
if err != nil {
diff --git a/ofdgo_editor_style.go b/ofdgo_editor_style.go
index dbd76d2..de936b9 100644
--- a/ofdgo_editor_style.go
+++ b/ofdgo_editor_style.go
@@ -24,6 +24,7 @@ import (
// ObjectStyle 对象外观,nil字段保持原值,颜色的Alpha为nil时保留原颜色透明度
// StyleObjects使用对象坐标毫米,StyleCompositeObjects的线宽、虚线长度及偏移使用页面毫米
+// Cap为端点样式,Join为转角样式,MiterLimit为尖角截断值,DashPattern为虚线间隔序列
type ObjectStyle struct {
Alpha *int
Fill *bool
diff --git a/ofdgo_font_info.go b/ofdgo_font_info.go
index a179c51..021a940 100644
--- a/ofdgo_font_info.go
+++ b/ofdgo_font_info.go
@@ -35,7 +35,7 @@ const (
FontStatusMissing = "missing"
)
-// FontInfo OFD字体诊断信息,MatchedFace为实际匹配字体及原文件中的零起始索引,无法读取时为空
+// FontInfo 文档字体诊断信息,MatchedFace为实际匹配字体及原文件中的零起始索引,无法读取时为空
type FontInfo struct {
ID string `json:"id"`
FontName string `json:"fontName"`
@@ -141,7 +141,7 @@ func (r *Reader) fontDefinition(id string) *Font {
return font
}
-// FontInfos 获取OFD字体诊断信息
+// FontInfos 获取文档字体诊断信息
// 返回: []FontInfo 字体诊断列表, error 错误信息
func (r *Renderer) FontInfos() ([]FontInfo, error) {
scanner, err := r.ScanFontInfos()
@@ -202,7 +202,7 @@ func (s *FontInfoScanner) Infos() ([]FontInfo, error) {
return s.renderer.fontInfos(s.usage)
}
-// FontInfosFromPages 从页面内容获取OFD字体诊断信息
+// FontInfosFromPages 从页面内容获取文档字体诊断信息
// 入参: pages 页面内容列表
// 返回: []FontInfo 字体诊断列表, error 错误信息
func (r *Renderer) FontInfosFromPages(pages []*PageContent) ([]FontInfo, error) {
@@ -291,7 +291,7 @@ func (r *Renderer) fontInfoDocument() (*Document, error) {
return doc, nil
}
-// fontInfos 获取OFD字体诊断信息
+// fontInfos 获取文档字体诊断信息
// 入参: usage 字体使用次数
// 返回: []FontInfo 字体诊断列表, error 错误信息
func (r *Renderer) fontInfos(usage map[string]int) ([]FontInfo, error) {
diff --git a/ofdgo_geom_stroke.go b/ofdgo_geom_stroke.go
index ae16693..23655d5 100644
--- a/ofdgo_geom_stroke.go
+++ b/ofdgo_geom_stroke.go
@@ -26,7 +26,7 @@ type geometryPolyline struct {
direction Point
}
-// Stroke 将曲线、虚线及线帽连接转换为非零填充轮廓
+// Stroke 按端点和转角样式将曲线、虚线转换为非零填充轮廓
// 曲线按Tolerance展开,默认误差为0.001毫米,重叠轮廓保持同向绕数
// 结果未做布尔整理,按非零规则填充,区域运算需能处理重叠子路径的后端
// 入参: options 描边样式
@@ -283,7 +283,7 @@ func (line geometryPolyline) dash(pattern []float64, total, offset float64) ([]g
// stroke 追加同向矩形、连接楔形及圆盘组成的非零填充区域
// 入参: result 已有轮廓, options 描边样式
-// 返回: GeometryPath 填充轮廓, error 不支持的退化方形线帽
+// 返回: GeometryPath 填充轮廓, error 不支持的退化方头端点
func (line geometryPolyline) stroke(result GeometryPath, options StrokeOptions) (GeometryPath, error) {
half := options.Width / 2
polygon := func(points ...Point) {
diff --git a/ofdgo_paint.go b/ofdgo_paint.go
index ea8fbc2..64f23ab 100644
--- a/ofdgo_paint.go
+++ b/ofdgo_paint.go
@@ -37,7 +37,7 @@ func (r *Renderer) ResolveColor(value string, index *int, space string, alpha *i
// GradientStops 解析OFD渐变分段的位置与透明度,返回独立数据并保留原分段顺序
// 入参: segments 渐变分段, alpha 透明度
-// 返回: []ColorStop 后端无关的渐变分段
+// 返回: []ColorStop 后端无关的渐变色标
func (r *Renderer) GradientStops(segments []ShdSegment, alpha *int) []ColorStop {
var gradient []ColorStop
positions := gradientPositions(segments)
@@ -49,7 +49,7 @@ func (r *Renderer) GradientStops(segments []ShdSegment, alpha *int) []ColorStop
}
// parseColorWithAlpha 解析带透明度的颜色
-// 入参: value 颜色值, index 调色板索引, space 颜色空间标识, alpha 透明度
+// 入参: value 色值, index 调色板索引, space 颜色空间标识, alpha 透明度
// 返回: color.Color 颜色对象
func (r *Renderer) parseColorWithAlpha(value string, index *int, space string, alpha *int) color.Color {
kind, values := r.colorComponents(value, index, space)
@@ -89,7 +89,7 @@ func (r *Renderer) parseColorWithAlpha(value string, index *int, space string, a
}
// colorComponents 读取颜色空间及归一化分量,保留源位深供渐变插值
-// 入参: value 颜色值, index 调色板索引, space 颜色空间标识
+// 入参: value 色值, index 调色板索引, space 颜色空间标识
// 返回: string 颜色模型, [4]float64 单位分量
func (r *Renderer) colorComponents(value string, index *int, space string) (string, [4]float64) {
cs := r.colorDefinition(space)
diff --git a/ofdgo_renderer_geom_canvas.go b/ofdgo_renderer_geom_canvas.go
index 9e2fcb7..7282414 100644
--- a/ofdgo_renderer_geom_canvas.go
+++ b/ofdgo_renderer_geom_canvas.go
@@ -207,7 +207,7 @@ func canvasNativeStroke(geometry GeometryBackend) bool {
}
// canvasStrokeOptions 转换Canvas描边样式,虚线由调用方传入
-// 入参: width 描边宽度, cap 线帽, join 连接, tolerance 展开误差
+// 入参: width 线宽, cap 端点样式, join 转角样式, tolerance 展开误差
// 返回: StrokeOptions 公共描边样式
func canvasStrokeOptions(width float64, cap canvas.Capper, join canvas.Joiner, tolerance float64) StrokeOptions {
options := StrokeOptions{Width: width, Cap: "Butt", Join: "Miter", MiterLimit: defaultMiterLimit, Tolerance: tolerance}
@@ -274,14 +274,14 @@ func canvasFillClip(clip *canvas.Path) bool {
}
// strokeCanvasPath 将文字和复杂画刷的描边交给配置的几何后端
-// 入参: path Canvas路径, width 描边宽度, cap 线帽, join 连接
+// 入参: path Canvas路径, width 线宽, cap 端点样式, join 转角样式
// 返回: *canvas.Path 描边轮廓
func (r *Renderer) strokeCanvasPath(path *canvas.Path, width float64, cap canvas.Capper, join canvas.Joiner) *canvas.Path {
return r.strokeDashedCanvasPath(path, width, cap, join, 0, nil, false)
}
-// strokeDashedCanvasPath 通过公共几何后端展开虚线和线帽,保留零长度绘制段
-// 入参: path 路径, width 线宽, cap 线帽, join 连接, offset 虚线偏移, dashes 虚线数组, fill 是否直接填充或凸窗口裁剪
+// strokeDashedCanvasPath 通过公共几何后端展开虚线和端点样式,保留零长度绘制段
+// 入参: path 路径, width 线宽, cap 端点样式, join 转角样式, offset 虚线偏移, dashes 虚线间隔序列, fill 是否直接填充或凸窗口裁剪
// 返回: *canvas.Path 描边填充轮廓
func (r *Renderer) strokeDashedCanvasPath(path *canvas.Path, width float64, cap canvas.Capper, join canvas.Joiner, offset float64, dashes []float64, fill bool) *canvas.Path {
geometry, err := r.Geometry()
diff --git a/ofdgo_renderer_measure_canvas.go b/ofdgo_renderer_measure_canvas.go
index 526bad7..9dbbeab 100644
--- a/ofdgo_renderer_measure_canvas.go
+++ b/ofdgo_renderer_measure_canvas.go
@@ -99,7 +99,7 @@ func (r *boundsRenderer) Size() (float64, float64) {
return 0, 0
}
-// RenderPath 收集填充与描边范围,保持线帽、连接和虚线语义
+// RenderPath 收集填充与描边范围,保持端点、转角和虚线语义
// 入参: path 路径, style 绘制样式, m 变换矩阵
func (r *boundsRenderer) RenderPath(path *canvas.Path, style canvas.Style, m canvas.Matrix) {
if style.HasFill() {
diff --git a/ofdgo_renderer_paint_canvas.go b/ofdgo_renderer_paint_canvas.go
index e92a45a..9fb6e44 100644
--- a/ofdgo_renderer_paint_canvas.go
+++ b/ofdgo_renderer_paint_canvas.go
@@ -49,8 +49,8 @@ func (r *Renderer) parseStrokePaint(strokeColor *StrokeColor, x, y, pageH float6
}
// canvasGradient 将公共颜色节点转换为Canvas渐变
-// 入参: stops 渐变分段
-// 返回: canvas.Grad 渐变分段
+// 入参: stops 渐变色标
+// 返回: canvas.Grad 渐变色标
func canvasGradient(stops []ColorStop) canvas.Grad {
var gradient canvas.Grad
for _, stop := range stops {
diff --git a/ofdgo_renderer_path_canvas.go b/ofdgo_renderer_path_canvas.go
index 3cac9c2..afb6b70 100644
--- a/ofdgo_renderer_path_canvas.go
+++ b/ofdgo_renderer_path_canvas.go
@@ -75,9 +75,9 @@ func (s *pathStyle) applyStrokeColor(r *Renderer, stroke *StrokeColor, bx, by, p
s.strokePaint = r.parseStrokePaint(strokeColorNode, bx, by, pageH)
}
-// pathLineCap 转换线帽样式
-// 入参: cap 线帽名称, fallback 默认线帽
-// 返回: canvas.Capper 线帽样式
+// pathLineCap 转换端点样式
+// 入参: cap 端点样式名称, fallback 默认端点样式
+// 返回: canvas.Capper 端点样式
func pathLineCap(cap string, fallback canvas.Capper) canvas.Capper {
switch cap {
case "Butt":
@@ -90,8 +90,8 @@ func pathLineCap(cap string, fallback canvas.Capper) canvas.Capper {
return fallback
}
-// applyLineJoin 应用线连接样式及尖角限制
-// 入参: join 线连接名称, limit 尖角限制, explicit 是否显式指定零截断值
+// applyLineJoin 应用转角样式及尖角截断值
+// 入参: join 转角样式名称, limit 尖角截断值, explicit 是否显式指定零截断值
func (s *pathStyle) applyLineJoin(join string, limit float64, explicit bool) {
if limit > 0 || explicit {
s.miterLimit = limit
diff --git a/ofdgo_res.go b/ofdgo_res.go
index 4acd77f..1097687 100644
--- a/ofdgo_res.go
+++ b/ofdgo_res.go
@@ -84,6 +84,7 @@ type DrawParams struct {
}
// DrawParam 绘制参数
+// Cap为端点样式,Join为转角样式,MiterLimit为尖角截断值,DashPattern为虚线间隔序列
// MiterLimitSet为true时,零截断值覆盖继承值,否则零值表示未指定
type DrawParam struct {
ID string `xml:"ID,attr"`