]> git.kianting.info Git - clo/blob - src/libclo/index.ts
english breakline, and generate try to count the text size
[clo] / src / libclo / index.ts
1 import { isKeyObject, isStringObject } from "util/types";
2 import {tkTree} from "../parser";
3 import {FontStyle, TextStyle, TextWeight} from "../canva";
4 import { JSDOM } from "jsdom";
5
6 /**
7 * TYPES
8 */
9
10 /**
11 * text direction
12 * LTR - left to right
13 * TTB - top to bottom
14 * etc.
15 */
16 export enum Direction{
17 LTR,
18 RTL,
19 TTB,
20 BTT,
21 }
22
23 /**
24 * frame box is a subclass of box
25 * - directionInsideLine : text direction inside a line
26 * - baselineskip : the distance between baselines in px
27 */
28 export interface FrameBox extends Box{
29 directionInsideLine : Direction,
30 baseLineskip : number | null,
31 }
32
33 /**
34 * a basic Box
35 * - x :
36 * - y :
37 * - textStyle :
38 * - direction :
39 * - width :
40 * - content :
41 */
42 export interface Box{
43 x : number | null,
44 y : number | null,
45 textStyle : TextStyle | null,
46 direction : Direction,
47 width : number,
48 height : number,
49 content : string | Box[] | null,
50 }
51
52
53 /**
54 * DEFAULT CONST PART
55 */
56 export const A4_IN_PX = {"width" : 793.7,
57 "height" : 1122.5};
58
59 export const defaultTextStyle : TextStyle = {
60 family : "FreeSerif",
61 size : ptToPx(12),
62 textWeight : TextWeight.REGULAR,
63 fontStyle : FontStyle.ITALIC,
64 }
65
66 export const defaultFrameStyle : FrameBox = {
67 directionInsideLine : Direction.LTR,
68 direction : Direction.TTB,
69 baseLineskip : ptToPx(15),
70 textStyle : defaultTextStyle,
71 x : A4_IN_PX.width * 0.10,
72 y : A4_IN_PX.height * 0.10,
73 width : A4_IN_PX.width * 0.80,
74 height : A4_IN_PX.height * 0.80,
75 content : null,
76 };
77
78 /**
79 * definition for cjk scripts
80 * - Hani : Han Character
81 * - Hang : Hangul
82 * - Bopo : Bopomofo
83 * - Kana : Katakana
84 * - Hira : Hiragana
85 */
86 export const cjkvBlocksInRegex = ["Hani", "Hang", "Bopo", "Kana", "Hira"];
87
88 export const cjkvRegexPattern = new RegExp("((?:" +
89 cjkvBlocksInRegex.map((x)=>"\\p{Script_Extensions="+x+"}").join("|") + ")+)", "gu");
90 /**
91 * FUNCTION PART
92 */
93 /**
94 * convert from ptToPx
95 * @param pt pt size value
96 * @returns the corresponding px value
97 */
98 export function ptToPx(pt : number) : number{
99 return pt * 4.0 / 3.0;
100 }
101
102
103
104 /**
105 * REGISTER PART
106 */
107
108 /**
109 * convert '\n\n' to newline command ["nl"]
110 * @param arr the input `tkTree`
111 * @param clo the `Clo` object
112 * @returns the input tktree
113 */
114 export function twoReturnsToNewline(arr : tkTree, clo : Clo): tkTree{
115 var middle : tkTree = [];
116
117 for (let i = 0; i < arr.length; i++) {
118 var item = arr[i];
119 if (!Array.isArray(item)){
120 middle = middle.concat(item.split(/(\n\n)/g));
121 }
122 else{
123 middle.push(item);
124 }
125 }
126
127 var result : tkTree = [];
128 for (let j = 0; j < middle.length; j++){
129 var item = middle[j];
130 if (!Array.isArray(item) && item == "\n\n"){
131 result.push(["nl"]); // push a newline command to the result `tkTree`
132 }
133 else{
134 result.push(middle[j]);
135 }
136 }
137
138 return result;
139 }
140
141 /**
142 * split CJKV and non-CJKV
143 *
144 * @param arr : input tkTree
145 * @returns a splitted tkTree (by CJK and NonCJK)
146 * - Examples:
147 * ```
148 * [`many臺中daylight`] => [`many`, `臺中`, `dahylight`]
149 * ```
150 */
151 export function splitCJKV(arr : tkTree, clo : Clo): tkTree{
152 var result : tkTree = [];
153 for (let i = 0; i < arr.length; i++) {
154 var item = arr[i];
155 if (!Array.isArray(item)){
156 result = result.concat(item.split(cjkvRegexPattern));
157 }
158 else{
159 result.push(item);
160 }
161 }
162
163 return result;
164 }
165
166 /**
167 * hyphenation for a clo document
168 * @param arr the array for a `tkTree`
169 * @param clo the Clo object
170 */
171 export function hyphenForClo(arr : tkTree, clo : Clo): tkTree{
172 let hyphenLanguage : string = clo.attrs["hyphenLanguage"];
173 let res = hyphenTkTree(arr, hyphenLanguage);
174 return res;
175
176 }
177
178 /**
179 * convert spaces to Breakpoint
180 * \s+ => ["bp" [\s+] ""]
181 * @param arr the tkTree input text stream
182 * @param clo the Clo object
183 * @returns the converted object
184 */
185 export function spacesToBreakpoint(arr : tkTree, clo : Clo) : tkTree{
186 let spacePattern = /^([ \t]+)$/g;
187 var result : tkTree = [];
188 for (let i = 0; i < arr.length; i++){
189 var item = arr[i];
190 if (!Array.isArray(item) && item.match(spacePattern)){
191 result.push([ 'bp', item, "" ]); // push a newline command to the result `tkTree`
192 }
193 else{
194 result.push(item);
195 }
196 }
197
198 return result;
199 }
200
201 /**
202 * remove all the `` (empty string) in the arr
203 * @param arr the tkTree to be filtered
204 * @param clo the Clo file
205 */
206 export function filterEmptyString(arr : tkTree, clo : Clo) : tkTree{
207 if (Array.isArray(arr)){
208 arr.filter((x)=>{return x != ``;});
209 }
210
211 return arr;
212 }
213
214
215 /**
216 * OTHER FUNCTIONS
217 */
218
219 /**
220 * hyphenate for a tkTree
221 * - hyphenation => ["bp", "", "-"]
222 * @param arr the tkTree array
223 * @param lang ISO 639 code for the language
224 */
225 export function hyphenTkTree(arr : tkTree, lang: string) : tkTree{
226 // import corresponding hyphen language data and function
227 let hyphen = require("hyphen/"+lang);
228
229 let result :tkTree[] = [];
230 for (let i = 0; i < arr.length; i++) {
231 let element = arr[i];
232 let splitter = "分"; // a CJKV
233 if (!Array.isArray(element)){
234 let hyphenatedElement : string = hyphen.hyphenateSync(element, {hyphenChar :splitter});
235 let hyphenatedSplitted : tkTree = hyphenatedElement.split(splitter);
236 var newSplitted : tkTree = [];
237 for (var j=0; j<hyphenatedSplitted.length-1;j++){
238 newSplitted.push(hyphenatedSplitted[j]);
239 // "bp" for breakpoint
240 newSplitted.push(["bp", "", "-"]); //insert a breakable point (bp) mark
241 }
242 newSplitted.push(hyphenatedSplitted[hyphenatedSplitted.length-1]);
243
244 result = result.concat(newSplitted);
245
246 }else{
247 result.push(element);
248 }
249
250 }
251
252 return result;
253 }
254
255 /**
256 * calculate the text width and Height with a given `TextStyle`
257 * @param preprocessed
258 * @param defaultFontStyle
259 */
260 export function calculateTextWidthHeight(preprocessed : tkTree, style : TextStyle): void {
261 var dom = new JSDOM(`<!DOCTYPE html><html><head></head>
262 <body><canvas id="canvas"></canvas></body></html>`);
263
264 try {
265 let canvas = dom.window.document.getElementById("canvas");
266 console.log(canvas);
267
268 /*if (!(canvas instanceof HTMLElement)){
269 throw new Error('the <canvas="canvas"> in the jsdom\'s DOM is not found.');
270
271 }*/
272
273 let context = (<HTMLCanvasElement>canvas).getContext("2d");
274 console.log(context);
275 if (context == null){
276 throw new Error('`canvas.getContext("2d");` can\'t be executed.');
277
278 }
279
280 context.font = `normal normal ${style.size}px ${style.family}`;
281 console.log(context.font);
282 let txt = `Hello john`;
283 console.log(txt);
284 let measured = context.measureText(txt);
285 let width = measured.width;
286 let height = measured.actualBoundingBoxAscent;
287 let depth = measured.actualBoundingBoxDescent;
288
289 console.log("width: "+width);
290 console.log("height: "+height);
291 console.log("depth: "+depth);
292
293
294 } catch (error) {
295 console.log("Exception "+error);
296 }
297
298
299 }
300
301
302
303
304 /**
305 * whole document-representing class
306 */
307 export class Clo{
308 /** storing the text string into the main frame */
309 mainStream : Array<string>;
310 /** array of preprocessor functions to preprocess the `mainStream` */
311 preprocessors : Array<Function>;
312 /** the attributes for the Clo */
313 attrs: {[index: string]:any} ; // a4 size(x,y)
314
315
316 constructor(){
317 this.preprocessors = [];
318 this.mainStream = [];
319 this.attrs = {
320 "page" : A4_IN_PX, // default for a4. in px of [x, y]
321 "defaultFrameStyle" : defaultFrameStyle, // defaultFrameStyle
322 "hyphenLanguage" : 'en' // hyphenated in the language (in ISO 639)
323 };
324
325
326
327 // register the precessor functions
328 this.preprocessorRegister(splitCJKV);
329 this.preprocessorRegister(hyphenForClo);
330 this.preprocessorRegister(twoReturnsToNewline);
331 this.preprocessorRegister(spacesToBreakpoint);
332 this.preprocessorRegister(filterEmptyString);
333 }
334
335 public setAttr(attr : string, val : any):void{
336 Object.assign(this.attrs, attr, val);
337 }
338
339 public getAttr(attr:string) : any{
340 if (Object.keys(this.attrs).length === 0){
341 return this.attrs[attr];
342 }else{
343 return undefined;
344 }
345
346 }
347
348 /**
349 * register a function of preprocessor
350 * @param f a function
351 */
352 public preprocessorRegister(f : Function){
353 this.preprocessors.push(f);
354 }
355
356 public generatePdf(){
357 // preprocessed
358 var preprocessed = this.mainStream;
359 for (var i = 0; i<this.preprocessors.length; i++){
360 preprocessed = this.preprocessors[i](preprocessed, this);
361 }
362 // generate the width and height of the stream
363
364 let defaultFontStyle : TextStyle = this.attrs["defaultFrameStyle"].textStyle;
365 calculateTextWidthHeight(preprocessed, defaultFontStyle);
366
367 // TODO
368 console.log(preprocessed);
369 }
370
371
372 }
373
374 /*
375 export let a = new Clo();
376 export default a; */