@stll/folio-core 0.33.2 → 0.34.0

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.
Files changed (44) hide show
  1. package/dist/ai-edits/apply.js +137 -23
  2. package/dist/ai-edits/snapshot.js +24 -2
  3. package/dist/ai-edits/types.d.ts +6 -6
  4. package/dist/compare/formatting.d.ts +1 -1
  5. package/dist/compare/formatting.js +38 -9
  6. package/dist/compare/verification.js +18 -1
  7. package/dist/controller/headerFooterEditorManager.js +11 -9
  8. package/dist/controller/layoutPipeline.js +24 -3
  9. package/dist/display-list/build/watermarkPrimitives.js +15 -3
  10. package/dist/document-operations.js +30 -3
  11. package/dist/docx/headerFooterParser.js +8 -14
  12. package/dist/docx/paragraphParser.js +45 -0
  13. package/dist/docx/serializer/headerFooterSerializer.js +21 -2
  14. package/dist/docx/serializer/paragraphSerializer.d.ts +1 -1
  15. package/dist/docx/serializer/paragraphSerializer.js +19 -9
  16. package/dist/docx/settingsParser.js +3 -0
  17. package/dist/docx/watermarkParser.d.ts +2 -4
  18. package/dist/docx/watermarkParser.js +4 -6
  19. package/dist/headless-layout.js +14 -2
  20. package/dist/layout-bridge/convert/footnoteLayout.d.ts +2 -2
  21. package/dist/layout-bridge/convert/footnoteLayout.js +23 -10
  22. package/dist/layout-bridge/convert/headerFooterLayout.js +13 -2
  23. package/dist/layout-bridge/convert/toFlowBlocks.js +96 -13
  24. package/dist/layout-engine/index.js +11 -4
  25. package/dist/layout-engine/justifiedLineFit.d.ts +4 -4
  26. package/dist/layout-engine/justifiedLineFit.js +4 -4
  27. package/dist/layout-engine/measure/lineBreakProvider.js +1 -0
  28. package/dist/layout-engine/measure/measureBlocks.js +1 -1
  29. package/dist/layout-engine/measure/measureParagraph.js +29 -29
  30. package/dist/layout-painter/renderPage.js +6 -1
  31. package/dist/layout-painter/renderParagraph.js +18 -8
  32. package/dist/layout-painter/renderWatermark.js +11 -4
  33. package/dist/prosemirror/attrs/index.js +11 -0
  34. package/dist/prosemirror/commands/comments.js +22 -2
  35. package/dist/prosemirror/conversion/fromProseDoc.js +32 -5
  36. package/dist/prosemirror/conversion/toProseDoc.js +49 -11
  37. package/dist/prosemirror/extensions/core/DocExtension.js +7 -1
  38. package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
  39. package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +1 -0
  40. package/dist/prosemirror/schema/marks.d.ts +2 -8
  41. package/dist/utils/fontResolver.js +51 -0
  42. package/dist/utils/formatToStyle.js +41 -1
  43. package/dist/watermark/index.js +7 -0
  44. package/package.json +2 -2
