]> git.kianting.info Git - clo/blobdiff - src/canva.ts
20231016:basic font guessing and `putText` function
[clo] / src / canva.ts
diff --git a/src/canva.ts b/src/canva.ts
new file mode 100644 (file)
index 0000000..92650c6
--- /dev/null
@@ -0,0 +1,141 @@
+const { execSync } = require('child_process');
+import { PDFDocument, RGB, ColorTypes } from "pdf-lib";
+import { readFileSync, writeFileSync } from "fs";
+import fontkit from '@pdf-lib/fontkit';
+
+
+export interface CloCommand {
+    cmdName : string,
+    args : TextStreamUnit[], 
+}
+
+export type TextStreamUnit = string | CloCommand;
+
+/**
+ * a clo document
+ */
+export interface Clo{
+    mainText : TextStreamUnit[],
+    mainFontStyle? : FontStyle,
+    PDFCanvas : PDFDocument,
+
+}
+
+/**
+ * Font Style Interface
+ * name : eg. "FreeSans"
+ * size : in px, not in pt.
+ * textWeight : TextWeight.REGULAR ,etc
+ * textWeight : TextStyle.ITALIC ,etc
+ */
+export interface FontStyle{
+    name : string,
+    size : number, 
+    textWeight : TextWeight,
+    textStyle : TextStyle,
+    color? : string,
+};
+
+export enum TextWeight {
+    REGULAR,
+    BOLD,
+  };
+
+export enum TextStyle{
+    NORMAL,
+    ITALIC,
+    OBLIQUE,
+};
+
+/**
+ * guess the font path of a font style with fontconfig's commands
+ * @param style the font style
+ * @returns the font path in string, if found none or .ttc, return a empty string.
+ */
+export function fontStyleTofontPath(style : FontStyle) : string{
+    try {
+        let fcMatchOut = execSync(
+            `fc-match "${style.name}":${TextWeight[style.textWeight]}:`+
+            `${TextStyle[style.textStyle]}`);
+        
+        let fontFileName : string = fcMatchOut.toString().match(/^[^:]+/g)[0];
+
+        if (fontFileName.match(/[.]ttc$/g)){
+            console.log("WARNING: the program doesn't support .ttc font format!\n"+
+                "Font file name: "+
+                fontFileName);
+            return "";
+        }
+
+        let fcListOut = execSync(
+            `fc-list | grep ${fontFileName}`);
+        let fontPath : string = fcListOut.toString().match(/^[^:]+/g)[0];
+        return fontPath;
+    } catch (error) {
+        console.log("WARNING: You should install `fontconfig` to select the font.");
+        return "";
+    }
+};
+
+export function hexToRGB(hex : string) : RGB{
+    let matched = hex.match(/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/);
+
+    var result : RGB ;
+    if (!matched){
+        console.log("WARNING: the hex color code is not valid. set to #000000")
+        result = {
+            type : ColorTypes.RGB,
+            red: 0,
+            green: 0,
+            blue: 0, 
+        }
+    }else{
+        result = {
+            type : ColorTypes.RGB,
+            red: parseInt(matched[1], 16),
+            green: parseInt(matched[2], 16),
+            blue: parseInt(matched[3], 16)
+      };
+    }
+
+    return result;
+}
+
+/**
+ * put text in a clo canva.
+ * @param clo : the clo object
+ * @param str input string
+ * @param sty input fontstyle
+ * @param PageNo : the input page, 0-indexed.
+ * @param x base x-point from left
+ * @param y base y-point from top
+ * @returns a new updated clo object
+ */
+export async function putText(clo : Clo, str : string, sty : FontStyle,
+    pageNo : number, x : number, y : number): Promise<Clo>{
+    
+    clo.PDFCanvas.registerFontkit(fontkit);
+    let canvaPage =  clo.PDFCanvas.getPage(pageNo);
+
+
+    const fontBytes = readFileSync(fontStyleTofontPath(sty));
+    const fontEmbed =  await clo.PDFCanvas.embedFont(fontBytes);
+
+    var textColor : RGB;
+    if (sty.color === undefined){
+        textColor =  hexToRGB("#000000");
+    }else{
+        textColor =  hexToRGB(sty.color);
+    }
+
+    let drawTextOptions = {
+        x : x,
+        y : canvaPage.getHeight() - y,
+        font : fontEmbed,
+        size : sty.size,
+        color : textColor};
+
+    canvaPage.drawText(str, drawTextOptions);
+
+    return clo;
+};
\ No newline at end of file