|
@@ -6,20 +6,29 @@ interface Attr {
|
|
|
[key: string]: number | string
|
|
[key: string]: number | string
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * ★ 判断是否为 PowerPoint 列表的「悬挂缩进」
|
|
|
|
|
+ *
|
|
|
|
|
+ * PPT 项目符号列表用「负首行缩进 + 正左边距」实现:text-indent 为负(如 -36pt)
|
|
|
|
|
+ * 且 margin-left 为正(如 36pt)。这种组合在 PPTist 的 li::before 项目符号布局下
|
|
|
|
|
+ * 会导致文字偏移、对不齐,应清除(交给 li::before 正常生成符号并对齐)。
|
|
|
|
|
+ */
|
|
|
|
|
+function isHangingIndent(textIndent: string, marginLeft: string): boolean {
|
|
|
|
|
+ if (!textIndent || !marginLeft) return false
|
|
|
|
|
+
|
|
|
|
|
+ const indentVal = parseFloat(textIndent)
|
|
|
|
|
+ const marginVal = parseFloat(marginLeft)
|
|
|
|
|
+
|
|
|
|
|
+ // 负首行缩进 + 正左边距 = 悬挂缩进
|
|
|
|
|
+ return indentVal < 0 && marginVal > 0
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const orderedList: NodeSpec = {
|
|
const orderedList: NodeSpec = {
|
|
|
attrs: {
|
|
attrs: {
|
|
|
- order: {
|
|
|
|
|
- default: 1,
|
|
|
|
|
- },
|
|
|
|
|
- listStyleType: {
|
|
|
|
|
- default: '',
|
|
|
|
|
- },
|
|
|
|
|
- fontsize: {
|
|
|
|
|
- default: '',
|
|
|
|
|
- },
|
|
|
|
|
- color: {
|
|
|
|
|
- default: '',
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ order: { default: 1 },
|
|
|
|
|
+ listStyleType: { default: '' },
|
|
|
|
|
+ fontsize: { default: '' },
|
|
|
|
|
+ color: { default: '' },
|
|
|
},
|
|
},
|
|
|
content: 'list_item+',
|
|
content: 'list_item+',
|
|
|
group: 'block',
|
|
group: 'block',
|
|
@@ -36,8 +45,8 @@ const orderedList: NodeSpec = {
|
|
|
if (color) attr['color'] = color
|
|
if (color) attr['color'] = color
|
|
|
|
|
|
|
|
return attr
|
|
return attr
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
],
|
|
],
|
|
|
toDOM: (node: Node) => {
|
|
toDOM: (node: Node) => {
|
|
|
const { order, listStyleType, fontsize, color } = node.attrs
|
|
const { order, listStyleType, fontsize, color } = node.attrs
|
|
@@ -49,22 +58,15 @@ const orderedList: NodeSpec = {
|
|
|
const attr: Attr = { style }
|
|
const attr: Attr = { style }
|
|
|
if (order !== 1) attr['start'] = order
|
|
if (order !== 1) attr['start'] = order
|
|
|
|
|
|
|
|
-
|
|
|
|
|
return ['ol', attr, 0]
|
|
return ['ol', attr, 0]
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const bulletList: NodeSpec = {
|
|
const bulletList: NodeSpec = {
|
|
|
attrs: {
|
|
attrs: {
|
|
|
- listStyleType: {
|
|
|
|
|
- default: '',
|
|
|
|
|
- },
|
|
|
|
|
- fontsize: {
|
|
|
|
|
- default: '',
|
|
|
|
|
- },
|
|
|
|
|
- color: {
|
|
|
|
|
- default: '',
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ listStyleType: { default: '' },
|
|
|
|
|
+ fontsize: { default: '' },
|
|
|
|
|
+ color: { default: '' },
|
|
|
},
|
|
},
|
|
|
content: 'list_item+',
|
|
content: 'list_item+',
|
|
|
group: 'block',
|
|
group: 'block',
|
|
@@ -80,8 +82,8 @@ const bulletList: NodeSpec = {
|
|
|
if (color) attr['color'] = color
|
|
if (color) attr['color'] = color
|
|
|
|
|
|
|
|
return attr
|
|
return attr
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
],
|
|
],
|
|
|
toDOM: (node: Node) => {
|
|
toDOM: (node: Node) => {
|
|
|
const { listStyleType, fontsize, color } = node.attrs
|
|
const { listStyleType, fontsize, color } = node.attrs
|
|
@@ -94,18 +96,12 @@ const bulletList: NodeSpec = {
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/*
|
|
|
|
|
-const listItem: NodeSpec = {
|
|
|
|
|
- ..._listItem,
|
|
|
|
|
- content: 'paragraph block*',
|
|
|
|
|
- group: 'block',
|
|
|
|
|
-}
|
|
|
|
|
-*/
|
|
|
|
|
const listItem: NodeSpec = {
|
|
const listItem: NodeSpec = {
|
|
|
attrs: {
|
|
attrs: {
|
|
|
textAlign: { default: '' },
|
|
textAlign: { default: '' },
|
|
|
textAlignLast: { default: '' },
|
|
textAlignLast: { default: '' },
|
|
|
textIndent: { default: '' },
|
|
textIndent: { default: '' },
|
|
|
|
|
+ fontsize: { default: '' },
|
|
|
marginTop: { default: '' },
|
|
marginTop: { default: '' },
|
|
|
marginBottom: { default: '' },
|
|
marginBottom: { default: '' },
|
|
|
marginLeft: { default: '' },
|
|
marginLeft: { default: '' },
|
|
@@ -117,7 +113,7 @@ const listItem: NodeSpec = {
|
|
|
paddingLeft: { default: '' },
|
|
paddingLeft: { default: '' },
|
|
|
whiteSpace: { default: 'normal' },
|
|
whiteSpace: { default: 'normal' },
|
|
|
writingMode: { default: '' },
|
|
writingMode: { default: '' },
|
|
|
- // 新增字体相关属性
|
|
|
|
|
|
|
+ // 字体相关
|
|
|
fontWeight: { default: '' },
|
|
fontWeight: { default: '' },
|
|
|
fontStyle: { default: '' },
|
|
fontStyle: { default: '' },
|
|
|
textDecoration: { default: '' },
|
|
textDecoration: { default: '' },
|
|
@@ -132,56 +128,64 @@ const listItem: NodeSpec = {
|
|
|
{
|
|
{
|
|
|
tag: 'li',
|
|
tag: 'li',
|
|
|
getAttrs(dom) {
|
|
getAttrs(dom) {
|
|
|
- const el = dom as HTMLElement;
|
|
|
|
|
- const style = el.style;
|
|
|
|
|
-
|
|
|
|
|
- const textAlign = style.textAlign || '';
|
|
|
|
|
- const textAlignLast = style.textAlignLast || '';
|
|
|
|
|
- const textIndent = style.textIndent || '';
|
|
|
|
|
- const marginTop = style.marginTop || '';
|
|
|
|
|
- const marginBottom = style.marginBottom || '';
|
|
|
|
|
- const marginLeft = style.marginLeft || '';
|
|
|
|
|
- const marginRight = style.marginRight || '';
|
|
|
|
|
- const lineHeight = style.lineHeight || '';
|
|
|
|
|
- const paddingTop = style.paddingTop || '';
|
|
|
|
|
- const paddingRight = style.paddingRight || '';
|
|
|
|
|
- const paddingBottom = style.paddingBottom || '';
|
|
|
|
|
- const paddingLeft = style.paddingLeft || '';
|
|
|
|
|
- const whiteSpace = style.whiteSpace || 'normal';
|
|
|
|
|
- const writingMode = style.writingMode || '';
|
|
|
|
|
-
|
|
|
|
|
- // 读取新增属性
|
|
|
|
|
- const fontWeight = style.fontWeight || '';
|
|
|
|
|
- const fontStyle = style.fontStyle || '';
|
|
|
|
|
- const textDecoration = style.textDecoration || '';
|
|
|
|
|
- const textDecorationLine = style.textDecorationLine || '';
|
|
|
|
|
- const verticalAlign = style.verticalAlign || '';
|
|
|
|
|
- const letterSpacing = style.letterSpacing || '';
|
|
|
|
|
- const textShadow = style.textShadow || '';
|
|
|
|
|
|
|
+ const el = dom as HTMLElement
|
|
|
|
|
+ const style = el.style
|
|
|
|
|
+
|
|
|
|
|
+ let textIndent = style.textIndent || ''
|
|
|
|
|
+ let marginLeft = style.marginLeft || ''
|
|
|
|
|
+
|
|
|
|
|
+ // ★ 1. 负 margin-left 一律清除(不限于悬挂缩进组合)
|
|
|
|
|
+ // PPTist 的 li::before 已经提供符号占位。若 li 上再带负 margin-left,
|
|
|
|
|
+ // 整段文字会被拉到符号之外,和左边元素重叠。
|
|
|
|
|
+ // 之前只处理"负 text-indent + 正 margin-left"的组合,漏了单负
|
|
|
|
|
+ // margin-left 的情况(WPS 里很常见:marL 负值 + indent 0/正数)。
|
|
|
|
|
+ if (marginLeft && parseFloat(marginLeft) < 0) {
|
|
|
|
|
+ marginLeft = ''
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ★ 2. 悬挂缩进组合(负 text-indent + 正 margin-left)仍要清除
|
|
|
|
|
+ if (isHangingIndent(textIndent, marginLeft)) {
|
|
|
|
|
+ textIndent = ''
|
|
|
|
|
+ marginLeft = ''
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ★ 3. 负 text-indent 一律清除
|
|
|
|
|
+ // li::before 已提供首行缩进的视觉等效,再叠一个负 text-indent
|
|
|
|
|
+ // 会让首行越出符号左边。
|
|
|
|
|
+ if (textIndent && parseFloat(textIndent) < 0) {
|
|
|
|
|
+ textIndent = ''
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
- textAlign,
|
|
|
|
|
- textAlignLast,
|
|
|
|
|
|
|
+ textAlign: style.textAlign || '',
|
|
|
|
|
+ textAlignLast: style.textAlignLast || '',
|
|
|
textIndent,
|
|
textIndent,
|
|
|
- marginTop,
|
|
|
|
|
- marginBottom,
|
|
|
|
|
|
|
+ // ★ 4. li 不再保留 font-size
|
|
|
|
|
+ // 原因:li::before { padding: 0.125em 0; margin-right: 0.4em }
|
|
|
|
|
+ // 的 em 按 li 的 font-size 计算。li 字号 37px 时 ::before 占位
|
|
|
|
|
+ // 约 43px,比 16px 字号时的 18px 多出 25px,把整段文字推到符号
|
|
|
|
|
+ // 右边、和左侧其他元素重叠。
|
|
|
|
|
+ // li 内实际文字的字号由内部 span 决定,li 上不需要再挂字号。
|
|
|
|
|
+ fontsize: '',
|
|
|
|
|
+ marginTop: style.marginTop || '',
|
|
|
|
|
+ marginBottom: style.marginBottom || '',
|
|
|
marginLeft,
|
|
marginLeft,
|
|
|
- marginRight,
|
|
|
|
|
- lineHeight,
|
|
|
|
|
- paddingTop,
|
|
|
|
|
- paddingRight,
|
|
|
|
|
- paddingBottom,
|
|
|
|
|
- paddingLeft,
|
|
|
|
|
- whiteSpace,
|
|
|
|
|
- writingMode,
|
|
|
|
|
- fontWeight,
|
|
|
|
|
- fontStyle,
|
|
|
|
|
- textDecoration,
|
|
|
|
|
- textDecorationLine,
|
|
|
|
|
- verticalAlign,
|
|
|
|
|
- letterSpacing,
|
|
|
|
|
- textShadow,
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ marginRight: style.marginRight || '',
|
|
|
|
|
+ lineHeight: style.lineHeight || '',
|
|
|
|
|
+ paddingTop: style.paddingTop || '',
|
|
|
|
|
+ paddingRight: style.paddingRight || '',
|
|
|
|
|
+ paddingBottom: style.paddingBottom || '',
|
|
|
|
|
+ paddingLeft: style.paddingLeft || '',
|
|
|
|
|
+ whiteSpace: style.whiteSpace || 'normal',
|
|
|
|
|
+ writingMode: style.writingMode || '',
|
|
|
|
|
+ fontWeight: style.fontWeight || '',
|
|
|
|
|
+ fontStyle: style.fontStyle || '',
|
|
|
|
|
+ textDecoration: style.textDecoration || '',
|
|
|
|
|
+ textDecorationLine: style.textDecorationLine || '',
|
|
|
|
|
+ verticalAlign: style.verticalAlign || '',
|
|
|
|
|
+ letterSpacing: style.letterSpacing || '',
|
|
|
|
|
+ textShadow: style.textShadow || '',
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
],
|
|
],
|
|
@@ -190,6 +194,7 @@ const listItem: NodeSpec = {
|
|
|
textAlign,
|
|
textAlign,
|
|
|
textAlignLast,
|
|
textAlignLast,
|
|
|
textIndent,
|
|
textIndent,
|
|
|
|
|
+ // fontsize 不再从 attrs 取出(见下方注释)
|
|
|
marginTop,
|
|
marginTop,
|
|
|
marginBottom,
|
|
marginBottom,
|
|
|
marginLeft,
|
|
marginLeft,
|
|
@@ -208,92 +213,82 @@ const listItem: NodeSpec = {
|
|
|
verticalAlign,
|
|
verticalAlign,
|
|
|
letterSpacing,
|
|
letterSpacing,
|
|
|
textShadow,
|
|
textShadow,
|
|
|
- } = node.attrs;
|
|
|
|
|
|
|
+ } = node.attrs
|
|
|
|
|
|
|
|
- let style = '';
|
|
|
|
|
|
|
+ let style = ''
|
|
|
|
|
|
|
|
if (textAlign && textAlign !== 'left') {
|
|
if (textAlign && textAlign !== 'left') {
|
|
|
- style += `text-align: ${textAlign};`;
|
|
|
|
|
|
|
+ style += `text-align: ${textAlign};`
|
|
|
}
|
|
}
|
|
|
if (textAlignLast) {
|
|
if (textAlignLast) {
|
|
|
- style += `text-align-last: ${textAlignLast};`;
|
|
|
|
|
|
|
+ style += `text-align-last: ${textAlignLast};`
|
|
|
}
|
|
}
|
|
|
if (textIndent) {
|
|
if (textIndent) {
|
|
|
- style += `text-indent: (100% - ${textIndent});`;
|
|
|
|
|
|
|
+ style += `text-indent: ${textIndent};`
|
|
|
}
|
|
}
|
|
|
- if (marginTop) style += `margin-top: ${marginTop};`;
|
|
|
|
|
- if (marginBottom) style += `margin-bottom: ${marginBottom};`;
|
|
|
|
|
- if (marginLeft) {
|
|
|
|
|
- const str = String(marginLeft).trim();
|
|
|
|
|
- const match = str.match(/^([+-]?\d*\.?\d+)(px|pt|em|rem|%|vw|vh)?$/i);
|
|
|
|
|
- if (match) {
|
|
|
|
|
- let num = parseFloat(match[1]);
|
|
|
|
|
- const unit = match[2] || 'px';
|
|
|
|
|
- const absNum = Math.abs(num);
|
|
|
|
|
- const val = absNum + unit;
|
|
|
|
|
- style += `margin-left: max(min(0px, 100% - ${val}), 0px);`;
|
|
|
|
|
- } else {
|
|
|
|
|
- style += `margin-left: ${marginLeft};`;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if (marginRight) style += `margin-right: ${marginRight};`;
|
|
|
|
|
|
|
+ // ★ li 上不再输出 font-size
|
|
|
|
|
+ // 让 li::before 的 padding/margin 按继承字号(16px)计算,
|
|
|
|
|
+ // 符号占位回到 ~18px,文字位置恢复正常。
|
|
|
|
|
+ // 实际文字的字号由内部 <span> 承担。
|
|
|
|
|
+ if (marginTop) style += `margin-top: ${marginTop};`
|
|
|
|
|
+ if (marginBottom) style += `margin-bottom: ${marginBottom};`
|
|
|
|
|
+ if (marginLeft) style += `margin-left: ${marginLeft};`
|
|
|
|
|
+ if (marginRight) style += `margin-right: ${marginRight};`
|
|
|
if (lineHeight) {
|
|
if (lineHeight) {
|
|
|
- let finalValue;
|
|
|
|
|
- const str = String(lineHeight).trim();
|
|
|
|
|
- if (/^-?\d+(\.\d+)?$/.test(str)) {
|
|
|
|
|
- finalValue = parseFloat(str) * 1.2;
|
|
|
|
|
- } else {
|
|
|
|
|
- finalValue = lineHeight;
|
|
|
|
|
- }
|
|
|
|
|
- style += `line-height: ${finalValue};`;
|
|
|
|
|
|
|
+ style += `line-height: ${lineHeight};`
|
|
|
}
|
|
}
|
|
|
- else{
|
|
|
|
|
- style += `line-height: 1.2;`;
|
|
|
|
|
|
|
+ else {
|
|
|
|
|
+ style += `line-height: 1.2;`
|
|
|
}
|
|
}
|
|
|
- if (paddingTop) style += `padding-top: ${paddingTop};`;
|
|
|
|
|
- if (paddingRight) style += `padding-right: ${paddingRight};`;
|
|
|
|
|
- if (paddingBottom) style += `padding-bottom: ${paddingBottom};`;
|
|
|
|
|
- if (paddingLeft) style += `padding-left: ${paddingLeft};`;
|
|
|
|
|
|
|
+ if (paddingTop) style += `padding-top: ${paddingTop};`
|
|
|
|
|
+ if (paddingRight) style += `padding-right: ${paddingRight};`
|
|
|
|
|
+ if (paddingBottom) style += `padding-bottom: ${paddingBottom};`
|
|
|
|
|
+ if (paddingLeft) style += `padding-left: ${paddingLeft};`
|
|
|
if (whiteSpace && whiteSpace !== 'normal') {
|
|
if (whiteSpace && whiteSpace !== 'normal') {
|
|
|
- style += `white-space: ${whiteSpace};`;
|
|
|
|
|
|
|
+ style += `white-space: ${whiteSpace};`
|
|
|
}
|
|
}
|
|
|
if (writingMode) {
|
|
if (writingMode) {
|
|
|
- style += `writing-mode: ${writingMode};`;
|
|
|
|
|
|
|
+ style += `writing-mode: ${writingMode};`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 字体相关样式
|
|
|
|
|
+ if (fontWeight) style += `font-weight: ${fontWeight};`
|
|
|
|
|
+ if (fontStyle) style += `font-style: ${fontStyle};`
|
|
|
|
|
+ if (textDecoration) {
|
|
|
|
|
+ style += `text-decoration: ${textDecoration};`
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (textDecorationLine) {
|
|
|
|
|
+ style += `text-decoration-line: ${textDecorationLine};`
|
|
|
}
|
|
}
|
|
|
|
|
+ if (verticalAlign) style += `vertical-align: ${verticalAlign};`
|
|
|
|
|
+ if (letterSpacing) style += `letter-spacing: ${letterSpacing};`
|
|
|
|
|
+ if (textShadow) style += `text-shadow: ${textShadow};`
|
|
|
|
|
|
|
|
- // 新增字体相关样式输出
|
|
|
|
|
- if (fontWeight) style += `font-weight: ${fontWeight};`;
|
|
|
|
|
- if (fontStyle) style += `font-style: ${fontStyle};`;
|
|
|
|
|
- if (textDecoration) style += `text-decoration: ${textDecoration};`;
|
|
|
|
|
- if (textDecorationLine) style += `text-decoration-line: ${textDecorationLine};`;
|
|
|
|
|
- if (verticalAlign) style += `vertical-align: ${verticalAlign};`;
|
|
|
|
|
- if (letterSpacing) style += `letter-spacing: ${letterSpacing};`;
|
|
|
|
|
- if (textShadow) style += `text-shadow: ${textShadow};`;
|
|
|
|
|
-
|
|
|
|
|
- let isEmpty = false;
|
|
|
|
|
- const firstChild = node.content.firstChild;
|
|
|
|
|
|
|
+ let isEmpty = false
|
|
|
|
|
+ const firstChild = node.content.firstChild
|
|
|
if (firstChild && firstChild.type.name === 'paragraph') {
|
|
if (firstChild && firstChild.type.name === 'paragraph') {
|
|
|
if (firstChild.content.size === 0) {
|
|
if (firstChild.content.size === 0) {
|
|
|
- isEmpty = true;
|
|
|
|
|
|
|
+ isEmpty = true
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- if (node.content.size === 0) isEmpty = true;
|
|
|
|
|
|
|
+ if (node.content.size === 0) isEmpty = true
|
|
|
|
|
|
|
|
- const attrs: { style?: string; class?: string } = {};
|
|
|
|
|
- if (style) attrs.style = style;
|
|
|
|
|
- if (isEmpty) attrs.class = 'empty-li';
|
|
|
|
|
|
|
+ const attrs: { style?: string; class?: string } = {}
|
|
|
|
|
+ if (style) attrs.style = style
|
|
|
|
|
+ if (isEmpty) attrs.class = 'empty-li'
|
|
|
|
|
|
|
|
- return ['li', attrs, 0];
|
|
|
|
|
|
|
+ return ['li', attrs, 0]
|
|
|
},
|
|
},
|
|
|
-};
|
|
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const paragraph: NodeSpec = {
|
|
const paragraph: NodeSpec = {
|
|
|
- whitespace: "pre",
|
|
|
|
|
|
|
+ whitespace: 'pre',
|
|
|
attrs: {
|
|
attrs: {
|
|
|
textAlign: { default: '' },
|
|
textAlign: { default: '' },
|
|
|
textAlignLast: { default: '' },
|
|
textAlignLast: { default: '' },
|
|
|
indent: { default: 0 },
|
|
indent: { default: 0 },
|
|
|
textIndent: { default: 0 },
|
|
textIndent: { default: 0 },
|
|
|
|
|
+ fontsize: { default: '' },
|
|
|
marginTop: { default: '' },
|
|
marginTop: { default: '' },
|
|
|
marginBottom: { default: '' },
|
|
marginBottom: { default: '' },
|
|
|
marginLeft: { default: '' },
|
|
marginLeft: { default: '' },
|
|
@@ -305,7 +300,7 @@ const paragraph: NodeSpec = {
|
|
|
paddingLeft: { default: '' },
|
|
paddingLeft: { default: '' },
|
|
|
whiteSpace: { default: 'normal' },
|
|
whiteSpace: { default: 'normal' },
|
|
|
writingMode: { default: '' },
|
|
writingMode: { default: '' },
|
|
|
- // 新增字体相关属性
|
|
|
|
|
|
|
+ // 字体相关
|
|
|
fontWeight: { default: '' },
|
|
fontWeight: { default: '' },
|
|
|
fontStyle: { default: '' },
|
|
fontStyle: { default: '' },
|
|
|
textDecoration: { default: '' },
|
|
textDecoration: { default: '' },
|
|
@@ -320,72 +315,47 @@ const paragraph: NodeSpec = {
|
|
|
{
|
|
{
|
|
|
tag: 'p',
|
|
tag: 'p',
|
|
|
getAttrs: dom => {
|
|
getAttrs: dom => {
|
|
|
- const el = dom as HTMLElement;
|
|
|
|
|
- const style = el.style;
|
|
|
|
|
-
|
|
|
|
|
- const textAlign = style.textAlign || '';
|
|
|
|
|
- const textAlignLast = style.textAlignLast || '';
|
|
|
|
|
- const marginTop = style.marginTop || '';
|
|
|
|
|
- const marginBottom = style.marginBottom || '';
|
|
|
|
|
- const marginLeft = style.marginLeft || '';
|
|
|
|
|
- const marginRight = style.marginRight || '';
|
|
|
|
|
- const textIndent = style.textIndent || '';
|
|
|
|
|
- const lineHeight = style.lineHeight || '';
|
|
|
|
|
- const paddingTop = style.paddingTop || '';
|
|
|
|
|
- const paddingRight = style.paddingRight || '';
|
|
|
|
|
- const paddingBottom = style.paddingBottom || '';
|
|
|
|
|
- const paddingLeft = style.paddingLeft || '';
|
|
|
|
|
- const whiteSpace = style.whiteSpace || 'normal';
|
|
|
|
|
- const writingMode = style.writingMode || '';
|
|
|
|
|
-
|
|
|
|
|
- // 读取新增属性
|
|
|
|
|
- const fontWeight = style.fontWeight || '';
|
|
|
|
|
- const fontStyle = style.fontStyle || '';
|
|
|
|
|
- const textDecoration = style.textDecoration || '';
|
|
|
|
|
- const textDecorationLine = style.textDecorationLine || '';
|
|
|
|
|
- const verticalAlign = style.verticalAlign || '';
|
|
|
|
|
- const letterSpacing = style.letterSpacing || '';
|
|
|
|
|
- const textShadow = style.textShadow || '';
|
|
|
|
|
|
|
+ const el = dom as HTMLElement
|
|
|
|
|
+ const style = el.style
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
- textAlign,
|
|
|
|
|
- textAlignLast,
|
|
|
|
|
- textIndent,
|
|
|
|
|
- marginTop,
|
|
|
|
|
- marginBottom,
|
|
|
|
|
- marginLeft,
|
|
|
|
|
- marginRight,
|
|
|
|
|
- lineHeight,
|
|
|
|
|
- paddingTop,
|
|
|
|
|
- paddingRight,
|
|
|
|
|
- paddingBottom,
|
|
|
|
|
- paddingLeft,
|
|
|
|
|
- whiteSpace,
|
|
|
|
|
- writingMode,
|
|
|
|
|
- fontWeight,
|
|
|
|
|
- fontStyle,
|
|
|
|
|
- textDecoration,
|
|
|
|
|
- textDecorationLine,
|
|
|
|
|
- verticalAlign,
|
|
|
|
|
- letterSpacing,
|
|
|
|
|
- textShadow,
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ textAlign: style.textAlign || '',
|
|
|
|
|
+ textAlignLast: style.textAlignLast || '',
|
|
|
|
|
+ textIndent: style.textIndent || '',
|
|
|
|
|
+ // ★ 段落级 font-size 必须保留:导入的 HTML 里空行撑高段落依赖
|
|
|
|
|
+ // p 上的 font-size(strut = line-height × 段落字号)。丢失后 strut
|
|
|
|
|
+ // 按继承的 16px 计算,小字号空行会被撑高,内容整体下移错位。
|
|
|
|
|
+ fontsize: style.fontSize || '',
|
|
|
|
|
+ marginTop: style.marginTop || '',
|
|
|
|
|
+ marginBottom: style.marginBottom || '',
|
|
|
|
|
+ marginLeft: style.marginLeft || '',
|
|
|
|
|
+ marginRight: style.marginRight || '',
|
|
|
|
|
+ lineHeight: style.lineHeight || '',
|
|
|
|
|
+ paddingTop: style.paddingTop || '',
|
|
|
|
|
+ paddingRight: style.paddingRight || '',
|
|
|
|
|
+ paddingBottom: style.paddingBottom || '',
|
|
|
|
|
+ paddingLeft: style.paddingLeft || '',
|
|
|
|
|
+ whiteSpace: style.whiteSpace || 'normal',
|
|
|
|
|
+ writingMode: style.writingMode || '',
|
|
|
|
|
+ fontWeight: style.fontWeight || '',
|
|
|
|
|
+ fontStyle: style.fontStyle || '',
|
|
|
|
|
+ textDecoration: style.textDecoration || '',
|
|
|
|
|
+ textDecorationLine: style.textDecorationLine || '',
|
|
|
|
|
+ verticalAlign: style.verticalAlign || '',
|
|
|
|
|
+ letterSpacing: style.letterSpacing || '',
|
|
|
|
|
+ textShadow: style.textShadow || '',
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
- {
|
|
|
|
|
- tag: 'img',
|
|
|
|
|
- ignore: true,
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- tag: 'pre',
|
|
|
|
|
- skip: true,
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ { tag: 'img', ignore: true },
|
|
|
|
|
+ { tag: 'pre', skip: true },
|
|
|
],
|
|
],
|
|
|
toDOM: (node: Node) => {
|
|
toDOM: (node: Node) => {
|
|
|
const {
|
|
const {
|
|
|
textAlign,
|
|
textAlign,
|
|
|
textAlignLast,
|
|
textAlignLast,
|
|
|
textIndent,
|
|
textIndent,
|
|
|
|
|
+ fontsize,
|
|
|
marginTop,
|
|
marginTop,
|
|
|
marginBottom,
|
|
marginBottom,
|
|
|
marginLeft,
|
|
marginLeft,
|
|
@@ -404,71 +374,70 @@ const paragraph: NodeSpec = {
|
|
|
verticalAlign,
|
|
verticalAlign,
|
|
|
letterSpacing,
|
|
letterSpacing,
|
|
|
textShadow,
|
|
textShadow,
|
|
|
- } = node.attrs;
|
|
|
|
|
|
|
+ } = node.attrs
|
|
|
|
|
|
|
|
- let style = '';
|
|
|
|
|
|
|
+ let style = ''
|
|
|
|
|
|
|
|
if (textAlign && textAlign !== 'left') {
|
|
if (textAlign && textAlign !== 'left') {
|
|
|
- style += `text-align: ${textAlign};`;
|
|
|
|
|
|
|
+ style += `text-align: ${textAlign};`
|
|
|
}
|
|
}
|
|
|
if (textAlignLast) {
|
|
if (textAlignLast) {
|
|
|
- style += `text-align-last: ${textAlignLast};`;
|
|
|
|
|
|
|
+ style += `text-align-last: ${textAlignLast};`
|
|
|
}
|
|
}
|
|
|
if (textIndent) {
|
|
if (textIndent) {
|
|
|
- style += `text-indent: (100% - ${textIndent});`;
|
|
|
|
|
|
|
+ style += `text-indent: ${textIndent};`
|
|
|
}
|
|
}
|
|
|
- if (marginTop) style += `margin-top: ${marginTop};`;
|
|
|
|
|
- if (marginBottom) style += `margin-bottom: ${marginBottom};`;
|
|
|
|
|
- if (marginLeft) style += `margin-left: ${marginLeft};`;
|
|
|
|
|
- if (marginRight) style += `margin-right: ${marginRight};`;
|
|
|
|
|
|
|
+ // ★ 段落级字号回写:空行撑高、CSS strut 都依赖 p 上的 font-size
|
|
|
|
|
+ if (fontsize) style += `font-size: ${fontsize};`
|
|
|
|
|
+ if (marginTop) style += `margin-top: ${marginTop};`
|
|
|
|
|
+ if (marginBottom) style += `margin-bottom: ${marginBottom};`
|
|
|
|
|
+ if (marginLeft) style += `margin-left: ${marginLeft};`
|
|
|
|
|
+ if (marginRight) style += `margin-right: ${marginRight};`
|
|
|
if (lineHeight) {
|
|
if (lineHeight) {
|
|
|
- let finalValue;
|
|
|
|
|
- const str = String(lineHeight).trim();
|
|
|
|
|
- if (/^-?\d+(\.\d+)?$/.test(str)) {
|
|
|
|
|
- finalValue = parseFloat(str) * 1.2;
|
|
|
|
|
- } else {
|
|
|
|
|
- finalValue = lineHeight;
|
|
|
|
|
- }
|
|
|
|
|
- style += `line-height: ${finalValue};`;
|
|
|
|
|
|
|
+ style += `line-height: ${lineHeight};`
|
|
|
}
|
|
}
|
|
|
- else{
|
|
|
|
|
- style += `line-height: 1.2;`;
|
|
|
|
|
|
|
+ else {
|
|
|
|
|
+ style += `line-height: 1.2;`
|
|
|
}
|
|
}
|
|
|
- if (paddingTop) style += `padding-top: ${paddingTop};`;
|
|
|
|
|
- if (paddingRight) style += `padding-right: ${paddingRight};`;
|
|
|
|
|
- if (paddingBottom) style += `padding-bottom: ${paddingBottom};`;
|
|
|
|
|
- if (paddingLeft) style += `padding-left: ${paddingLeft};`;
|
|
|
|
|
|
|
+ if (paddingTop) style += `padding-top: ${paddingTop};`
|
|
|
|
|
+ if (paddingRight) style += `padding-right: ${paddingRight};`
|
|
|
|
|
+ if (paddingBottom) style += `padding-bottom: ${paddingBottom};`
|
|
|
|
|
+ if (paddingLeft) style += `padding-left: ${paddingLeft};`
|
|
|
if (whiteSpace && whiteSpace !== 'normal') {
|
|
if (whiteSpace && whiteSpace !== 'normal') {
|
|
|
- style += `white-space: ${whiteSpace};`;
|
|
|
|
|
|
|
+ style += `white-space: ${whiteSpace};`
|
|
|
}
|
|
}
|
|
|
if (writingMode) {
|
|
if (writingMode) {
|
|
|
- style += `writing-mode: ${writingMode};`;
|
|
|
|
|
|
|
+ style += `writing-mode: ${writingMode};`
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 新增字体相关样式输出
|
|
|
|
|
- if (fontWeight) style += `font-weight: ${fontWeight};`;
|
|
|
|
|
- if (fontStyle) style += `font-style: ${fontStyle};`;
|
|
|
|
|
- if (textDecoration) style += `text-decoration: ${textDecoration};`;
|
|
|
|
|
- if (textDecorationLine) style += `text-decoration-line: ${textDecorationLine};`;
|
|
|
|
|
- if (verticalAlign) style += `vertical-align: ${verticalAlign};`;
|
|
|
|
|
- if (letterSpacing) style += `letter-spacing: ${letterSpacing};`;
|
|
|
|
|
- if (textShadow) style += `text-shadow: ${textShadow};`;
|
|
|
|
|
-
|
|
|
|
|
- const attrs: { style?: string; class?: string } = {};
|
|
|
|
|
- if (style) attrs.style = style;
|
|
|
|
|
- return ['p', attrs, 0];
|
|
|
|
|
|
|
+ // 字体相关样式
|
|
|
|
|
+ if (fontWeight) style += `font-weight: ${fontWeight};`
|
|
|
|
|
+ if (fontStyle) style += `font-style: ${fontStyle};`
|
|
|
|
|
+ if (textDecoration) {
|
|
|
|
|
+ style += `text-decoration: ${textDecoration};`
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (textDecorationLine) {
|
|
|
|
|
+ style += `text-decoration-line: ${textDecorationLine};`
|
|
|
|
|
+ }
|
|
|
|
|
+ if (verticalAlign) style += `vertical-align: ${verticalAlign};`
|
|
|
|
|
+ if (letterSpacing) style += `letter-spacing: ${letterSpacing};`
|
|
|
|
|
+ if (textShadow) style += `text-shadow: ${textShadow};`
|
|
|
|
|
+
|
|
|
|
|
+ const attrs: { style?: string; class?: string } = {}
|
|
|
|
|
+ if (style) attrs.style = style
|
|
|
|
|
+ return ['p', attrs, 0]
|
|
|
},
|
|
},
|
|
|
-};
|
|
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const hardBreak: NodeSpec = {
|
|
const hardBreak: NodeSpec = {
|
|
|
- inline: true, // 内联节点
|
|
|
|
|
- group: 'inline', // 属于 inline 组
|
|
|
|
|
- selectable: false, // 不可被光标单独选中
|
|
|
|
|
|
|
+ inline: true,
|
|
|
|
|
+ group: 'inline',
|
|
|
|
|
+ selectable: false,
|
|
|
parseDOM: [{ tag: 'br' }],
|
|
parseDOM: [{ tag: 'br' }],
|
|
|
toDOM() {
|
|
toDOM() {
|
|
|
- return ['br'];
|
|
|
|
|
|
|
+ return ['br']
|
|
|
},
|
|
},
|
|
|
-};
|
|
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const {
|
|
const {
|
|
|
doc,
|
|
doc,
|
|
@@ -484,5 +453,5 @@ export default {
|
|
|
'ordered_list': orderedList,
|
|
'ordered_list': orderedList,
|
|
|
'bullet_list': bulletList,
|
|
'bullet_list': bulletList,
|
|
|
'list_item': listItem,
|
|
'list_item': listItem,
|
|
|
- hard_break: hardBreak, // 新增
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ hard_break: hardBreak,
|
|
|
|
|
+}
|