@@ -223,6 +223,7 @@ const paragraphNodeSpec = {
223
223
  pageBreakBefore: { default: null },
224
224
  renderedPageBreakBefore: { default: null },
225
225
  _pageBreakCarrier: { default: null },
226
+ _detachedWatermarkHost: { default: null },
226
227
  _trailingPageBreak: { default: null },
227
228
  keepNext: { default: null },
228
229
  keepLines: { default: null },
@@ -50,6 +50,7 @@ const RunFormattingOverrideExtension = createMarkExtension({
50
50
  schemaMarkName: "runFormattingOverride",
51
51
  markSpec: {
52
52
  attrs: {
53
+ directFontProperties: { default: null },
53
54
  bold: { default: null },
54
55
  italic: { default: null },
55
56
  underline: { default: null },
@@ -121,17 +121,11 @@ type RunPropertyChangeMarkAttrs = {
121
121
  provenance: TrackedChangeProvenance;
122
122
  suggestionId?: string;
123
123
  };
124
- type RunFormattingOverrideAttrs = { [K in keyof Pick<document_d_exports.TextFormatting, "bold" | "italic" | "strike" | "allCaps" | "smallCaps" | "hidden" | "emboss" | "imprint" | "shadow" | "outline">]?: boolean; } & {
124
+ type RunFormattingOverrideAttrs = Partial<Record<"bold" | "boldCs" | "cs" | "italic" | "italicCs" | "strike" | "allCaps" | "smallCaps" | "hidden" | "emboss" | "imprint" | "shadow" | "outline", boolean>> & {
125
+ directFontProperties?: readonly ("fontFamily" | "fontSize" | "color")[];
125
126
  doubleStrike?: false;
126
127
  rtl?: false;
127
- /** Independent complex-script weight (`w:bCs`). */
128
- boldCs?: boolean;
129
- /** Force complex-script formatting for the full run (`w:cs`). */
130
- cs?: boolean;
131
- /** Independent complex-script size in half-points (`w:szCs`). */
132
128
  fontSizeCs?: number;
133
- /** Independent complex-script slant (`w:iCs`). */
134
- italicCs?: boolean;
135
129
  underline?: "none";
136
130
  };
137
131
  /**
@@ -191,6 +191,23 @@ const FONT_MAPPINGS = {
191
191
  unitsPerEm: 2048
192
192
  })
193
193
  },
194
+ david: {
195
+ googleFont: "Noto Serif Hebrew",
196
+ category: "serif",
197
+ fallbackStack: [
198
+ "David",
199
+ "Noto Serif Hebrew",
200
+ "Times New Roman",
201
+ "serif"
202
+ ],
203
+ singleLineRatio: singleLineRatioOf({
204
+ source: "hhea",
205
+ hheaAscent: 1505,
206
+ hheaDescent: -510,
207
+ hheaLineGap: 0,
208
+ unitsPerEm: 2048
209
+ })
210
+ },
194
211
  georgia: {
195
212
  googleFont: "Tinos",
196
213
  category: "serif",
@@ -628,6 +645,40 @@ const FONT_MAPPINGS = {
628
645
  note: "Measured at 11pt against a 15.36pt reference-layout line pitch; the face ships with the office suite, not as a readable font file."
629
646
  })
630
647
  },
648
+ frankruehl: {
649
+ googleFont: "Frank Ruhl Libre",
650
+ category: "serif",
651
+ fallbackStack: [
652
+ "FrankRuehl",
653
+ "Frank Ruhl Libre",
654
+ "Times New Roman",
655
+ "serif"
656
+ ],
657
+ singleLineRatio: singleLineRatioOf({
658
+ source: "hhea",
659
+ hheaAscent: 1462,
660
+ hheaDescent: -442,
661
+ hheaLineGap: 0,
662
+ unitsPerEm: 2048
663
+ })
664
+ },
665
+ miriam: {
666
+ googleFont: "Miriam Libre",
667
+ category: "sans-serif",
668
+ fallbackStack: [
669
+ "Miriam",
670
+ "Miriam Libre",
671
+ "Arial",
672
+ "sans-serif"
673
+ ],
674
+ singleLineRatio: singleLineRatioOf({
675
+ source: "hhea",
676
+ hheaAscent: 1546,
677
+ hheaDescent: -512,
678
+ hheaLineGap: 0,
679
+ unitsPerEm: 2048
680
+ })
681
+ },
631
682
  "ms mincho": {
632
683
  googleFont: "Noto Serif JP",
633
684
  category: "serif",
@@ -151,6 +151,33 @@ function borderToStyle(border, side = "", theme) {
151
151
  });
152
152
  return style;
153
153
  }
154
+ const HALF_STEP_SHADING_PATTERNS = /* @__PURE__ */ new Set([
155
+ "pct12",
156
+ "pct37",
157
+ "pct62",
158
+ "pct87"
159
+ ]);
160
+ const AUTO_PATTERN_COLOR = "000000";
161
+ const AUTO_PATTERN_BACKGROUND = "FFFFFF";
162
+ function percentageShadingRatio(pattern) {
163
+ if (!pattern?.startsWith("pct")) return;
164
+ const percentage = Number.parseInt(pattern.slice(3), 10);
165
+ if (!Number.isFinite(percentage)) return;
166
+ return (percentage + (HALF_STEP_SHADING_PATTERNS.has(pattern) ? .5 : 0)) / 100;
167
+ }
168
+ function resolvePatternHex({ color, fallback, theme }) {
169
+ const resolved = resolveShadingColor(color, theme);
170
+ return /^#(?<hex>[0-9A-F]{6})$/iu.exec(resolved)?.groups?.["hex"]?.toUpperCase() ?? fallback;
171
+ }
172
+ function blendShadingColors({ foreground, background, ratio }) {
173
+ const channels = [];
174
+ for (let offset = 0; offset < 6; offset += 2) {
175
+ const foregroundChannel = Number.parseInt(foreground.slice(offset, offset + 2), 16);
176
+ const backgroundChannel = Number.parseInt(background.slice(offset, offset + 2), 16);
177
+ channels.push(Math.round(foregroundChannel * ratio + backgroundChannel * (1 - ratio)).toString(16).padStart(2, "0"));
178
+ }
179
+ return `#${channels.join("").toUpperCase()}`;
180
+ }
154
181
  /**
155
182
  * Convert ShadingProperties to background color
156
183
  *
@@ -161,13 +188,26 @@ function borderToStyle(border, side = "", theme) {
161
188
  function resolveShadingFill(shading, theme) {
162
189
  if (!shading) return "";
163
190
  if (shading.pattern === "nil") return "";
191
+ const percentageRatio = percentageShadingRatio(shading.pattern);
192
+ if (percentageRatio !== void 0) return blendShadingColors({
193
+ foreground: resolvePatternHex({
194
+ color: shading.color,
195
+ fallback: AUTO_PATTERN_COLOR,
196
+ theme
197
+ }),
198
+ background: resolvePatternHex({
199
+ color: shading.fill,
200
+ fallback: AUTO_PATTERN_BACKGROUND,
201
+ theme
202
+ }),
203
+ ratio: percentageRatio
204
+ });
164
205
  if (shading.fill) {
165
206
  if (shading.fill.auto) return "";
166
207
  if (shading.fill.rgb === "auto" || shading.fill.rgb === "FFFFFF") return "";
167
208
  return resolveShadingColor(shading.fill, theme);
168
209
  }
169
210
  if (shading.pattern === "solid" && shading.color) return resolveShadingColor(shading.color, theme);
170
- if (shading.pattern && shading.pattern.startsWith("pct") && shading.color) return resolveShadingColor(shading.color, theme);
171
211
  return "";
172
212
  }
173
213
  /**
@@ -1,3 +1,4 @@
1
+ import { isEmptyParagraph } from "../docx/paragraphParser.js";
1
2
  //#region src/watermark/index.ts
2
3
  /**
3
4
  * Schemes allowed for a picture watermark's external image target
@@ -42,6 +43,12 @@ function setDocumentWatermark(doc, watermark) {
42
43
  const nextHeaders = /* @__PURE__ */ new Map();
43
44
  if (headers) for (const [rId, header] of headers) {
44
45
  const next = { ...header };
46
+ const retainedHostIndex = next.watermarkBlockIndex;
47
+ const retainedHost = next.rawWatermarkXml !== void 0 && retainedHostIndex !== void 0 ? next.content.at(retainedHostIndex) : void 0;
48
+ if (retainedHostIndex !== void 0 && retainedHost?.type === "paragraph" && isEmptyParagraph(retainedHost)) {
49
+ next.content = [...next.content];
50
+ next.content.splice(retainedHostIndex, 1);
51
+ }
45
52
  if (watermark === void 0) {
46
53
  delete next.watermark;
47
54
  delete next.watermarkBlockIndex;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/folio-core",
3
- "version": "0.33.2",
3
+ "version": "0.34.0",
4
4
  "description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
5
5
  "keywords": [
6
6
  "document-model",
@@ -115,7 +115,7 @@
115
115
  "perf": "bun scripts/profile-editor.ts"
116
116
  },
117
117
  "dependencies": {
118
- "@stll/docx-core": "^0.19.1",
118
+ "@stll/docx-core": "^0.19.2",
119
119
  "@stll/docx-utils": "^0.1.0",
120
120
  "@stll/template-conditions": "^0.1.0",
121
121
  "better-result": "3.0.1",