X-Git-Url: https://git.kianting.info/?a=blobdiff_plain;f=src%2Flibclo%2Findex.ts;h=d074e9c91ce127b4d03f560ae25a6f8c4c74b9aa;hb=714bbb4a64531268d17ca8f7ae60800ec2046a27;hp=b9c3b094ca39a23ffa0fe7e9a48487cbaf7f89a2;hpb=9c51921978468526aed0da6060900fe54967848e;p=clo diff --git a/src/libclo/index.ts b/src/libclo/index.ts index b9c3b09..d074e9c 100644 --- a/src/libclo/index.ts +++ b/src/libclo/index.ts @@ -1,14 +1,13 @@ -import { isBoxedPrimitive, isKeyObject, isStringObject } from "util/types"; import {tkTree} from "../parser"; -import {FontStyle, TextStyle, TextWeight, fontStyleTofont} from "../canva"; -import { JSDOM } from "jsdom"; +import {FontStyle, TextStyle, TextWeight, fontStyleTofont, fontPathPSNamePair} from "../canva"; import * as fontkit from "fontkit"; -import * as util from "node:util"; import * as breakLines from "./breakLines"; -import "pdfkit"; -import PDFKitPage from "pdfkit/js/page"; -import { ColorTypes, PDFDocument, rgb } from "pdf-lib"; +const PDFDocument = require('pdfkit'); import * as fs from "fs"; +import { Style } from "util"; +import { time } from "console"; +import {memfs} from "memfs"; + /** * TYPES @@ -26,12 +25,17 @@ export enum Direction{ TTB, BTT, } - /** * Horizonal glue. * - stretchFactor : the stretch factor in float */ export interface HGlue{ + isHorizonalGlue : true, + stretchFactor: number +} + +export interface VGlue{ + isHorizonalGlue : false, stretchFactor: number } @@ -40,6 +44,11 @@ export interface BreakPoint{ newLined : BoxesItem } +/** BR is like html br */ +export interface BR extends BreakPoint{ + isBR : true; +} + export type BoxesItem = HGlue | Box | BreakPoint | BoxesItem[] ; /** @@ -62,11 +71,11 @@ export interface CharBox extends Box{ /** * a basic Box - * - x : - * - y : + * - x : pt + * - y : pt * - textStyle : * - direction : - * - width : x_advance + * - width : x_advance pt * - content : */ export interface Box{ @@ -87,7 +96,7 @@ export const A4_IN_PX = {"width" : 793.7, "height" : 1122.5}; export const defaultTextStyle : TextStyle = { - family : "FreeSerif", + family : "Noto Sans CJK TC", size : ptToPx(12), textWeight : TextWeight.REGULAR, fontStyle : FontStyle.ITALIC, @@ -98,10 +107,10 @@ export const defaultFrameStyle : FrameBox = { direction : Direction.TTB, baseLineskip : ptToPx(15), textStyle : defaultTextStyle, - x : A4_IN_PX.width * 0.10, - y : A4_IN_PX.height * 0.10, - width : A4_IN_PX.width * 0.80, - height : A4_IN_PX.height * 0.80, + x : A4_IN_PX.width * 0.10 , + y : A4_IN_PX.height * 0.10 , + width : A4_IN_PX.width * 0.80 , + height : A4_IN_PX.height * 0.80 , content : null, }; @@ -136,7 +145,7 @@ export function ptToPx(pt : number) : number{ */ /** - * convert '\n\n' to newline command ["nl"] + * convert '\n\n' to new paragraph command ["br"] * @param arr the input `tkTree` * @param clo the `Clo` object * @returns the input tktree @@ -158,7 +167,7 @@ export function twoReturnsToNewline(arr : tkTree, clo : Clo): tkTree{ for (let j = 0; j < middle.length; j++){ var item = middle[j]; if (!Array.isArray(item) && item == "\n\n"){ - result.push(["nl"]); // push a newline command to the result `tkTree` + result.push(["br"]); // push a newline command to the result `tkTree` } else{ result.push(middle[j]); @@ -283,6 +292,8 @@ export function hyphenTkTree(arr : tkTree, lang: string) : tkTree{ return result; } + + /** * calculate the text width and Height with a given `TextStyle` * @param preprocessed @@ -290,9 +301,14 @@ export function hyphenTkTree(arr : tkTree, lang: string) : tkTree{ */ export async function calculateTextWidthHeight(element : tkTree, style : TextStyle): Promise { var res = []; + var styleCache = {}; + var fontCache = {}; for (var i=0; istyleCache, fontCache); + styleCache = item[1]; + fontCache = item[2]; + res.push(item[0]); } res = res.flat(); @@ -306,18 +322,37 @@ export async function calculateTextWidthHeight(element : tkTree, style : TextSty * @param preprocessed * @param defaultFontStyle */ -export async function calculateTextWidthHeightAux(element : tkTree, style : TextStyle): Promise { +export async function calculateTextWidthHeightAux(element : tkTree, + style : TextStyle, + styleCache : TextStyle, + fontCache : fontkit.Font): Promise<[BoxesItem, TextStyle, fontkit.Font] > { var result : BoxesItem = []; - + var font; + + if (style === styleCache){ + font = fontCache; + }else { let fontPair = fontStyleTofont(style); + if (fontPair.path.match(/\.ttc$/)){ - var font = await fontkit.openSync(fontPair.path, fontPair.psName); + font = await fontkit.openSync(fontPair.path, fontPair.psName); + styleCache = style; + fontCache = font; + } else{ - var font = await fontkit.openSync(fontPair.path); + font = await fontkit.openSync(fontPair.path); + styleCache = style; + fontCache = font; + } + + + } + + if (!Array.isArray(element)){ var run = font.layout(element, undefined, undefined, undefined, "ltr"); @@ -332,8 +367,8 @@ export async function calculateTextWidthHeightAux(element : tkTree, style : Text y : null, textStyle : style, direction : Direction.LTR, - width : (runGlyphsItem.advanceWidth)*(style.size)/1000, - height : (runGlyphsItem.bbox.maxY - runGlyphsItem.bbox.minY)*(style.size)/1000, + width : (runGlyphsItem.advanceWidth)*(style.size)/1000 * 0.75, // in pt + height : (runGlyphsItem.bbox.maxY - runGlyphsItem.bbox.minY)*(style.size)/1000 * 0.75, // in pt content : element[j], minX : runGlyphsItem.bbox.minX, maxX : runGlyphsItem.bbox.maxX, @@ -344,19 +379,20 @@ export async function calculateTextWidthHeightAux(element : tkTree, style : Text result.push(item); } - return result; + return [result, styleCache, fontCache]; - + // break point of a line }else if(element[0] == "bp"){ - var beforeNewLine = await calculateTextWidthHeightAux(element[1], style); + + var beforeNewLine = (await calculateTextWidthHeightAux(element[1], style, styleCache, fontCache))[0]; if (Array.isArray(beforeNewLine)){ beforeNewLine = beforeNewLine.flat(); } - let afterNewLine = await calculateTextWidthHeightAux(element[2], style); + let afterNewLine = (await calculateTextWidthHeightAux(element[2], style, styleCache, fontCache))[0]; if (Array.isArray(afterNewLine)){ afterNewLine = afterNewLine.flat(); } @@ -366,18 +402,66 @@ export async function calculateTextWidthHeightAux(element : tkTree, style : Text newLined : afterNewLine, } - return breakPointNode; + + return [breakPointNode, styleCache, fontCache]; + // hglue }else if(element[0] == "hglue" && !Array.isArray(element[1])){ - let hGlue : HGlue = {stretchFactor : parseFloat(element[1])} - return hGlue; + let hGlue : HGlue = { + isHorizonalGlue : true, + stretchFactor : parseFloat(element[1])} + return [hGlue, styleCache, fontCache]; + } + // new line
+ else if(element[0] == "br"){ + let brBoxItem = await calculateTextWidthHeightAux(["hglue", "10000"], + style, styleCache, fontCache); + //
+ let BR : BR = { + isBR : true, + original : brBoxItem[0], + newLined : brBoxItem[0]}; + return [BR, styleCache, fontCache]; } else{ - return calculateTextWidthHeight(element, style); + return [await calculateTextWidthHeight(element, style), styleCache, fontCache]; } } +/** + * put childrenBox inside VBox + */ +export function putInVBox(childrenBox: Box[], parentBox: Box) : Box{ + var voffset = Array(childrenBox.length).fill(0); + for (var i=0;iapplyVOffset(x, voffset)); + } + return box; +} /** * whole document-representing class @@ -432,71 +516,234 @@ export class Clo{ } public async generatePdf(){ + // preprocessed var preprocessed = this.mainStream; for (var i = 0; ithis.attrs["defaultFrameStyle"])); + let segmentedNodes = paragraphized.map((x)=>breakLineAlgorithms.segmentedNodes(x, this.attrs.defaultFrameStyle.width)); + + let segmentedNodesToBox = segmentedNodes.map((x)=> + this.segmentedNodesToFrameBoxAux(x, this.attrs.defaultFrameStyle)); + + let boxWithParagraph = putInVBox(segmentedNodesToBox, this.attrs.defaultFrameStyle); + + console.log(boxWithParagraph); + + // fix the bug of main Frame x & y + if(boxWithParagraph.x !== null) + {boxWithParagraph.x *= 0.75} + if(boxWithParagraph.y !== null) + {boxWithParagraph.y *= 0.75} + + let boxesFixed = this.fixenBoxesPosition(boxWithParagraph); + + + (boxesFixed.content).map((e)=>{console.log(e.y)}); + // generate pdf - const pdfDoc = await PDFDocument.create(); - var page = pdfDoc.addPage(); - page.drawText('You can create PDFs!'); + const doc = new PDFDocument({size: 'A4'}); + doc.pipe(fs.createWriteStream('output.pdf')); + this.grid(doc); + + let styleCache : any = {}; + let fontPairCache : fontPathPSNamePair = {path : "", psName : ""}; + await this.putText(doc, boxesFixed, styleCache, fontPairCache); + // putChar + doc.end(); + + + } - for (var j = 0; j<1000; j+=5){ - if (j %50 == 0){ - page.drawText(j.toString(), {x: 50, y: j}); + paragraphize(calculated : BoxesItem[]): BoxesItem[][]{ + var res : BoxesItem[][] = [[]]; + for (var i=0;i(calculated[i])){ + res[res.length-1] = res[res.length-1].concat(calculated[i]); + res.push([]); + }else{ + res[res.length-1] = res[res.length-1].concat(calculated[i]); } + } + + res = res.filter((x)=>x.length !== 0); + return res; + } - page.drawLine({ - start: { x: 0, y: j }, - end: { x: 1000, y: j }, - thickness: 0.5, - color: rgb(0.75, 0.2, 0.2), - opacity: 0.20, - }); + async putText(doc : PDFKit.PDFDocument, box : Box, styleCache : TextStyle, + fontPairCache : fontPathPSNamePair): + Promise<[PDFKit.PDFDocument, TextStyle, fontPathPSNamePair]>{ + var fontPair; + + + if (box.textStyle !== null){ + + if(box.textStyle == styleCache){ + fontPair = fontPairCache; + }else{ + fontPair = fontStyleTofont(box.textStyle); + styleCache = box.textStyle; + fontPairCache = fontPair; + let textColor = box.textStyle.color; + + if (fontPair.path.match(/\.ttc$/g)){ + doc + .fillColor(textColor !== undefined ? textColor : "#000000") + .font(fontPair.path, fontPair.psName) + .fontSize(box.textStyle.size * 0.75);} + else{ + doc + .fillColor(textColor !== undefined ? textColor : "#000000") + .font(fontPair.path) + .fontSize(box.textStyle.size * 0.75); // 0.75 must added! + } } + + if (box.textStyle.color !== undefined){ + doc.fill(box.textStyle.color); + } + + if (Array.isArray(box.content)){ + for (var k=0; kthis.removeBreakPoints (x).flat()); let segmentedNodeUnglue = segmentedNodesFixed.map((x)=>this.removeGlue(x, frame).flat()); - - for (var i=0; icurrentLineSkip ){ @@ -528,6 +775,8 @@ export class Clo{ } bigBox.content = bigBoxContent; + let bigBoxHeight = bigBoxContent.map((x)=>x.height).reduce((x,y)=>x+y, 0); + bigBox.height = bigBoxHeight; return bigBox; } @@ -552,7 +801,7 @@ export class Clo{ let glueRemovedWidth = glueRemoved.map((x)=>{if("width" in x){ return x.width} else{return 0;}}) .reduce((acc, cur)=>acc+cur , 0); - let offset = frame.width - glueRemovedWidth; + let offset = frame.width * 0.75 - glueRemovedWidth; var res = []; for (var i=0; i