Skip to content

Commit f70a33e

Browse files
authored
Merge pull request #484 from Harbour-Enterprises/har-9459_dropcaps
HAR-9459 Fonts formatting: Dropcaps
2 parents c43bc9b + e8f232e commit f70a33e

7 files changed

Lines changed: 124 additions & 9 deletions

File tree

packages/super-editor/src/assets/styles/elements/prosemirror.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,3 +303,10 @@ https://github.com/ProseMirror/prosemirror-tables/blob/master/demo/index.html
303303
.ProseMirror-focused .ProseMirror-gapcursor {
304304
display: block;
305305
}
306+
307+
.super-doc-dropcap {
308+
float: left;
309+
display: flex;
310+
align-items: baseline;
311+
margin-top: -5px;
312+
}

packages/super-editor/src/core/super-converter/exporter.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ function generateParagraphProperties(node) {
172172
const { styleId } = attrs;
173173
if (styleId) pPrElements.push({ name: 'w:pStyle', attributes: { 'w:val': styleId } });
174174

175-
const { spacing, indent, textAlign, textIndent, lineHeight, marksAttrs, keepLines, keepNext } = attrs;
175+
const { spacing, indent, textAlign, textIndent, lineHeight, marksAttrs, keepLines, keepNext, dropcap } = attrs;
176176
if (spacing) {
177177
const { lineSpaceBefore, lineSpaceAfter, line, lineRule } = spacing;
178178

@@ -262,6 +262,19 @@ function generateParagraphProperties(node) {
262262
});
263263
}
264264

265+
if (dropcap) {
266+
pPrElements.push({
267+
name: 'w:framePr',
268+
attributes: {
269+
'w:dropCap': dropcap.type,
270+
'w:lines': dropcap.lines,
271+
'w:wrap': dropcap.wrap,
272+
'w:vAnchor': dropcap.vAnchor,
273+
'w:hAnchor': dropcap.hAnchor,
274+
},
275+
});
276+
}
277+
265278
if (!pPrElements.length) return null;
266279

267280
return {

packages/super-editor/src/core/super-converter/v2/importer/paragraphNodeImporter.js

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export const handleParagraphNode = (params) => {
5151
const pPr = node.elements?.find((el) => el.name === 'w:pPr');
5252
const styleTag = pPr?.elements?.find((el) => el.name === 'w:pStyle');
5353
const nestedRPr = pPr?.elements?.find((el) => el.name === 'w:rPr');
54+
const framePr = pPr?.elements?.find((el) => el.name === 'w:framePr');
5455

5556
if (nestedRPr) {
5657
schemaNode.attrs.marksAttrs = parseMarks(nestedRPr, []);
@@ -67,13 +68,13 @@ export const handleParagraphNode = (params) => {
6768
schemaNode.attrs.indent = {};
6869
}
6970

70-
if (indent.left) {
71+
if (indent.left || indent.left === 0) {
7172
schemaNode.attrs.indent.left = indent.left;
7273
}
73-
if (indent.right) {
74+
if (indent.right || indent.right === 0) {
7475
schemaNode.attrs.indent.right = indent.right;
7576
}
76-
if (indent.firstLine) {
77+
if (indent.firstLine || indent.firstLine === 0) {
7778
schemaNode.attrs.indent.firstLine = indent.firstLine;
7879
}
7980
if (indent.hanging) {
@@ -104,6 +105,16 @@ export const handleParagraphNode = (params) => {
104105
schemaNode.attrs['spacing'] = getParagraphSpacing(node, docx, schemaNode.attrs['styleId'], schemaNode.attrs.marksAttrs);
105106
schemaNode.attrs['rsidRDefault'] = defaultStyleId;
106107
}
108+
109+
if (framePr && framePr.attributes['w:dropCap']) {
110+
schemaNode.attrs.dropcap = {
111+
type: framePr.attributes['w:dropCap'],
112+
lines: framePr.attributes['w:lines'],
113+
wrap: framePr.attributes['w:wrap'],
114+
hAnchor: framePr.attributes['w:hAnchor'],
115+
vAnchor: framePr.attributes['w:vAnchor'],
116+
}
117+
}
107118

108119
schemaNode.attrs['filename'] = filename;
109120

@@ -322,13 +333,13 @@ export function getDefaultStyleDefinition(defaultStyleId, docx) {
322333
if (pageBreakBefore) {
323334
if (!pageBreakBefore.attributes?.['w:val']) pageBreakBeforeVal = 1;
324335
else pageBreakBeforeVal = Number(pageBreakBefore?.attributes?.['w:val'])
325-
};
336+
}
326337
const pageBreakAfter = pPr?.elements?.find((el) => el.name === 'w:pageBreakAfter');
327338
let pageBreakAfterVal;
328339
if (pageBreakAfter) {
329340
if (!pageBreakAfter.attributes?.['w:val']) pageBreakAfterVal = 1;
330341
else pageBreakAfterVal = Number(pageBreakAfter?.attributes?.['w:val'])
331-
};
342+
}
332343

333344
const parsedAttrs = {
334345
name,
@@ -356,7 +367,7 @@ export function getDefaultStyleDefinition(defaultStyleId, docx) {
356367
parsedStyles[kebabCase(key)] = value
357368
});
358369
return;
359-
};
370+
}
360371

361372
parsedStyles[type] = attrs;
362373
});
@@ -414,7 +425,7 @@ export function preProcessNodesForFldChar(nodes) {
414425
});
415426

416427
return processedNodes;
417-
};
428+
}
418429

