mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 13:56:32 +08:00
Compare commits
2
Commits
da3f64c74a
...
ba68acbc60
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba68acbc60 | ||
|
|
a75b90dffc |
No files matched your search
@@ -3968,7 +3968,7 @@ async function editCanvasObject(item, point = null) {
|
|||||||
if (!isForegroundDocument(tab) || openSeq !== state.openSeq) return;
|
if (!isForegroundDocument(tab) || openSeq !== state.openSeq) return;
|
||||||
let rich = null;
|
let rich = null;
|
||||||
if (item.richText) rich = await loadRichText(item, face, tab, data.bytes);
|
if (item.richText) rich = await loadRichText(item, face, tab, data.bytes);
|
||||||
else if (!item.scoped && layoutKnown) {
|
else if (layoutKnown) {
|
||||||
try { rich = await loadRichText(item, face, tab, data.bytes); } catch {}
|
try { rich = await loadRichText(item, face, tab, data.bytes); } catch {}
|
||||||
}
|
}
|
||||||
if (!isForegroundDocument(tab) || openSeq !== state.openSeq) return;
|
if (!isForegroundDocument(tab) || openSeq !== state.openSeq) return;
|
||||||
@@ -4284,7 +4284,6 @@ async function toggleTextTool() {
|
|||||||
const font = state.textFonts[Number(fontPicker.value)];
|
const font = state.textFonts[Number(fontPicker.value)];
|
||||||
state.textDefaults = { type: "TextObject", size: style.size, color: style.color, wrap: style.wrap,
|
state.textDefaults = { type: "TextObject", size: style.size, color: style.color, wrap: style.wrap,
|
||||||
bold: Boolean(style.bold), italic: Boolean(style.italic), underline: Boolean(style.underline), strikeout: Boolean(style.strikeout), shadow: Boolean(style.shadow), background: style.background || "",
|
bold: Boolean(style.bold), italic: Boolean(style.italic), underline: Boolean(style.underline), strikeout: Boolean(style.strikeout), shadow: Boolean(style.shadow), background: style.background || "",
|
||||||
wrapOverride: style === state.textDefaults ? style.wrapOverride : style.wrap,
|
|
||||||
align: style.align || "left", paragraphHeight: style.paragraphHeight || 0, letterSpacing: style.letterSpacing || 0,
|
align: style.align || "left", paragraphHeight: style.paragraphHeight || 0, letterSpacing: style.letterSpacing || 0,
|
||||||
leftIndent: style.leftIndent || 0, rightIndent: style.rightIndent || 0, firstLineIndent: style.firstLineIndent || 0,
|
leftIndent: style.leftIndent || 0, rightIndent: style.rightIndent || 0, firstLineIndent: style.firstLineIndent || 0,
|
||||||
fontChoice: font && !font.disabled ? font : state.textDefaults.fontChoice };
|
fontChoice: font && !font.disabled ? font : state.textDefaults.fontChoice };
|
||||||
@@ -4312,7 +4311,7 @@ async function beginCanvasText(index, box, surface, page) {
|
|||||||
await face.load();
|
await face.load();
|
||||||
if (!isForegroundDocument(tab) || openSeq !== state.openSeq || !surface.isConnected) return;
|
if (!isForegroundDocument(tab) || openSeq !== state.openSeq || !surface.isConnected) return;
|
||||||
const node = document.createElement("div");
|
const node = document.createElement("div");
|
||||||
const item = { ...state.textDefaults, ...box, wrap: state.textDefaults.wrapOverride ?? box.wrap ?? state.textDefaults.wrap,
|
const item = { ...state.textDefaults, ...box, wrap: Boolean(box.wrap),
|
||||||
index, page, surface, node, text: "", draft: true, fontData: data };
|
index, page, surface, node, text: "", draft: true, fontData: data };
|
||||||
const model = { ...richTextModel(item), autoWidth: !box.wrap };
|
const model = { ...richTextModel(item), autoWidth: !box.wrap };
|
||||||
const key = `temp-font-${crypto.randomUUID()}`;
|
const key = `temp-font-${crypto.randomUUID()}`;
|
||||||
@@ -4578,7 +4577,6 @@ async function changeTextAlignment() {
|
|||||||
|
|
||||||
async function changeParagraph(wrap) {
|
async function changeParagraph(wrap) {
|
||||||
const item = currentTextStyle();
|
const item = currentTextStyle();
|
||||||
const explicitWrap = wrap !== undefined;
|
|
||||||
wrap ??= item.wrap;
|
wrap ??= item.wrap;
|
||||||
if (document.body.hasAttribute("data-busy") || item.draft) return;
|
if (document.body.hasAttribute("data-busy") || item.draft) return;
|
||||||
if (!el.textLineHeight.checkValidity()) {
|
if (!el.textLineHeight.checkValidity()) {
|
||||||
@@ -4593,7 +4591,6 @@ async function changeParagraph(wrap) {
|
|||||||
const spacing = inputMillimeters(el.textSpacing, item.letterSpacing || 0);
|
const spacing = inputMillimeters(el.textSpacing, item.letterSpacing || 0);
|
||||||
if (item === state.textDefaults) {
|
if (item === state.textDefaults) {
|
||||||
Object.assign(item, { wrap: Boolean(wrap), align: el.textAlign.value, paragraphHeight: height, letterSpacing: spacing });
|
Object.assign(item, { wrap: Boolean(wrap), align: el.textAlign.value, paragraphHeight: height, letterSpacing: spacing });
|
||||||
if (explicitWrap) item.wrapOverride = Boolean(wrap);
|
|
||||||
updateObjectControls(canvasEditor.selected, true);
|
updateObjectControls(canvasEditor.selected, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -12490,12 +12487,13 @@ function updateObjectControls(item, reset = false) {
|
|||||||
el.undoButton.disabled = !state.editorInfo?.canUndo || !state.ready || state.exporting;
|
el.undoButton.disabled = !state.editorInfo?.canUndo || !state.ready || state.exporting;
|
||||||
el.redoButton.disabled = !state.editorInfo?.canRedo || !state.ready || state.exporting;
|
el.redoButton.disabled = !state.editorInfo?.canRedo || !state.ready || state.exporting;
|
||||||
}
|
}
|
||||||
el.textWrap.disabled = el.textLineHeight.disabled = el.textSpacing.disabled = el.textSize.disabled || Boolean(item?.items || item?.draft) || Boolean(canvasEditor.input);
|
el.textLineHeight.disabled = el.textSpacing.disabled = el.textSize.disabled || Boolean(item?.items || item?.draft) || Boolean(canvasEditor.input);
|
||||||
|
el.textWrap.disabled = el.textLineHeight.disabled || text === state.textDefaults;
|
||||||
const reflow = document.querySelector("#textReflow"), positioned = canvasEditor.input?.source && (item?.selectionIDs || !canEditObject(item, "layoutKnown"));
|
const reflow = document.querySelector("#textReflow"), positioned = canvasEditor.input?.source && (item?.selectionIDs || !canEditObject(item, "layoutKnown"));
|
||||||
reflow.hidden = !positioned;
|
reflow.hidden = !positioned;
|
||||||
el.textWrap.hidden = !!positioned;
|
el.textWrap.hidden = !!positioned;
|
||||||
reflow.disabled = textDisabled || !canEditObject(item, "reflow") || !!canvasEditor.input?.reflow;
|
reflow.disabled = textDisabled || !canEditObject(item, "reflow") || !!canvasEditor.input?.reflow;
|
||||||
el.paragraphButton.disabled = el.textWrap.disabled;
|
el.paragraphButton.disabled = el.textLineHeight.disabled;
|
||||||
el.textAlign.disabled = el.textSize.disabled || Boolean(item?.items || item?.selectionIDs);
|
el.textAlign.disabled = el.textSize.disabled || Boolean(item?.items || item?.selectionIDs);
|
||||||
const layoutKnown = text === state.textDefaults || Boolean(item?.draft) || canEditObject(item, "layoutKnown");
|
const layoutKnown = text === state.textDefaults || Boolean(item?.draft) || canEditObject(item, "layoutKnown");
|
||||||
const rich = canvasEditor.input?.rich;
|
const rich = canvasEditor.input?.rich;
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ function sourceTextScale(editing) {
|
|||||||
return Math.max(Math.hypot(a, b), Math.hypot(c, d)) || 1;
|
return Math.max(Math.hypot(a, b), Math.hypot(c, d)) || 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function textFrameBox(item) {
|
||||||
|
return { x: 0, y: 0, width: item.paragraphWidth ?? item.textFrame.width, height: item.textFrame.height };
|
||||||
|
}
|
||||||
|
|
||||||
export function fontFaceStyle(data) {
|
export function fontFaceStyle(data) {
|
||||||
const style = { weight: "400", style: "normal" };
|
const style = { weight: "400", style: "normal" };
|
||||||
const bytes = ArrayBuffer.isView(data) ? new DataView(data.buffer, data.byteOffset, data.byteLength) : new DataView(data);
|
const bytes = ArrayBuffer.isView(data) ? new DataView(data.buffer, data.byteOffset, data.byteLength) : new DataView(data);
|
||||||
@@ -936,8 +940,8 @@ export class CanvasEditor {
|
|||||||
});
|
});
|
||||||
if (item.contour) item.contour.svg.setAttribute("viewBox", `${item.x} ${item.y} ${item.width} ${item.height}`);
|
if (item.contour) item.contour.svg.setAttribute("viewBox", `${item.x} ${item.y} ${item.width} ${item.height}`);
|
||||||
if (item.textFrame) {
|
if (item.textFrame) {
|
||||||
const { matrix: [a, b, c, d, e, f], width, height } = item.textFrame;
|
const [a, b, c, d, e, f] = item.textFrame.matrix;
|
||||||
const box = item.textFrame.content || { x: 0, y: 0, width, height };
|
const box = textFrameBox(item);
|
||||||
for (const handle of item.node.children) {
|
for (const handle of item.node.children) {
|
||||||
if (handle.dataset.corner !== "w" && handle.dataset.corner !== "e") continue;
|
if (handle.dataset.corner !== "w" && handle.dataset.corner !== "e") continue;
|
||||||
const x = box.x + (handle.dataset.corner === "e" ? box.width : 0), y = box.y + box.height / 2;
|
const x = box.x + (handle.dataset.corner === "e" ? box.width : 0), y = box.y + box.height / 2;
|
||||||
@@ -1265,7 +1269,7 @@ export class CanvasEditor {
|
|||||||
if (drag.item.textFrame) {
|
if (drag.item.textFrame) {
|
||||||
const frame = drag.item.textFrame, [a, b, c, d] = frame.matrix;
|
const frame = drag.item.textFrame, [a, b, c, d] = frame.matrix;
|
||||||
const dx = (d * (to.x - from.x) - c * (to.y - from.y)) / (a * d - b * c);
|
const dx = (d * (to.x - from.x) - c * (to.y - from.y)) / (a * d - b * c);
|
||||||
const box = reshapeBox({ geometry: frame.content || { x: 0, y: 0, width: frame.width, height: frame.height } }, dx, 0, drag.corner, false);
|
const box = reshapeBox({ geometry: textFrameBox(drag.item) }, dx, 0, drag.corner, false);
|
||||||
drag.box = { x: drag.item.x + box.x, width: box.width };
|
drag.box = { x: drag.item.x + box.x, width: box.width };
|
||||||
const bounds = transformedBox(box, frame.matrix);
|
const bounds = transformedBox(box, frame.matrix);
|
||||||
Object.assign(drag.item.node.style, { left: `${bounds.x * PX_PER_MM}px`, top: `${bounds.y * PX_PER_MM}px`,
|
Object.assign(drag.item.node.style, { left: `${bounds.x * PX_PER_MM}px`, top: `${bounds.y * PX_PER_MM}px`,
|
||||||
@@ -1429,7 +1433,7 @@ export class CanvasEditor {
|
|||||||
this.drag = null;
|
this.drag = null;
|
||||||
this.viewer.releasePointerCapture(drag.pointerID);
|
this.viewer.releasePointerCapture(drag.pointerID);
|
||||||
const frame = drag.item.textFrame;
|
const frame = drag.item.textFrame;
|
||||||
const geometry = drag.item.oriented?.box || drag.item.geometry || (frame ? { x: drag.item.x + (frame.content?.x || 0), width: frame.content?.width ?? frame.width } : drag.item);
|
const geometry = drag.item.oriented?.box || drag.item.geometry || (frame ? { x: drag.item.x, width: textFrameBox(drag.item).width } : drag.item);
|
||||||
const changed = Object.keys(drag.box).some((key) => drag.box[key] !== geometry[key]);
|
const changed = Object.keys(drag.box).some((key) => drag.box[key] !== geometry[key]);
|
||||||
const apply = drag.item.shape || drag.item.oriented ? this.options.onReshape : canEditObject(drag.item, "stretch") ? this.options.onResize : this.options.onTextWidth;
|
const apply = drag.item.shape || drag.item.oriented ? this.options.onReshape : canEditObject(drag.item, "stretch") ? this.options.onResize : this.options.onTextWidth;
|
||||||
Promise.resolve(changed && apply(drag.item, drag.box)).finally(() => {
|
Promise.resolve(changed && apply(drag.item, drag.box)).finally(() => {
|
||||||
@@ -1779,9 +1783,9 @@ export class CanvasEditor {
|
|||||||
else paragraph.append(document.createElement("br"));
|
else paragraph.append(document.createElement("br"));
|
||||||
input.append(paragraph);
|
input.append(paragraph);
|
||||||
}
|
}
|
||||||
const left = item.leftIndent || 0;
|
const left = rich?.model.layout.leftIndent || item.leftIndent || 0;
|
||||||
const frame = { ...(item.textFrame || { width: item.width, height: item.height, matrix: [1, 0, 0, 1, item.x, item.y] }) };
|
const frame = { ...(rich?.frame || item.textFrame || { width: item.width, height: item.height, matrix: [1, 0, 0, 1, item.x, item.y] }) };
|
||||||
if (item.wrap && item.paragraphWidth != null) frame.width = item.paragraphWidth;
|
if (!rich?.frame && item.paragraphWidth != null) frame.width = item.paragraphWidth;
|
||||||
const [a, b, c, d, e, f] = frame.matrix;
|
const [a, b, c, d, e, f] = frame.matrix;
|
||||||
Object.assign(input.style, {
|
Object.assign(input.style, {
|
||||||
left: "0px", top: "0px",
|
left: "0px", top: "0px",
|
||||||
|
|||||||
+23
-38
@@ -3401,7 +3401,7 @@ func (e *businessEngine) editorObjects(index int, text *ofdgo.PageText) ([]any,
|
|||||||
inverse, _ := matrix.Invert()
|
inverse, _ := matrix.Invert()
|
||||||
local := inverse.TransformBox(box)
|
local := inverse.TransformBox(box)
|
||||||
width := math.Max(object.TextObject.Size, local.X+local.W)
|
width := math.Max(object.TextObject.Size, local.X+local.W)
|
||||||
if layout.Wrap {
|
if layout != (ofdgo.TextLayout{}) {
|
||||||
item["paragraphWidth"] = frame.W
|
item["paragraphWidth"] = frame.W
|
||||||
}
|
}
|
||||||
frame = ofdgo.Box{W: width, H: math.Max(object.TextObject.Size, local.Y+local.H)}
|
frame = ofdgo.Box{W: width, H: math.Max(object.TextObject.Size, local.Y+local.H)}
|
||||||
@@ -4017,7 +4017,7 @@ func (e *businessEngine) compositeObjects(args []platformValue) (any, error) {
|
|||||||
inverse, _ := member.Matrix.Invert()
|
inverse, _ := member.Matrix.Invert()
|
||||||
local := inverse.TransformBox(box)
|
local := inverse.TransformBox(box)
|
||||||
width := math.Max(object.Size, local.X+local.W)
|
width := math.Max(object.Size, local.X+local.W)
|
||||||
if layout.Wrap {
|
if layout != (ofdgo.TextLayout{}) {
|
||||||
frame, err := object.TextFrame()
|
frame, err := object.TextFrame()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -4111,16 +4111,8 @@ func (e *businessEngine) changeCompositeObjects(args []platformValue) (any, erro
|
|||||||
if len(indexes) != 1 {
|
if len(indexes) != 1 {
|
||||||
return fmt.Errorf("text layout requires one member")
|
return fmt.Errorf("text layout requires one member")
|
||||||
}
|
}
|
||||||
layout := ofdgo.TextLayout{AutoBidi: true, Wrap: args[6].Bool(), Align: args[7].String(), LineHeight: args[8].Float(), LetterSpacing: args[9].Float(), LeftIndent: args[10].Float(), RightIndent: args[11].Float(), FirstLineIndent: args[12].Float()}
|
path.Children = append(slices.Clone(path.Children), indexes[0])
|
||||||
if !args[4].IsNull() {
|
return e.layoutEditorText(page, path, args[4:])
|
||||||
return e.currentEditor.ResizeCompositeTextFrame(page, path, indexes[0], args[4].Float(), args[5].Float(), layout)
|
|
||||||
}
|
|
||||||
member, err := e.currentEditor.CompositeObject(page, path, indexes[0])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
value, _ := member.Object.TextObject.TextLayout()
|
|
||||||
return e.currentEditor.LayoutCompositeText(page, path, indexes[0], value, layout)
|
|
||||||
case "fit", "resetCrop":
|
case "fit", "resetCrop":
|
||||||
if len(indexes) != 1 {
|
if len(indexes) != 1 {
|
||||||
return fmt.Errorf("image operation requires one member")
|
return fmt.Errorf("image operation requires one member")
|
||||||
@@ -4969,8 +4961,12 @@ func (e *businessEngine) editorRichText(item map[string]any, object ofdgo.Graphi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
frame := matrix.TransformBox(contentBounds)
|
|
||||||
format, layout := value.Runs[0].Format, value.Layout
|
format, layout := value.Runs[0].Format, value.Layout
|
||||||
|
selection := contentBounds
|
||||||
|
if !value.AutoWidth || layout.Wrap {
|
||||||
|
selection = ofdgo.Box{W: width, H: height}
|
||||||
|
}
|
||||||
|
frame := matrix.TransformBox(selection)
|
||||||
var content strings.Builder
|
var content strings.Builder
|
||||||
for _, run := range value.Runs {
|
for _, run := range value.Runs {
|
||||||
content.WriteString(run.Text)
|
content.WriteString(run.Text)
|
||||||
@@ -4987,7 +4983,7 @@ func (e *businessEngine) editorRichText(item map[string]any, object ofdgo.Graphi
|
|||||||
item["letterSpacing"], item["leftIndent"], item["rightIndent"], item["firstLineIndent"] = layout.LetterSpacing, layout.LeftIndent, layout.RightIndent, layout.FirstLineIndent
|
item["letterSpacing"], item["leftIndent"], item["rightIndent"], item["firstLineIndent"] = layout.LetterSpacing, layout.LeftIndent, layout.RightIndent, layout.FirstLineIndent
|
||||||
item["paragraphWidth"], item["bounds"] = width, e.editorBox(frame)
|
item["paragraphWidth"], item["bounds"] = width, e.editorBox(frame)
|
||||||
item["x"], item["y"], item["width"], item["height"] = frame.X, frame.Y, frame.W, frame.H
|
item["x"], item["y"], item["width"], item["height"] = frame.X, frame.Y, frame.W, frame.H
|
||||||
item["textFrame"] = map[string]any{"width": width, "height": height, "matrix": e.editorMatrix(matrix), "content": e.editorBox(contentBounds)}
|
item["textFrame"] = map[string]any{"width": width, "height": height, "matrix": e.editorMatrix(matrix)}
|
||||||
capabilities := item["capabilities"].(map[string]any)
|
capabilities := item["capabilities"].(map[string]any)
|
||||||
for _, key := range []string{"textContent", "rewriteText", "replaceFont", "reflow", "layoutKnown", "paint"} {
|
for _, key := range []string{"textContent", "rewriteText", "replaceFont", "reflow", "layoutKnown", "paint"} {
|
||||||
capabilities[key] = true
|
capabilities[key] = true
|
||||||
@@ -5313,32 +5309,21 @@ func (e *businessEngine) layoutText(args []platformValue) (any, error) {
|
|||||||
if e.currentEditor == nil {
|
if e.currentEditor == nil {
|
||||||
return nil, fmt.Errorf("no document is being edited")
|
return nil, fmt.Errorf("no document is being edited")
|
||||||
}
|
}
|
||||||
page, id := args[0].Int(), args[1].String()
|
return e.changeObjects(func() error {
|
||||||
object, err := e.currentEditor.Object(page, id)
|
return e.layoutEditorText(args[0].Int(), ofdgo.ObjectPath{ID: args[1].String()}, args[2:])
|
||||||
if err != nil {
|
})
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
if object.Type != "TextObject" {
|
|
||||||
return nil, fmt.Errorf("object is not text")
|
// layoutEditorText 将正文和组合内文字的段落调整交给库统一处理
|
||||||
|
// 入参: page 页面索引, path 完整对象路径, args 偏移、宽度及段落选项
|
||||||
|
// 返回: error 排版或提交错误
|
||||||
|
func (e *businessEngine) layoutEditorText(page int, path ofdgo.ObjectPath, args []platformValue) error {
|
||||||
|
offset, width := 0.0, 0.0
|
||||||
|
if !args[0].IsNull() {
|
||||||
|
offset, width = args[0].Float(), args[1].Float()
|
||||||
}
|
}
|
||||||
if !args[2].IsNull() {
|
layout := ofdgo.TextLayout{AutoBidi: true, Wrap: args[2].Bool(), Align: args[3].String(), LineHeight: args[4].Float(), LetterSpacing: args[5].Float(), LeftIndent: args[6].Float(), RightIndent: args[7].Float(), FirstLineIndent: args[8].Float()}
|
||||||
object.TextObject, err = object.TextObject.ResizeTextFrame(args[2].Float(), args[3].Float())
|
return e.currentEditor.LayoutTextFrame(page, path, layout, offset, width)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value, _ := object.TextObject.TextLayout()
|
|
||||||
if err := e.currentEditor.LayoutText(&object.TextObject, value, ofdgo.TextLayout{AutoBidi: true, Wrap: args[4].Bool(), Align: args[5].String(), LineHeight: args[6].Float(), LetterSpacing: args[7].Float(), LeftIndent: args[8].Float(), RightIndent: args[9].Float(), FirstLineIndent: args[10].Float()}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
revision := e.currentEditor.Revision()
|
|
||||||
if err := e.currentEditor.UpdateObject(page, id, object); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if e.currentEditor.Revision() == revision {
|
|
||||||
return e.editorSummary(), nil
|
|
||||||
}
|
|
||||||
return e.previewEditor(e.currentEditor, e.currentSession.Renderer.RenderAnnotations)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// replaceImage 替换图片资源并更新预览
|
// replaceImage 替换图片资源并更新预览
|
||||||
|
|||||||
@@ -743,7 +743,7 @@ func (e *Editor) promoteTextValue(t TextObject) (RichText, error) {
|
|||||||
}
|
}
|
||||||
color = fmt.Sprintf("%d %d %d", r>>8, g>>8, b>>8)
|
color = fmt.Sprintf("%d %d %d", r>>8, g>>8, b>>8)
|
||||||
}
|
}
|
||||||
result := RichText{Runs: []RichTextRun{{Text: value, Format: TextFormat{Font: t.Font, Size: t.Size, Color: color, Bold: t.Weight == 700, Italic: t.Italic}}}, Layout: layout}
|
result := RichText{AutoWidth: t.layout == nil, Runs: []RichTextRun{{Text: value, Format: TextFormat{Font: t.Font, Size: t.Size, Color: color, Bold: t.Weight == 700, Italic: t.Italic}}}, Layout: layout}
|
||||||
return result, result.Validate()
|
return result, result.Validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1134,7 +1134,8 @@ func (e *Editor) TargetRichText(page int, path ObjectPath) (RichText, error) {
|
|||||||
return value, err
|
return value, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareRichText 读取独立富文本模型和局部编辑框,显式重排时保留原首行起点及页面变换
|
// PrepareRichText 读取独立富文本模型和局部编辑框,定位文字采用自然宽度,已知段落保留宽度
|
||||||
|
// 显式重排保留原首行起点及页面变换,不从原始Boundary推断段落宽度
|
||||||
// 入参: page 页面索引, path 完整对象路径, reflow 是否允许重排普通文字
|
// 入参: page 页面索引, path 完整对象路径, reflow 是否允许重排普通文字
|
||||||
// 返回: RichText 内容, Box 局部编辑框, Matrix 页面变换, error 路径、外观或排版错误
|
// 返回: RichText 内容, Box 局部编辑框, Matrix 页面变换, error 路径、外观或排版错误
|
||||||
func (e *Editor) PrepareRichText(page int, path ObjectPath, reflow bool) (RichText, Box, Matrix, error) {
|
func (e *Editor) PrepareRichText(page int, path ObjectPath, reflow bool) (RichText, Box, Matrix, error) {
|
||||||
@@ -1193,6 +1194,7 @@ func (e *Editor) PrepareRichText(page int, path ObjectPath, reflow bool) (RichTe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return RichText{}, Box{}, Matrix{}, err
|
return RichText{}, Box{}, Matrix{}, err
|
||||||
}
|
}
|
||||||
|
autoWidth := text.layout == nil
|
||||||
if reflow {
|
if reflow {
|
||||||
value, layout := text.TextLayout()
|
value, layout := text.TextLayout()
|
||||||
layout.AutoBidi = true
|
layout.AutoBidi = true
|
||||||
@@ -1201,7 +1203,18 @@ func (e *Editor) PrepareRichText(page int, path ObjectPath, reflow bool) (RichTe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
value, err := e.promoteTextValue(text)
|
value, err := e.promoteTextValue(text)
|
||||||
return value, Box{W: frame.W, H: frame.H}, matrix.Multiply(TranslationMatrix(dx, dy)), err
|
if err != nil {
|
||||||
|
return RichText{}, Box{}, Matrix{}, err
|
||||||
|
}
|
||||||
|
value.AutoWidth = autoWidth
|
||||||
|
if autoWidth {
|
||||||
|
result, err := e.layoutRichText(value, frame.W)
|
||||||
|
if err != nil {
|
||||||
|
return RichText{}, Box{}, Matrix{}, err
|
||||||
|
}
|
||||||
|
frame.W, frame.H = result.width, result.bounds.Y+result.bounds.H
|
||||||
|
}
|
||||||
|
return value, Box{W: frame.W, H: frame.H}, matrix.Multiply(TranslationMatrix(dx, dy)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReflowRichText 原子重排完整路径文字,保留局部宽度、页面变换及共享实例隔离
|
// ReflowRichText 原子重排完整路径文字,保留局部宽度、页面变换及共享实例隔离
|
||||||
@@ -1218,3 +1231,24 @@ func (e *Editor) ReflowRichText(page int, path ObjectPath, value RichText) error
|
|||||||
path.Children = path.Children[:len(path.Children)-1]
|
path.Children = path.Children[:len(path.Children)-1]
|
||||||
return e.updateCompositeRichText(page, path, index, value, 0, 0, true)
|
return e.updateCompositeRichText(page, path, index, value, 0, 0, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LayoutTextFrame 统一调整普通文字及富文本的段落选项和局部宽度,保留原首行起点与页面变换
|
||||||
|
// 对齐应用于所有段落;定位文字沿用自然宽度,显式改宽后采用固定宽度,提交一条撤销记录
|
||||||
|
// 入参: page 页面索引, path 完整对象路径, options 段落选项, offset 左边界局部偏移, width 新宽度,零值保留宽度模式
|
||||||
|
// 返回: error 路径、尺寸或排版错误
|
||||||
|
func (e *Editor) LayoutTextFrame(page int, path ObjectPath, options TextLayout, offset, width float64) error {
|
||||||
|
if !finite(offset) || !finite(width) || width < 0 || width == 0 && offset != 0 {
|
||||||
|
return fmt.Errorf("invalid text frame")
|
||||||
|
}
|
||||||
|
value, _, _, err := e.PrepareRichText(page, path, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value.Layout, value.Paragraphs = options, nil
|
||||||
|
if len(path.Children) == 0 {
|
||||||
|
return e.updateRichTextFrame(page, path.ID, value, offset, width, true)
|
||||||
|
}
|
||||||
|
index := path.Children[len(path.Children)-1]
|
||||||
|
path.Children = path.Children[:len(path.Children)-1]
|
||||||
|
return e.updateCompositeRichText(page, path, index, value, offset, width, true)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user