2 var __createBinding
= (this && this.__createBinding
) || (Object
.create
? (function(o
, m
, k
, k2
) {
3 if (k2
=== undefined) k2
= k
;
4 var desc
= Object
.getOwnPropertyDescriptor(m
, k
);
5 if (!desc
|| ("get" in desc
? !m
.__esModule
: desc
.writable
|| desc
.configurable
)) {
6 desc
= { enumerable
: true, get: function() { return m
[k
]; } };
8 Object
.defineProperty(o
, k2
, desc
);
9 }) : (function(o
, m
, k
, k2
) {
10 if (k2
=== undefined) k2
= k
;
13 var __setModuleDefault
= (this && this.__setModuleDefault
) || (Object
.create
? (function(o
, v
) {
14 Object
.defineProperty(o
, "default", { enumerable
: true, value
: v
});
18 var __importStar
= (this && this.__importStar
) || function (mod
) {
19 if (mod
&& mod
.__esModule
) return mod
;
21 if (mod
!= null) for (var k
in mod
) if (k
!== "default" && Object
.prototype.hasOwnProperty
.call(mod
, k
)) __createBinding(result
, mod
, k
);
22 __setModuleDefault(result
, mod
);
25 var __awaiter
= (this && this.__awaiter
) || function (thisArg
, _arguments
, P
, generator
) {
26 function adopt(value
) { return value
instanceof P
? value
: new P(function (resolve
) { resolve(value
); }); }
27 return new (P
|| (P
= Promise
))(function (resolve
, reject
) {
28 function fulfilled(value
) { try { step(generator
.next(value
)); } catch (e
) { reject(e
); } }
29 function rejected(value
) { try { step(generator
["throw"](value
)); } catch (e
) { reject(e
); } }
30 function step(result
) { result
.done
? resolve(result
.value
) : adopt(result
.value
).then(fulfilled
, rejected
); }
31 step((generator
= generator
.apply(thisArg
, _arguments
|| [])).next());
34 Object
.defineProperty(exports
, "__esModule", { value
: true });
35 exports
.Clo
= exports
.applyVOffset
= exports
.putInVBox
= exports
.calculateTextWidthHeightAux
= exports
.calculateTextWidthHeight
= exports
.hyphenTkTree
= exports
.filterEmptyString
= exports
.spacesToBreakpoint
= exports
.hyphenForClo
= exports
.splitCJKV
= exports
.twoReturnsToNewline
= exports
.ptToPx
= exports
.cjkvRegexPattern
= exports
.cjkvBlocksInRegex
= exports
.defaultFrameStyle
= exports
.defaultTextStyle
= exports
.A4_IN_PX
= exports
.Direction
= void 0;
36 const canva_1
= require("../canva");
37 const fontkit
= __importStar(require("fontkit"));
38 const breakLines
= __importStar(require("./breakLines"));
39 const PDFDocument
= require('pdfkit');
40 const memfs_1
= require("memfs");
51 (function (Direction
) {
52 Direction
[Direction
["LTR"] = 0] = "LTR";
53 Direction
[Direction
["RTL"] = 1] = "RTL";
54 Direction
[Direction
["TTB"] = 2] = "TTB";
55 Direction
[Direction
["BTT"] = 3] = "BTT";
56 })(Direction
|| (exports
.Direction
= Direction
= {}));
60 exports
.A4_IN_PX
= { "width": 793.7,
62 exports
.defaultTextStyle
= {
63 family
: "Noto Sans CJK TC",
65 textWeight
: canva_1
.TextWeight
.REGULAR
,
66 fontStyle
: canva_1
.FontStyle
.ITALIC
,
68 exports
.defaultFrameStyle
= {
69 directionInsideLine
: Direction
.LTR
,
70 direction
: Direction
.TTB
,
71 baseLineskip
: ptToPx(15),
72 textStyle
: exports
.defaultTextStyle
,
73 x
: exports
.A4_IN_PX
.width
* 0.10,
74 y
: exports
.A4_IN_PX
.height
* 0.10,
75 width
: exports
.A4_IN_PX
.width
* 0.80,
76 height
: exports
.A4_IN_PX
.height
* 0.80,
80 * definition for cjk scripts
81 * - Hani : Han Character
87 exports
.cjkvBlocksInRegex
= ["Hani", "Hang", "Bopo", "Kana", "Hira"];
88 exports
.cjkvRegexPattern
= new RegExp("((?:" +
89 exports
.cjkvBlocksInRegex
.map((x
) => "\\p{Script_Extensions=" + x
+ "}").join("|") + ")+)", "gu");
95 * @param pt pt size value
96 * @returns the corresponding px value
99 return pt
* 4.0 / 3.0;
101 exports
.ptToPx
= ptToPx
;
106 * convert '\n\n' to new paragraph command ["br"]
107 * @param arr the input `tkTree`
108 * @param clo the `Clo` object
109 * @returns the input tktree
111 function twoReturnsToNewline(arr
, clo
) {
113 for (let i
= 0; i
< arr
.length
; i
++) {
115 if (!Array
.isArray(item
)) {
116 middle
= middle
.concat(item
.split(/(\n\n)/g));
123 for (let j
= 0; j
< middle
.length
; j
++) {
124 var item
= middle
[j
];
125 if (!Array
.isArray(item
) && item
== "\n\n") {
126 result
.push(["br"]); // push a newline command to the result `tkTree`
129 result
.push(middle
[j
]);
134 exports
.twoReturnsToNewline
= twoReturnsToNewline
;
136 * split CJKV and non-CJKV
138 * @param arr : input tkTree
139 * @returns a splitted tkTree (by CJK and NonCJK)
142 * [`many臺中daylight`] => [`many`, `臺中`, `dahylight`]
145 function splitCJKV(arr
, clo
) {
147 for (let i
= 0; i
< arr
.length
; i
++) {
149 if (!Array
.isArray(item
)) {
150 result
= result
.concat(item
.split(exports
.cjkvRegexPattern
));
158 exports
.splitCJKV
= splitCJKV
;
160 * hyphenation for a clo document
161 * @param arr the array for a `tkTree`
162 * @param clo the Clo object
164 function hyphenForClo(arr
, clo
) {
165 let hyphenLanguage
= clo
.attrs
["hyphenLanguage"];
166 let res
= hyphenTkTree(arr
, hyphenLanguage
);
169 exports
.hyphenForClo
= hyphenForClo
;
171 * convert spaces to Breakpoint
172 * \s+ => ["bp" [\s+] ""]
173 * @param arr the tkTree input text stream
174 * @param clo the Clo object
175 * @returns the converted object
177 function spacesToBreakpoint(arr
, clo
) {
178 let spacePattern
= /^([ \t]+)$/g;
180 for (let i
= 0; i
< arr
.length
; i
++) {
182 if (!Array
.isArray(item
) && item
.match(spacePattern
)) {
183 // push a breakpoint command to the result `tkTree`
184 result
.push(['bp', [["hglue", "0.1"], item
], ""]);
192 exports
.spacesToBreakpoint
= spacesToBreakpoint
;
194 * remove all the `` (empty string) in the arr
195 * @param arr the tkTree to be filtered
196 * @param clo the Clo file
198 function filterEmptyString(arr
, clo
) {
199 if (Array
.isArray(arr
)) {
200 arr
.filter((x
) => { return x
!= ``; });
204 exports
.filterEmptyString
= filterEmptyString
;
209 * hyphenate for a tkTree
210 * - hyphenation => ["bp", "", "-"]
211 * @param arr the tkTree array
212 * @param lang ISO 639 code for the language
214 function hyphenTkTree(arr
, lang
) {
215 // import corresponding hyphen language data and function
216 let hyphen
= require("hyphen/" + lang
);
218 for (let i
= 0; i
< arr
.length
; i
++) {
219 let element
= arr
[i
];
220 let splitter
= "分"; // a CJKV
221 if (!Array
.isArray(element
)) {
222 let hyphenatedElement
= hyphen
.hyphenateSync(element
, { hyphenChar
: splitter
});
223 let hyphenatedSplitted
= hyphenatedElement
.split(splitter
);
224 var newSplitted
= [];
225 for (var j
= 0; j
< hyphenatedSplitted
.length
- 1; j
++) {
226 newSplitted
.push(hyphenatedSplitted
[j
]);
227 // "bp" for breakpoint
228 newSplitted
.push(["bp", "", "-"]); //insert a breakable point (bp) mark
230 newSplitted
.push(hyphenatedSplitted
[hyphenatedSplitted
.length
- 1]);
231 result
= result
.concat(newSplitted
);
234 result
.push(element
);
239 exports
.hyphenTkTree
= hyphenTkTree
;
241 * calculate the text width and Height with a given `TextStyle`
242 * @param preprocessed
243 * @param defaultFontStyle
245 function calculateTextWidthHeight(element
, style
) {
246 return __awaiter(this, void 0, void 0, function* () {
250 for (var i
= 0; i
< element
.length
; i
++) {
251 let item
= yield calculateTextWidthHeightAux(element
[i
], style
, styleCache
, fontCache
);
252 styleCache
= item
[1];
260 exports
.calculateTextWidthHeight
= calculateTextWidthHeight
;
262 * calculate the text width and Height with a given `TextStyle`
263 * @param preprocessed
264 * @param defaultFontStyle
266 function calculateTextWidthHeightAux(element
, style
, styleCache
, fontCache
) {
267 return __awaiter(this, void 0, void 0, function* () {
270 if (style
=== styleCache
) {
274 let fontPair
= (0, canva_1
.fontStyleTofont
)(style
);
275 if (fontPair
.path
.match(/\.ttc$/)) {
276 font
= yield fontkit
.openSync(fontPair
.path
, fontPair
.psName
);
281 font
= yield fontkit
.openSync(fontPair
.path
);
286 if (!Array
.isArray(element
)) {
287 var run
= font
.layout(element
, undefined, undefined, undefined, "ltr");
288 for (var j
= 0; j
< run
.glyphs
.length
; j
++) {
289 let runGlyphsItem
= run
.glyphs
[j
];
294 direction
: Direction
.LTR
,
295 width
: (runGlyphsItem
.advanceWidth
) * (style
.size
) / 1000 * 0.75,
296 height
: (runGlyphsItem
.bbox
.maxY
- runGlyphsItem
.bbox
.minY
) * (style
.size
) / 1000 * 0.75,
298 minX
: runGlyphsItem
.bbox
.minX
,
299 maxX
: runGlyphsItem
.bbox
.maxX
,
300 minY
: runGlyphsItem
.bbox
.minY
,
301 maxY
: runGlyphsItem
.bbox
.maxY
305 return [result
, styleCache
, fontCache
];
306 // break point of a line
308 else if (element
[0] == "bp") {
309 var beforeNewLine
= (yield calculateTextWidthHeightAux(element
[1], style
, styleCache
, fontCache
))[0];
310 if (Array
.isArray(beforeNewLine
)) {
311 beforeNewLine
= beforeNewLine
.flat();
313 let afterNewLine
= (yield calculateTextWidthHeightAux(element
[2], style
, styleCache
, fontCache
))[0];
314 if (Array
.isArray(afterNewLine
)) {
315 afterNewLine
= afterNewLine
.flat();
317 let breakPointNode
= {
318 original
: beforeNewLine
,
319 newLined
: afterNewLine
,
321 return [breakPointNode
, styleCache
, fontCache
];
324 else if (element
[0] == "hglue" && !Array
.isArray(element
[1])) {
326 isHorizonalGlue
: true,
327 stretchFactor
: parseFloat(element
[1])
329 return [hGlue
, styleCache
, fontCache
];
332 else if (element
[0] == "br") {
333 let brBoxItem
= yield calculateTextWidthHeightAux(["hglue", "10000"], style
, styleCache
, fontCache
);
337 original
: brBoxItem
[0],
338 newLined
: brBoxItem
[0]
340 return [BR
, styleCache
, fontCache
];
343 return [yield calculateTextWidthHeight(element
, style
), styleCache
, fontCache
];
347 exports
.calculateTextWidthHeightAux
= calculateTextWidthHeightAux
;
349 * put childrenBox inside VBox
351 function putInVBox(childrenBox
, parentBox
) {
352 var voffset
= Array(childrenBox
.length
).fill(0);
353 for (var i
= 0; i
< childrenBox
.length
- 1; i
++) {
354 voffset
[i
+ 1] = voffset
[i
] + childrenBox
[i
].height
;
356 console
.log("~", voffset
);
357 for (var i
= 0; i
< childrenBox
.length
; i
++) {
358 childrenBox
[i
] = applyVOffset(childrenBox
[i
], voffset
[i
]);
359 childrenBox
[i
].y
+= voffset
[i
];
361 parentBox
.content
= childrenBox
;
364 exports
.putInVBox
= putInVBox
;
366 * apply vertical offset to a box
367 * @param box the box to be applied
368 * @param voffset the vertical offset
369 * @returns applied box
371 function applyVOffset(box
, voffset
) {
372 if (box
.y
!== null) {
375 if (Array
.isArray(box
.content
)) {
376 box
.content
= box
.content
.map((x
) => applyVOffset(x
, voffset
));
380 exports
.applyVOffset
= applyVOffset
;
382 * whole document-representing class
386 this.preprocessors
= [];
387 this.mainStream
= [];
389 "page": exports
.A4_IN_PX
,
390 "defaultFrameStyle": exports
.defaultFrameStyle
,
391 "hyphenLanguage": 'en' // hyphenated in the language (in ISO 639)
393 // register the precessor functions
394 this.preprocessorRegister(splitCJKV
);
395 this.preprocessorRegister(hyphenForClo
);
396 this.preprocessorRegister(twoReturnsToNewline
);
397 this.preprocessorRegister(spacesToBreakpoint
);
398 this.preprocessorRegister(filterEmptyString
);
401 Object
.assign(this.attrs
, attr
, val
);
404 if (Object
.keys(this.attrs
).length
=== 0) {
405 return this.attrs
[attr
];
412 * register a function of preprocessor
413 * @param f a function
415 preprocessorRegister(f
) {
416 this.preprocessors
.push(f
);
419 return __awaiter(this, void 0, void 0, function* () {
421 var preprocessed
= this.mainStream
;
422 for (var i
= 0; i
< this.preprocessors
.length
; i
++) {
423 preprocessed
= this.preprocessors
[i
](preprocessed
, this);
425 // generate the width and height of the stream
426 let defaultFontStyle
= this.attrs
.defaultFrameStyle
.textStyle
;
427 // calculate the width and height of each chars
428 let calculated
= yield calculateTextWidthHeight(preprocessed
, defaultFontStyle
);
430 let paragraphized
= this.paragraphize(calculated
);
431 let breakLineAlgorithms
= new breakLines
.BreakLineAlgorithm();
432 let segmentedNodes
= paragraphized
.map((x
) => breakLineAlgorithms
.segmentedNodes(x
, this.attrs
.defaultFrameStyle
.width
));
433 let segmentedNodesToBox
= segmentedNodes
.map((x
) => this.segmentedNodesToFrameBoxAux(x
, this.attrs
.defaultFrameStyle
));
434 let boxWithParagraph
= putInVBox(segmentedNodesToBox
, this.attrs
.defaultFrameStyle
);
435 console
.log(boxWithParagraph
);
436 // fix the bug of main Frame x & y
437 if (boxWithParagraph
.x
!== null) {
438 boxWithParagraph
.x
*= 0.75;
440 if (boxWithParagraph
.y
!== null) {
441 boxWithParagraph
.y
*= 0.75;
443 let boxesFixed
= this.fixenBoxesPosition(boxWithParagraph
);
444 boxesFixed
.content
.map((e
) => { console
.log(e
.y
); });
446 const doc
= new PDFDocument({ size
: 'A4' });
447 // let fsMemory = memfs();
448 doc
.pipe(memfs_1
.vol
.createWriteStream('output.pdf'));
451 let fontPairCache
= { path
: "", psName
: "" };
452 yield this.putText(doc
, boxesFixed
, styleCache
, fontPairCache
);
457 paragraphize(calculated
) {
459 for (var i
= 0; i
< calculated
.length
; i
++) {
460 if ("isBR" in (calculated
[i
])) {
461 res
[res
.length
- 1] = res
[res
.length
- 1].concat(calculated
[i
]);
465 res
[res
.length
- 1] = res
[res
.length
- 1].concat(calculated
[i
]);
468 res
= res
.filter((x
) => x
.length
!== 0);
471 putText(doc
, box
, styleCache
, fontPairCache
) {
472 return __awaiter(this, void 0, void 0, function* () {
474 if (box
.textStyle
!== null) {
475 if (box
.textStyle
== styleCache
) {
476 fontPair
= fontPairCache
;
479 fontPair
= (0, canva_1
.fontStyleTofont
)(box
.textStyle
);
480 styleCache
= box
.textStyle
;
481 fontPairCache
= fontPair
;
482 let textColor
= box
.textStyle
.color
;
483 if (fontPair
.path
.match(/\.ttc$/g)) {
485 .fillColor(textColor
!== undefined ? textColor
: "#000000")
486 .font(fontPair
.path
, fontPair
.psName
)
487 .fontSize(box
.textStyle
.size
* 0.75);
491 .fillColor(textColor
!== undefined ? textColor
: "#000000")
493 .fontSize(box
.textStyle
.size
* 0.75); // 0.75 must added!
496 if (box
.textStyle
.color
!== undefined) {
497 doc
.fill(box
.textStyle
.color
);
499 if (Array
.isArray(box
.content
)) {
500 for (var k
= 0; k
< box
.content
.length
; k
++) {
501 let tmp
= yield this.putText(doc
, box
.content
[k
], styleCache
, fontPairCache
);
504 fontPairCache
= tmp
[2];
507 else if (box
.content
!== null) {
508 yield doc
.text(box
.content
, (box
.x
!== null ? box
.x
: undefined), (box
.y
!== null ? box
.y
: undefined));
511 return [doc
, styleCache
, fontPairCache
];
516 for (var j
= 0; j
< exports
.A4_IN_PX
.width
; j
+= 5) {
518 doc
.save().fill('#000000')
519 .fontSize(8).text(j
.toString(), j
* 0.75, 50);
523 .strokeColor("#dddddd")
525 .lineTo(j
* 0.75, 1000)
531 .strokeColor("#dddddd")
533 .lineTo(j
* 0.75, 1000)
536 for (var i
= 0; i
< 1050; i
+= 5) {
539 .fontSize(8).text(i
.toString(), 50, i
* 0.75);
543 .strokeColor("#bbbbbb")
545 .lineTo(1000, i
* 0.75)
551 .strokeColor("#bbbbbb")
553 .lineTo(1000, i
* 0.75)
563 * make all the nest boxes's position fixed
564 * @param box the main boxes
565 * @returns the fixed boxes
567 fixenBoxesPosition(box
) {
568 var currX
= (box
.x
!== null ? box
.x
: 0); // current x
569 var currY
= (box
.y
!== null ? box
.y
: 0); // current y
570 if (Array
.isArray(box
.content
)) {
571 for (var i
= 0; i
< box
.content
.length
; i
++) {
572 if (box
.direction
== Direction
.LTR
) {
573 box
.content
[i
].x
= currX
;
574 box
.content
[i
].y
= currY
;
575 let elementWidth
= box
.content
[i
].width
;
576 if (elementWidth
!== null) {
577 currX
+= elementWidth
;
580 if (box
.direction
== Direction
.TTB
) {
581 box
.content
[i
].x
= currX
;
582 box
.content
[i
].y
= currY
;
583 let elementHeight
= box
.content
[i
].height
;
584 if (elementHeight
!== null) {
585 currY
+= elementHeight
;
588 box
.content
[i
] = this.fixenBoxesPosition(box
.content
[i
]);
594 * input a `segmentedNodes` and a layed `frame`, return a big `Box` that nodes is put in.
595 * @param segmentedNodes the segmentnodes to be input
596 * @param frame the frame to be layed out.
597 * @returns the big `Box`.
599 segmentedNodesToFrameBoxAux(segmentedNodes
, frame
) {
600 let baseLineskip
= frame
.baseLineskip
;
601 let boxArrayEmpty
= [];
603 x
: (frame
.x
!== null ? frame
.x
* 0.75 : null),
604 y
: (frame
.y
!== null ? frame
.y
* 0.75 : null),
605 textStyle
: frame
.textStyle
,
606 direction
: frame
.direction
,
608 height
: frame
.height
,
609 content
: boxArrayEmpty
,
611 var bigBoxContent
= boxArrayEmpty
;
612 let segmentedNodesFixed
= segmentedNodes
.map((x
) => this.removeBreakPoints(x
).flat());
613 let segmentedNodeUnglue
= segmentedNodesFixed
.map((x
) => this.removeGlue(x
, frame
).flat());
614 for (var i
= 0; i
< segmentedNodeUnglue
.length
; i
++) {
615 var currentLineSkip
= baseLineskip
;
616 var glyphMaxHeight
= this.getGlyphMaxHeight(segmentedNodesFixed
[i
]);
617 if (currentLineSkip
=== null || glyphMaxHeight
> currentLineSkip
) {
618 currentLineSkip
= glyphMaxHeight
;
620 var currentLineBox
= {
623 textStyle
: exports
.defaultTextStyle
,
624 direction
: frame
.directionInsideLine
,
626 height
: currentLineSkip
,
627 content
: segmentedNodeUnglue
[i
],
629 bigBoxContent
.push(currentLineBox
);
631 bigBox
.content
= bigBoxContent
;
632 let bigBoxHeight
= bigBoxContent
.map((x
) => x
.height
).reduce((x
, y
) => x
+ y
, 0);
633 bigBox
.height
= bigBoxHeight
;
637 * get the max height of the glyph`[a, b, c]`
638 * @param nodeLine the node line [a, b, c, ...]
641 getGlyphMaxHeight(nodeLine
) {
642 let segmentedNodeLineHeight
= nodeLine
.map((x
) => { if ("height" in x
&& x
.height
> 0.0) {
648 let maxHeight
= Math
.max(...segmentedNodeLineHeight
);
651 removeGlue(nodeLine
, frame
) {
652 let breakLineAlgorithms
= new breakLines
.BreakLineAlgorithm();
653 let glueRemoved
= nodeLine
.filter((x
) => !breakLineAlgorithms
.isHGlue(x
));
654 let onlyGlue
= nodeLine
.filter((x
) => breakLineAlgorithms
.isHGlue(x
));
655 let sumStretchFactor
= onlyGlue
.map((x
) => { if ("stretchFactor" in x
) {
656 return x
.stretchFactor
;
661 .reduce((acc
, cur
) => acc
+ cur
, 0);
662 let glueRemovedWidth
= glueRemoved
.map((x
) => { if ("width" in x
) {
668 .reduce((acc
, cur
) => acc
+ cur
, 0);
669 let offset
= frame
.width
* 0.75 - glueRemovedWidth
;
671 for (var i
= 0; i
< nodeLine
.length
; i
++) {
672 var ele
= nodeLine
[i
];
673 if (breakLineAlgorithms
.isHGlue(ele
)) {
678 direction
: frame
.directionInsideLine
,
679 //width : 0, // ragged
680 width
: ele
.stretchFactor
/ sumStretchFactor
* offset
,
694 * @param boxitemline boxitem in a line with a breakpoint
695 * @returns boxitemline with break points removed
697 removeBreakPoints(boxitemline
) {
699 let breakLineAlgorithms
= new breakLines
.BreakLineAlgorithm();
700 for (var i
= 0; i
< boxitemline
.length
; i
++) {
701 let ele
= boxitemline
[i
];
702 if (breakLineAlgorithms
.isBreakPoint(ele
)) {
703 if (i
== boxitemline
.length
- 1) {
704 res
.push(ele
.newLined
);
707 res
.push(ele
.original
);
719 export let a = new Clo();