419430

420431
/**

packages/super-editor/src/extensions/linked-styles/linked-styles.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,11 @@ export const getSpacingStyle = (spacing) => {
189189
* @returns {String} The style string
190190
*/
191191
export const getSpacingStyleString = (spacing) => {
192-
const { lineSpaceBefore, lineSpaceAfter } = spacing;
192+
const { lineSpaceBefore, lineSpaceAfter, line } = spacing;
193193
return `
194194
${lineSpaceBefore ? `margin-top: ${lineSpaceBefore}px;` : ''}
195195
${lineSpaceAfter ? `margin-bottom: ${lineSpaceAfter}px;` : ''}
196+
${line ? getLineHeightValueString(line, '') : ''}
196197
`.trim();
197198
};
198199

packages/super-editor/src/extensions/paragraph/paragraph.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Plugin, PluginKey } from 'prosemirror-state';
2+
import { Decoration, DecorationSet } from 'prosemirror-view';
13
import { Node, Attribute, Schema } from '@core/index.js';
24
import { getSpacingStyleString, getMarksStyle } from '@extensions/linked-styles/index.js';
35

@@ -68,6 +70,14 @@ export const Paragraph = Node.create({
6870
return { style };
6971
},
7072
},
73+
class: {
74+
renderDOM: (attributes) => {
75+
if (attributes.dropcap) {
76+
return { class: `super-doc-dropcap`};
77+
}
78+
return null;
79+
}
80+
},
7181
styleId: { rendered: false },
7282
attributes: {
7383
rendered: false,
@@ -77,6 +87,7 @@ export const Paragraph = Node.create({
7787
keepLines: { rendered: false },
7888
keepNext: { rendered: false },
7989
paragraphProperties: { rendered: false },
90+
dropcap: { rendered: false },
8091
};
8192
},
8293

@@ -96,4 +107,54 @@ export const Paragraph = Node.create({
96107
renderDOM({ htmlAttributes }) {
97108
return ['p', Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
98109
},
110+
111+
addPmPlugins() {
112+
const { view } = this.editor;
113+
const dropcapPlugin = new Plugin({
114+
name: 'dropcapPlugin',
115+
key: new PluginKey('dropcapPlugin'),
116+
state: {
117+
init(_, state) {
118+
let decorations = getDropcapDecorations(state, view);
119+
return DecorationSet.create(state.doc, decorations);
120+
},
121+
122+
apply(tr, oldDecorationSet, oldState, newState) {
123+
const decorations = getDropcapDecorations(newState, view);
124+
return DecorationSet.create(newState.doc, decorations);
125+
},
126+
},
127+
props: {
128+
decorations(state) {
129+
return this.getState(state);
130+
},
131+
},
132+
});
133+
134+
return [dropcapPlugin];
135+
}
99136
});
137+
138+
const getDropcapDecorations = (state, view) => {
139+
let decorations = [];
140+
state.doc.descendants((node, pos) => {
141+
if (node.attrs.dropcap?.type === 'margin') {
142+
const width = getDropcapWidth(view, pos);
143+
144+
decorations.push(
145+
Decoration.node(pos, pos + node.nodeSize, { style: `margin-left: -${width}px;` }),
146+
);
147+
}
148+
});
149+
return decorations;
150+
};
151+
152+
function getDropcapWidth(view, pos) {
153+
const domNode = view.nodeDOM(pos);
154+
if (domNode) {
155+
const range = document.createRange();
156+
range.selectNodeContents(domNode);
157+
return range.getBoundingClientRect().width;
158+
}
159+
return 0;
160+
}
2.36 MB
Binary file not shown.

packages/super-editor/src/tests/import/paragraphNodeImporter.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,25 @@ describe('paragraph tests to check indentation', () => {
274274
expect(indent.firstLine).toBe(29);
275275
});
276276
});
277+
278+
describe('paragraph with dropcaps', () => {
279+
280+
it('correctly gets dropcaps data', async () => {
281+
const dataName = 'dropcaps.docx';
282+
const docx = await getTestDataByFileName(dataName);
283+
const documentXml = docx['word/document.xml'];
284+
285+
const doc = documentXml.elements[0];
286+
const body = doc.elements[0];
287+
const content = body.elements;
288+
289+
const { nodes } = handleParagraphNode({ nodes: [content[1]], docx, nodeListHandler: defaultNodeListHandler() });
290+
291+
const node = nodes[0];
292+
expect(node.type).toBe('paragraph');
293+
294+
const { attrs } = node;
295+
const { dropcap } = attrs;
296+
expect(dropcap.type).toBe('drop');
297+
});
298+
});

0 commit comments

Comments
 (0)