]>
git.kianting.info Git - clo/blob - src/libclo/index.ts
bb884fae1260a5fed6209a6895b835091f3a4607
1 import { isBoxedPrimitive
, isKeyObject
, isStringObject
} from
"util/types";
2 import {tkTree
} from
"../parser";
3 import {FontStyle
, TextStyle
, TextWeight
, fontStyleTofont
} from
"../canva";
4 import { JSDOM
} from
"jsdom";
5 import * as fontkit from
"fontkit";
17 export enum Direction
{
25 * frame box is a subclass of box
26 * - directionInsideLine : text direction inside a line
27 * - baselineskip : the distance between baselines in px
29 export interface FrameBox
extends Box
{
30 directionInsideLine
: Direction
,
31 baseLineskip
: number | null,
34 export interface CharBox
extends Box
{
54 textStyle
: TextStyle
| null,
55 direction
: Direction
,
58 content
: string | Box
[] | null,
65 export const A4_IN_PX
= {"width" : 793.7,
68 export const defaultTextStyle
: TextStyle
= {
71 textWeight
: TextWeight
.REGULAR
,
72 fontStyle
: FontStyle
.ITALIC
,
75 export const defaultFrameStyle
: FrameBox
= {
76 directionInsideLine
: Direction
.LTR
,
77 direction
: Direction
.TTB
,
78 baseLineskip
: ptToPx(15),
79 textStyle
: defaultTextStyle
,
80 x
: A4_IN_PX
.width
* 0.10,
81 y
: A4_IN_PX
.height
* 0.10,
82 width
: A4_IN_PX
.width
* 0.80,
83 height
: A4_IN_PX
.height
* 0.80,
88 * definition for cjk scripts
89 * - Hani : Han Character
95 export const cjkvBlocksInRegex
= ["Hani", "Hang", "Bopo", "Kana", "Hira"];
97 export const cjkvRegexPattern
= new RegExp("((?:" +
98 cjkvBlocksInRegex
.map((x
)=>"\\p{Script_Extensions="+x
+"}").join("|") + ")+)", "gu");
103 * convert from ptToPx
104 * @param pt pt size value
105 * @returns the corresponding px value
107 export function ptToPx(pt
: number) : number{
108 return pt
* 4.0 / 3.0;
118 * convert '\n\n' to newline command ["nl"]
119 * @param arr the input `tkTree`
120 * @param clo the `Clo` object
121 * @returns the input tktree
123 export function twoReturnsToNewline(arr
: tkTree
, clo
: Clo
): tkTree
{
124 var middle
: tkTree
= [];
126 for (let i
= 0; i
< arr
.length
; i
++) {
128 if (!Array.isArray(item
)){
129 middle
= middle
.concat(item
.split(/(\n\n)/g
));
136 var result
: tkTree
= [];
137 for (let j
= 0; j
< middle
.length
; j
++){
138 var item
= middle
[j
];
139 if (!Array.isArray(item
) && item
== "\n\n"){
140 result
.push(["nl"]); // push a newline command to the result `tkTree`
143 result
.push(middle
[j
]);
151 * split CJKV and non-CJKV
153 * @param arr : input tkTree
154 * @returns a splitted tkTree (by CJK and NonCJK)
157 * [`many臺中daylight`] => [`many`, `臺中`, `dahylight`]
160 export function splitCJKV(arr
: tkTree
, clo
: Clo
): tkTree
{
161 var result
: tkTree
= [];
162 for (let i
= 0; i
< arr
.length
; i
++) {
164 if (!Array.isArray(item
)){
165 result
= result
.concat(item
.split(cjkvRegexPattern
));
176 * hyphenation for a clo document
177 * @param arr the array for a `tkTree`
178 * @param clo the Clo object
180 export function hyphenForClo(arr
: tkTree
, clo
: Clo
): tkTree
{
181 let hyphenLanguage
: string = clo
.attrs
["hyphenLanguage"];
182 let res
= hyphenTkTree(arr
, hyphenLanguage
);
188 * convert spaces to Breakpoint
189 * \s+ => ["bp" [\s+] ""]
190 * @param arr the tkTree input text stream
191 * @param clo the Clo object
192 * @returns the converted object
194 export function spacesToBreakpoint(arr
: tkTree
, clo
: Clo
) : tkTree
{
195 let spacePattern
= /^([ \t]+)$
/g
;
196 var result
: tkTree
= [];
197 for (let i
= 0; i
< arr
.length
; i
++){
199 if (!Array.isArray(item
) && item
.match(spacePattern
)){
200 result
.push([ 'bp', item
, "" ]); // push a newline command to the result `tkTree`
211 * remove all the `` (empty string) in the arr
212 * @param arr the tkTree to be filtered
213 * @param clo the Clo file
215 export function filterEmptyString(arr
: tkTree
, clo
: Clo
) : tkTree
{
216 if (Array.isArray(arr
)){
217 arr
.filter((x
)=>{return x
!= ``;});
229 * hyphenate for a tkTree
230 * - hyphenation => ["bp", "", "-"]
231 * @param arr the tkTree array
232 * @param lang ISO 639 code for the language
234 export function hyphenTkTree(arr
: tkTree
, lang
: string) : tkTree
{
235 // import corresponding hyphen language data and function
236 let hyphen
= require("hyphen/"+lang
);
238 let result
:tkTree
[] = [];
239 for (let i
= 0; i
< arr
.length
; i
++) {
240 let element
= arr
[i
];
241 let splitter
= "分"; // a CJKV
242 if (!Array.isArray(element
)){
243 let hyphenatedElement
: string = hyphen
.hyphenateSync(element
, {hyphenChar
:splitter
});
244 let hyphenatedSplitted
: tkTree
= hyphenatedElement
.split(splitter
);
245 var newSplitted
: tkTree
= [];
246 for (var j
=0; j
<hyphenatedSplitted
.length
-1;j
++){
247 newSplitted
.push(hyphenatedSplitted
[j
]);
248 // "bp" for breakpoint
249 newSplitted
.push(["bp", "", "-"]); //insert a breakable point (bp) mark
251 newSplitted
.push(hyphenatedSplitted
[hyphenatedSplitted
.length
-1]);
253 result
= result
.concat(newSplitted
);
256 result
.push(element
);
265 * calculate the text width and Height with a given `TextStyle`
266 * @param preprocessed
267 * @param defaultFontStyle
269 export async function calculateTextWidthHeight(element
: tkTree
, style
: TextStyle
): Promise
<any> {
272 for (var i
=0; i
<element
.length
; i
++){
273 res
.push(await calculateTextWidthHeightAux(element
[i
], style
));
283 * calculate the text width and Height with a given `TextStyle`
284 * @param preprocessed
285 * @param defaultFontStyle
287 export async function calculateTextWidthHeightAux(element
: tkTree
, style
: TextStyle
): Promise
<any> {
288 var result
: any = [];
292 let fontPair
= fontStyleTofont(style
);
293 if (fontPair
.path
.match(/\
.ttc$
/)){
294 var font
= await fontkit
.openSync(fontPair
.path
, fontPair
.psName
);
297 var font
= await fontkit
.openSync(fontPair
.path
);
299 if (!Array.isArray(element
)){
300 var run
= font
.layout(element
, undefined, undefined, undefined, "ltr");
304 for (var j
=0;j
<run
.glyphs
.length
;j
++){
305 let runGlyphsItem
= run
.glyphs
[j
];
308 let item
: CharBox
= {
312 direction
: Direction
.LTR
,
313 width
: (runGlyphsItem
.advanceWidth
)*(style
.size
)/1000,
314 height
: (runGlyphsItem
.bbox
.maxY
- runGlyphsItem
.bbox
.minY
)*(style
.size
)/1000,
315 content
: element
[j
],
316 minX
: runGlyphsItem
.bbox
.minX
,
317 maxX
: runGlyphsItem
.bbox
.maxX
,
318 minY
: runGlyphsItem
.bbox
.minY
,
319 maxY
: runGlyphsItem
.bbox
.maxY
330 }else if(element
[0] == "bp"){
331 let beforeNewLine
= await calculateTextWidthHeightAux(element
[1], style
);
332 let afterNewLine
= await calculateTextWidthHeightAux(element
[2], style
);
334 return ["bp", beforeNewLine
, afterNewLine
];
336 return calculateTextWidthHeight(element
[1], style
);
344 * whole document-representing class
347 /** storing the text string into the main frame */
348 mainStream
: Array<string>;
349 /** array of preprocessor functions to preprocess the `mainStream` */
350 preprocessors
: Array<Function>;
351 /** the attributes for the Clo */
352 attrs
: {[index
: string]:any} ; // a4 size(x,y)
356 this.preprocessors
= [];
357 this.mainStream
= [];
359 "page" : A4_IN_PX
, // default for a4. in px of [x, y]
360 "defaultFrameStyle" : defaultFrameStyle
, // defaultFrameStyle
361 "hyphenLanguage" : 'en' // hyphenated in the language (in ISO 639)
366 // register the precessor functions
367 this.preprocessorRegister(splitCJKV
);
368 this.preprocessorRegister(hyphenForClo
);
369 this.preprocessorRegister(twoReturnsToNewline
);
370 this.preprocessorRegister(spacesToBreakpoint
);
371 this.preprocessorRegister(filterEmptyString
);
374 public setAttr(attr
: string, val
: any):void{
375 Object.assign(this.attrs
, attr
, val
);
378 public getAttr(attr
:string) : any{
379 if (Object.keys(this.attrs
).length
=== 0){
380 return this.attrs
[attr
];
388 * register a function of preprocessor
389 * @param f a function
391 public preprocessorRegister(f
: Function){
392 this.preprocessors
.push(f
);
395 public generatePdf(){
397 var preprocessed
= this.mainStream
;
398 for (var i
= 0; i
<this.preprocessors
.length
; i
++){
399 preprocessed
= this.preprocessors
[i
](preprocessed
, this);
401 // generate the width and height of the stream
403 let defaultFontStyle
: TextStyle
= this.attrs
["defaultFrameStyle"].textStyle
;
404 calculateTextWidthHeight(preprocessed
, defaultFontStyle
);
407 console
.log(preprocessed
);
414 export let a = new Clo();