]> git.kianting.info Git - clo/blobdiff - src/libclo/index.ts
231120 fix the algorithm
[clo] / src / libclo / index.ts
index 53b837c63572ddbc503bb4f69d3ba0fe581364c7..e5c2108442e2deae04678c9a11e261f70f6f5ed7 100644 (file)
@@ -1,10 +1,10 @@
-import { isBoxedPrimitive, isKeyObject, isStringObject } from "util/types";
 import {tkTree} from "../parser";
 import {FontStyle, TextStyle, TextWeight, fontStyleTofont} from "../canva";
-import { JSDOM } from "jsdom";
 import * as fontkit from "fontkit";
-import * as util from "node:util";
 import * as breakLines from "./breakLines";
+const PDFDocument = require('pdfkit');
+import * as fs from "fs";
+
 
 /**
  * TYPES
@@ -58,11 +58,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{
@@ -94,10 +94,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,
 };
 
@@ -328,8 +328,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,
@@ -435,17 +435,286 @@ export class Clo{
         }
         // generate the width and height of the stream
 
-        let defaultFontStyle : TextStyle = this.attrs["defaultFrameStyle"].textStyle;
+        let defaultFontStyle : TextStyle = this.attrs.defaultFrameStyle.textStyle;
         let a = await calculateTextWidthHeight(preprocessed, defaultFontStyle);
 
+        let breakLineAlgorithms = new breakLines.BreakLineAlgorithm();
         // TODO
-        console.log(util.inspect(a, true, 100));
-        console.log(breakLines.totalCost(a,3,100));
+        //console.log(breakLineAlgorithms.totalCost(a,70));
+        let segmentedNodes = breakLineAlgorithms.segmentedNodes(a, this.attrs.defaultFrameStyle.width);
+
+        let segmentedNodesToBox =
+            this.segmentedNodesToFrameBox(segmentedNodes, <FrameBox>this.attrs.defaultFrameStyle);
+
+
+
+        let boxesFixed = this.fixenBoxesPosition(segmentedNodesToBox);
+
+        
+
+
+        // generate pdf7
+        const doc = new PDFDocument({size: 'A4'});
+        doc.pipe(fs.createWriteStream('output.pdf'));
+        this.grid(doc);
+
+        await this.putText(doc, boxesFixed);
+        // putChar
+        doc.end();
+
+    }
+
+    async putText(doc : PDFKit.PDFDocument, box : Box): Promise<PDFKit.PDFDocument>{
+    
+        if (box.textStyle !== null){
+            let fontInfo = fontStyleTofont(box.textStyle);
+        
+            if (fontInfo.path.match(/\.ttc$/g)){
+                doc
+                .font(fontInfo.path, fontInfo.psName)
+                .fontSize(box.textStyle.size * 0.75);}
+            else{
+                doc
+                .font(fontInfo.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; k<box.content.length; k++){
+
+                    doc = await this.putText(doc, box.content[k]);
+                }
+            }else if (box.content !== null){
+                await doc.text(box.content,
+                    (box.x!==null? box.x: undefined),
+                    (box.y!==null? box.y: undefined));
+            }
+        
+        }
+        return doc;
+    };
+
+
+
+    private grid(doc: any) {
+        for (var j = 0; j <  A4_IN_PX.width; j += 5) {
+            if (j % 50 == 0) {
+                doc.save().fill('#000000')
+                    .fontSize(8).text(j.toString(), j*0.75, 50);
+
+                doc
+                    .save()
+                    .lineWidth(0.4)
+                    .strokeColor("#dddddd")
+                    .moveTo(j*0.75, 0)
+                    .lineTo(j*0.75, 1000)
+                    .stroke();
+            }
+
+            doc
+                .save()
+                .lineWidth(0.2)
+                .strokeColor("#dddddd")
+                .moveTo(j*0.75, 0)
+                .lineTo(j*0.75, 1000)
+                .stroke();
+        }
+
+        for (var i = 0; i < 1050; i += 5) {
+            if (i % 50 == 0) {
+                doc.save()
+                    .fontSize(8).text(i.toString(), 50, i*0.75);
+
+                doc
+                    .save()
+                    .lineWidth(0.4)
+                    .strokeColor("#bbbbbb")
+                    .moveTo(0, i*0.75)
+                    .lineTo(1000, i*0.75)
+                    .stroke();
+            }
+            doc
+                .save()
+                .lineWidth(0.2)
+                .strokeColor("#bbbbbb")
+                .moveTo(0, i*0.75)
+                .lineTo(1000, i*0.75)
+                .stroke();
+        }
+        doc
+            .save()
+            .moveTo(0, 200)
+            .lineTo(1000, 200)
+            .fill('#FF3300');
+    }
+
+    /**
+     * make all the nest boxes's position fixed
+     * @param box the main boxes
+     * @returns the fixed boxes
+     */
+    fixenBoxesPosition(box : Box) : Box{
+        var currX : number = (box.x!==null?box.x:0); // current x
+        var currY : number =(box.y!==null?box.y:0); // current y
+        if (Array.isArray(box.content)){
+            for (var i=0; i<box.content.length; i++){
+                if (box.direction == Direction.LTR){
+                    box.content[i].x = currX;
+                    box.content[i].y = currY;
+                    let elementWidth = box.content[i].width;
+                    if(elementWidth !== null){
+                        currX += elementWidth;
+                    }
+
+                }
+                if (box.direction == Direction.TTB){
+                    box.content[i].x = currX;
+                    box.content[i].y = currY;
+                    let elementHeight = box.content[i].height;
+                    if(elementHeight !== null){
+                        currY += elementHeight;
+                    }
+
+                }
+
+
+                box.content[i] = this.fixenBoxesPosition(box.content[i]);
+            }
+        }
+
+        return box;
+    }
+
+    /**
+     * input a `segmentedNodes` and a layed `frame`, return a big `Box` that nodes is put in.
+     * @param segmentedNodes the segmentnodes to be input
+     * @param frame the frame to be layed out.
+     * @returns the big `Box`.
+     */
+    segmentedNodesToFrameBox(segmentedNodes : BoxesItem[][], frame : FrameBox) : Box{
+        let baseLineskip = frame.baseLineskip;
+        let boxArrayEmpty  : Box[] = [];
+        let bigBox : Box = {
+            x : (frame.x !==null? frame.x * 0.75 : null),
+            y : (frame.y !==null? frame.y * 0.75 : null),
+            textStyle :  frame.textStyle,
+            direction : frame.direction,
+            width : frame.width,
+            height :frame.height,
+            content : boxArrayEmpty,
+        }
+
+        var bigBoxContent : Box[] = boxArrayEmpty;
+
+        let segmentedNodesFixed = segmentedNodes.map((x)=>this.removeBreakPoints
+(x).flat());
+        let segmentedNodeUnglue = segmentedNodesFixed.map((x)=>this.removeGlue(x, frame).flat());
+
+        for (var i=0; i<segmentedNodeUnglue.length; i++){
+            var currentLineSkip = baseLineskip;
+            var glyphMaxHeight = this.getGlyphMaxHeight(segmentedNodesFixed[i]);
+            if (currentLineSkip === null || glyphMaxHeight >currentLineSkip ){
+                currentLineSkip = glyphMaxHeight;
+            }
+
+            var currentLineBox : Box = {
+                x : null,
+                y : null,
+                textStyle : defaultTextStyle,
+                direction : frame.directionInsideLine,
+                width :  frame.width,
+                height : currentLineSkip,
+                content : <Box[]>segmentedNodeUnglue[i],
+            }
+
+            bigBoxContent.push(currentLineBox);
+
+        }
+
+        bigBox.content = bigBoxContent;
+
+        return bigBox;
+    }
+
+    /**
+     * get the max height of the glyph`[a, b, c]`
+     * @param nodeLine the node line [a, b, c, ...]
+     * @returns 
+     */
+    getGlyphMaxHeight(nodeLine : BoxesItem[]) : number{
+        let segmentedNodeLineHeight = nodeLine.map((x : BoxesItem)=>{if ("height" in x && x.height > 0.0){return x.height}else{return 0.0}});
+        let maxHeight = Math.max(...segmentedNodeLineHeight);
+        return maxHeight;
+    }
+
+    removeGlue(nodeLine : BoxesItem[], frame : FrameBox) : BoxesItem[]{
+        let breakLineAlgorithms = new breakLines.BreakLineAlgorithm();
+        let glueRemoved = nodeLine.filter((x)=>!breakLineAlgorithms.isHGlue(x));
+        let onlyGlue = nodeLine.filter((x)=>breakLineAlgorithms.isHGlue(x));
+        let sumStretchFactor = onlyGlue.map((x)=>{if("stretchFactor" in x){ return x.stretchFactor} else{return 0;}})
+            .reduce((acc, cur)=>acc+cur , 0);
+
+        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 * 0.75 - glueRemovedWidth;
+        var res = [];
+        for (var i=0; i<nodeLine.length; i++){
+            var ele = nodeLine[i];
+            if (breakLineAlgorithms.isHGlue(ele)){
+                let tmp : Box = {
+                    x : null,
+                    y : null,
+                    textStyle : null,
+                    direction : frame.directionInsideLine,
+                    //width : 0, // ragged
+                    width : ele.stretchFactor / sumStretchFactor * offset,
+                    height : 0,
+                    content : "",
+
+                }
+
+                res.push(tmp);
+            }else{
+                res.push(ele);
+            }
+        }
+
+        return res;
+    }
+
+    /**
+     * remove breakpoints
+     * @param boxitemline boxitem in a line with a breakpoint
+     * @returns boxitemline with break points removed
+     */
+    removeBreakPoints(boxitemline : BoxesItem[]) : BoxesItem[]{
+        var res : BoxesItem[]  = [];
+        let breakLineAlgorithms = new breakLines.BreakLineAlgorithm();
+
+        for (var i = 0; i<boxitemline.length; i++){
+            let ele = boxitemline[i];
+            if (breakLineAlgorithms.isBreakPoint(ele)){
+                if (i == boxitemline.length-1){
+                    res.push(ele.newLined);
+                }else{
+                    res.push(ele.original);
+                }
+            }else{
+                res.push(ele);
+            }
+        }
+
+        return res;
     }
 
     
 }
 
+
 /*
 export let a = new Clo();
 export default a; */
\ No newline at end of file