]> git.kianting.info Git - anotherTypesetter/commitdiff
add putchar of the language
authorTan Kian-ting <chenjt30@gmail.com>
Thu, 11 Apr 2024 14:47:42 +0000 (22:47 +0800)
committerTan Kian-ting <chenjt30@gmail.com>
Thu, 11 Apr 2024 14:47:42 +0000 (22:47 +0800)
README.md
package-lock.json
package.json
src/index.js
src/index.ts
text.lisp
text.lisp.pdf

index 217e3faf9e54e1dd750af31b6f552bed5c90c2b3..6801888b815b2d652bfa57ef689c8dee13f5d4fd 100644 (file)
--- a/README.md
+++ b/README.md
@@ -12,6 +12,6 @@
  - [v] add pdf page 240410 (addPDFPages)
  - [v] create pdf 240410
  - [ ] close pdf
- - [ ] add character
+ - [v] add character
  - [ ] add path
  - [ ] basic typesetting format
\ No newline at end of file
index 09be7cca0ab481d25e53f45d51010e492c6ee92a..54984020d60a307c83ad6d38caa652958cfe4281 100644 (file)
@@ -9,6 +9,7 @@
       "version": "0.0.1",
       "license": "MIT",
       "dependencies": {
+        "@pdf-lib/fontkit": "^1.1.1",
         "pdf-lib": "^1.17.1",
         "typescript-parsec": "^0.3.4"
       },
         "node": ">= 8"
       }
     },
+    "node_modules/@pdf-lib/fontkit": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@pdf-lib/fontkit/-/fontkit-1.1.1.tgz",
+      "integrity": "sha512-KjMd7grNapIWS/Dm0gvfHEilSyAmeLvrEGVcqLGi0VYebuqqzTbgF29efCx7tvx+IEbG3zQciRSWl3GkUSvjZg==",
+      "dependencies": {
+        "pako": "^1.0.6"
+      }
+    },
     "node_modules/@pdf-lib/standard-fonts": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
         "fastq": "^1.6.0"
       }
     },
+    "@pdf-lib/fontkit": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@pdf-lib/fontkit/-/fontkit-1.1.1.tgz",
+      "integrity": "sha512-KjMd7grNapIWS/Dm0gvfHEilSyAmeLvrEGVcqLGi0VYebuqqzTbgF29efCx7tvx+IEbG3zQciRSWl3GkUSvjZg==",
+      "requires": {
+        "pako": "^1.0.6"
+      }
+    },
     "@pdf-lib/standard-fonts": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
index 0f9f047efdd0d9e41f475f6b16fc8e2f7befb777..f01ade4a65a93eb0c0fde59358614591c67b2dff 100644 (file)
@@ -16,6 +16,7 @@
   "author": "Tan Kian-ting",
   "license": "MIT",
   "dependencies": {
+    "@pdf-lib/fontkit": "^1.1.1",
     "pdf-lib": "^1.17.1",
     "typescript-parsec": "^0.3.4"
   },
index 4c125a11453c15b5a9cd446c68a90df635b67bc7..3b0d71890ec0c30bcabb3c53b5b85cff9b4394fb 100644 (file)
@@ -18,9 +18,13 @@ var __importStar = (this && this.__importStar) || function (mod) {
     __setModuleDefault(result, mod);
     return result;
 };
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
 Object.defineProperty(exports, "__esModule", { value: true });
 const fs = __importStar(require("fs"));
 const pdf_lib_1 = require("pdf-lib");
+const fontkit_1 = __importDefault(require("@pdf-lib/fontkit"));
 const typescript_parsec_1 = require("typescript-parsec");
 const typescript_parsec_2 = require("typescript-parsec");
 /** input lisp file */
@@ -358,6 +362,34 @@ function cons(h, t) {
     };
     return rtnList;
 }
