@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
- * @param payload
58
- * @param includeText
59
- * @returns
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 getMeasureCacheKey;
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 = 50;
26680
- /** 布局大小缓存限制 */
26681
- static LAYOUT_SIZE_CACHE_LIMIT = 100;
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 getMeasureCacheKey(payload, includeText = true) {
26758
- return JSON.stringify({
26759
- text: includeText ? payload.text ?? "" : "",
26760
- fontSize: payload.fontSize ?? "",
26761
- fontFamily: payload.fontFamily ?? "",
26762
- fontStyle: payload.fontStyle ?? "",
26763
- fontVariant: payload.fontVariant ?? "",
26764
- lineHeight: payload.lineHeight ?? "",
26765
- padding: payload.padding ?? "",
26766
- letterSpacing: payload.letterSpacing ?? ""
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.getMeasureCacheKey(config, false);
26885
+ const cacheKey = this.measureStyleKey(config);
26783
26886
  if (this.fontMetricsCache.has(cacheKey)) {
26784
26887
  const result2 = this.fontMetricsCache.get(cacheKey);
26785
- this.fontMetricsCache.delete(cacheKey);
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
- if (this.fontMetricsCache.size >= this.FONT_METRICS_CACHE_LIMIT) {
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
- const config = Object.assign({}, payload, {
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 config = Object.assign({}, payload, {
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.delete(cacheKey);
26852
- this.layoutSizeCache.set(cacheKey, result2);
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
- const { width, height } = TextUtil.getLayoutSize(payload);
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gct-paas/word",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "GCT 在线 word",
5
5
  "keywords": [
6
6
  "vue",