md2hwp
Version:
Convert Markdown to HWP (Hangul Word Processor) format
307 lines (306 loc) • 25.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HwpxGenerator = void 0;
const jszip_1 = __importDefault(require("jszip"));
class HwpxGenerator {
constructor(options = {}) {
this.paragraphId = 0;
this.options = {
title: options.title || 'Document',
author: options.author || 'md2hwp',
pageWidth: options.pageWidth || 59528,
pageHeight: options.pageHeight || 84188,
marginLeft: options.marginLeft || 8504,
marginRight: options.marginRight || 8504,
marginTop: options.marginTop || 5668,
marginBottom: options.marginBottom || 4252,
};
}
async generate(contents) {
const zip = new jszip_1.default();
// mimetype MUST be uncompressed and first
zip.file('mimetype', 'application/hwp+zip', {
compression: 'STORE',
compressionOptions: { level: 0 }
});
zip.file('version.xml', this.generateVersionXml());
zip.file('settings.xml', this.generateSettingsXml());
// META-INF directory
const metaInf = zip.folder('META-INF');
if (metaInf) {
metaInf.file('manifest.xml', this.generateManifestXml());
metaInf.file('container.xml', this.generateContainerXml());
metaInf.file('container.rdf', this.generateContainerRdf());
}
// Contents directory
const contentFolder = zip.folder('Contents');
if (contentFolder) {
contentFolder.file('content.hpf', this.generateContentHpf());
contentFolder.file('header.xml', this.generateHeaderXml());
contentFolder.file('section0.xml', this.generateSection(contents));
}
// Preview directory
const previewFolder = zip.folder('Preview');
if (previewFolder) {
const previewText = this.generatePreviewText(contents);
previewFolder.file('PrvText.txt', previewText);
}
const buffer = await zip.generateAsync({
type: 'nodebuffer',
compression: 'DEFLATE',
compressionOptions: { level: 9 }
});
return buffer;
}
generateVersionXml() {
return `xml version="1.0" encoding="UTF-8" standalone="yes" <hv:HCFVersion xmlns:hv="http://www.hancom.co.kr/hwpml/2011/version" tagetApplication="WORDPROCESSOR" major="5" minor="1" micro="1" buildNumber="0" os="10" xmlVersion="1.5" application="md2hwp" appVersion="1.0.0"/>`;
}
generateSettingsXml() {
return `xml version="1.0" encoding="UTF-8" standalone="yes" <ha:HWPApplicationSetting xmlns:ha="http://www.hancom.co.kr/hwpml/2011/app" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0"><ha:CaretPosition listIDRef="0" paraIDRef="0" pos="0"/></ha:HWPApplicationSetting>`;
}
generateManifestXml() {
return `xml version="1.0" encoding="UTF-8" standalone="yes" <odf:manifest xmlns:odf="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"/>`;
}
generateContainerXml() {
return `xml version="1.0" encoding="UTF-8" standalone="yes" <ocf:container xmlns:ocf="urn:oasis:names:tc:opendocument:xmlns:container" xmlns:hpf="http://www.hancom.co.kr/schema/2011/hpf"><ocf:rootfiles><ocf:rootfile full-path="Contents/content.hpf" media-type="application/hwpml-package+xml"/><ocf:rootfile full-path="Preview/PrvText.txt" media-type="text/plain"/></ocf:rootfiles></ocf:container>`;
}
generateContainerRdf() {
return `xml version="1.0" encoding="UTF-8"<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"></rdf:RDF>`;
}
generateContentHpf() {
const now = new Date().toISOString();
return `xml version="1.0" encoding="UTF-8" standalone="yes" <opf:package xmlns:ha="http://www.hancom.co.kr/hwpml/2011/app" xmlns:hp="http://www.hancom.co.kr/hwpml/2011/paragraph" xmlns:hs="http://www.hancom.co.kr/hwpml/2011/section" xmlns:hh="http://www.hancom.co.kr/hwpml/2011/head" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf/" version="" unique-identifier="" id=""><opf:metadata><opf:title>${this.escapeXml(this.options.title)}</opf:title><opf:language>ko</opf:language><opf:meta name="creator" content="text">${this.escapeXml(this.options.author)}</opf:meta><opf:meta name="CreatedDate" content="text">${now}</opf:meta><opf:meta name="ModifiedDate" content="text">${now}</opf:meta></opf:metadata><opf:manifest><opf:item id="header" href="Contents/header.xml" media-type="application/xml"/><opf:item id="section0" href="Contents/section0.xml" media-type="application/xml"/><opf:item id="settings" href="settings.xml" media-type="application/xml"/></opf:manifest><opf:spine><opf:itemref idref="header" linear="yes"/><opf:itemref idref="section0" linear="yes"/></opf:spine></opf:package>`;
}
generateCharPr(id, height, bold = false) {
const baseAttrs = `id="${id}" height="${height}" textColor="#000000" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"`;
const fontRefVals = bold ? 'hangul="0" latin="0" hanja="1" japanese="1" other="1" symbol="1" user="1"' : 'hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"';
const boldTag = bold ? `<hh:bold/>` : '';
return `<hh:charPr ${baseAttrs}><hh:fontRef ${fontRefVals}/><hh:ratio hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/><hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/><hh:relSz hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/><hh:offset hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>${boldTag}<hh:underline type="NONE" shape="SOLID" color="#000000"/><hh:strikeout shape="NONE" color="#000000"/><hh:outline type="NONE"/><hh:shadow type="NONE" color="#C0C0C0" offsetX="10" offsetY="10"/></hh:charPr>`;
}
generateParaPr(id, lineSpacing, leftMargin = '0', prevMargin = '0') {
return `<hh:paraPr id="${id}" tabPrIDRef="0" condense="0" fontLineHeight="0" snapToGrid="1" suppressLineNumbers="0" checked="0"><hh:align horizontal="LEFT" vertical="BASELINE"/><hh:heading type="NONE" idRef="0" level="0"/><hh:breakSetting breakLatinWord="KEEP_WORD" breakNonLatinWord="BREAK_WORD" widowOrphan="0" keepWithNext="0" keepLines="0" pageBreakBefore="0" lineWrap="BREAK"/><hh:autoSpacing eAsianEng="0" eAsianNum="0"/><hh:margin><hc:intent value="0" unit="HWPUNIT"/><hc:left value="${leftMargin}" unit="HWPUNIT"/><hc:right value="0" unit="HWPUNIT"/><hc:prev value="${prevMargin}" unit="HWPUNIT"/><hc:next value="0" unit="HWPUNIT"/></hh:margin><hh:lineSpacing type="PERCENT" value="${lineSpacing}" unit="HWPUNIT"/><hh:border borderFillIDRef="1" offsetLeft="0" offsetRight="0" offsetTop="0" offsetBottom="0" connect="0" ignoreMargin="0"/></hh:paraPr>`;
}
generateHeaderXml() {
const nsDecl = `xmlns:ha="http://www.hancom.co.kr/hwpml/2011/app" xmlns:hp="http://www.hancom.co.kr/hwpml/2011/paragraph" xmlns:hs="http://www.hancom.co.kr/hwpml/2011/section" xmlns:hc="http://www.hancom.co.kr/hwpml/2011/core" xmlns:hh="http://www.hancom.co.kr/hwpml/2011/head"`;
return `xml version="1.0" encoding="UTF-8" standalone="yes" <hh:head ${nsDecl} version="1.5" secCnt="1"><hh:beginNum page="1" footnote="1" endnote="1" pic="1" tbl="1" equation="1"/><hh:refList><hh:fontfaces itemCnt="2"><hh:fontface lang="HANGUL" fontCnt="1"><hh:font id="0" face="맑은 고딕" type="TTF" isEmbedded="0"/></hh:fontface><hh:fontface lang="LATIN" fontCnt="1"><hh:font id="0" face="Arial" type="TTF" isEmbedded="0"/></hh:fontface><hh:fontface lang="HANJA" fontCnt="1"><hh:font id="0" face="맑은 고딕" type="TTF" isEmbedded="0"/></hh:fontface><hh:fontface lang="JAPANESE" fontCnt="1"><hh:font id="0" face="맑은 고딕" type="TTF" isEmbedded="0"/></hh:fontface><hh:fontface lang="OTHER" fontCnt="1"><hh:font id="0" face="Arial" type="TTF" isEmbedded="0"/></hh:fontface><hh:fontface lang="SYMBOL" fontCnt="1"><hh:font id="0" face="Arial" type="TTF" isEmbedded="0"/></hh:fontface><hh:fontface lang="USER" fontCnt="1"><hh:font id="0" face="Arial" type="TTF" isEmbedded="0"/></hh:fontface></hh:fontfaces><hh:borderFills itemCnt="2"><hh:borderFill id="1" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0"><hh:slash type="NONE" Crooked="0" isCounter="0"/><hh:backSlash type="NONE" Crooked="0" isCounter="0"/><hh:leftBorder type="NONE" width="0.1 mm" color="#000000"/><hh:rightBorder type="NONE" width="0.1 mm" color="#000000"/><hh:topBorder type="NONE" width="0.1 mm" color="#000000"/><hh:bottomBorder type="NONE" width="0.1 mm" color="#000000"/><hh:diagonal type="SOLID" width="0.1 mm" color="#000000"/></hh:borderFill><hh:borderFill id="2" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0"><hh:slash type="NONE" Crooked="0" isCounter="0"/><hh:backSlash type="NONE" Crooked="0" isCounter="0"/><hh:leftBorder type="SOLID" width="0.12 mm" color="#000000"/><hh:rightBorder type="SOLID" width="0.12 mm" color="#000000"/><hh:topBorder type="SOLID" width="0.12 mm" color="#000000"/><hh:bottomBorder type="SOLID" width="0.12 mm" color="#000000"/><hh:diagonal type="SOLID" width="0.12 mm" color="#000000"/></hh:borderFill></hh:borderFills><hh:charProperties itemCnt="6">${this.generateCharPr('0', '1000', false)}${this.generateCharPr('1', '1000', true)}${this.generateCharPr('2', '1400', true)}${this.generateCharPr('3', '1300', true)}${this.generateCharPr('4', '1200', true)}${this.generateCharPr('5', '1100', true)}</hh:charProperties><hh:tabProperties itemCnt="1"><hh:tabPr id="0" autoTabLeft="0" autoTabRight="0"/></hh:tabProperties><hh:paraProperties itemCnt="14">${this.generateParaPr('0', '140', '0', '0')}${this.generateParaPr('1', '150', '0', '0')}${this.generateParaPr('2', '150', '800', '0')}${this.generateParaPr('3', '150', '1600', '0')}${this.generateParaPr('4', '150', '2400', '0')}${this.generateParaPr('5', '150', '3200', '0')}${this.generateParaPr('6', '150', '4000', '0')}${this.generateParaPr('7', '150', '4800', '0')}${this.generateParaPr('8', '150', '5600', '0')}${this.generateParaPr('9', '150', '6400', '0')}${this.generateParaPr('10', '150', '7200', '0')}${this.generateParaPr('20', '160', '0', '0')}${this.generateParaPr('21', '160', '0', '400')}</hh:paraProperties><hh:styles itemCnt="1"><hh:style id="0" name="바탕글" engName="Normal" paraPrIDRef="0" charPrIDRef="0" nextStyleIDRef="0"/></hh:styles></hh:refList></hh:head>`;
}
generateSection(contents) {
const nsDecl = `xmlns:ha="http://www.hancom.co.kr/hwpml/2011/app" xmlns:hp="http://www.hancom.co.kr/hwpml/2011/paragraph" xmlns:hs="http://www.hancom.co.kr/hwpml/2011/section" xmlns:hc="http://www.hancom.co.kr/hwpml/2011/core" xmlns:hh="http://www.hancom.co.kr/hwpml/2011/head"`;
let vertPos = 0;
const paragraphs = [];
let isFirst = true;
let previousType = null;
for (const content of contents) {
const p = this.generateContent(content, vertPos, isFirst, previousType);
paragraphs.push(p.xml);
vertPos = p.nextVertPos;
isFirst = false;
previousType = content.type;
}
return `xml version="1.0" encoding="UTF-8" standalone="yes" <hs:sec ${nsDecl}>${paragraphs.join('')}</hs:sec>`;
}
generateContent(content, vertPos, isFirst, previousType) {
switch (content.type) {
case 'heading':
return this.generateHeading(content, vertPos, isFirst, previousType);
case 'paragraph':
return this.generateParagraph(content, vertPos, isFirst, previousType);
case 'list':
return this.generateList(content, vertPos, isFirst, previousType, 0);
case 'table':
return this.generateTable(content, vertPos, isFirst, previousType);
case 'image':
return this.generateImage(content, vertPos, isFirst, previousType);
default:
return this.generateParagraph(content, vertPos, isFirst, previousType);
}
}
generateHeading(content, vertPos, isFirst, previousType) {
const text = this.escapeXml(content.content || '');
const level = content.level || 1;
// Add extra spacing if previous content was not a heading
if (previousType && previousType !== 'heading') {
vertPos += 300; // Extra gap before heading
}
// Map heading level to charPr ID and properties
// H1: charPr 2, height 1400
// H2: charPr 3, height 1300
// H3: charPr 4, height 1200
// H4: charPr 5, height 1100
// H5/H6: charPr 0, height 1000 (normal size)
let charPrIDRef;
let charHeight;
let baseline;
let spacing;
switch (level) {
case 1: // H1
charPrIDRef = '2';
charHeight = 1400;
baseline = 1190;
spacing = 840;
break;
case 2: // H2
charPrIDRef = '3';
charHeight = 1300;
baseline = 1105;
spacing = 780;
break;
case 3: // H3
charPrIDRef = '4';
charHeight = 1200;
baseline = 1020;
spacing = 720;
break;
case 4: // H4
charPrIDRef = '5';
charHeight = 1100;
baseline = 935;
spacing = 660;
break;
default: // H5, H6 and others
charPrIDRef = '0';
charHeight = 1000;
baseline = 850;
spacing = 600;
break;
}
const lineHeight = charHeight;
const secPr = isFirst ? this.generateSecPr() : '';
// Use paraPrIDRef="0" for headings (140% line spacing)
const xml = `<hp:p id="${this.paragraphId++}" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">${secPr}<hp:run charPrIDRef="${charPrIDRef}"><hp:t>${text}</hp:t></hp:run><hp:linesegarray><hp:lineseg textpos="0" vertpos="${vertPos}" vertsize="${lineHeight}" textheight="${charHeight}" baseline="${baseline}" spacing="${spacing}" horzpos="0" horzsize="42520" flags="393216"/></hp:linesegarray></hp:p>`;
return { xml, nextVertPos: vertPos + lineHeight + spacing };
}
generateParagraph(content, vertPos, isFirst, previousType) {
let text = '';
let charPrIDRef = '0';
// Use paraPr id="21" (with top margin) when following a heading
// Otherwise use paraPr id="20" (normal spacing)
const paraPrIDRef = previousType === 'heading' ? '21' : '20';
if (content.children && content.children.length > 0) {
const runs = content.children.map(child => {
const t = this.escapeXml(child.content || '');
const cid = child.style?.bold ? '1' : '0';
return `<hp:run charPrIDRef="${cid}"><hp:t>${t}</hp:t></hp:run>`;
}).join('');
const secPr = isFirst ? this.generateSecPr() : '';
const xml = `<hp:p id="${this.paragraphId++}" paraPrIDRef="${paraPrIDRef}" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">${secPr}${runs}</hp:p>`;
return { xml, nextVertPos: vertPos + 1600 };
}
text = this.escapeXml(content.content || '');
const secPr = isFirst ? this.generateSecPr() : '';
const xml = `<hp:p id="${this.paragraphId++}" paraPrIDRef="${paraPrIDRef}" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">${secPr}<hp:run charPrIDRef="0"><hp:t>${text}</hp:t></hp:run></hp:p>`;
return { xml, nextVertPos: vertPos + 1600 };
}
generateList(content, vertPos, isFirst, previousType, level = 0) {
if (!content.children)
return { xml: '', nextVertPos: vertPos };
// Add extra spacing before list (only for top-level lists)
if (level === 0 && previousType && previousType !== 'list') {
vertPos += 300;
}
const paragraphs = [];
let currentVertPos = vertPos;
let first = isFirst;
// Calculate paragraph property ID based on level
// level 0 → id="1" (no indent)
// level 1 → id="2" (800 HWPUNIT indent)
// level 2 → id="3" (1600 HWPUNIT indent), etc.
const paraPrId = Math.min(level + 1, 10);
for (const item of content.children) {
// Handle nested lists
if (item.type === 'list') {
const nestedResult = this.generateList(item, currentVertPos, first, 'list', level + 1);
paragraphs.push(nestedResult.xml);
currentVertPos = nestedResult.nextVertPos;
first = false;
continue;
}
const secPr = first ? this.generateSecPr() : '';
let runs = '';
// Handle list items with mixed content (bold + normal text)
if (item.children && item.children.length > 0) {
const textRuns = item.children.map(child => {
const t = this.escapeXml(child.content || '');
const cid = child.style?.bold ? '1' : '0';
return `<hp:run charPrIDRef="${cid}"><hp:t>${t}</hp:t></hp:run>`;
}).join('');
runs = `<hp:run charPrIDRef="0"><hp:t>• </hp:t></hp:run>${textRuns}`;
}
else {
// Simple text content
const text = this.escapeXml(item.content || '');
runs = `<hp:run charPrIDRef="0"><hp:t>• ${text}</hp:t></hp:run>`;
}
// Use pre-defined paragraph property with indentation
const xml = `<hp:p id="${this.paragraphId++}" paraPrIDRef="${paraPrId}" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">${secPr}${runs}</hp:p>`;
paragraphs.push(xml);
currentVertPos += 1600;
first = false;
}
// Add extra spacing after list (only for top-level lists)
const extraSpacing = level === 0 ? 300 : 0;
return { xml: paragraphs.join(''), nextVertPos: currentVertPos + extraSpacing };
}
generateTable(content, vertPos, isFirst, previousType) {
if (!content.rows || content.rows.length === 0) {
return { xml: '', nextVertPos: vertPos };
}
// Add extra spacing before table
if (previousType && previousType !== 'table') {
vertPos += 400;
}
const rowCnt = content.rows.length;
const colCnt = Math.max(...content.rows.map(row => row.cells.length));
const tableWidth = 41954; // Table width
const cellWidth = Math.floor(tableWidth / colCnt);
const cellHeight = 700; // Increased cell height for better readability
const tableHeight = rowCnt * (cellHeight + 1200);
// Generate table rows
const rows = content.rows.map((row, rowIndex) => {
const cells = row.cells.map((cell, colIndex) => {
const text = this.escapeXml(cell.content);
return `<hp:tc name="" header="0" hasMargin="1" protect="0" editable="0" dirty="0" borderFillIDRef="2"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="CENTER" linkListIDRef="0" linkListNextIDRef="0" textWidth="${cellWidth - 1020}" textHeight="0" hasTextRef="0" hasNumRef="0"><hp:p id="0" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0"><hp:run charPrIDRef="0"><hp:t>${text}</hp:t></hp:run></hp:p></hp:subList><hp:cellAddr colAddr="${colIndex}" rowAddr="${rowIndex}"/><hp:cellSpan colSpan="1" rowSpan="1"/><hp:cellSz width="${cellWidth}" height="${cellHeight}"/><hp:cellMargin left="510" right="510" top="200" bottom="200"/></hp:tc>`;
}).join('');
return `<hp:tr>${cells}</hp:tr>`;
}).join('');
const secPr = isFirst ? this.generateSecPr() : '';
const tableId = Math.floor(Math.random() * 2000000000);
// Table paragraph
const tablePara = `<hp:p id="${this.paragraphId++}" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">${secPr}<hp:run charPrIDRef="0"><hp:tbl id="${tableId}" zOrder="0" numberingType="TABLE" textWrap="TOP_AND_BOTTOM" textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" pageBreak="CELL" repeatHeader="1" rowCnt="${rowCnt}" colCnt="${colCnt}" cellSpacing="0" borderFillIDRef="1" noAdjust="0"><hp:sz width="${tableWidth}" widthRelTo="ABSOLUTE" height="${tableHeight}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="0" affectLSpacing="0" flowWithText="1" allowOverlap="0" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="COLUMN" vertAlign="TOP" horzAlign="LEFT" vertOffset="0" horzOffset="0"/><hp:outMargin left="283" right="283" top="400" bottom="600"/><hp:inMargin left="510" right="510" top="200" bottom="200"/>${rows}</hp:tbl><hp:t/></hp:run><hp:linesegarray><hp:lineseg textpos="0" vertpos="${vertPos}" vertsize="1000" textheight="1000" baseline="850" spacing="600" horzpos="0" horzsize="0" flags="393216"/></hp:linesegarray></hp:p>`;
// Add empty paragraph after table for spacing
const emptyPara = `<hp:p id="${this.paragraphId++}" paraPrIDRef="20" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0"><hp:run charPrIDRef="0"><hp:t> </hp:t></hp:run></hp:p>`;
const xml = tablePara + emptyPara;
// Add extra spacing after table (increased from 600 to 2800)
return { xml, nextVertPos: vertPos + tableHeight + 2800 };
}
generateImage(content, vertPos, isFirst, previousType) {
const alt = content.alt || content.src || 'Image';
const text = `[이미지: ${this.escapeXml(alt)}]`;
const secPr = isFirst ? this.generateSecPr() : '';
const xml = `<hp:p id="${this.paragraphId++}" paraPrIDRef="20" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">${secPr}<hp:run charPrIDRef="0"><hp:t>${text}</hp:t></hp:run></hp:p>`;
return { xml, nextVertPos: vertPos + 1600 };
}
generateSecPr() {
return `<hp:run charPrIDRef="0"><hp:secPr id="" textDirection="HORIZONTAL" spaceColumns="1134" tabStop="8000" tabStopVal="4000" tabStopUnit="HWPUNIT" outlineShapeIDRef="1" memoShapeIDRef="0" textVerticalWidthHead="0" masterPageCnt="0"><hp:grid lineGrid="0" charGrid="0" wonggojiFormat="0"/><hp:startNum pageStartsOn="BOTH" page="0" pic="0" tbl="0" equation="0"/><hp:visibility hideFirstHeader="0" hideFirstFooter="0" hideFirstMasterPage="0" border="SHOW_ALL" fill="SHOW_ALL" hideFirstPageNum="0" hideFirstEmptyLine="0" showLineNumber="0"/><hp:lineNumberShape restartType="0" countBy="0" distance="0" startNumber="0"/><hp:pagePr landscape="WIDELY" width="${this.options.pageWidth}" height="${this.options.pageHeight}" gutterType="LEFT_ONLY"><hp:margin header="4252" footer="4252" gutter="0" left="${this.options.marginLeft}" right="${this.options.marginRight}" top="${this.options.marginTop}" bottom="${this.options.marginBottom}"/></hp:pagePr><hp:footNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="-1" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="283" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="EACH_COLUMN" beneathText="0"/></hp:footNotePr><hp:endNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="14692344" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="0" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="END_OF_DOCUMENT" beneathText="0"/></hp:endNotePr><hp:pageBorderFill type="BOTH" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill><hp:pageBorderFill type="EVEN" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill><hp:pageBorderFill type="ODD" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill></hp:secPr><hp:ctrl><hp:colPr id="" type="NEWSPAPER" layout="LEFT" colCount="1" sameSz="1" sameGap="0"/></hp:ctrl></hp:run>`;
}
generatePreviewText(contents) {
return contents.map(content => {
if (content.children) {
return content.children.map(c => c.content || '').join('');
}
return content.content || '';
}).join('\n');
}
escapeXml(text) {
return text
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
}
exports.HwpxGenerator = HwpxGenerator;