@a3s-lab/office 0.27.0 → 0.29.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 (47) hide show
  1. package/README.md +41 -1
  2. package/dist/0~1403.js +1 -1
  3. package/dist/0~4980.js +1 -1
  4. package/dist/0~5093.js +164 -5
  5. package/dist/0~7043.js +241 -241
  6. package/dist/0~7048.js +1 -1
  7. package/dist/0~7614.js +1 -1
  8. package/dist/0~document-editor.js +626 -12
  9. package/dist/0~presentation-editor.js +1 -1
  10. package/dist/0~spreadsheet-editor.js +1 -2
  11. package/dist/0~work-docx-export.js +396 -20
  12. package/dist/0~work-docx-import.js +7 -3
  13. package/dist/0~work-office-diagnostics.js +98 -4
  14. package/dist/0~work-pptx-export.js +1 -1
  15. package/dist/0~work-pptx-import.js +1 -1
  16. package/dist/0~work-presentation-charts.js +1 -1
  17. package/dist/1544.js +1 -1
  18. package/dist/4104.js +2 -2
  19. package/dist/4121.js +1510 -0
  20. package/dist/6282.js +220 -682
  21. package/dist/8715.js +1 -1
  22. package/dist/core.js +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/internal/features/work/editors/document-command-catalog.d.ts +9 -0
  25. package/dist/internal/features/work/editors/document-font-dialog-model.d.ts +33 -2
  26. package/dist/internal/features/work/editors/document-font-dialog-run-border-model.d.ts +24 -0
  27. package/dist/internal/features/work/editors/document-font-dialog-run-border-section.d.ts +10 -0
  28. package/dist/internal/features/work/work-document-character-formatting.d.ts +4 -0
  29. package/dist/internal/features/work/work-document-format-changes.d.ts +5 -0
  30. package/dist/internal/features/work/work-document-legacy-text-effects.d.ts +27 -0
  31. package/dist/internal/features/work/work-document-run-border.d.ts +11 -0
  32. package/dist/internal/features/work/work-document-word-line-metrics.d.ts +5 -0
  33. package/dist/internal/features/work/work-docx-hidden-text-export.d.ts +1 -0
  34. package/dist/internal/features/work/work-docx-legacy-text-effects-diagnostics.d.ts +3 -0
  35. package/dist/internal/features/work/work-docx-legacy-text-effects-export.d.ts +18 -0
  36. package/dist/internal/features/work/work-docx-legacy-text-effects.d.ts +12 -0
  37. package/dist/internal/features/work/work-docx-run-border-diagnostics.d.ts +3 -0
  38. package/dist/internal/features/work/work-docx-run-border-export.d.ts +17 -0
  39. package/dist/internal/features/work/work-docx-run-border.d.ts +20 -0
  40. package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +6 -0
  41. package/dist/internal/features/work/work-docx-run-property-order.d.ts +2 -0
  42. package/dist/office-kernel.wasm +0 -0
  43. package/dist/styles.css +72 -0
  44. package/docs/latest/en/browser-editor-architecture.md +36 -0
  45. package/package.json +7 -1
  46. package/dist/5184.js +0 -649
  47. package/dist/9424.js +0 -16
package/dist/8715.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createOfficeId as createWorkId, directChild, attribute } from "./5184.js";
1
+ import { createOfficeId as createWorkId, directChild, attribute } from "./4121.js";
2
2
  const CELL_REFERENCE = /\$?([A-Z]{1,3})\$?([1-9]\d*)/i;
3
3
  const CELL_OR_RANGE = new RegExp(`^${CELL_REFERENCE.source}(?::${CELL_REFERENCE.source})?$`, 'i');
4
4
  const COLUMN_RANGE = /^\$?([A-Z]{1,3}):\$?([A-Z]{1,3})$/i;
