@gct-paas/word 0.1.23 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -60,6 +60,7 @@ export declare const DEFAULT_PAGE_SIZE: {
|
|
|
60
60
|
export declare const DEFAULT_PAGE_PADDING: [number, number, number, number];
|
|
61
61
|
export declare const DEFAULT_CELL_PADDING: [number, number, number, number];
|
|
62
62
|
export declare const DEFAULT_FONT_SIZE = 16;
|
|
63
|
+
/** 回退链在不同 OS 上可能命中不同实体字体;跨平台一致布局请配合 Web 字体加载与 TextUtil.awaitDocumentFonts() */
|
|
63
64
|
export declare const DEFAULT_FONT_FAMILY = "Roboto,RobotoDraft,Helvetica,Arial,sans-serif";
|
|
64
65
|
export declare const DEFAULT_TEXT_COLOR = "black";
|
|
65
66
|
export declare const ZERO_WIDTH_SPACE = "\u200B";
|
|
@@ -2,7 +2,7 @@ import { default as Konva } from 'konva';
|
|
|
2
2
|
export declare class TextUtil {
|
|
3
3
|
/** 字体度量缓存限制 */
|
|
4
4
|
private static readonly FONT_METRICS_CACHE_LIMIT;
|
|
5
|
-
/**
|
|
5
|
+
/** 布局大小缓存限制(按「样式 + 单段文本」键,长文档可提高命中率) */
|
|
6
6
|
private static readonly LAYOUT_SIZE_CACHE_LIMIT;
|
|
7
7
|
/** 东亚常见全宽字符块 */
|
|
8
8
|
private static readonly EAST_ASIAN_FULL_WIDTH_REGEX;
|
|
@@ -21,6 +21,11 @@ export declare class TextUtil {
|
|
|
21
21
|
private static readonly VISUAL_SPACING_MAP;
|
|
22
22
|
private static fontMetricsCache;
|
|
23
23
|
private static layoutSizeCache;
|
|
24
|
+
/**
|
|
25
|
+
* 等待文档字体就绪后再做 Canvas 度量,可减轻 Mac/Windows 因 Web 字体未加载导致的度量差异。
|
|
26
|
+
* 无 Font Loading API 时立即 resolve。
|
|
27
|
+
*/
|
|
28
|
+
static awaitDocumentFonts(): Promise<void>;
|
|
24
29
|
/**
|
|
25
30
|
* 验证中文字符
|
|
26
31
|
* @param char
|
|
@@ -52,13 +57,31 @@ export declare class TextUtil {
|
|
|
52
57
|
* @returns
|
|
53
58
|
*/
|
|
54
59
|
static getVisualSpacing(char: string, fontSize: number): number;
|
|
60
|
+
/** 与度量路径一致:lineHeight 固定为 1 的样式副本 */
|
|
61
|
+
private static measureBase;
|
|
55
62
|
/**
|
|
56
|
-
*
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
* 字体级缓存键(不含 text),与 getFontMetrics 语义一致
|
|
64
|
+
*/
|
|
65
|
+
private static measureStyleKey;
|
|
66
|
+
/**
|
|
67
|
+
* 布局缓存键:样式签名 + 编码后的文本
|
|
68
|
+
*/
|
|
69
|
+
private static layoutCacheKey;
|
|
70
|
+
private static touchLru;
|
|
71
|
+
private static evictOldestIfFull;
|
|
72
|
+
/**
|
|
73
|
+
* 单次 Konva.Text 计算 width / height(advance width 含视觉间距)
|
|
60
74
|
*/
|
|
61
|
-
private static
|
|
75
|
+
private static computeLayoutSizeUncached;
|
|
76
|
+
/**
|
|
77
|
+
* 一次度量:布局宽高 + 字体 ascent/descent(双缓存均未命中时只创建一个 Konva.Text)
|
|
78
|
+
*/
|
|
79
|
+
static measureTextComplete(payload: Konva.TextConfig): {
|
|
80
|
+
width: number;
|
|
81
|
+
height: number;
|
|
82
|
+
ascent: number;
|
|
83
|
+
descent: number;
|
|
84
|
+
};
|
|
62
85
|
/**
|
|
63
86
|
* 计算字体度量
|
|
64
87
|
* ascent / descent
|
package/dist/index.es.js
CHANGED
|
@@ -26676,9 +26676,9 @@ const mainData2WordData = (data) => {
|
|
|
26676
26676
|
};
|
|
26677
26677
|
class TextUtil {
|
|
26678
26678
|
/** 字体度量缓存限制 */
|
|
26679
|
-
static FONT_METRICS_CACHE_LIMIT =
|
|
26680
|
-
/**
|
|
26681
|
-
static LAYOUT_SIZE_CACHE_LIMIT =
|
|
26679
|
+
static FONT_METRICS_CACHE_LIMIT = 80;
|
|
26680
|
+
/** 布局大小缓存限制(按「样式 + 单段文本」键,长文档可提高命中率) */
|
|
26681
|
+
static LAYOUT_SIZE_CACHE_LIMIT = 500;
|
|
26682
26682
|
/** 东亚常见全宽字符块 */
|
|
26683
26683
|
static EAST_ASIAN_FULL_WIDTH_REGEX = /[\u3000-\u303F\u3040-\u30FF\u31F0-\u31FF\uFF01-\uFF60\uFFE0-\uFFE6]/u;
|
|
26684
26684
|
/** 表单/文档中常见的几何符号 */
|
|
@@ -26704,6 +26704,20 @@ class TextUtil {
|
|
|
26704
26704
|
};
|
|
26705
26705
|
static fontMetricsCache = /* @__PURE__ */ new Map();
|
|
26706
26706
|
static layoutSizeCache = /* @__PURE__ */ new Map();
|
|
26707
|
+
/**
|
|
26708
|
+
* 等待文档字体就绪后再做 Canvas 度量,可减轻 Mac/Windows 因 Web 字体未加载导致的度量差异。
|
|
26709
|
+
* 无 Font Loading API 时立即 resolve。
|
|
26710
|
+
*/
|
|
26711
|
+
static awaitDocumentFonts() {
|
|
26712
|
+
if (typeof document === "undefined") {
|
|
26713
|
+
return Promise.resolve();
|
|
26714
|
+
}
|
|
26715
|
+
const fonts = document.fonts;
|
|
26716
|
+
if (!fonts || typeof fonts.ready?.then !== "function") {
|
|
26717
|
+
return Promise.resolve();
|
|
26718
|
+
}
|
|
26719
|
+
return fonts.ready;
|
|
26720
|
+
}
|
|
26707
26721
|
/**
|
|
26708
26722
|
* 验证中文字符
|
|
26709
26723
|
* @param char
|
|
@@ -26748,23 +26762,112 @@ class TextUtil {
|
|
|
26748
26762
|
}
|
|
26749
26763
|
return fontSize2 * ratio;
|
|
26750
26764
|
}
|
|
26765
|
+
/** 与度量路径一致:lineHeight 固定为 1 的样式副本 */
|
|
26766
|
+
static measureBase(payload) {
|
|
26767
|
+
return Object.assign({}, payload, { lineHeight: 1 });
|
|
26768
|
+
}
|
|
26751
26769
|
/**
|
|
26752
|
-
*
|
|
26753
|
-
* @param payload
|
|
26754
|
-
* @param includeText
|
|
26755
|
-
* @returns
|
|
26770
|
+
* 字体级缓存键(不含 text),与 getFontMetrics 语义一致
|
|
26756
26771
|
*/
|
|
26757
|
-
static
|
|
26758
|
-
|
|
26759
|
-
|
|
26760
|
-
|
|
26761
|
-
|
|
26762
|
-
|
|
26763
|
-
|
|
26764
|
-
|
|
26765
|
-
|
|
26766
|
-
|
|
26767
|
-
|
|
26772
|
+
static measureStyleKey(payload) {
|
|
26773
|
+
const c2 = this.measureBase(payload);
|
|
26774
|
+
return [
|
|
26775
|
+
c2.fontSize ?? "",
|
|
26776
|
+
c2.fontFamily ?? "",
|
|
26777
|
+
c2.fontStyle ?? "",
|
|
26778
|
+
c2.fontVariant ?? "",
|
|
26779
|
+
c2.lineHeight ?? "",
|
|
26780
|
+
c2.padding ?? "",
|
|
26781
|
+
c2.letterSpacing ?? ""
|
|
26782
|
+
].join("");
|
|
26783
|
+
}
|
|
26784
|
+
/**
|
|
26785
|
+
* 布局缓存键:样式签名 + 编码后的文本
|
|
26786
|
+
*/
|
|
26787
|
+
static layoutCacheKey(payload) {
|
|
26788
|
+
const c2 = this.measureBase(payload);
|
|
26789
|
+
return `${this.measureStyleKey(payload)}${encodeURIComponent(c2.text ?? "")}`;
|
|
26790
|
+
}
|
|
26791
|
+
static touchLru(map2, key, value) {
|
|
26792
|
+
map2.delete(key);
|
|
26793
|
+
map2.set(key, value);
|
|
26794
|
+
}
|
|
26795
|
+
static evictOldestIfFull(map2, limit) {
|
|
26796
|
+
if (map2.size >= limit) {
|
|
26797
|
+
const firstKey = map2.keys().next().value;
|
|
26798
|
+
map2.delete(firstKey);
|
|
26799
|
+
}
|
|
26800
|
+
}
|
|
26801
|
+
/**
|
|
26802
|
+
* 单次 Konva.Text 计算 width / height(advance width 含视觉间距)
|
|
26803
|
+
*/
|
|
26804
|
+
static computeLayoutSizeUncached(payload) {
|
|
26805
|
+
const config = this.measureBase(payload);
|
|
26806
|
+
const text = new Konva.Text(config);
|
|
26807
|
+
const char = payload.text ?? "";
|
|
26808
|
+
const fontSize2 = payload.fontSize ?? 0;
|
|
26809
|
+
const width = this.isFullWidthChar(char) ? fontSize2 : text.width() + this.getVisualSpacing(char, fontSize2);
|
|
26810
|
+
return {
|
|
26811
|
+
width,
|
|
26812
|
+
height: text.height()
|
|
26813
|
+
};
|
|
26814
|
+
}
|
|
26815
|
+
/**
|
|
26816
|
+
* 一次度量:布局宽高 + 字体 ascent/descent(双缓存均未命中时只创建一个 Konva.Text)
|
|
26817
|
+
*/
|
|
26818
|
+
static measureTextComplete(payload) {
|
|
26819
|
+
const layoutKey = this.layoutCacheKey(payload);
|
|
26820
|
+
const fontKey = this.measureStyleKey(payload);
|
|
26821
|
+
const layoutCached = this.layoutSizeCache.get(layoutKey);
|
|
26822
|
+
const fontCached = this.fontMetricsCache.get(fontKey);
|
|
26823
|
+
if (layoutCached && fontCached) {
|
|
26824
|
+
this.touchLru(this.layoutSizeCache, layoutKey, layoutCached);
|
|
26825
|
+
this.touchLru(this.fontMetricsCache, fontKey, fontCached);
|
|
26826
|
+
return {
|
|
26827
|
+
width: layoutCached.width,
|
|
26828
|
+
height: layoutCached.height,
|
|
26829
|
+
ascent: fontCached.ascent,
|
|
26830
|
+
descent: fontCached.descent
|
|
26831
|
+
};
|
|
26832
|
+
}
|
|
26833
|
+
if (layoutCached && !fontCached) {
|
|
26834
|
+
this.touchLru(this.layoutSizeCache, layoutKey, layoutCached);
|
|
26835
|
+
const { ascent: ascent2, descent: descent2 } = this.getFontMetrics(payload);
|
|
26836
|
+
return {
|
|
26837
|
+
width: layoutCached.width,
|
|
26838
|
+
height: layoutCached.height,
|
|
26839
|
+
ascent: ascent2,
|
|
26840
|
+
descent: descent2
|
|
26841
|
+
};
|
|
26842
|
+
}
|
|
26843
|
+
if (!layoutCached && fontCached) {
|
|
26844
|
+
const layout2 = this.computeLayoutSizeUncached(payload);
|
|
26845
|
+
this.evictOldestIfFull(this.layoutSizeCache, this.LAYOUT_SIZE_CACHE_LIMIT);
|
|
26846
|
+
this.layoutSizeCache.set(layoutKey, layout2);
|
|
26847
|
+
this.touchLru(this.fontMetricsCache, fontKey, fontCached);
|
|
26848
|
+
return {
|
|
26849
|
+
width: layout2.width,
|
|
26850
|
+
height: layout2.height,
|
|
26851
|
+
ascent: fontCached.ascent,
|
|
26852
|
+
descent: fontCached.descent
|
|
26853
|
+
};
|
|
26854
|
+
}
|
|
26855
|
+
const config = this.measureBase(payload);
|
|
26856
|
+
const text = new Konva.Text(config);
|
|
26857
|
+
const char = payload.text ?? "";
|
|
26858
|
+
const fontSize2 = payload.fontSize ?? 0;
|
|
26859
|
+
const width = this.isFullWidthChar(char) ? fontSize2 : text.width() + this.getVisualSpacing(char, fontSize2);
|
|
26860
|
+
const height = text.height();
|
|
26861
|
+
const sizeHg = text.measureSize("Hg");
|
|
26862
|
+
const ascent = sizeHg.actualBoundingBoxAscent;
|
|
26863
|
+
const descent = sizeHg.actualBoundingBoxDescent;
|
|
26864
|
+
const layout = { width, height };
|
|
26865
|
+
const font = { ascent, descent };
|
|
26866
|
+
this.evictOldestIfFull(this.layoutSizeCache, this.LAYOUT_SIZE_CACHE_LIMIT);
|
|
26867
|
+
this.evictOldestIfFull(this.fontMetricsCache, this.FONT_METRICS_CACHE_LIMIT);
|
|
26868
|
+
this.layoutSizeCache.set(layoutKey, layout);
|
|
26869
|
+
this.fontMetricsCache.set(fontKey, font);
|
|
26870
|
+
return { width, height, ascent, descent };
|
|
26768
26871
|
}
|
|
26769
26872
|
/**
|
|
26770
26873
|
* 计算字体度量
|
|
@@ -26779,11 +26882,10 @@ class TextUtil {
|
|
|
26779
26882
|
text: targetText,
|
|
26780
26883
|
lineHeight: 1
|
|
26781
26884
|
});
|
|
26782
|
-
const cacheKey = this.
|
|
26885
|
+
const cacheKey = this.measureStyleKey(config);
|
|
26783
26886
|
if (this.fontMetricsCache.has(cacheKey)) {
|
|
26784
26887
|
const result2 = this.fontMetricsCache.get(cacheKey);
|
|
26785
|
-
this.fontMetricsCache
|
|
26786
|
-
this.fontMetricsCache.set(cacheKey, result2);
|
|
26888
|
+
this.touchLru(this.fontMetricsCache, cacheKey, result2);
|
|
26787
26889
|
return result2;
|
|
26788
26890
|
}
|
|
26789
26891
|
const text = new Konva.Text(config);
|
|
@@ -26792,10 +26894,7 @@ class TextUtil {
|
|
|
26792
26894
|
ascent: size.actualBoundingBoxAscent,
|
|
26793
26895
|
descent: size.actualBoundingBoxDescent
|
|
26794
26896
|
};
|
|
26795
|
-
|
|
26796
|
-
const firstKey = this.fontMetricsCache.keys().next().value;
|
|
26797
|
-
this.fontMetricsCache.delete(firstKey);
|
|
26798
|
-
}
|
|
26897
|
+
this.evictOldestIfFull(this.fontMetricsCache, this.FONT_METRICS_CACHE_LIMIT);
|
|
26799
26898
|
this.fontMetricsCache.set(cacheKey, result);
|
|
26800
26899
|
return result;
|
|
26801
26900
|
}
|
|
@@ -26824,13 +26923,7 @@ class TextUtil {
|
|
|
26824
26923
|
if (this.isFullWidthChar(char)) {
|
|
26825
26924
|
return fontSize2;
|
|
26826
26925
|
}
|
|
26827
|
-
|
|
26828
|
-
lineHeight: 1
|
|
26829
|
-
});
|
|
26830
|
-
const text = new Konva.Text(config);
|
|
26831
|
-
const glyphWidth = text.width();
|
|
26832
|
-
const visualSpacing = this.getVisualSpacing(char, fontSize2);
|
|
26833
|
-
return glyphWidth + visualSpacing;
|
|
26926
|
+
return this.getLayoutSize(payload).width;
|
|
26834
26927
|
}
|
|
26835
26928
|
/**
|
|
26836
26929
|
* 计算单字符布局大小
|
|
@@ -26842,27 +26935,16 @@ class TextUtil {
|
|
|
26842
26935
|
* @returns
|
|
26843
26936
|
*/
|
|
26844
26937
|
static getLayoutSize(payload) {
|
|
26845
|
-
const
|
|
26846
|
-
lineHeight: 1
|
|
26847
|
-
});
|
|
26848
|
-
const cacheKey = this.getMeasureCacheKey(config);
|
|
26938
|
+
const cacheKey = this.layoutCacheKey(payload);
|
|
26849
26939
|
if (this.layoutSizeCache.has(cacheKey)) {
|
|
26850
26940
|
const result2 = this.layoutSizeCache.get(cacheKey);
|
|
26851
|
-
this.layoutSizeCache
|
|
26852
|
-
|
|
26853
|
-
return result2;
|
|
26854
|
-
}
|
|
26855
|
-
const text = new Konva.Text(config);
|
|
26856
|
-
const result = {
|
|
26857
|
-
width: this.getAdvanceWidth(payload),
|
|
26858
|
-
height: text.height()
|
|
26859
|
-
};
|
|
26860
|
-
if (this.layoutSizeCache.size >= this.LAYOUT_SIZE_CACHE_LIMIT) {
|
|
26861
|
-
const firstKey = this.layoutSizeCache.keys().next().value;
|
|
26862
|
-
this.layoutSizeCache.delete(firstKey);
|
|
26941
|
+
this.touchLru(this.layoutSizeCache, cacheKey, result2);
|
|
26942
|
+
return { width: result2.width, height: result2.height };
|
|
26863
26943
|
}
|
|
26944
|
+
const result = this.computeLayoutSizeUncached(payload);
|
|
26945
|
+
this.evictOldestIfFull(this.layoutSizeCache, this.LAYOUT_SIZE_CACHE_LIMIT);
|
|
26864
26946
|
this.layoutSizeCache.set(cacheKey, result);
|
|
26865
|
-
return result;
|
|
26947
|
+
return { width: result.width, height: result.height };
|
|
26866
26948
|
}
|
|
26867
26949
|
/**
|
|
26868
26950
|
* 清除度量缓存
|
|
@@ -26942,14 +27024,7 @@ class TextRun extends LayoutNode {
|
|
|
26942
27024
|
* @returns
|
|
26943
27025
|
*/
|
|
26944
27026
|
static measureText(payload) {
|
|
26945
|
-
|
|
26946
|
-
const { ascent, descent } = TextUtil.getFontMetrics(payload);
|
|
26947
|
-
return {
|
|
26948
|
-
width,
|
|
26949
|
-
height,
|
|
26950
|
-
ascent,
|
|
26951
|
-
descent
|
|
26952
|
-
};
|
|
27027
|
+
return TextUtil.measureTextComplete(payload);
|
|
26953
27028
|
}
|
|
26954
27029
|
/**
|
|
26955
27030
|
* 计算文字大小 度量
|
|
@@ -51257,6 +51332,8 @@ async function initializeDocumentEngine(props, payload, result) {
|
|
|
51257
51332
|
initDocModelJson: JSON.stringify(docModel.toXmlJson())
|
|
51258
51333
|
}
|
|
51259
51334
|
};
|
|
51335
|
+
console.log("rawData", rawData);
|
|
51336
|
+
await TextUtil.awaitDocumentFonts();
|
|
51260
51337
|
const doc = new Doc({
|
|
51261
51338
|
model: docModel,
|
|
51262
51339
|
mode: fillModeType,
|
|
@@ -56994,6 +57071,12 @@ function useDocController(factory2, ops) {
|
|
|
56994
57071
|
rawData() {
|
|
56995
57072
|
return doc.dataManager.getRawData();
|
|
56996
57073
|
},
|
|
57074
|
+
setRawData(data, defaultDataMap) {
|
|
57075
|
+
doc.dataManager.setRawData(data, defaultDataMap);
|
|
57076
|
+
},
|
|
57077
|
+
setMultiple(updates) {
|
|
57078
|
+
return doc.dataManager.setMultiple(updates);
|
|
57079
|
+
},
|
|
56997
57080
|
getSubTableInfoList() {
|
|
56998
57081
|
return doc.model?.getSubTableInfoList() ?? [];
|
|
56999
57082
|
},
|
|
@@ -54,6 +54,10 @@ export interface DocControllerMethods extends DocOperations {
|
|
|
54
54
|
setModel(model: DocModel): void;
|
|
55
55
|
/** 获取当前文档原始填报数据快照 */
|
|
56
56
|
rawData(): Recordable<any>;
|
|
57
|
+
/** 设置当前文档整个原始数据 */
|
|
58
|
+
setRawData(data: Record<string, any>, defaultDataMap?: Record<string, any>): void;
|
|
59
|
+
/** 根据 jsonpath 批量设置文档数据 */
|
|
60
|
+
setMultiple(updates: Record<string, any>): boolean;
|
|
57
61
|
/** 获取当前文档的所有子表信息 */
|
|
58
62
|
getSubTableInfoList(): Array<{
|
|
59
63
|
field: string;
|