From: Tan Kian-ting Date: Thu, 11 Apr 2024 14:47:42 +0000 (+0800) Subject: add putchar of the language X-Git-Url: https://git.kianting.info/?a=commitdiff_plain;h=64bd9bae260906e2caac76fb1d7b9a724a496522;p=anotherTypesetter add putchar of the language --- diff --git a/README.md b/README.md index 217e3fa..6801888 100644 --- 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 diff --git a/package-lock.json b/package-lock.json index 09be7cc..5498402 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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" }, @@ -158,6 +159,14 @@ "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", @@ -1827,6 +1836,14 @@ "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", diff --git a/package.json b/package.json index 0f9f047..f01ade4 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/src/index.js b/src/index.js index 4c125a1..3b0d718 100644 --- a/src/index.js +++ b/src/index.js @@ -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); diff --git a/src/index.ts b/src/index.ts index cf96017..2b266a3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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{ + 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 { 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 { 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'); diff --git a/text.lisp b/text.lisp index 637a51d..1d38465 100644 --- 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 diff --git a/text.lisp.pdf b/text.lisp.pdf index a64d82f..08e0868 100644 Binary files a/text.lisp.pdf and b/text.lisp.pdf differ