package/dist/core.js CHANGED
@@ -3,7 +3,7 @@ export { AGENT_PROPOSAL_PROTOCOL, OFFICE_NOTIFICATION_EVENT, createAgentProposal
3
3
  export { DOCUMENT_SNAPSHOT_MEDIA_TYPE, DOCUMENT_SNAPSHOT_SCHEMA, DOCUMENT_SNAPSHOT_VERSION, DOCUMENT_SOURCE_MEDIA_TYPE, DOCUMENT_SOURCE_SCHEMA, DOCUMENT_SOURCE_VERSION, OFFICE_COLLABORATION_DEFAULT_MAX_TRANSPORT_PAYLOAD_BYTES, OFFICE_COLLABORATION_MAX_TRANSPORT_PAYLOAD_BYTES, OFFICE_COLLABORATION_PRESENCE_FIELD, OFFICE_FILE_ACCEPT, applyDocumentSource, artifactExtension, artifactExtension as workArtifactExtension, artifactKindLabel, artifactKindLabel as workArtifactKindLabel, createArtifactBlob, createOfficeCollaborationPresence, createOfficeCollaborationTransportBinding, createPdfCollaborationContent, decodeDocumentSnapshot, defaultPptxRuntimeUrl, downloadArtifact, downloadArtifactPdf, encodeDocumentSnapshot, forgetSourceBlob, importOfficeFile, officeKindForFile, pdfPagesForExport, projectDocumentSource, readSourceBlob, registerSourceBlob } from "./4104.js";
4
4
  export { OFFICE_COLLABORATION_NAMESPACE, OFFICE_COLLABORATION_PROTOCOL, OfficeCollaborationError, createOfficeCollaborationSession, readOfficeCollaborationMetadata } from "./9787.js";
5
5
  export { assertPdfCollaborationSource, createOfficePdfCollaborationBinding, initializeOfficePdfCollaboration, readOfficePdfCollaboration, readOfficePdfCollaborationSource, replaceOfficePdfCollaboration } from "./3818.js";
6
- export { createArtifact, createOfficeId, officeTemplates } from "./5184.js";
6
+ export { createArtifact, createOfficeId, officeTemplates } from "./4121.js";
7
7
  export { createOfficeDocumentCollaborationBinding, initializeOfficeDocumentCollaboration, officeDocumentCollaborationFragment, readOfficeDocumentCollaboration } from "./6282.js";
8
8
  export { createOfficeMarkdownCollaborationBinding, initializeOfficeMarkdownCollaboration, readOfficeMarkdownCollaboration, replaceOfficeMarkdownCollaboration } from "./2591.js";
9
9
  export { createOfficePresentationCollaborationBinding, initializeOfficePresentationCollaboration, readOfficePresentationCollaboration, replaceOfficePresentationCollaboration } from "./4560.js";
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ export { DOCUMENT_SNAPSHOT_MEDIA_TYPE, DOCUMENT_SNAPSHOT_SCHEMA, DOCUMENT_SOURCE
6
6
  export { DocumentEditor, MarkdownEditor, PDF_EVIDENCE_COORDINATE_BASIS, PdfViewer, PresentationEditor, SpreadsheetEditor, defaultDocumentArabicLayoutFontUrl, defaultDocumentHebrewLayoutFontUrl, defaultDocumentLatinLayoutFontUrl, defaultDocumentLayoutFontUrl, defaultDocumentLayoutFonts, defaultOfficeKernelWasmUrl, defaultPdfiumWasmUrl, preloadOfficeEditor } from "./432.js";
7
7
  export { OFFICE_COLLABORATION_NAMESPACE, OFFICE_COLLABORATION_PROTOCOL, OfficeCollaborationError, createOfficeCollaborationSession, readOfficeCollaborationMetadata } from "./9787.js";
8
8
  export { assertPdfCollaborationSource, createOfficePdfCollaborationBinding, initializeOfficePdfCollaboration, readOfficePdfCollaboration, readOfficePdfCollaborationSource, replaceOfficePdfCollaboration } from "./3818.js";
9
- export { createArtifact, createOfficeId, officeTemplates } from "./5184.js";
9
+ export { createArtifact, createOfficeId, officeTemplates } from "./4121.js";
10
10
  export { createOfficeDocumentCollaborationBinding, initializeOfficeDocumentCollaboration, officeDocumentCollaborationFragment, readOfficeDocumentCollaboration } from "./6282.js";
11
11
  export { createOfficeMarkdownCollaborationBinding, initializeOfficeMarkdownCollaboration, readOfficeMarkdownCollaboration, replaceOfficeMarkdownCollaboration } from "./2591.js";
12
12
  export { createOfficePresentationCollaborationBinding, initializeOfficePresentationCollaboration, readOfficePresentationCollaboration, replaceOfficePresentationCollaboration } from "./4560.js";
@@ -333,6 +333,15 @@ export declare const documentCommandCatalog: {
333
333
  readonly editor: readonly ["Mod-Shift-h"];
334
334
  };
335
335
  };
336
+ readonly runBorder: {
337
+ readonly id: "font.runBorder";
338
+ readonly label: "字符边框";
339
+ readonly location: {
340
+ readonly area: "ribbon";
341
+ readonly tab: "home";
342
+ readonly group: "font";
343
+ };
344
+ };
336
345
  readonly clearFormatting: {
337
346
  readonly id: "font.clearFormatting";
338
347
  readonly label: "清除格式";
@@ -1,6 +1,7 @@
1
1
  import type { Editor } from '@tiptap/core';
2
2
  import { type WorkDocumentEmphasisMark } from '../work-document-emphasis';
3
3
  import { type DocumentFontDialogScriptFontPatch, type DocumentFontFamilySource } from './document-font-dialog-script-font-model';
4
+ import { type DocumentFontDialogRunBorderDraft, type DocumentFontDialogRunBorderPatch, type DocumentFontDialogRunBorderSource } from './document-font-dialog-run-border-model';
4
5
  export type { DocumentFontFamilySource } from './document-font-dialog-script-font-model';
5
6
  export type DocumentCharacterSpacingMode = 'condensed' | 'expanded' | 'mixed' | 'normal';
6
7
  export type DocumentCharacterPositionMode = 'lowered' | 'mixed' | 'normal' | 'raised';
@@ -31,6 +32,23 @@ export interface DocumentFontDialogSource {
31
32
  mixed: boolean;
32
33
  value: boolean | null;
33
34
  };
35
+ legacyTextOutline: {
36
+ mixed: boolean;
37
+ value: boolean | null;
38
+ };
39
+ legacyTextShadow: {
40
+ mixed: boolean;
41
+ value: boolean | null;
42
+ };
43
+ legacyTextEmboss: {
44
+ mixed: boolean;
45
+ value: boolean | null;
46
+ };
47
+ legacyTextImprint: {
48
+ mixed: boolean;
49
+ value: boolean | null;
50
+ };
51
+ runBorder: DocumentFontDialogRunBorderSource;
34
52
  latinFont: DocumentFontFamilySource;
35
53
  eastAsiaFont: DocumentFontFamilySource;
36
54
  complexScriptFont: DocumentFontFamilySource;
@@ -39,7 +57,7 @@ export interface DocumentFontDialogSource {
39
57
  previewText: string;
40
58
  selectedCharacters: number;
41
59
  }
42
- export interface DocumentFontDialogDraft {
60
+ export interface DocumentFontDialogDraft extends DocumentFontDialogRunBorderDraft {
43
61
  characterScaleMode: DocumentCharacterScaleMode;
44
62
  characterScalePercent: string;
45
63
  characterPositionMode: DocumentCharacterPositionMode;
@@ -50,17 +68,25 @@ export interface DocumentFontDialogDraft {
50
68
  kerningThresholdPoints: string;
51
69
  emphasisMark: DocumentEmphasisMarkMode;
52
70
  hiddenText: boolean;
71
+ legacyTextOutline: boolean;
72
+ legacyTextShadow: boolean;
73
+ legacyTextEmboss: boolean;
74
+ legacyTextImprint: boolean;
53
75
  latinFont: string;
54
76
  eastAsiaFont: string;
55
77
  complexScriptFont: string;
56
78
  }
57
- export interface DocumentFontDialogPatch extends DocumentFontDialogScriptFontPatch {
79
+ export interface DocumentFontDialogPatch extends DocumentFontDialogScriptFontPatch, DocumentFontDialogRunBorderPatch {
58
80
  characterScalePercent?: number;
59
81
  characterPositionHalfPoints?: number;
60
82
  characterSpacingTwips?: number;
61
83
  kerningThresholdHalfPoints?: number | null;
62
84
  emphasisMark?: WorkDocumentEmphasisMark | null;
63
85
  hiddenText?: boolean;
86
+ legacyTextOutline?: boolean;
87
+ legacyTextShadow?: boolean;
88
+ legacyTextEmboss?: boolean;
89
+ legacyTextImprint?: boolean;
64
90
  }
65
91
  export interface DocumentFontDialogTouched {
66
92
  characterPosition: boolean;
@@ -70,6 +96,11 @@ export interface DocumentFontDialogTouched {
70
96
  eastAsiaFont: boolean;
71
97
  emphasisMark: boolean;
72
98
  hiddenText: boolean;
99
+ legacyTextOutline: boolean;
100
+ legacyTextShadow: boolean;
101
+ legacyTextEmboss: boolean;
102
+ legacyTextImprint: boolean;
103
+ runBorder: boolean;
73
104
  kerning: boolean;
74
105
  latinFont: boolean;
75
106
  }
@@ -0,0 +1,24 @@
1
+ import { type DocumentRunBorder, type DocumentRunBorderStyle } from '../work-document-run-border';
2
+ export type DocumentFontDialogRunBorderMode = 'inherit' | 'mixed' | 'none' | 'value';
3
+ export interface DocumentFontDialogRunBorderSource {
4
+ mixed: boolean;
5
+ value: DocumentRunBorder | null;
6
+ }
7
+ export interface DocumentFontDialogRunBorderDraft {
8
+ runBorderMode: DocumentFontDialogRunBorderMode;
9
+ runBorderStyle: DocumentRunBorderStyle;
10
+ runBorderColor: 'auto' | `#${string}`;
11
+ runBorderWidthPoints: string;
12
+ runBorderSpacingPoints: string;
13
+ runBorderShadow: boolean;
14
+ runBorderFrame: boolean;
15
+ }
16
+ export interface DocumentFontDialogRunBorderPatch {
17
+ runBorder?: DocumentRunBorder | null;
18
+ }
19
+ export declare function addDocumentFontDialogRunBorderValue(values: Map<string, DocumentRunBorder | null>, source: unknown): void;
20
+ export declare function selectedDocumentFontDialogRunBorderValue(values: ReadonlyMap<string, DocumentRunBorder | null>): DocumentFontDialogRunBorderSource;
21
+ export declare function createDocumentFontDialogRunBorderDraft(source: DocumentFontDialogRunBorderSource): DocumentFontDialogRunBorderDraft;
22
+ export declare function documentFontDialogRunBorderDraftError(draft: DocumentFontDialogRunBorderDraft): string | null;
23
+ export declare function documentFontDialogRunBorderPatch(source: DocumentFontDialogRunBorderSource, draft: DocumentFontDialogRunBorderDraft, touched: boolean): DocumentFontDialogRunBorderPatch;
24
+ export declare function documentFontDialogRunBorderFromDraft(source: DocumentFontDialogRunBorderSource, draft: DocumentFontDialogRunBorderDraft): DocumentRunBorder | null;
@@ -0,0 +1,10 @@
1
+ import type { CSSProperties } from 'react';
2
+ import { type DocumentFontDialogRunBorderDraft, type DocumentFontDialogRunBorderSource } from './document-font-dialog-run-border-model';
3
+ export declare function DocumentFontDialogRunBorderSection({ source, draft, touched, onDraftChange, onTouched, }: {
4
+ source: DocumentFontDialogRunBorderSource;
5
+ draft: DocumentFontDialogRunBorderDraft;
6
+ touched: boolean;
7
+ onDraftChange: (patch: Partial<DocumentFontDialogRunBorderDraft>) => void;
8
+ onTouched: () => void;
9
+ }): import("react").JSX.Element;
10
+ export declare function documentFontDialogRunBorderPreviewStyle(source: DocumentFontDialogRunBorderSource, draft: DocumentFontDialogRunBorderDraft): CSSProperties;
@@ -1,6 +1,7 @@
1
1
  import { Extension, type Editor } from '@tiptap/core';
2
2
  import { type WorkDocumentEmphasisMark } from './work-document-emphasis';
3
3
  import { type WorkDocumentTextCase } from './work-document-text-case';
4
+ import { type DocumentRunBorder } from './work-document-run-border';
4
5
  declare module '@tiptap/core' {
5
6
  interface Commands<ReturnType> {
6
7
  documentCharacterFormatting: {
@@ -18,6 +19,9 @@ declare module '@tiptap/core' {
18
19
  toggleDocumentHiddenText: () => ReturnType;
19
20
  setDocumentTextCase: (textCase: WorkDocumentTextCase) => ReturnType;
20
21
  toggleDocumentTextCase: (textCase: Exclude<WorkDocumentTextCase, 'none'>) => ReturnType;
22
+ setDocumentRunBorder: (border: DocumentRunBorder) => ReturnType;
23
+ unsetDocumentRunBorder: () => ReturnType;
24
+ toggleDocumentRunBorder: () => ReturnType;
21
25
  };
22
26
  }
23
27
  }
@@ -31,6 +31,11 @@ export declare function importedDocumentCharacterFormatting(formatting: {
31
31
  kerningThresholdHalfPoints?: number;
32
32
  emphasisMark?: WorkDocumentEmphasisMark;
33
33
  hiddenText?: boolean;
34
+ legacyTextOutline?: boolean;
35
+ legacyTextShadow?: boolean;
36
+ legacyTextEmboss?: boolean;
37
+ legacyTextImprint?: boolean;
38
+ runBorder?: string;
34
39
  wordLineHeightFactor?: number;
35
40
  wordSnapToGrid?: boolean;
36
41
  fontSize?: number;
@@ -0,0 +1,27 @@
1
+ export declare const DOCUMENT_LEGACY_TEXT_OUTLINE_ATTRIBUTE = "data-office-legacy-text-outline";
2
+ export declare const DOCUMENT_LEGACY_TEXT_SHADOW_ATTRIBUTE = "data-office-legacy-text-shadow";
3
+ export declare const DOCUMENT_LEGACY_TEXT_EMBOSS_ATTRIBUTE = "data-office-legacy-text-emboss";
4
+ export declare const DOCUMENT_LEGACY_TEXT_IMPRINT_ATTRIBUTE = "data-office-legacy-text-imprint";
5
+ export declare const DOCUMENT_LEGACY_TEXT_EFFECT_NAMES: readonly ["outline", "shadow", "emboss", "imprint"];
6
+ export type WorkDocumentLegacyTextEffectName = (typeof DOCUMENT_LEGACY_TEXT_EFFECT_NAMES)[number];
7
+ export interface WorkDocumentLegacyTextEffects {
8
+ outline?: boolean;
9
+ shadow?: boolean;
10
+ emboss?: boolean;
11
+ imprint?: boolean;
12
+ }
13
+ export interface WorkDocumentLegacyTextEffectCss {
14
+ WebkitTextFillColor?: string;
15
+ WebkitTextStroke?: string;
16
+ paintOrder?: string;
17
+ textShadow?: string;
18
+ }
19
+ export declare function normalizeDocumentLegacyTextEffect(value: unknown): boolean | null;
20
+ export declare function normalizeDocumentLegacyTextEffects(value: unknown): WorkDocumentLegacyTextEffects | null;
21
+ export declare function documentLegacyTextEffectsConflict(effects: WorkDocumentLegacyTextEffects): boolean;
22
+ export declare function documentLegacyTextEffectFromElement(element: HTMLElement, effect: WorkDocumentLegacyTextEffectName): boolean | null;
23
+ export declare function documentLegacyTextEffectsFromElement(element: HTMLElement): WorkDocumentLegacyTextEffects | null;
24
+ export declare function documentLegacyTextEffectsFromTextStyleAttributes(attributes: Record<string, unknown>): WorkDocumentLegacyTextEffects | null;
25
+ export declare function documentLegacyTextEffectsDomAttributes(value: WorkDocumentLegacyTextEffects): Record<string, string>;
26
+ export declare function documentLegacyTextEffectsCss(value: WorkDocumentLegacyTextEffects): WorkDocumentLegacyTextEffectCss;
27
+ export declare function documentLegacyTextStyleAttributeName(effect: WorkDocumentLegacyTextEffectName): string;
@@ -0,0 +1,11 @@
1
+ import { type DocumentParagraphBorder, type DocumentParagraphBorderStyle } from './work-document-paragraph-borders';
2
+ export declare const DOCUMENT_RUN_BORDER_ATTRIBUTE = "data-office-run-border";
3
+ export type DocumentRunBorder = DocumentParagraphBorder;
4
+ export type DocumentRunBorderStyle = DocumentParagraphBorderStyle;
5
+ export declare const DOCUMENT_RUN_BORDER_STYLES: readonly DocumentRunBorderStyle[];
6
+ export declare function normalizeDocumentRunBorder(source: unknown): DocumentRunBorder | null;
7
+ export declare function parseDocumentRunBorder(source: unknown): DocumentRunBorder | null;
8
+ export declare function serializeDocumentRunBorder(source: unknown): string | undefined;
9
+ export declare function parseDocumentRunBorderElement(element: HTMLElement): DocumentRunBorder | null;
10
+ export declare function documentRunBorderDomAttributes(source: unknown): Record<string, string>;
11
+ export declare function documentRunBorderIsVisible(source: unknown): boolean;
@@ -9,6 +9,11 @@ declare module '@tiptap/extension-text-style' {
9
9
  characterSpacingTwips?: number | null;
10
10
  emphasisMark?: WorkDocumentEmphasisMark | null;
11
11
  hiddenText?: boolean | null;
12
+ legacyTextOutline?: boolean | null;
13
+ legacyTextShadow?: boolean | null;
14
+ legacyTextEmboss?: boolean | null;
15
+ legacyTextImprint?: boolean | null;
16
+ runBorder?: string | null;
12
17
  kerningThresholdHalfPoints?: number | null;
13
18
  scriptFonts?: string | null;
14
19
  scriptFontSlot?: WorkDocumentScriptFontSlot | null;
@@ -11,6 +11,7 @@ export declare class DocxHiddenTextPatchCollector {
11
11
  private readonly markersByValueAndStyle;
12
12
  constructor(source: string);
13
13
  marker(value: boolean, inheritedStyle?: string): string;
14
+ lookup(marker: string | undefined): DocxHiddenTextPatch | undefined;
14
15
  private originalStyle;
15
16
  }
16
17
  export declare function patchDocxHiddenText(buffer: ArrayBuffer, patches: readonly DocxHiddenTextPatch[]): Promise<ArrayBuffer>;
@@ -0,0 +1,3 @@
1
+ import { type OoxmlPackage } from './work-ooxml-package';
2
+ import type { WorkCompatibilityIssue } from './work-types';
3
+ export declare function diagnoseDocxLegacyTextEffects(archive: OoxmlPackage, document: Document): Promise<WorkCompatibilityIssue[]>;
@@ -0,0 +1,18 @@
1
+ import { type WorkDocumentLegacyTextEffects } from './work-document-legacy-text-effects';
2
+ export interface DocxLegacyTextEffectsPatch {
3
+ marker: string;
4
+ style?: string;
5
+ effects: WorkDocumentLegacyTextEffects;
6
+ }
7
+ export declare class DocxLegacyTextEffectsPatchCollector {
8
+ readonly patches: DocxLegacyTextEffectsPatch[];
9
+ private nextMarker;
10
+ private readonly occupied;
11
+ private readonly patchesByMarker;
12
+ private readonly markersByEffectsAndStyle;
13
+ constructor(source: string);
14
+ marker(value: WorkDocumentLegacyTextEffects, inheritedStyle?: string): string;
15
+ lookup(marker: string | undefined): DocxLegacyTextEffectsPatch | undefined;
16
+ private originalStyle;
17
+ }
18
+ export declare function patchDocxLegacyTextEffects(buffer: ArrayBuffer, patches: readonly DocxLegacyTextEffectsPatch[]): Promise<ArrayBuffer>;
@@ -0,0 +1,12 @@
1
+ import { type WorkDocumentLegacyTextEffects } from './work-document-legacy-text-effects';
2
+ export type DocxLegacyTextEffectsInspection = {
3
+ status: 'absent';
4
+ } | {
5
+ status: 'invalid';
6
+ } | {
7
+ status: 'valid';
8
+ value: WorkDocumentLegacyTextEffects;
9
+ };
10
+ export declare function inspectDocxLegacyTextEffects(properties: Element | null | undefined): DocxLegacyTextEffectsInspection;
11
+ export declare function docxLegacyTextEffectsFromProperties(properties: Element | null | undefined): WorkDocumentLegacyTextEffects | undefined;
12
+ export declare function resolveDocxLegacyTextEffects(propertySources: readonly Element[]): WorkDocumentLegacyTextEffects | undefined;
@@ -0,0 +1,3 @@
1
+ import { type OoxmlPackage } from './work-ooxml-package';
2
+ import type { WorkCompatibilityIssue } from './work-types';
3
+ export declare function diagnoseDocxRunBorders(archive: OoxmlPackage, document: Document): Promise<WorkCompatibilityIssue[]>;
@@ -0,0 +1,17 @@
1
+ import type { IRunOptions } from 'docx';
2
+ import { type DocumentRunBorder } from './work-document-run-border';
3
+ export interface DocxRunBorderPatch {
4
+ marker: string;
5
+ border: DocumentRunBorder;
6
+ }
7
+ export declare class DocxRunBorderPatchCollector {
8
+ readonly patches: DocxRunBorderPatch[];
9
+ private nextMarker;
10
+ private readonly usedColors;
11
+ constructor(sourceHtml: string);
12
+ marker(source: unknown): string | null;
13
+ hasMarker(value: string | null | undefined): value is string;
14
+ }
15
+ export declare function documentRunBorderDocxOptions(source: unknown, collector: DocxRunBorderPatchCollector): Pick<IRunOptions, 'border'>;
16
+ export declare function patchDocxRunBorders(buffer: ArrayBuffer, patches: readonly DocxRunBorderPatch[]): Promise<ArrayBuffer>;
17
+ export declare function setDocxRunBorderAttributes(document: Document, element: Element, border: DocumentRunBorder): void;
@@ -0,0 +1,20 @@
1
+ import type { DocumentRunBorder } from './work-document-run-border';
2
+ import { type DocxThemeSource } from './work-docx-theme';
3
+ export type DocxRunBorderInspection = {
4
+ status: 'absent';
5
+ spoofedCount: number;
6
+ } | {
7
+ status: 'invalid';
8
+ spoofedCount: number;
9
+ } | {
10
+ status: 'valid';
11
+ value: DocumentRunBorder;
12
+ spoofedCount: number;
13
+ };
14
+ export interface ResolvedDocxRunBorder {
15
+ border: DocumentRunBorder | undefined;
16
+ invalidCount: number;
17
+ spoofedCount: number;
18
+ }
19
+ export declare function inspectDocxRunBorder(properties: Element | null | undefined, themeSource?: DocxThemeSource): DocxRunBorderInspection;
20
+ export declare function resolveDocxRunBorder(propertySources: readonly Element[], themeSource?: DocxThemeSource): ResolvedDocxRunBorder;
@@ -7,6 +7,7 @@ import { type WorkDocumentTextCase } from './work-document-text-case';
7
7
  import { type WorkDocumentUnderlineFormatting } from './work-document-underline';
8
8
  import { type WorkDocumentStrikeFormatting } from './work-document-strike';
9
9
  import { type WorkDocumentScriptFontSlot, type WorkDocumentScriptFonts } from './work-document-script-fonts';
10
+ import { type DocumentRunBorder } from './work-document-run-border';
10
11
  export interface ImportedDocxRunFormatting {
11
12
  bold?: boolean;
12
13
  italic?: boolean;
@@ -20,6 +21,10 @@ export interface ImportedDocxRunFormatting {
20
21
  kerningThresholdHalfPoints?: number;
21
22
  emphasisMark?: WorkDocumentEmphasisMark;
22
23
  hiddenText?: boolean;
24
+ legacyTextOutline?: boolean;
25
+ legacyTextShadow?: boolean;
26
+ legacyTextEmboss?: boolean;
27
+ legacyTextImprint?: boolean;
23
28
  fontFamily?: string;
24
29
  scriptFonts?: WorkDocumentScriptFonts;
25
30
  scriptFontSlot?: WorkDocumentScriptFontSlot;
@@ -31,6 +36,7 @@ export interface ImportedDocxRunFormatting {
31
36
  themeColor?: DocxThemeColorReference;
32
37
  themeFill?: DocxThemeColorReference;
33
38
  textCase?: WorkDocumentTextCase;
39
+ runBorder?: DocumentRunBorder;
34
40
  }
35
41
  export interface ImportedDocxRunFormattingMarker {
36
42
  startMarker: string;
@@ -0,0 +1,2 @@
1
+ export declare const DOCX_RUN_PROPERTY_ORDER: readonly ["rStyle", "rFonts", "b", "bCs", "i", "iCs", "caps", "smallCaps", "strike", "dstrike", "outline", "shadow", "emboss", "imprint", "noProof", "snapToGrid", "vanish", "webHidden", "color", "spacing", "w", "kern", "position", "sz", "szCs", "highlight", "u", "effect", "bdr", "shd", "fitText", "vertAlign", "rtl", "cs", "em", "lang", "eastAsianLayout", "specVanish", "rPrChange"];
2
+ export declare const DOCX_RUN_PROPERTY_RANK: Map<string, number>;
Binary file
package/dist/styles.css CHANGED
@@ -9705,6 +9705,46 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
9705
9705
  min-height: 32px;
9706
9706
  }
9707
9707
 
9708
+ .work-document-font-dialog-legacy-effects {
9709
+ border: 1px solid color-mix(in srgb, var(--a3s-line) 78%, transparent);
9710
+ background: var(--a3s-panel-soft);
9711
+ border-radius: 8px;
9712
+ grid-column: 1 / -1;
9713
+ grid-template-columns: repeat(4, minmax(0, 1fr));
9714
+ gap: 8px 16px;
9715
+ min-width: 0;
9716
+ margin: 0;
9717
+ padding: 10px 12px;
9718
+ display: grid;
9719
+ }
9720
+
9721
+ .work-document-font-dialog-run-border {
9722
+ border: 1px solid color-mix(in srgb, var(--a3s-line) 78%, transparent);
9723
+ background: var(--a3s-panel-soft);
9724
+ border-radius: 8px;
9725
+ grid-column: 1 / -1;
9726
+ grid-template-columns: repeat(3, minmax(0, 1fr));
9727
+ gap: 10px 14px;
9728
+ min-width: 0;
9729
+ margin: 0;
9730
+ padding: 10px 12px 12px;
9731
+ display: grid;
9732
+ }
9733
+
9734
+ .work-document-font-dialog-run-border > legend {
9735
+ color: var(--a3s-muted);
9736
+ padding: 0 4px;
9737
+ font-size: 11px;
9738
+ font-weight: 700;
9739
+ }
9740
+
9741
+ .work-document-font-dialog-run-border-effects {
9742
+ align-items: end;
9743
+ gap: 16px;
9744
+ min-height: 32px;
9745
+ display: flex;
9746
+ }
9747
+
9708
9748
  .work-document-font-dialog-field > .work-office-select, .work-document-font-dialog-field .work-office-number-field, .work-document-font-dialog-field button[role="combobox"] {
9709
9749
  width: 100%;
9710
9750
  }
@@ -9771,6 +9811,14 @@ button.work-office-collaboration-participant:hover .work-office-collaboration-lo
9771
9811
  grid-template-columns: minmax(0, 1fr);
9772
9812
  }
9773
9813
 
9814
+ .work-document-font-dialog-legacy-effects {
9815
+ grid-template-columns: repeat(2, minmax(0, 1fr));
9816
+ }
9817
+
9818
+ .work-document-font-dialog-run-border {
9819
+ grid-template-columns: minmax(0, 1fr);
9820
+ }
9821
+
9774
9822
  .work-document-font-dialog-preview {
9775
9823
  min-height: 88px;
9776
9824
  }
@@ -9806,6 +9854,30 @@ sub [data-office-character-position-half-points], sup [data-office-character-pos
9806
9854
  display: none;
9807
9855
  }
9808
9856
 
9857
+ [data-office-legacy-text-outline="true"] {
9858
+ -webkit-text-fill-color: transparent;
9859
+ -webkit-text-stroke: .045em currentColor;
9860
+ paint-order: stroke fill;
9861
+ }
9862
+
9863
+ [data-office-legacy-text-shadow="true"] {
9864
+ text-shadow: .08em .08em;
9865
+ }
9866
+
9867
+ [data-office-legacy-text-emboss="true"] {
9868
+ -webkit-text-fill-color: currentColor;
9869
+ -webkit-text-stroke: 0 transparent;
9870
+ paint-order: normal;
9871
+ text-shadow: -.045em -.045em #ffffffe6, .045em .045em #00000085;
9872
+ }
9873
+
9874
+ [data-office-legacy-text-imprint="true"] {
9875
+ -webkit-text-fill-color: currentColor;
9876
+ -webkit-text-stroke: 0 transparent;
9877
+ paint-order: normal;
9878
+ text-shadow: .045em .045em #ffffffe6, -.045em -.045em #00000085;
9879
+ }
9880
+
9809
9881
  .work-document-list-tools {
9810
9882
  align-items: center;
9811
9883
  gap: 2px;
@@ -685,6 +685,42 @@ uses browser-authoritative measurement because its invisible run must not
685
685
  contribute Worker/WASM advances. This is an explicit per-paragraph fallback,
686
686
  not a second document tree or a second pagination model.
687
687
 
688
+ Native outline, shadow, emboss, and imprint are resolved through four nullable
689
+ TextStyle properties before DOM serialization and DOCX export. Outline and
690
+ shadow may coexist; emboss and imprint are each exclusive with the other
691
+ enabled effects. Import rejects duplicate, misplaced, namespace-spoofed,
692
+ text-bearing, child-bearing, extra-attribute, unknown, and conflicting property
693
+ sets before they reach paint. Export uses the canonical WordprocessingML run
694
+ property order and collision-safe nested `w:rStyle` markers. Legacy effects are
695
+ the inner marker and hidden text is the outer marker; hidden text is patched
696
+ first and effects second, while only markers that reach a real generated text
697
+ run are committed. This retains an original character style without orphaning
698
+ an empty nested container. The four effects change paint but not advances, so
699
+ eligible paragraphs remain on Worker/WASM layout and the browser/PDF CSS
700
+ projection stays an explicitly bounded approximation.
701
+
702
+ Native character borders are resolved as one validated TextStyle value before
703
+ layout eligibility, DOM serialization, and DOCX export. The closed model
704
+ accepts 25 visible `w:bdr` line styles plus `nil` and `none`, direct or resolved
705
+ theme colors with tint/shade identity, bounded eighth-point width, point
706
+ spacing, shadow, and frame fields. Duplicate, misplaced, art-style,
707
+ namespace-spoofed, child- or text-bearing, extra-attribute, out-of-range, and
708
+ unresolved-theme leaves normalize to an explicit `nil` reset and emit a
709
+ compatibility diagnostic instead of enabling untrusted paint or reviving
710
+ inherited formatting.
711
+
712
+ The canonical DOM mark stores semantic JSON in `data-office-run-border`; its
713
+ CSS projection uses four-edge border paint, padding, shadow, and
714
+ `box-decoration-break: clone`. A visible border changes inline geometry, so
715
+ the containing paragraph deliberately uses browser-authoritative measurement.
716
+ Explicit `nil` and `none` add no geometry and remain eligible for Worker/WASM
717
+ shaping. The DOCX generator allocates a collision-checked temporary border
718
+ color only inside generated runs, then patches the body, page chrome, and note
719
+ parts to exact `w:bdr` attributes before formatting-change snapshots are
720
+ written. This keeps direct/style inheritance and prior `w:rPrChange` values
721
+ exact while treating browser and PDF line-style paint as a bounded visual
722
+ approximation.
723
+
688
724
  Tables, images, code blocks, and other complex content use explicit
689
725
  format-specific measurement. Top-level tables are row flows; eligible rows can
690
726
  additionally expose synchronized direct-cell block fragments. Ordered and bullet
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a3s-lab/office",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "description": "Open-source collaborative browser editors for documents, Markdown, spreadsheets, presentations, and PDFs.",
5
5
  "keywords": [
6
6
  "office",
@@ -89,6 +89,8 @@
89
89
  "playground:visual": "playwright test --config playwright.config.ts",
90
90
  "playground:visual:document-emphasis": "playwright test visual-tests/document-emphasis.functional.spec.ts --config playwright.config.ts",
91
91
  "playground:visual:document-hidden-text": "playwright test visual-tests/document-hidden-text.functional.spec.ts --config playwright.config.ts",
92
+ "playground:visual:document-legacy-text-effects": "playwright test visual-tests/document-legacy-text-effects.functional.spec.ts --config playwright.config.ts",
93
+ "playground:visual:document-run-border": "playwright test visual-tests/document-run-border.functional.spec.ts --config playwright.config.ts",
92
94
  "playground:visual:document-kerning": "playwright test visual-tests/document-kerning.functional.spec.ts --config playwright.config.ts",
93
95
  "playground:visual:document-character-scale": "playwright test visual-tests/document-character-scale.functional.spec.ts --config playwright.config.ts",
94
96
  "playground:visual:document-character-position": "playwright test visual-tests/document-character-position.functional.spec.ts --config playwright.config.ts",
@@ -198,6 +200,10 @@
198
200
  "test:e2e:writer-emphasis:check": "a3s-test check tests/e2e/word-emphasis.acl --json",
199
201
  "test:e2e:writer-hidden-text": "A3S_TEST_SUITE=tests/e2e/word-hidden-text.acl bash scripts/run-a3s-test-web-gate.sh",
200
202
  "test:e2e:writer-hidden-text:check": "a3s-test check tests/e2e/word-hidden-text.acl --json",
203
+ "test:e2e:writer-legacy-text-effects": "A3S_TEST_SUITE=tests/e2e/word-legacy-text-effects.acl bash scripts/run-a3s-test-web-gate.sh",
204
+ "test:e2e:writer-legacy-text-effects:check": "a3s-test check tests/e2e/word-legacy-text-effects.acl --json",
205
+ "test:e2e:writer-run-border": "A3S_TEST_SUITE=tests/e2e/word-run-border.acl bash scripts/run-a3s-test-web-gate.sh",
206
+ "test:e2e:writer-run-border:check": "a3s-test check tests/e2e/word-run-border.acl --json",
201
207
  "test:e2e:writer-kerning": "A3S_TEST_SUITE=tests/e2e/word-kerning.acl bash scripts/run-a3s-test-web-gate.sh",
202
208
  "test:e2e:writer-kerning:check": "a3s-test check tests/e2e/word-kerning.acl --json",
203
209
  "test:e2e:writer-character-scale": "A3S_TEST_SUITE=tests/e2e/word-character-scale.acl bash scripts/run-a3s-test-web-gate.sh",