@courtifyai/docx-render 1.0.0 → 1.0.1
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.
- package/dist/index.es.js +2915 -0
- package/dist/index.umd.js +27 -0
- package/dist/style.css +1 -0
- package/package.json +5 -1
- package/debug-comments.cjs +0 -19
- package/index.html +0 -312
- package/src/comments/comments-parser.ts +0 -159
- package/src/comments/index.ts +0 -6
- package/src/font-table/font-loader.ts +0 -379
- package/src/font-table/font-parser.ts +0 -258
- package/src/font-table/index.ts +0 -22
- package/src/index.ts +0 -137
- package/src/parser/document-parser.ts +0 -1606
- package/src/parser/index.ts +0 -3
- package/src/parser/xml-parser.ts +0 -152
- package/src/renderer/document-renderer.ts +0 -2163
- package/src/renderer/index.ts +0 -1
- package/src/styles/index.css +0 -692
- package/src/theme/index.ts +0 -8
- package/src/theme/theme-parser.ts +0 -172
- package/src/theme/theme-utils.ts +0 -148
- package/src/types/index.ts +0 -847
- package/tsconfig.json +0 -27
- package/vite.config.ts +0 -26
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 主题解析器
|
|
3
|
-
* 解析 DOCX 主题 XML(word/theme/theme1.xml)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { ITheme, IColorScheme, IFontScheme, IFontInfo, IThemeColors } from '../types'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 解析主题 XML
|
|
10
|
-
*/
|
|
11
|
-
export function parseTheme(root: Element): ITheme {
|
|
12
|
-
const theme: ITheme = {
|
|
13
|
-
colorScheme: { name: '', colors: {} },
|
|
14
|
-
fontScheme: { name: '', majorFont: {}, minorFont: {} },
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// 查找 a:themeElements
|
|
18
|
-
const themeElements = findElement(root, 'themeElements')
|
|
19
|
-
if (!themeElements) return theme
|
|
20
|
-
|
|
21
|
-
// 遍历主题元素
|
|
22
|
-
for (const el of getChildElements(themeElements)) {
|
|
23
|
-
const localName = el.localName
|
|
24
|
-
|
|
25
|
-
if (localName === 'clrScheme') {
|
|
26
|
-
theme.colorScheme = parseColorScheme(el)
|
|
27
|
-
} else if (localName === 'fontScheme') {
|
|
28
|
-
theme.fontScheme = parseFontScheme(el)
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return theme
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 解析颜色方案
|
|
37
|
-
* <a:clrScheme name="Office">
|
|
38
|
-
* <a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1>
|
|
39
|
-
* <a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1>
|
|
40
|
-
* <a:accent1><a:srgbClr val="4472C4"/></a:accent1>
|
|
41
|
-
* ...
|
|
42
|
-
* </a:clrScheme>
|
|
43
|
-
*/
|
|
44
|
-
export function parseColorScheme(el: Element): IColorScheme {
|
|
45
|
-
const result: IColorScheme = {
|
|
46
|
-
name: el.getAttribute('name') || '',
|
|
47
|
-
colors: {},
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// 遍历每个颜色定义
|
|
51
|
-
for (const colorEl of getChildElements(el)) {
|
|
52
|
-
const colorName = colorEl.localName
|
|
53
|
-
const colorValue = extractColorValue(colorEl)
|
|
54
|
-
|
|
55
|
-
if (colorValue) {
|
|
56
|
-
result.colors[colorName as keyof IThemeColors] = colorValue
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return result
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* 提取颜色值
|
|
65
|
-
* 支持 srgbClr 和 sysClr 两种格式
|
|
66
|
-
*/
|
|
67
|
-
function extractColorValue(colorEl: Element): string | null {
|
|
68
|
-
// 尝试 srgbClr
|
|
69
|
-
const srgbClr = findElement(colorEl, 'srgbClr')
|
|
70
|
-
if (srgbClr) {
|
|
71
|
-
const val = srgbClr.getAttribute('val')
|
|
72
|
-
return val ? `#${val}` : null
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// 尝试 sysClr(系统颜色,取 lastClr 属性)
|
|
76
|
-
const sysClr = findElement(colorEl, 'sysClr')
|
|
77
|
-
if (sysClr) {
|
|
78
|
-
const lastClr = sysClr.getAttribute('lastClr')
|
|
79
|
-
return lastClr ? `#${lastClr}` : null
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return null
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 解析字体方案
|
|
87
|
-
* <a:fontScheme name="Office">
|
|
88
|
-
* <a:majorFont>
|
|
89
|
-
* <a:latin typeface="Calibri Light"/>
|
|
90
|
-
* <a:ea typeface=""/>
|
|
91
|
-
* <a:cs typeface=""/>
|
|
92
|
-
* </a:majorFont>
|
|
93
|
-
* <a:minorFont>
|
|
94
|
-
* <a:latin typeface="Calibri"/>
|
|
95
|
-
* <a:ea typeface=""/>
|
|
96
|
-
* <a:cs typeface=""/>
|
|
97
|
-
* </a:minorFont>
|
|
98
|
-
* </a:fontScheme>
|
|
99
|
-
*/
|
|
100
|
-
export function parseFontScheme(el: Element): IFontScheme {
|
|
101
|
-
const result: IFontScheme = {
|
|
102
|
-
name: el.getAttribute('name') || '',
|
|
103
|
-
majorFont: {},
|
|
104
|
-
minorFont: {},
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
for (const fontEl of getChildElements(el)) {
|
|
108
|
-
const localName = fontEl.localName
|
|
109
|
-
|
|
110
|
-
if (localName === 'majorFont') {
|
|
111
|
-
result.majorFont = parseFontInfo(fontEl)
|
|
112
|
-
} else if (localName === 'minorFont') {
|
|
113
|
-
result.minorFont = parseFontInfo(fontEl)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return result
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* 解析字体信息
|
|
122
|
-
*/
|
|
123
|
-
function parseFontInfo(el: Element): IFontInfo {
|
|
124
|
-
const result: IFontInfo = {}
|
|
125
|
-
|
|
126
|
-
for (const child of getChildElements(el)) {
|
|
127
|
-
const localName = child.localName
|
|
128
|
-
const typeface = child.getAttribute('typeface')
|
|
129
|
-
|
|
130
|
-
if (typeface) {
|
|
131
|
-
if (localName === 'latin') {
|
|
132
|
-
result.latin = typeface
|
|
133
|
-
} else if (localName === 'ea') {
|
|
134
|
-
result.ea = typeface
|
|
135
|
-
} else if (localName === 'cs') {
|
|
136
|
-
result.cs = typeface
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return result
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* 查找子元素(忽略命名空间)
|
|
146
|
-
*/
|
|
147
|
-
function findElement(parent: Element, localName: string): Element | null {
|
|
148
|
-
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
149
|
-
const node = parent.childNodes[i]
|
|
150
|
-
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
151
|
-
const el = node as Element
|
|
152
|
-
if (el.localName === localName) {
|
|
153
|
-
return el
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return null
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* 获取所有子元素
|
|
162
|
-
*/
|
|
163
|
-
function getChildElements(parent: Element): Element[] {
|
|
164
|
-
const result: Element[] = []
|
|
165
|
-
for (let i = 0; i < parent.childNodes.length; i++) {
|
|
166
|
-
const node = parent.childNodes[i]
|
|
167
|
-
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
168
|
-
result.push(node as Element)
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
return result
|
|
172
|
-
}
|
package/src/theme/theme-utils.ts
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 主题颜色工具函数
|
|
3
|
-
* 提供主题颜色解析和转换能力
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { ITheme, IThemeColorRef, IThemeColors } from '../types'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 主题颜色名称到标准名称的映射
|
|
10
|
-
* Word 使用的属性名和 theme.xml 中的元素名可能不同
|
|
11
|
-
*/
|
|
12
|
-
const THEME_COLOR_MAP: Record<string, keyof IThemeColors> = {
|
|
13
|
-
// 标准映射
|
|
14
|
-
dark1: 'dk1',
|
|
15
|
-
dark2: 'dk2',
|
|
16
|
-
light1: 'lt1',
|
|
17
|
-
light2: 'lt2',
|
|
18
|
-
accent1: 'accent1',
|
|
19
|
-
accent2: 'accent2',
|
|
20
|
-
accent3: 'accent3',
|
|
21
|
-
accent4: 'accent4',
|
|
22
|
-
accent5: 'accent5',
|
|
23
|
-
accent6: 'accent6',
|
|
24
|
-
hyperlink: 'hlink',
|
|
25
|
-
followedHyperlink: 'folHlink',
|
|
26
|
-
// 直接映射(XML 中的原始名称)
|
|
27
|
-
dk1: 'dk1',
|
|
28
|
-
dk2: 'dk2',
|
|
29
|
-
lt1: 'lt1',
|
|
30
|
-
lt2: 'lt2',
|
|
31
|
-
hlink: 'hlink',
|
|
32
|
-
folHlink: 'folHlink',
|
|
33
|
-
// 文本颜色别名
|
|
34
|
-
text1: 'dk1',
|
|
35
|
-
text2: 'dk2',
|
|
36
|
-
background1: 'lt1',
|
|
37
|
-
background2: 'lt2',
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* 解析主题颜色引用,返回实际颜色值
|
|
42
|
-
*
|
|
43
|
-
* @param theme - 主题对象
|
|
44
|
-
* @param colorRef - 主题颜色引用
|
|
45
|
-
* @returns 解析后的颜色值(如 #RRGGBB)或 undefined
|
|
46
|
-
*/
|
|
47
|
-
export function resolveThemeColor(
|
|
48
|
-
theme: ITheme | undefined,
|
|
49
|
-
colorRef: IThemeColorRef
|
|
50
|
-
): string | undefined {
|
|
51
|
-
if (!theme?.colorScheme?.colors) return undefined
|
|
52
|
-
|
|
53
|
-
const themeColorName = colorRef.themeColor
|
|
54
|
-
const mappedName = THEME_COLOR_MAP[themeColorName] || themeColorName
|
|
55
|
-
const baseColor = theme.colorScheme.colors[mappedName]
|
|
56
|
-
|
|
57
|
-
if (!baseColor) return undefined
|
|
58
|
-
|
|
59
|
-
// 如果有 tint 或 shade,需要调整颜色
|
|
60
|
-
if (colorRef.themeTint !== undefined || colorRef.themeShade !== undefined) {
|
|
61
|
-
return applyTintShade(baseColor, colorRef.themeTint, colorRef.themeShade)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return baseColor
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* 应用色调(tint)和阴影(shade)调整
|
|
69
|
-
*
|
|
70
|
-
* tint: 向白色混合(0 = 原色,255 = 纯白)
|
|
71
|
-
* shade: 向黑色混合(0 = 原色,255 = 纯黑)
|
|
72
|
-
*
|
|
73
|
-
* @param color - 基础颜色(#RRGGBB 格式)
|
|
74
|
-
* @param tint - 色调值(0-255)
|
|
75
|
-
* @param shade - 阴影值(0-255)
|
|
76
|
-
* @returns 调整后的颜色
|
|
77
|
-
*/
|
|
78
|
-
export function applyTintShade(
|
|
79
|
-
color: string,
|
|
80
|
-
tint?: number,
|
|
81
|
-
shade?: number
|
|
82
|
-
): string {
|
|
83
|
-
// 解析颜色
|
|
84
|
-
const hex = color.replace('#', '')
|
|
85
|
-
const r = parseInt(hex.substr(0, 2), 16)
|
|
86
|
-
const g = parseInt(hex.substr(2, 2), 16)
|
|
87
|
-
const b = parseInt(hex.substr(4, 2), 16)
|
|
88
|
-
|
|
89
|
-
let finalR = r
|
|
90
|
-
let finalG = g
|
|
91
|
-
let finalB = b
|
|
92
|
-
|
|
93
|
-
// 应用 tint(向白色混合)
|
|
94
|
-
// OOXML 规范: result = color + (255 - color) * tint / 255
|
|
95
|
-
if (tint !== undefined && tint > 0) {
|
|
96
|
-
const tintFactor = tint / 255
|
|
97
|
-
finalR = Math.round(r + (255 - r) * tintFactor)
|
|
98
|
-
finalG = Math.round(g + (255 - g) * tintFactor)
|
|
99
|
-
finalB = Math.round(b + (255 - b) * tintFactor)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// 应用 shade(向黑色混合)
|
|
103
|
-
// OOXML 规范: result = color * (1 - shade / 255)
|
|
104
|
-
if (shade !== undefined && shade > 0) {
|
|
105
|
-
const shadeFactor = 1 - shade / 255
|
|
106
|
-
finalR = Math.round(finalR * shadeFactor)
|
|
107
|
-
finalG = Math.round(finalG * shadeFactor)
|
|
108
|
-
finalB = Math.round(finalB * shadeFactor)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 确保在有效范围内
|
|
112
|
-
finalR = Math.max(0, Math.min(255, finalR))
|
|
113
|
-
finalG = Math.max(0, Math.min(255, finalG))
|
|
114
|
-
finalB = Math.max(0, Math.min(255, finalB))
|
|
115
|
-
|
|
116
|
-
// 转回 hex
|
|
117
|
-
return `#${toHex(finalR)}${toHex(finalG)}${toHex(finalB)}`
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* 数字转两位十六进制
|
|
122
|
-
*/
|
|
123
|
-
function toHex(n: number): string {
|
|
124
|
-
const hex = n.toString(16)
|
|
125
|
-
return hex.length === 1 ? '0' + hex : hex
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* 获取主题字体
|
|
130
|
-
*
|
|
131
|
-
* @param theme - 主题对象
|
|
132
|
-
* @param fontType - 字体类型(major/minor)
|
|
133
|
-
* @param script - 文字类型(latin/ea/cs)
|
|
134
|
-
* @returns 字体名称或 undefined
|
|
135
|
-
*/
|
|
136
|
-
export function resolveThemeFont(
|
|
137
|
-
theme: ITheme | undefined,
|
|
138
|
-
fontType: 'major' | 'minor',
|
|
139
|
-
script: 'latin' | 'ea' | 'cs' = 'latin'
|
|
140
|
-
): string | undefined {
|
|
141
|
-
if (!theme?.fontScheme) return undefined
|
|
142
|
-
|
|
143
|
-
const fontInfo = fontType === 'major'
|
|
144
|
-
? theme.fontScheme.majorFont
|
|
145
|
-
: theme.fontScheme.minorFont
|
|
146
|
-
|
|
147
|
-
return fontInfo[script]
|
|
148
|
-
}
|