hwpx-js 0.1.0 → 0.1.2
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/README.md +5 -5
- package/dist/index.cjs +409 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +407 -40
- package/dist/index.js.map +1 -1
- package/package.json +9 -1
package/dist/index.d.cts
CHANGED
|
@@ -184,6 +184,7 @@ interface Paragraph {
|
|
|
184
184
|
paraPrIDRef: number;
|
|
185
185
|
styleIDRef: number;
|
|
186
186
|
runs: Run[];
|
|
187
|
+
pageBreak?: boolean;
|
|
187
188
|
}
|
|
188
189
|
type Run = TextRun | TableRun | PictureRun | DrawingRun | BreakRun;
|
|
189
190
|
interface TextRun {
|
|
@@ -279,6 +280,13 @@ interface ReadOptions {
|
|
|
279
280
|
partial?: boolean;
|
|
280
281
|
}
|
|
281
282
|
|
|
283
|
+
interface PdfOptions {
|
|
284
|
+
/** 한글 폰트명 (기본: 'Malgun Gothic'). PDF 뷰어에 설치된 폰트 */
|
|
285
|
+
fontName?: string;
|
|
286
|
+
/** Latin 폰트명 (기본: 'Helvetica') */
|
|
287
|
+
latinFontName?: string;
|
|
288
|
+
}
|
|
289
|
+
|
|
282
290
|
interface TextStyleOptions {
|
|
283
291
|
fontSize?: number;
|
|
284
292
|
bold?: boolean;
|
|
@@ -440,5 +448,9 @@ declare function write(doc: HWPXDocument, opts?: WriteOptions): Uint8Array;
|
|
|
440
448
|
* 포맷 자동 감지 (HWPX ZIP / HWP5 OLE)
|
|
441
449
|
*/
|
|
442
450
|
declare function read(data: Uint8Array, opts?: ReadOptions): HWPXDocument;
|
|
451
|
+
/**
|
|
452
|
+
* HWPXDocument를 PDF 바이너리(Uint8Array)로 변환
|
|
453
|
+
*/
|
|
454
|
+
declare function writePdf(doc: HWPXDocument, opts?: PdfOptions): Uint8Array;
|
|
443
455
|
|
|
444
|
-
export { type BinDataItem, type BorderFill, type BorderLine, type BreakRun, type BulletProperty, type CellMerge, type CellPadding, type CharProperty, type ColumnDef, type ColumnSettings, type DocumentHead, type DocumentMeta, type DrawingObject, type DrawingRun, type FillBrush, type FontFace, HWPXBuilder, type HWPXDocument, HWPXError, HWPXValidationError, type HeaderFooterDef, type HeaderFooterOptions, type ImageOptions, type NumberingLevel, type NumberingProperty, type PageMargin, type PageSettings, type ParaProperty, type Paragraph, type Picture, type PictureRun, type ReadOptions, type Run, type Section, type SectionDef, type Style, type StyledSegment, type SubDocument, type Table, type TableCell, type TableOptions, type TableRow, type TableRun, type TextRun, type TextStyleOptions, type WriteOptions, createDefaultBorderFill, createDefaultCharProperty, createDefaultDocument, createDefaultFontFaces, createDefaultHead, createDefaultMeta, createDefaultParaProperty, createDefaultSectionDef, createDefaultStyles, read, index as utils, write };
|
|
456
|
+
export { type BinDataItem, type BorderFill, type BorderLine, type BreakRun, type BulletProperty, type CellMerge, type CellPadding, type CharProperty, type ColumnDef, type ColumnSettings, type DocumentHead, type DocumentMeta, type DrawingObject, type DrawingRun, type FillBrush, type FontFace, HWPXBuilder, type HWPXDocument, HWPXError, HWPXValidationError, type HeaderFooterDef, type HeaderFooterOptions, type ImageOptions, type NumberingLevel, type NumberingProperty, type PageMargin, type PageSettings, type ParaProperty, type Paragraph, type PdfOptions, type Picture, type PictureRun, type ReadOptions, type Run, type Section, type SectionDef, type Style, type StyledSegment, type SubDocument, type Table, type TableCell, type TableOptions, type TableRow, type TableRun, type TextRun, type TextStyleOptions, type WriteOptions, createDefaultBorderFill, createDefaultCharProperty, createDefaultDocument, createDefaultFontFaces, createDefaultHead, createDefaultMeta, createDefaultParaProperty, createDefaultSectionDef, createDefaultStyles, read, index as utils, write, writePdf };
|
package/dist/index.d.ts
CHANGED
|
@@ -184,6 +184,7 @@ interface Paragraph {
|
|
|
184
184
|
paraPrIDRef: number;
|
|
185
185
|
styleIDRef: number;
|
|
186
186
|
runs: Run[];
|
|
187
|
+
pageBreak?: boolean;
|
|
187
188
|
}
|
|
188
189
|
type Run = TextRun | TableRun | PictureRun | DrawingRun | BreakRun;
|
|
189
190
|
interface TextRun {
|
|
@@ -279,6 +280,13 @@ interface ReadOptions {
|
|
|
279
280
|
partial?: boolean;
|
|
280
281
|
}
|
|
281
282
|
|
|
283
|
+
interface PdfOptions {
|
|
284
|
+
/** 한글 폰트명 (기본: 'Malgun Gothic'). PDF 뷰어에 설치된 폰트 */
|
|
285
|
+
fontName?: string;
|
|
286
|
+
/** Latin 폰트명 (기본: 'Helvetica') */
|
|
287
|
+
latinFontName?: string;
|
|
288
|
+
}
|
|
289
|
+
|
|
282
290
|
interface TextStyleOptions {
|
|
283
291
|
fontSize?: number;
|
|
284
292
|
bold?: boolean;
|
|
@@ -440,5 +448,9 @@ declare function write(doc: HWPXDocument, opts?: WriteOptions): Uint8Array;
|
|
|
440
448
|
* 포맷 자동 감지 (HWPX ZIP / HWP5 OLE)
|
|
441
449
|
*/
|
|
442
450
|
declare function read(data: Uint8Array, opts?: ReadOptions): HWPXDocument;
|
|
451
|
+
/**
|
|
452
|
+
* HWPXDocument를 PDF 바이너리(Uint8Array)로 변환
|
|
453
|
+
*/
|
|
454
|
+
declare function writePdf(doc: HWPXDocument, opts?: PdfOptions): Uint8Array;
|
|
443
455
|
|
|
444
|
-
export { type BinDataItem, type BorderFill, type BorderLine, type BreakRun, type BulletProperty, type CellMerge, type CellPadding, type CharProperty, type ColumnDef, type ColumnSettings, type DocumentHead, type DocumentMeta, type DrawingObject, type DrawingRun, type FillBrush, type FontFace, HWPXBuilder, type HWPXDocument, HWPXError, HWPXValidationError, type HeaderFooterDef, type HeaderFooterOptions, type ImageOptions, type NumberingLevel, type NumberingProperty, type PageMargin, type PageSettings, type ParaProperty, type Paragraph, type Picture, type PictureRun, type ReadOptions, type Run, type Section, type SectionDef, type Style, type StyledSegment, type SubDocument, type Table, type TableCell, type TableOptions, type TableRow, type TableRun, type TextRun, type TextStyleOptions, type WriteOptions, createDefaultBorderFill, createDefaultCharProperty, createDefaultDocument, createDefaultFontFaces, createDefaultHead, createDefaultMeta, createDefaultParaProperty, createDefaultSectionDef, createDefaultStyles, read, index as utils, write };
|
|
456
|
+
export { type BinDataItem, type BorderFill, type BorderLine, type BreakRun, type BulletProperty, type CellMerge, type CellPadding, type CharProperty, type ColumnDef, type ColumnSettings, type DocumentHead, type DocumentMeta, type DrawingObject, type DrawingRun, type FillBrush, type FontFace, HWPXBuilder, type HWPXDocument, HWPXError, HWPXValidationError, type HeaderFooterDef, type HeaderFooterOptions, type ImageOptions, type NumberingLevel, type NumberingProperty, type PageMargin, type PageSettings, type ParaProperty, type Paragraph, type PdfOptions, type Picture, type PictureRun, type ReadOptions, type Run, type Section, type SectionDef, type Style, type StyledSegment, type SubDocument, type Table, type TableCell, type TableOptions, type TableRow, type TableRun, type TextRun, type TextStyleOptions, type WriteOptions, createDefaultBorderFill, createDefaultCharProperty, createDefaultDocument, createDefaultFontFaces, createDefaultHead, createDefaultMeta, createDefaultParaProperty, createDefaultSectionDef, createDefaultStyles, read, index as utils, write, writePdf };
|
package/dist/index.js
CHANGED
|
@@ -779,14 +779,15 @@ var XmlParser = class {
|
|
|
779
779
|
// src/codecs/hwpx/xml/parse-header.ts
|
|
780
780
|
function parseHeaderXml(xml) {
|
|
781
781
|
const root = parseXml(xml);
|
|
782
|
+
const refList = findChild(root, "hh:refList") || root;
|
|
782
783
|
return {
|
|
783
|
-
fontFaces: parseFontFaces(
|
|
784
|
-
borderFills: parseBorderFills(
|
|
785
|
-
charProperties: parseCharProperties(
|
|
786
|
-
paraProperties: parseParaProperties(
|
|
787
|
-
styles: parseStyles(
|
|
788
|
-
bulletProperties: parseBullets(
|
|
789
|
-
numberingProperties: parseNumberings(
|
|
784
|
+
fontFaces: parseFontFaces(refList),
|
|
785
|
+
borderFills: parseBorderFills(refList),
|
|
786
|
+
charProperties: parseCharProperties(refList),
|
|
787
|
+
paraProperties: parseParaProperties(refList),
|
|
788
|
+
styles: parseStyles(refList),
|
|
789
|
+
bulletProperties: parseBullets(refList),
|
|
790
|
+
numberingProperties: parseNumberings(refList),
|
|
790
791
|
compatibleDoc: parseCompatibleDoc(root)
|
|
791
792
|
};
|
|
792
793
|
}
|
|
@@ -860,15 +861,21 @@ function parseCharProperties(root) {
|
|
|
860
861
|
if (!propsNode) return props;
|
|
861
862
|
for (const cpNode of findChildren(propsNode, "hh:charPr")) {
|
|
862
863
|
const fontRefNode = findChild(cpNode, "hh:fontRef");
|
|
864
|
+
const hasBoldElement = !!findChild(cpNode, "hh:bold");
|
|
865
|
+
const hasItalicElement = !!findChild(cpNode, "hh:italic");
|
|
866
|
+
const underlineNode = findChild(cpNode, "hh:underline");
|
|
867
|
+
const strikeoutNode = findChild(cpNode, "hh:strikeout");
|
|
868
|
+
const underline = underlineNode ? attrStr(underlineNode, "type", "NONE") : attrStr(cpNode, "underline", "NONE");
|
|
869
|
+
const strikeout = strikeoutNode ? attrStr(strikeoutNode, "shape", "NONE") === "NONE" ? "NONE" : "CONTINUOUS" : attrStr(cpNode, "strikeout", "NONE");
|
|
863
870
|
props.push({
|
|
864
871
|
id: attrInt(cpNode, "id"),
|
|
865
872
|
height: attrInt(cpNode, "height", 1e3),
|
|
866
873
|
textColor: parseColor(attrStr(cpNode, "textColor", "#000000")),
|
|
867
|
-
shadeColor: cpNode.attrs["shadeColor"] ? parseColor(attrStr(cpNode, "shadeColor")) : void 0,
|
|
868
|
-
bold: attrBool(cpNode, "bold"),
|
|
869
|
-
italic: attrBool(cpNode, "italic"),
|
|
870
|
-
underline
|
|
871
|
-
strikeout
|
|
874
|
+
shadeColor: cpNode.attrs["shadeColor"] && cpNode.attrs["shadeColor"] !== "none" ? parseColor(attrStr(cpNode, "shadeColor")) : void 0,
|
|
875
|
+
bold: hasBoldElement || attrBool(cpNode, "bold"),
|
|
876
|
+
italic: hasItalicElement || attrBool(cpNode, "italic"),
|
|
877
|
+
underline,
|
|
878
|
+
strikeout,
|
|
872
879
|
useFontSpace: attrBool(cpNode, "useFontSpace"),
|
|
873
880
|
useKerning: attrBool(cpNode, "useKerning"),
|
|
874
881
|
spacing: cpNode.attrs["spacing"] !== void 0 ? attrInt(cpNode, "spacing") : void 0,
|
|
@@ -892,24 +899,54 @@ function parseParaProperties(root) {
|
|
|
892
899
|
const propsNode = findChild(root, "hh:paraProperties");
|
|
893
900
|
if (!propsNode) return props;
|
|
894
901
|
for (const ppNode of findChildren(propsNode, "hh:paraPr")) {
|
|
895
|
-
const
|
|
896
|
-
const
|
|
902
|
+
const alignNode = findChild(ppNode, "hh:align");
|
|
903
|
+
const alignment = alignNode ? attrStr(alignNode, "horizontal", "JUSTIFY") : attrStr(ppNode, "align", "JUSTIFY");
|
|
904
|
+
let marginNode = findChild(ppNode, "hh:parMargin") || findChild(ppNode, "hh:margin");
|
|
905
|
+
let lineSpacingNode = findChild(ppNode, "hh:lineSpacing");
|
|
906
|
+
const switchNode = findChild(ppNode, "hp:switch");
|
|
907
|
+
if (switchNode) {
|
|
908
|
+
const caseNode = findChild(switchNode, "hp:case");
|
|
909
|
+
if (caseNode) {
|
|
910
|
+
if (!marginNode) marginNode = findChild(caseNode, "hh:margin");
|
|
911
|
+
if (!lineSpacingNode) lineSpacingNode = findChild(caseNode, "hh:lineSpacing");
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
let paraMargin = { left: 0, right: 0, indent: 0, prevSpacing: 0, nextSpacing: 0 };
|
|
915
|
+
if (marginNode) {
|
|
916
|
+
const leftChild = findChild(marginNode, "hc:left");
|
|
917
|
+
if (leftChild) {
|
|
918
|
+
const intentChild = findChild(marginNode, "hc:intent");
|
|
919
|
+
const rightChild = findChild(marginNode, "hc:right");
|
|
920
|
+
const prevChild = findChild(marginNode, "hc:prev");
|
|
921
|
+
const nextChild = findChild(marginNode, "hc:next");
|
|
922
|
+
paraMargin = {
|
|
923
|
+
left: leftChild ? attrInt(leftChild, "value") : 0,
|
|
924
|
+
right: rightChild ? attrInt(rightChild, "value") : 0,
|
|
925
|
+
indent: intentChild ? attrInt(intentChild, "value") : 0,
|
|
926
|
+
prevSpacing: prevChild ? attrInt(prevChild, "value") : 0,
|
|
927
|
+
nextSpacing: nextChild ? attrInt(nextChild, "value") : 0
|
|
928
|
+
};
|
|
929
|
+
} else {
|
|
930
|
+
paraMargin = {
|
|
931
|
+
left: attrInt(marginNode, "left"),
|
|
932
|
+
right: attrInt(marginNode, "right"),
|
|
933
|
+
indent: attrInt(marginNode, "indent"),
|
|
934
|
+
prevSpacing: attrInt(marginNode, "prev"),
|
|
935
|
+
nextSpacing: attrInt(marginNode, "next")
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
const headingNode = findChild(ppNode, "hh:heading");
|
|
897
940
|
props.push({
|
|
898
941
|
id: attrInt(ppNode, "id"),
|
|
899
|
-
alignment
|
|
900
|
-
heading: ppNode.attrs["heading"] ? ppNode.attrs["heading"] : void 0,
|
|
942
|
+
alignment,
|
|
943
|
+
heading: headingNode ? attrStr(headingNode, "type", "NONE") : ppNode.attrs["heading"] ? ppNode.attrs["heading"] : void 0,
|
|
901
944
|
breakBefore: ppNode.attrs["breakBefore"] ? ppNode.attrs["breakBefore"] : void 0,
|
|
902
945
|
lineSpacing: lineSpacingNode ? {
|
|
903
946
|
type: attrStr(lineSpacingNode, "type", "PERCENT"),
|
|
904
947
|
value: attrInt(lineSpacingNode, "value", 160)
|
|
905
948
|
} : { type: "PERCENT", value: 160 },
|
|
906
|
-
paraMargin
|
|
907
|
-
left: attrInt(marginNode, "left"),
|
|
908
|
-
right: attrInt(marginNode, "right"),
|
|
909
|
-
indent: attrInt(marginNode, "indent"),
|
|
910
|
-
prevSpacing: attrInt(marginNode, "prev"),
|
|
911
|
-
nextSpacing: attrInt(marginNode, "next")
|
|
912
|
-
} : { left: 0, right: 0, indent: 0, prevSpacing: 0, nextSpacing: 0 }
|
|
949
|
+
paraMargin
|
|
913
950
|
});
|
|
914
951
|
}
|
|
915
952
|
return props;
|
|
@@ -981,12 +1018,24 @@ function parseSectionXml(xml) {
|
|
|
981
1018
|
};
|
|
982
1019
|
}
|
|
983
1020
|
function parseSectionDef(root) {
|
|
984
|
-
|
|
1021
|
+
let secPr = findChild(root, "hs:secPr");
|
|
1022
|
+
if (!secPr) {
|
|
1023
|
+
for (const p of findChildren(root, "hp:p")) {
|
|
1024
|
+
for (const run of findChildren(p, "hp:run")) {
|
|
1025
|
+
const found = findChild(run, "hp:secPr");
|
|
1026
|
+
if (found) {
|
|
1027
|
+
secPr = found;
|
|
1028
|
+
break;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
if (secPr) break;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
985
1034
|
if (!secPr) {
|
|
986
1035
|
return defaultSectionDef();
|
|
987
1036
|
}
|
|
988
|
-
const pagePr = findChild(secPr, "hs:pagePr");
|
|
989
|
-
const pageMarginNode = findChild(secPr, "hs:pageMargin");
|
|
1037
|
+
const pagePr = findChild(secPr, "hp:pagePr") || findChild(secPr, "hs:pagePr");
|
|
1038
|
+
const pageMarginNode = findChild(secPr, "hs:pageMargin") || (pagePr ? findChild(pagePr, "hp:margin") || findChild(pagePr, "hs:margin") : void 0);
|
|
990
1039
|
const colPr = findChild(secPr, "hs:colPr");
|
|
991
1040
|
let columns;
|
|
992
1041
|
if (colPr && attrInt(colPr, "count", 1) > 1) {
|
|
@@ -1012,7 +1061,7 @@ function parseSectionDef(root) {
|
|
|
1012
1061
|
return {
|
|
1013
1062
|
pageWidth: pagePr ? attrInt(pagePr, "width", 59528) : 59528,
|
|
1014
1063
|
pageHeight: pagePr ? attrInt(pagePr, "height", 84188) : 84188,
|
|
1015
|
-
landscape: pagePr ? attrStr(pagePr, "landscape") === "LANDSCAPE" : false,
|
|
1064
|
+
landscape: pagePr ? attrStr(pagePr, "landscape") === "LANDSCAPE" || attrStr(pagePr, "landscape") === "WIDELY" : false,
|
|
1016
1065
|
gutterType: pagePr ? attrStr(pagePr, "gutterType", "LEFT_ONLY") : "LEFT_ONLY",
|
|
1017
1066
|
pageMargin: pageMarginNode ? parsePageMargin(pageMarginNode) : defaultPageMargin(),
|
|
1018
1067
|
columns,
|
|
@@ -1042,11 +1091,11 @@ function parseParagraph(pNode) {
|
|
|
1042
1091
|
for (const runNode of findChildren(pNode, "hp:run")) {
|
|
1043
1092
|
const charPrIDRef = attrInt(runNode, "charPrIDRef");
|
|
1044
1093
|
const lineBreak = findChild(runNode, "hp:lineBreak");
|
|
1045
|
-
const
|
|
1094
|
+
const pageBreak2 = findChild(runNode, "hp:pageBreak");
|
|
1046
1095
|
const colBreak = findChild(runNode, "hp:colBreak");
|
|
1047
1096
|
if (lineBreak) {
|
|
1048
1097
|
runs.push({ t: "break", breakType: "LINE", charPrIDRef });
|
|
1049
|
-
} else if (
|
|
1098
|
+
} else if (pageBreak2) {
|
|
1050
1099
|
runs.push({ t: "break", breakType: "PAGE", charPrIDRef });
|
|
1051
1100
|
} else if (colBreak) {
|
|
1052
1101
|
runs.push({ t: "break", breakType: "COLUMN", charPrIDRef });
|
|
@@ -1061,29 +1110,36 @@ function parseParagraph(pNode) {
|
|
|
1061
1110
|
runs.push({ t: "picture", picture: parsePicture(picNode), charPrIDRef });
|
|
1062
1111
|
continue;
|
|
1063
1112
|
}
|
|
1064
|
-
const runTextNode = findChild(runNode, "hp:runText");
|
|
1113
|
+
const runTextNode = findChild(runNode, "hp:runText") || findChild(runNode, "hp:t");
|
|
1065
1114
|
const text = runTextNode ? getTextContent(runTextNode) : "";
|
|
1066
1115
|
runs.push({ t: "text", text, charPrIDRef });
|
|
1067
1116
|
}
|
|
1068
1117
|
}
|
|
1118
|
+
const pageBreakVal = attrStr(pNode, "pageBreak", "0");
|
|
1119
|
+
const pageBreak = pageBreakVal === "1" || pageBreakVal === "true";
|
|
1069
1120
|
return {
|
|
1070
1121
|
paraPrIDRef: attrInt(pNode, "paraPrIDRef"),
|
|
1071
1122
|
styleIDRef: attrInt(pNode, "styleIDRef"),
|
|
1072
|
-
runs
|
|
1123
|
+
runs,
|
|
1124
|
+
...pageBreak ? { pageBreak: true } : {}
|
|
1073
1125
|
};
|
|
1074
1126
|
}
|
|
1075
1127
|
function parseTable(tblNode) {
|
|
1076
1128
|
const rowCount = attrInt(tblNode, "rowCnt", 1);
|
|
1077
1129
|
const colCount = attrInt(tblNode, "colCnt", 1);
|
|
1078
|
-
const width = attrInt(tblNode, "width", 0);
|
|
1079
1130
|
const borderFillIDRef = attrInt(tblNode, "borderFillIDRef", 1);
|
|
1080
1131
|
const cellSpacing = attrInt(tblNode, "cellSpacing", 0);
|
|
1132
|
+
const szNode = findChild(tblNode, "hp:sz");
|
|
1133
|
+
const width = szNode ? attrInt(szNode, "width", 0) : attrInt(tblNode, "width", 0);
|
|
1081
1134
|
const colSzNodes = findChildren(tblNode, "hp:colSz");
|
|
1082
|
-
|
|
1135
|
+
let colWidths = colSzNodes.map((n) => attrInt(n, "width", 0));
|
|
1083
1136
|
const rows = [];
|
|
1084
1137
|
for (const trNode of findChildren(tblNode, "hp:tr")) {
|
|
1085
1138
|
rows.push(parseTableRow(trNode));
|
|
1086
1139
|
}
|
|
1140
|
+
if (colWidths.length === 0 && rows.length > 0) {
|
|
1141
|
+
colWidths = rows[0].cells.map((c) => c.width);
|
|
1142
|
+
}
|
|
1087
1143
|
return {
|
|
1088
1144
|
rowCount,
|
|
1089
1145
|
colCount,
|
|
@@ -1095,21 +1151,31 @@ function parseTable(tblNode) {
|
|
|
1095
1151
|
};
|
|
1096
1152
|
}
|
|
1097
1153
|
function parseTableRow(trNode) {
|
|
1098
|
-
const height = attrInt(trNode, "height", 0);
|
|
1099
1154
|
const cells = [];
|
|
1100
1155
|
for (const tcNode of findChildren(trNode, "hp:tc")) {
|
|
1101
1156
|
cells.push(parseTableCell(tcNode));
|
|
1102
1157
|
}
|
|
1158
|
+
const height = attrInt(trNode, "height", 0) || (cells.length > 0 ? cells[0].height : 0);
|
|
1103
1159
|
return { height, cells };
|
|
1104
1160
|
}
|
|
1105
1161
|
function parseTableCell(tcNode) {
|
|
1106
|
-
const
|
|
1107
|
-
const
|
|
1108
|
-
const
|
|
1109
|
-
const
|
|
1162
|
+
const cellSpanNode = findChild(tcNode, "hp:cellSpan");
|
|
1163
|
+
const colSpan = cellSpanNode ? attrInt(cellSpanNode, "colSpan", 1) : attrInt(tcNode, "colSpan", 1);
|
|
1164
|
+
const rowSpan = cellSpanNode ? attrInt(cellSpanNode, "rowSpan", 1) : attrInt(tcNode, "rowSpan", 1);
|
|
1165
|
+
const cellSzNode = findChild(tcNode, "hp:cellSz");
|
|
1166
|
+
const width = cellSzNode ? attrInt(cellSzNode, "width", 0) : attrInt(tcNode, "width", 0);
|
|
1167
|
+
const height = cellSzNode ? attrInt(cellSzNode, "height", 0) : attrInt(tcNode, "height", 0);
|
|
1110
1168
|
const borderFillIDRef = attrInt(tcNode, "borderFillIDRef", 1);
|
|
1111
1169
|
let padding;
|
|
1112
|
-
|
|
1170
|
+
const cellMarginNode = findChild(tcNode, "hp:cellMargin");
|
|
1171
|
+
if (cellMarginNode) {
|
|
1172
|
+
padding = {
|
|
1173
|
+
left: attrInt(cellMarginNode, "left"),
|
|
1174
|
+
right: attrInt(cellMarginNode, "right"),
|
|
1175
|
+
top: attrInt(cellMarginNode, "top"),
|
|
1176
|
+
bottom: attrInt(cellMarginNode, "bottom")
|
|
1177
|
+
};
|
|
1178
|
+
} else if (tcNode.attrs["paddingLeft"] !== void 0) {
|
|
1113
1179
|
padding = {
|
|
1114
1180
|
left: attrInt(tcNode, "paddingLeft"),
|
|
1115
1181
|
right: attrInt(tcNode, "paddingRight"),
|
|
@@ -2026,6 +2092,303 @@ function detectFormat(data) {
|
|
|
2026
2092
|
return "unknown";
|
|
2027
2093
|
}
|
|
2028
2094
|
|
|
2095
|
+
// src/codecs/pdf/writer.ts
|
|
2096
|
+
var PdfBuilder = class {
|
|
2097
|
+
constructor() {
|
|
2098
|
+
this.objects = [];
|
|
2099
|
+
this.offsets = [];
|
|
2100
|
+
this.output = "";
|
|
2101
|
+
}
|
|
2102
|
+
nextId() {
|
|
2103
|
+
return this.objects.length + 1;
|
|
2104
|
+
}
|
|
2105
|
+
addObject(content) {
|
|
2106
|
+
const id = this.nextId();
|
|
2107
|
+
this.objects.push(content);
|
|
2108
|
+
return id;
|
|
2109
|
+
}
|
|
2110
|
+
build() {
|
|
2111
|
+
this.output = "%PDF-1.4\n%\xC0\xC1\xC2\xC3\n";
|
|
2112
|
+
for (let i = 0; i < this.objects.length; i++) {
|
|
2113
|
+
this.offsets.push(this.output.length);
|
|
2114
|
+
this.output += `${i + 1} 0 obj
|
|
2115
|
+
${this.objects[i]}
|
|
2116
|
+
endobj
|
|
2117
|
+
`;
|
|
2118
|
+
}
|
|
2119
|
+
const xrefOffset = this.output.length;
|
|
2120
|
+
this.output += "xref\n";
|
|
2121
|
+
this.output += `0 ${this.objects.length + 1}
|
|
2122
|
+
`;
|
|
2123
|
+
this.output += "0000000000 65535 f \n";
|
|
2124
|
+
for (const off of this.offsets) {
|
|
2125
|
+
this.output += off.toString().padStart(10, "0") + " 00000 n \n";
|
|
2126
|
+
}
|
|
2127
|
+
this.output += "trailer\n";
|
|
2128
|
+
this.output += `<< /Size ${this.objects.length + 1} /Root 1 0 R >>
|
|
2129
|
+
`;
|
|
2130
|
+
this.output += "startxref\n";
|
|
2131
|
+
this.output += `${xrefOffset}
|
|
2132
|
+
`;
|
|
2133
|
+
this.output += "%%EOF\n";
|
|
2134
|
+
return stringToBytes(this.output);
|
|
2135
|
+
}
|
|
2136
|
+
};
|
|
2137
|
+
function writePdf(doc, opts) {
|
|
2138
|
+
const korFont = opts?.fontName || "Malgun Gothic";
|
|
2139
|
+
const latFont = opts?.latinFontName || "Helvetica";
|
|
2140
|
+
const pdf = new PdfBuilder();
|
|
2141
|
+
const catalogId = pdf.addObject("");
|
|
2142
|
+
const pagesId = pdf.addObject("");
|
|
2143
|
+
const cidFontId = pdf.addObject(
|
|
2144
|
+
`<< /Type /Font /Subtype /CIDFontType2 /BaseFont /${sanitizeName(korFont)} /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> /DW 1000 >>`
|
|
2145
|
+
);
|
|
2146
|
+
const korFontId = pdf.addObject(
|
|
2147
|
+
`<< /Type /Font /Subtype /Type0 /BaseFont /${sanitizeName(korFont)} /Encoding /Identity-H /DescendantFonts [${cidFontId} 0 R] /ToUnicode ${pdf.nextId() + 1} 0 R >>`
|
|
2148
|
+
);
|
|
2149
|
+
const cmapStream = buildToUnicodeCMap();
|
|
2150
|
+
const cmapId = pdf.addObject(
|
|
2151
|
+
`<< /Length ${cmapStream.length} >>
|
|
2152
|
+
stream
|
|
2153
|
+
${cmapStream}
|
|
2154
|
+
endstream`
|
|
2155
|
+
);
|
|
2156
|
+
pdf.objects[korFontId - 1] = `<< /Type /Font /Subtype /Type0 /BaseFont /${sanitizeName(korFont)} /Encoding /Identity-H /DescendantFonts [${cidFontId} 0 R] /ToUnicode ${cmapId} 0 R >>`;
|
|
2157
|
+
const latFontId = pdf.addObject(
|
|
2158
|
+
`<< /Type /Font /Subtype /Type1 /BaseFont /${sanitizeName(latFont)} /Encoding /WinAnsiEncoding >>`
|
|
2159
|
+
);
|
|
2160
|
+
const fontResources = `/F1 ${korFontId} 0 R /F2 ${latFontId} 0 R`;
|
|
2161
|
+
const pageIds = [];
|
|
2162
|
+
for (const section of doc.sections) {
|
|
2163
|
+
const pageW = section.def.pageWidth / 7200 * 72;
|
|
2164
|
+
const pageH = section.def.pageHeight / 7200 * 72;
|
|
2165
|
+
const ml = section.def.pageMargin.left / 7200 * 72;
|
|
2166
|
+
const mr = section.def.pageMargin.right / 7200 * 72;
|
|
2167
|
+
const mt = section.def.pageMargin.top / 7200 * 72;
|
|
2168
|
+
const mb = section.def.pageMargin.bottom / 7200 * 72;
|
|
2169
|
+
const bodyW = pageW - ml - mr;
|
|
2170
|
+
const bodyH = pageH - mt - mb;
|
|
2171
|
+
const ctx = {
|
|
2172
|
+
doc,
|
|
2173
|
+
pdf,
|
|
2174
|
+
pagesId,
|
|
2175
|
+
fontResources,
|
|
2176
|
+
pageW,
|
|
2177
|
+
pageH,
|
|
2178
|
+
ml,
|
|
2179
|
+
mr,
|
|
2180
|
+
mt,
|
|
2181
|
+
mb,
|
|
2182
|
+
bodyW,
|
|
2183
|
+
bodyH,
|
|
2184
|
+
pageIds
|
|
2185
|
+
};
|
|
2186
|
+
renderSection(ctx, section);
|
|
2187
|
+
}
|
|
2188
|
+
pdf.objects[0] = `<< /Type /Catalog /Pages ${pagesId} 0 R >>`;
|
|
2189
|
+
const kids = pageIds.map((id) => `${id} 0 R`).join(" ");
|
|
2190
|
+
pdf.objects[1] = `<< /Type /Pages /Kids [${kids}] /Count ${pageIds.length} >>`;
|
|
2191
|
+
return pdf.build();
|
|
2192
|
+
}
|
|
2193
|
+
function renderSection(ctx, section) {
|
|
2194
|
+
let page = newPage(ctx);
|
|
2195
|
+
for (const para of section.paragraphs) {
|
|
2196
|
+
for (const run of para.runs) {
|
|
2197
|
+
if (run.t === "text") {
|
|
2198
|
+
const fontSize = getFontSize(ctx.doc, run.charPrIDRef);
|
|
2199
|
+
const lineHeight = fontSize * 1.6;
|
|
2200
|
+
if (page.y - lineHeight < 0) {
|
|
2201
|
+
flushPage(ctx, page);
|
|
2202
|
+
page = newPage(ctx);
|
|
2203
|
+
}
|
|
2204
|
+
if (run.text) {
|
|
2205
|
+
const color = getTextColor(ctx.doc, run.charPrIDRef);
|
|
2206
|
+
const bold = isBold(ctx.doc, run.charPrIDRef);
|
|
2207
|
+
if (color !== "0 0 0") {
|
|
2208
|
+
page.ops.push(color + " rg");
|
|
2209
|
+
}
|
|
2210
|
+
const lines = wrapText(run.text, fontSize, ctx.bodyW);
|
|
2211
|
+
for (const line of lines) {
|
|
2212
|
+
if (page.y - lineHeight < 0) {
|
|
2213
|
+
flushPage(ctx, page);
|
|
2214
|
+
page = newPage(ctx);
|
|
2215
|
+
}
|
|
2216
|
+
page.ops.push("BT");
|
|
2217
|
+
page.ops.push(`/F1 ${fontSize} Tf`);
|
|
2218
|
+
page.ops.push(`${ctx.ml} ${ctx.mt + page.y - lineHeight} Td`);
|
|
2219
|
+
page.ops.push(`<${toUtf16Hex(line)}> Tj`);
|
|
2220
|
+
page.ops.push("ET");
|
|
2221
|
+
page.y -= lineHeight;
|
|
2222
|
+
}
|
|
2223
|
+
if (color !== "0 0 0") {
|
|
2224
|
+
page.ops.push("0 0 0 rg");
|
|
2225
|
+
}
|
|
2226
|
+
} else {
|
|
2227
|
+
page.y -= fontSize * 1.6;
|
|
2228
|
+
}
|
|
2229
|
+
} else if (run.t === "table") {
|
|
2230
|
+
const tableHeight = estimateTableHeight(run.table, ctx.doc);
|
|
2231
|
+
if (page.y - tableHeight < 0 && page.y < ctx.bodyH - 10) {
|
|
2232
|
+
flushPage(ctx, page);
|
|
2233
|
+
page = newPage(ctx);
|
|
2234
|
+
}
|
|
2235
|
+
renderTable(ctx, page, run.table);
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
flushPage(ctx, page);
|
|
2240
|
+
}
|
|
2241
|
+
function newPage(ctx) {
|
|
2242
|
+
return { ops: [], y: ctx.bodyH };
|
|
2243
|
+
}
|
|
2244
|
+
function flushPage(ctx, page) {
|
|
2245
|
+
if (page.ops.length === 0) return;
|
|
2246
|
+
const stream = page.ops.join("\n");
|
|
2247
|
+
const streamId = ctx.pdf.addObject(
|
|
2248
|
+
`<< /Length ${byteLength(stream)} >>
|
|
2249
|
+
stream
|
|
2250
|
+
${stream}
|
|
2251
|
+
endstream`
|
|
2252
|
+
);
|
|
2253
|
+
const pageId = ctx.pdf.addObject(
|
|
2254
|
+
`<< /Type /Page /Parent ${ctx.pagesId} 0 R /MediaBox [0 0 ${fmt(ctx.pageW)} ${fmt(ctx.pageH)}] /Resources << /Font << ${ctx.fontResources} >> >> /Contents ${streamId} 0 R >>`
|
|
2255
|
+
);
|
|
2256
|
+
ctx.pageIds.push(pageId);
|
|
2257
|
+
}
|
|
2258
|
+
function renderTable(ctx, page, table) {
|
|
2259
|
+
const tableW = table.width / 7200 * 72;
|
|
2260
|
+
const scale = Math.min(1, ctx.bodyW / tableW);
|
|
2261
|
+
const startX = ctx.ml;
|
|
2262
|
+
const startY = ctx.mt + page.y;
|
|
2263
|
+
for (const row of table.rows) {
|
|
2264
|
+
const rowH = row.height / 7200 * 72 * scale;
|
|
2265
|
+
let cellX = startX;
|
|
2266
|
+
for (const cell of row.cells) {
|
|
2267
|
+
const cellW = cell.width / 7200 * 72 * scale;
|
|
2268
|
+
const cellH = cell.height / 7200 * 72 * scale;
|
|
2269
|
+
const cellY = startY - rowH;
|
|
2270
|
+
page.ops.push("0.5 w");
|
|
2271
|
+
page.ops.push(`${fmt(cellX)} ${fmt(cellY)} ${fmt(cellW)} ${fmt(cellH)} re S`);
|
|
2272
|
+
const text = cell.paragraphs.flatMap((p) => p.runs.filter((r) => r.t === "text").map((r) => r.text)).join(" ");
|
|
2273
|
+
if (text) {
|
|
2274
|
+
const fontSize = 8 * scale;
|
|
2275
|
+
const textX = cellX + 3;
|
|
2276
|
+
const textY = cellY + cellH - fontSize - 2;
|
|
2277
|
+
page.ops.push("BT");
|
|
2278
|
+
page.ops.push(`/F1 ${fmt(fontSize)} Tf`);
|
|
2279
|
+
page.ops.push(`${fmt(textX)} ${fmt(textY)} Td`);
|
|
2280
|
+
page.ops.push(`<${toUtf16Hex(text.substring(0, 100))}> Tj`);
|
|
2281
|
+
page.ops.push("ET");
|
|
2282
|
+
}
|
|
2283
|
+
cellX += cellW;
|
|
2284
|
+
}
|
|
2285
|
+
page.y -= rowH;
|
|
2286
|
+
startY === ctx.mt + page.y + rowH;
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
function estimateTableHeight(table, doc) {
|
|
2290
|
+
let h = 0;
|
|
2291
|
+
for (const row of table.rows) {
|
|
2292
|
+
h += row.height / 7200 * 72;
|
|
2293
|
+
}
|
|
2294
|
+
return h;
|
|
2295
|
+
}
|
|
2296
|
+
function getFontSize(doc, charPrIDRef) {
|
|
2297
|
+
const cp = doc.head.charProperties.find((c) => c.id === charPrIDRef);
|
|
2298
|
+
if (!cp) return 10;
|
|
2299
|
+
return cp.height / 100;
|
|
2300
|
+
}
|
|
2301
|
+
function getTextColor(doc, charPrIDRef) {
|
|
2302
|
+
const cp = doc.head.charProperties.find((c) => c.id === charPrIDRef);
|
|
2303
|
+
if (!cp || cp.textColor === 0) return "0 0 0";
|
|
2304
|
+
const r = (cp.textColor & 255) / 255;
|
|
2305
|
+
const g = (cp.textColor >> 8 & 255) / 255;
|
|
2306
|
+
const b = (cp.textColor >> 16 & 255) / 255;
|
|
2307
|
+
return `${fmt(r)} ${fmt(g)} ${fmt(b)}`;
|
|
2308
|
+
}
|
|
2309
|
+
function isBold(doc, charPrIDRef) {
|
|
2310
|
+
const cp = doc.head.charProperties.find((c) => c.id === charPrIDRef);
|
|
2311
|
+
return cp?.bold || false;
|
|
2312
|
+
}
|
|
2313
|
+
function wrapText(text, fontSize, maxWidth) {
|
|
2314
|
+
const lines = [];
|
|
2315
|
+
let current = "";
|
|
2316
|
+
let currentWidth = 0;
|
|
2317
|
+
for (const ch of text) {
|
|
2318
|
+
const w = ch.charCodeAt(0) > 127 ? fontSize : fontSize * 0.5;
|
|
2319
|
+
if (currentWidth + w > maxWidth && current) {
|
|
2320
|
+
lines.push(current);
|
|
2321
|
+
current = "";
|
|
2322
|
+
currentWidth = 0;
|
|
2323
|
+
}
|
|
2324
|
+
current += ch;
|
|
2325
|
+
currentWidth += w;
|
|
2326
|
+
}
|
|
2327
|
+
if (current) lines.push(current);
|
|
2328
|
+
return lines.length > 0 ? lines : [""];
|
|
2329
|
+
}
|
|
2330
|
+
function buildToUnicodeCMap() {
|
|
2331
|
+
return [
|
|
2332
|
+
"/CIDInit /ProcSet findresource begin",
|
|
2333
|
+
"12 dict begin",
|
|
2334
|
+
"begincmap",
|
|
2335
|
+
"/CIDSystemInfo",
|
|
2336
|
+
"<< /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def",
|
|
2337
|
+
"/CMapName /Adobe-Identity-UCS def",
|
|
2338
|
+
"/CMapType 2 def",
|
|
2339
|
+
"1 begincodespacerange",
|
|
2340
|
+
"<0000> <FFFF>",
|
|
2341
|
+
"endcodespacerange",
|
|
2342
|
+
"1 beginbfrange",
|
|
2343
|
+
"<0000> <FFFF> <0000>",
|
|
2344
|
+
"endbfrange",
|
|
2345
|
+
"endcmap",
|
|
2346
|
+
"CMapName currentdict /CMap defineresource pop",
|
|
2347
|
+
"end",
|
|
2348
|
+
"end"
|
|
2349
|
+
].join("\n");
|
|
2350
|
+
}
|
|
2351
|
+
function toUtf16Hex(str) {
|
|
2352
|
+
let hex = "";
|
|
2353
|
+
for (let i = 0; i < str.length; i++) {
|
|
2354
|
+
const code = str.charCodeAt(i);
|
|
2355
|
+
hex += code.toString(16).padStart(4, "0");
|
|
2356
|
+
}
|
|
2357
|
+
return hex.toUpperCase();
|
|
2358
|
+
}
|
|
2359
|
+
function sanitizeName(name) {
|
|
2360
|
+
return name.replace(/[^a-zA-Z0-9]/g, "");
|
|
2361
|
+
}
|
|
2362
|
+
function fmt(n) {
|
|
2363
|
+
return Number(n.toFixed(2)).toString();
|
|
2364
|
+
}
|
|
2365
|
+
function byteLength(str) {
|
|
2366
|
+
let len = 0;
|
|
2367
|
+
for (let i = 0; i < str.length; i++) {
|
|
2368
|
+
const code = str.charCodeAt(i);
|
|
2369
|
+
if (code <= 127) len++;
|
|
2370
|
+
else if (code <= 2047) len += 2;
|
|
2371
|
+
else len += 3;
|
|
2372
|
+
}
|
|
2373
|
+
return len;
|
|
2374
|
+
}
|
|
2375
|
+
function stringToBytes(str) {
|
|
2376
|
+
const bytes = [];
|
|
2377
|
+
for (let i = 0; i < str.length; i++) {
|
|
2378
|
+
const code = str.charCodeAt(i);
|
|
2379
|
+
if (code <= 255) {
|
|
2380
|
+
bytes.push(code);
|
|
2381
|
+
} else {
|
|
2382
|
+
if (code <= 2047) {
|
|
2383
|
+
bytes.push(192 | code >> 6, 128 | code & 63);
|
|
2384
|
+
} else {
|
|
2385
|
+
bytes.push(224 | code >> 12, 128 | code >> 6 & 63, 128 | code & 63);
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
return new Uint8Array(bytes);
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2029
2392
|
// src/utils/id.ts
|
|
2030
2393
|
var IdCounter = class {
|
|
2031
2394
|
constructor(start = 0) {
|
|
@@ -2358,6 +2721,9 @@ function read(data, opts) {
|
|
|
2358
2721
|
}
|
|
2359
2722
|
return readHwpx(data, opts);
|
|
2360
2723
|
}
|
|
2724
|
+
function writePdf2(doc, opts) {
|
|
2725
|
+
return writePdf(doc, opts);
|
|
2726
|
+
}
|
|
2361
2727
|
export {
|
|
2362
2728
|
HWPXBuilder,
|
|
2363
2729
|
HWPXError,
|
|
@@ -2373,6 +2739,7 @@ export {
|
|
|
2373
2739
|
createDefaultStyles,
|
|
2374
2740
|
read,
|
|
2375
2741
|
utils_exports as utils,
|
|
2376
|
-
write
|
|
2742
|
+
write,
|
|
2743
|
+
writePdf2 as writePdf
|
|
2377
2744
|
};
|
|
2378
2745
|
//# sourceMappingURL=index.js.map
|