remark-docx
remark-docx copied to clipboard
Table Formatting
Hi - Thanks for making this library. It's great! I have a question. I am trying to format a table where the conversion to docx format occurs on the server (node). I am using remark-gfm to create the table, but it has no style. Is it possible to pass docx table styles into remark-docx?
So far, I think docx
doesn't provide the ability to edit, outside of the patcher
which only lets you add things. However, I have a table in the middle of the markdown. Also, it doesn't look like docx style options like paragraphStyles
, default
, or document
styles accept options formatting tables. As far as I can tell, it seems like table styles must be passed into docx when the table is created new Table
, new Row
, and new Cell
. So, if I understand everything correctly, if there were to be table styles, they'd have to be applied when the table is created within remark-docx.
Any ideas on how that might work? Thanks!
const processor = unified()
.use(remarkParse)
.use(markdown)
.use(remarkBreaks)
.use(remarkGfm)
.use(docx, { output: 'buffer', styles });
Hello, I'm not familiar to table options of docx however it looks like that some options are not existed in global as you mentioned. https://docx.js.org/#/usage/tables
I have no time to implement them as an option of remark-docx but any contributions are welcome!
Thanks for responding so quickly. I'm going to put a little time into seeing if there is a workable solution. Thanks again!!
Okay, I got something working that will suit my needs in the short term. It's not amazing, but it works! Thankfully your code is easy to read and understand! I wanted to leave the changes here in case this is helpful to anyone else.
I added a tableStyles
object to the options. It is structured so that it provides for table options, a header row, and body rows. This is really all I need for now.
That object looks like this and I added it to the DocxOptions interface:
export type TableStyle = {
options: Omit<ITableOptions, 'rows'>,
header: {
row: ITableRowPropertiesOptions,
cell: ITableCellPropertiesOptions,
paragraph: IParagraphPropertiesOptions
},
body: {
row: ITableRowPropertiesOptions,
cell: ITableCellPropertiesOptions,
paragraph: IParagraphPropertiesOptions
},
}
It is passed in like so:
const processor = unified()
.use(remarkParse)
.use(remarkGfm)
.use(docx, { output: 'buffer', tableStyle });
That object is then passed into the context.
const mdastToDocx = async (node, { output = 'buffer', title, subject, creator, keywords, description, lastModifiedBy, revision, styles, background, tableStyle }, images) => {
const { nodes, footnotes } = convertNodes(node.children, {
deco: {},
images,
indent: 0,
tableStyle
},);
...
The Table, Row, Cell, and Paragraph constructors then apply the options and styles.
const buildTable = ({ children, align }, ctx) => {
const cellAligns = align === null || align === void 0 ? void 0 : align.map((a) => {
switch (a) {
case 'left':
return docx.AlignmentType.LEFT;
case 'right':
return docx.AlignmentType.RIGHT;
case 'center':
return docx.AlignmentType.CENTER;
default:
return docx.AlignmentType.LEFT;
}
});
return new docx.Table({
...ctx.tableStyle.options,
rows: children.map((r, i) => {
const style = i === 0 ? ctx.tableStyle.header : ctx.tableStyle.body;
return buildTableRow(r, ctx, cellAligns, style);
}),
});
};
const buildTableRow = ({ children }, ctx, cellAligns, style) => {
return new docx.TableRow({
...style.row,
children: children.map((c, i) => {
return buildTableCell(c, ctx, cellAligns === null || cellAligns === void 0 ? void 0 : cellAligns[i], style);
}),
});
};
const buildTableCell = ({ children }, ctx, align, style) => {
const { nodes } = convertNodes(children, ctx);
return new docx.TableCell({
...style.cell,
children: [
new docx.Paragraph({
...style.paragraph,
alignment: align,
children: nodes,
}),
],
});
};
Hi all, I need @generalleger 's code as well. Don't you think a PR is needed? I think it's actually an important addition to the package. Without it the table becomes pretty much unusable.
Thank you
Hi all, I need @generalleger 's code as well. Don't you think a PR is needed? I think it's actually an important addition to the package. Without it the table becomes pretty much unusable.
Thank you
Paulo - my code is pretty hacky - and I've since modified again to add a title page based on other data internal to my application. So, the code quality is not good enough for a PR. The code mods I described above should work though. If you can't get it to work, let me know and I'll try to post it to my public GH.