20554770
杨鑫
更新bug
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
/** 文本控件在元素框内的垂直对齐(config.verticalAlign) */
export type TextVerticalAlign = 'top' | 'center' | 'bottom';
export function readVerticalAlign(
config: Record<string, unknown> | undefined | null,
): TextVerticalAlign {
const raw = config?.verticalAlign ?? config?.VerticalAlign;
const v = String(raw ?? 'top').trim().toLowerCase();
if (v === 'center' || v === 'middle') return 'center';
if (v === 'bottom') return 'bottom';
return 'top';
}
export function verticalAlignToFlexJustify(
align: TextVerticalAlign,
): 'flex-start' | 'center' | 'flex-end' {
if (align === 'center') return 'center';
if (align === 'bottom') return 'flex-end';
return 'flex-start';
}
export function textAlignToFlexAlign(
align: string | undefined,
): 'flex-start' | 'center' | 'flex-end' {
const v = String(align ?? 'left').toLowerCase();
if (v === 'center') return 'center';
if (v === 'right') return 'flex-end';
return 'flex-start';
}
/** 画布/位图:文本块在元素框内的顶部 Y 偏移(不含 baseline) */
export function computeVerticalTextBlockOffset(
innerHeight: number,
blockHeight: number,
verticalAlign: TextVerticalAlign,
): number {
const extra = Math.max(0, innerHeight - blockHeight);
if (verticalAlign === 'center') return Math.floor(extra / 2);
if (verticalAlign === 'bottom') return extra;
return 0;
}
export function readFontWeight(
config: Record<string, unknown> | undefined | null,
): 'normal' | 'bold' {
const raw = config?.fontWeight ?? config?.FontWeight;
const v = String(raw ?? 'normal').trim().toLowerCase();
return v === 'bold' || v === '700' || v === 'bolder' ? 'bold' : 'normal';
}
export function readFontStyle(
config: Record<string, unknown> | undefined | null,
): 'normal' | 'italic' {
const raw = config?.fontStyle ?? config?.FontStyle;
const v = String(raw ?? 'normal').trim().toLowerCase();
return v === 'italic' || v === 'oblique' ? 'italic' : 'normal';
}
export function readTextDecoration(
config: Record<string, unknown> | undefined | null,
): 'none' | 'underline' {
const raw = config?.textDecoration ?? config?.TextDecoration;
const v = String(raw ?? 'none').trim().toLowerCase();
return v.includes('underline') ? 'underline' : 'none';
}
/** 元素级 border(兼容 Border / BorderType) */
export function readElementBorder(
el: { border?: string | null; Border?: string | null; BorderType?: string | null; borderType?: string | null },
): string {
const raw = el.border ?? el.Border ?? el.BorderType ?? el.borderType ?? 'none';
return String(raw).trim().toLowerCase() || 'none';
}
/** 读取元素 rotation(兼容大小写 / 旧数据) */
export function readElementRotation(
el: { rotation?: string | null },
|
40929347
杨鑫
feat: update labe...
|
78
|
): 'horizontal' | 'vertical' | 'right' | 'rotate180' {
|
20554770
杨鑫
更新bug
|
79
|
const v = String(el.rotation ?? 'horizontal').trim().toLowerCase();
|
40929347
杨鑫
feat: update labe...
|
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
if (v === 'vertical' || v === 'left' || v === 'rotate-left' || v === 'left90') return 'vertical';
if (v === 'right' || v === 'rotate-right' || v === 'right90') return 'right';
if (v === 'rotate180' || v === '180' || v === 'down' || v === 'inverted') return 'rotate180';
return 'horizontal';
}
export function elementRotationDegrees(
el: { rotation?: string | null },
): 0 | -90 | 90 | 180 {
const rot = readElementRotation(el);
if (rot === 'vertical') return -90;
if (rot === 'right') return 90;
if (rot === 'rotate180') return 180;
return 0;
}
export function rotateElementLeftValue(
el: { rotation?: string | null },
): 'horizontal' | 'vertical' | 'right' | 'rotate180' {
const current = elementRotationDegrees(el);
const next = (((current - 90) % 360) + 360) % 360;
if (next === 270) return 'vertical';
if (next === 180) return 'rotate180';
if (next === 90) return 'right';
return 'horizontal';
}
export function rotateElementRightValue(
el: { rotation?: string | null },
): 'horizontal' | 'vertical' | 'right' | 'rotate180' {
const current = elementRotationDegrees(el);
const next = (((current + 90) % 360) + 360) % 360;
if (next === 270) return 'vertical';
if (next === 180) return 'rotate180';
if (next === 90) return 'right';
return 'horizontal';
|
20554770
杨鑫
更新bug
|
116
117
|
}
|
c9bc327f
杨鑫
修改前端
|
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
/** 与 App lineHeightForTextElement 一致:leading-tight ≈ 1.25 */
export function textElementLineHeightPx(fontSize: number): number {
return Math.max(fontSize + 2, Math.round(fontSize * 1.25));
}
/** 矮框(约 1 行高)内的固定文案:预览/打印应单行展示,避免 break-all 折行后第二行被裁切 */
export function isShortSingleLineTextBox(
boxHeightPx: number,
fontSize: number,
text: string,
opts?: { breakAll?: boolean },
): boolean {
if (opts?.breakAll) return false;
if (String(text ?? '').includes('\n')) return false;
const lineH = textElementLineHeightPx(fontSize);
const descentPad = Math.max(2, Math.round(fontSize * 0.14));
const maxLines = Math.max(
1,
Math.floor((Math.max(0, boxHeightPx) + descentPad) / lineH),
);
return maxLines <= 1;
}
/** 单行文案在 inner 宽度内微缩字号(与 App canvas fit 逻辑一致,供 Web 预览) */
export function fitFontSizeToInnerWidthEstimate(
text: string,
innerWidthPx: number,
fontSize: number,
bold = false,
): number {
if (!text || innerWidthPx <= 0) return fontSize;
const factor = bold ? 0.62 : 0.55;
let size = fontSize;
for (let i = 0; i < 5; i++) {
const w = Math.max(4, String(text).length * size * factor);
if (w <= innerWidthPx) return size;
size = Math.max(8, Math.floor(size * (innerWidthPx / w) * 0.98));
}
return Math.max(8, size);
}
|
20554770
杨鑫
更新bug
|
159
160
161
|
/** 切换 horizontal / vertical 时交换宽高并保持中心点不变(与参考图 2 一致) */
export function patchElementRotationWithLayout(
el: { x: number; y: number; width: number; height: number; rotation?: string | null },
|
40929347
杨鑫
feat: update labe...
|
162
|
nextRotation: 'horizontal' | 'vertical' | 'right' | 'rotate180',
|
20554770
杨鑫
更新bug
|
163
|
): {
|
40929347
杨鑫
feat: update labe...
|
164
|
rotation: 'horizontal' | 'vertical' | 'right' | 'rotate180';
|
20554770
杨鑫
更新bug
|
165
166
167
168
169
170
|
x: number;
y: number;
width: number;
height: number;
} {
const prev = readElementRotation(el);
|
40929347
杨鑫
feat: update labe...
|
171
|
const shouldSwapBox = (rotation: string) => rotation === 'vertical' || rotation === 'right';
|
20554770
杨鑫
更新bug
|
172
173
|
if (prev === nextRotation) {
// vertical 但宽>高:选框与文字方向不一致,强制纠正
|
40929347
杨鑫
feat: update labe...
|
174
|
if (shouldSwapBox(nextRotation) && el.width > el.height) {
|
20554770
杨鑫
更新bug
|
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
|
const w = Math.max(1, el.width);
const h = Math.max(1, el.height);
const cx = el.x + w / 2;
const cy = el.y + h / 2;
const newW = h;
const newH = w;
return {
rotation: nextRotation,
width: newW,
height: newH,
x: Math.round(cx - newW / 2),
y: Math.round(cy - newH / 2),
};
}
return {
rotation: nextRotation,
x: el.x,
y: el.y,
width: el.width,
height: el.height,
};
}
|
40929347
杨鑫
feat: update labe...
|
197
198
199
200
201
202
203
204
205
|
if (!shouldSwapBox(prev) && !shouldSwapBox(nextRotation)) {
return {
rotation: nextRotation,
x: el.x,
y: el.y,
width: el.width,
height: el.height,
};
}
|
20554770
杨鑫
更新bug
|
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
|
const w = Math.max(1, el.width);
const h = Math.max(1, el.height);
const cx = el.x + w / 2;
const cy = el.y + h / 2;
const newW = h;
const newH = w;
return {
rotation: nextRotation,
width: newW,
height: newH,
x: Math.round(cx - newW / 2),
y: Math.round(cy - newH / 2),
};
}
/** 竖排且宽>高时纠正选框(落库 / 渲染用) */
export function canonicalElementGeometry<
T extends { rotation?: string | null; width: number; height: number; x: number; y: number },
>(el: T): T {
return normalizeElementRotationBox(el);
}
/** 旧模板 vertical 但宽>高时自动纠正选框 */
export function normalizeElementRotationBox<
T extends { rotation?: string | null; width: number; height: number; x: number; y: number },
>(el: T): T {
|
40929347
杨鑫
feat: update labe...
|
232
233
|
const rotation = readElementRotation(el);
if (rotation !== 'vertical' && rotation !== 'right') return el;
|
20554770
杨鑫
更新bug
|
234
235
236
|
if (el.width <= el.height) return el;
return {
...el,
|
40929347
杨鑫
feat: update labe...
|
237
238
|
rotation,
...patchElementRotationWithLayout({ ...el, rotation: 'horizontal' }, rotation),
|
20554770
杨鑫
更新bug
|
239
240
|
};
}
|