+/* PDF manipulation */
+async function drawText(pageIndex, fontFamily, textSize, color, x, y, text) {
+    let currentPage = pdfDoc.getPages()[0];
+    const fcMatch = await (0, child_process_1.spawnSync)('fc-match', ['--format=%{file}', fontFamily]);
+    const path = fcMatch.stdout.toString();
+    pdfDoc.registerFontkit(fontkit_1.default);
+    const fontBytes = fs.readFileSync(path);
+    console.log("A2A", (0, pdf_lib_1.rgb)(0, 0, 0));
+    const customFont = await pdfDoc.embedFont(fontBytes);
+    console.log("A3A", (0, pdf_lib_1.rgb)(0, 0, 0));
+    const rgbColor = await hexColorToRGB(color);
+    console.log("A4A", (0, pdf_lib_1.rgb)(0, 0, 0));
+    let a = await pdfDoc.getPage(0).drawText(text, {
+        x: x,
+        y: y,
+        size: textSize,
+        font: customFont,
+        color: rgbColor,
+    });
+    await pdfDoc.save();
+}
+async function hexColorToRGB(hex) {
+    let rgbHex = /[#]?(\d{2})(\d{2})(\d{2})/.exec(hex);
+    let r = parseInt(rgbHex[1], 16) / 256.0;
+    let g = parseInt(rgbHex[2], 16) / 256.0;
+    let b = parseInt(rgbHex[3], 16) / 256.0;
+    return (0, pdf_lib_1.rgb)(r, g, b);
+}
 function listRef(l, i) {
     const realI = i.int;
     if (realI >= l.list.length || realI < 0) {
@@ -395,7 +427,7 @@ function isItemId(x) {
 function isClosure(x) {
     return x.hasOwnProperty('type') && x.hasOwnProperty('vars');
 }
-function interp(prog, env) {
+async function interp(prog, env) {
     if (Array.isArray(prog)) {
         if (!Array.isArray(prog[0])) {
             const op = prog[0];
@@ -462,7 +494,7 @@ function interp(prog, env) {
                                 const vari = binding[0];
                                 if (vari.hasOwnProperty("id")) {
                                     const variName = vari.id;
-                                    const data = interp(binding[1], env);
+                                    const data = await interp(binding[1], env);
                                     if (op.id === "letrec") {
                                         newEnv = extendEnv(newEnv, variName, true, data);
                                     }
@@ -482,7 +514,7 @@ function interp(prog, env) {
                         throw invalidLengthException('if', 3);
                     }
                     else {
-                        const cond = interp(prog[1], env);
+                        const cond = await interp(prog[1], env);
                         if (Array.isArray(cond)) {
                             throw new Error("cond can't be reduced to a constant");
                         }
@@ -499,9 +531,9 @@ function interp(prog, env) {
                     }
                 }
                 else {
-                    const argsMapped = prog.slice(1).map((x) => {
+                    const argsMapped = await Promise.all(prog.slice(1).map(async (x) => {
                         return interp(x, env);
-                    });
+                    }));
                     // binary basic operator
                     if (op.id === "+") {
                         return interpBinary(add, argsMapped);
@@ -674,6 +706,7 @@ function interp(prog, env) {
                             return { type: ItemType.Unit };
                         }
                     }
+                    // PDFManipulation
                     else if (op.id === "addPDFPage") {
                         if (prog.length !== 2) {
                             throw invalidLengthException('addPDFPage', 1);
@@ -687,8 +720,23 @@ function interp(prog, env) {
                                 type: ItemType.Unit,
                             };
                         }
-                        const rtn = argsMapped[argsMapped.length - 1];
-                        return rtn;
+                    }
+                    else if (op.id === "drawText") {
+                        if (prog.length !== 7) {
+                            throw invalidLengthException('drawText', 6);
+                        }
+                        else {
+                            const fontFamily = argsMapped[0].str;
+                            const textSize = argsMapped[1].int;
+                            const color = argsMapped[2].str;
+                            const x = argsMapped[3].flo;
+                            const y = argsMapped[4].flo;
+                            const text = argsMapped[5].str;
+                            drawText(pdfDoc.getPageCount() - 1, fontFamily, textSize, color, x, y, text);
+                            return {
+                                type: ItemType.Unit,
+                            };
+                        }
                     }
                     // procedures returning the last called command
                     else if (op.id === "begin") {
@@ -697,7 +745,7 @@ function interp(prog, env) {
                     }
                     // other named function call
                     else {
-                        const caller = interp(prog[0], env);
+                        const caller = await interp(prog[0], env);
                         const varArgs = caller.vars;
                         const varArgLen = varArgs.length;
                         const argsMappedLen = argsMapped.length;
@@ -736,10 +784,10 @@ function interp(prog, env) {
             // the caller which is a higher-function call
         }
         else {
-            const argsMapped = prog.slice(1).map((x) => {
+            const argsMapped = await Promise.all(prog.slice(1).map((x) => {
                 return interp(x, env);
-            });
-            const caller = interp(prog[0], env);
+            }));
+            const caller = await interp(prog[0], env);
             const varArgs = caller.vars;
             const varArgLen = varArgs.length;
             const argsMappedLen = argsMapped.length;
@@ -788,9 +836,9 @@ function interp(prog, env) {
         }
     }
 }
-function evaluate(expr) {
+async function evaluate(expr) {
     const input = (0, typescript_parsec_1.expectSingleResult)((0, typescript_parsec_1.expectEOF)(LISP.parse(tokenizer.parse(expr))));
-    const interped = interp(input, emptyEnv);
+    const interped = await interp(input, emptyEnv);
     return astToString(interped);
 }
 // evaluate(`(main '((text 12)) [ 快狐跳懶狗\\\\\\\[\\\]\\\(\\\)(italic "fox and dog") (bold [OK])])`)
@@ -798,6 +846,7 @@ function evaluate(expr) {
 // eval print loop
 const readline = require("node:readline");
 const node_process_1 = require("node:process");
+const child_process_1 = require("child_process");
 const rl = readline.createInterface({
     input: process.stdin,
     output: process.stdout,
@@ -805,7 +854,7 @@ const rl = readline.createInterface({
 async function run() {
     pdfDoc = await pdf_lib_1.PDFDocument.create();
     const prog = fs.readFileSync(filename, { encoding: 'utf8' });
-    console.log(evaluate(prog));
+    console.log(await evaluate(prog));
     const pdfBytes = await pdfDoc.save();
     fs.writeFileSync(filename + '.pdf', pdfBytes, 'binary');
     (0, node_process_1.exit)(0);
index cf96017e1f46356f5caadca48b19815f3c6bb7d5..2b266a30f63bede7e41ce4bf7540176bd4f94688 100644 (file)
@@ -1,5 +1,6 @@
 import * as fs from 'fs';
-import { PDFDocument } from 'pdf-lib'
+import { PDFDocument , RGB, rgb, StandardFonts} from 'pdf-lib';
+import fontkit from '@pdf-lib/fontkit';
 import { Token } from "typescript-parsec";
 import {
   buildLexer,
@@ -465,6 +466,48 @@ function cons(h: AST, t : List) : List {
     return rtnList;
 }
 
+/* PDF manipulation */
+async function drawText(pageIndex : number,
+                fontFamily : string,
+                textSize : number,
+                color : string,
+                x : number,
+                y : number,
+                text : string){
+  let currentPage = pdfDoc.getPages()[0];
+
+const fcMatch = await spawnSync('fc-match', ['--format=%{file}', fontFamily]);
+const path = fcMatch.stdout.toString();
+ pdfDoc.registerFontkit(fontkit);
+   const fontBytes = fs.readFileSync(path);
+   console.log("A2A",rgb(0,0,0));
+
+  const customFont = await pdfDoc.embedFont(fontBytes);
+  console.log("A3A",rgb(0,0,0));
+
+  const rgbColor = await hexColorToRGB(color);
+  console.log("A4A",rgb(0,0,0));
+
+  let a = await pdfDoc.getPage(0).drawText(text, {
+    x: x,
+    y: y,
+    size: textSize,
+    font: customFont,
+    color: rgbColor,
+  });
+  await pdfDoc.save();
+  
+}
+
+
+async function hexColorToRGB(hex: string): Promise<RGB>{
+  let rgbHex = /[#]?(\d{2})(\d{2})(\d{2})/.exec(hex);
+  let r = parseInt((rgbHex as RegExpExecArray)[1], 16)/256.0;
+  let g = parseInt((rgbHex as RegExpExecArray)[2], 16)/256.0;
+  let b = parseInt((rgbHex as RegExpExecArray)[3], 16)/256.0;
+  return rgb(r,g,b);
+}
+
 function listRef(l: List, i: ItemInt): AST {
   const realI = i.int;
   if (realI >= l.list.length || realI < 0){
@@ -509,7 +552,7 @@ function isClosure(x: any): x is Closure {
     return x.hasOwnProperty('type') && x.hasOwnProperty('vars');
 }
 
-function interp(prog: AST, env: Env): AST {
+async function interp(prog: AST, env: Env): Promise<AST> {
   if (Array.isArray(prog)) {
     if (!Array.isArray(prog[0])) {
       const op = prog[0];
@@ -572,7 +615,7 @@ function interp(prog: AST, env: Env): AST {
                         const vari = binding[0];
                         if (vari.hasOwnProperty("id")){
                             const variName = (vari as ItemId).id;
-                            const data = interp(binding[1], env);
+                            const data = await interp(binding[1], env);
                             if (op.id === "letrec"){
                               newEnv = extendEnv(newEnv, variName , true, data);
                             }else{
@@ -590,7 +633,7 @@ function interp(prog: AST, env: Env): AST {
             if (prog.length !== 4){
                 throw invalidLengthException('if', 3);
             }else{
-              const cond = interp(prog[1], env);
+              const cond = await interp(prog[1], env);
                 if (Array.isArray(cond)){
                     throw new Error("cond can't be reduced to a constant");
                 }else if (cond.type !== ItemType.Bool){
@@ -605,9 +648,9 @@ function interp(prog: AST, env: Env): AST {
         }
         else{
 
-        const argsMapped = prog.slice(1).map((x) => {
+        const argsMapped = await Promise.all( prog.slice(1).map(async (x) => {
           return interp(x, env);
-        });
+        }));
         // binary basic operator
         if (op.id === "+") {
           return interpBinary(add, argsMapped);
@@ -750,6 +793,7 @@ function interp(prog: AST, env: Env): AST {
             return {type:ItemType.Unit};
           }
         }
+        // PDFManipulation
         else if (op.id === "addPDFPage"){
           if (prog.length !== 2){
             throw invalidLengthException('addPDFPage', 1);
@@ -761,20 +805,41 @@ function interp(prog: AST, env: Env): AST {
               type:ItemType.Unit,
             }
           }
-
-            const rtn = argsMapped[argsMapped.length-1];
-            return rtn;
+        }
+        else if (op.id === "drawText"){
+          if (prog.length !== 7){
+            throw invalidLengthException('drawText', 6);
+          }else{
+            const fontFamily = (argsMapped[0] as ItemStr).str;
+            const textSize = (argsMapped[1] as ItemInt).int;
+            const color = (argsMapped[2] as ItemStr).str;
+            const x = (argsMapped[3] as ItemFlo).flo;
+            const y = (argsMapped[4] as ItemFlo).flo;
+            const text = (argsMapped[5] as ItemStr).str;
+            drawText(
+              pdfDoc.getPageCount()-1,
+              fontFamily,
+              textSize,
+              color,
+              x,
+              y,
+              text);
+            return {
+              type:ItemType.Unit,
+            }
+          }
         }
         // procedures returning the last called command
         else if (op.id === "begin"){
           const rtn = argsMapped[argsMapped.length-1];
           return rtn;
       }
+      
         
         // other named function call
         else {
 
-          const caller = interp(prog[0],env);
+          const caller = await interp(prog[0],env);
 
 
           const varArgs = ((caller as Closure).vars as ItemId[]);
@@ -816,10 +881,10 @@ function interp(prog: AST, env: Env): AST {
       }
     // the caller which is a higher-function call
     } else {
-      const argsMapped = prog.slice(1).map((x) => {
+      const argsMapped = await Promise.all(prog.slice(1).map((x) => {
         return interp(x, env);
-      });
-      const caller = interp(prog[0], env);
+      }));
+      const caller = await interp(prog[0], env);
 
       const varArgs = (caller as Closure).vars as ItemId[];
       const varArgLen = varArgs.length;
@@ -876,9 +941,9 @@ function interp(prog: AST, env: Env): AST {
   }
 }
 
-function evaluate(expr: string): string {
+async function evaluate(expr: string): Promise<string> {
   const input = expectSingleResult(expectEOF(LISP.parse(tokenizer.parse(expr))));
-  const interped = interp(input, emptyEnv);
+  const interped = await interp(input, emptyEnv);
   return astToString(interped);
 }
 
@@ -888,6 +953,7 @@ function evaluate(expr: string): string {
 // eval print loop
 import readline = require("node:readline");
 import { exit } from "node:process";
+import { spawnSync } from 'child_process';
 
 const rl = readline.createInterface({
   input: process.stdin,
@@ -901,7 +967,7 @@ async function run(){
 
   const prog = fs.readFileSync(filename, { encoding: 'utf8' });
 
-  console.log(evaluate(prog));
+  console.log(await evaluate(prog));
 
   const pdfBytes = await pdfDoc.save();
   fs.writeFileSync(filename+'.pdf', pdfBytes, 'binary');
index 637a51d268d6ea44dde71be50824d81d8fa70970..1d38465507d90193436906f29f30fee4803f2a0a 100644 (file)
--- a/text.lisp
+++ b/text.lisp
@@ -1,12 +1,39 @@
-(letrec ((
-    map (lambda (f l)
+(letrec (
+    (defaultFontFormat
+        '(("fontFamily" "Gentium")
+
+          ("color" "#000000")
+          ("size" 12)        
+        )
+    )
+    (map (lambda (f l)
         (if (!= l '())
             (cons (f (car l)) (map f (cdr l)))
-            '()))
-))
+            '())))
+    (emptyDict '())
+    (extendDict (lambda (dict var data) (cons (cons var (cons data '())) dict)))
+    (dictRef (lambda (dict key)
+        (if (= dict '()) false 
+            (if (= key (car (car dict))) (car (cdr (car dict))) (dictRef (cdr dict) key))
+        )))
+    )
+
 (begin
 (addPDFPage '())
+(drawText
+    (dictRef defaultFontFormat "fontFamily")
+    (dictRef defaultFontFormat "size")
+    (dictRef defaultFontFormat "color")
+    40.0
+    50.0
+    "blah"
+)
 (addPDFPage '())
-(map (lambda (x) (+ x 2)) '(8 9 10))
 
-))
\ No newline at end of file
+(map (lambda (x) (+ x 2)) '(8 9 10))
+(let ((dict emptyDict))
+    (let ((dictExtended 
+        (extendDict
+        (extendDict emptyDict 1 2)  2 4)))
+        (dictRef dictExtended 2)
+))))
\ No newline at end of file
index a64d82f335f66e819b6a9b348d8843bc1c7ccb57..08e08686487b894715567faaf5aac7d89e0e3554 100644 (file)
Binary files a/text.lisp.pdf and b/text.lisp.pdf differ