add putchar of the language
This commit is contained in:
parent
3359c79098
commit
64bd9bae26
7 changed files with 197 additions and 37 deletions
|
@ -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
|
17
package-lock.json
generated
17
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
77
src/index.js
77
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);
|
||||
|
|
98
src/index.ts
98
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<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');
|
||||
|
|
39
text.lisp
39
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))
|
||||
|
||||
))
|
||||
(map (lambda (x) (+ x 2)) '(8 9 10))
|
||||
(let ((dict emptyDict))
|
||||
(let ((dictExtended
|
||||
(extendDict
|
||||
(extendDict emptyDict 1 2) 2 4)))
|
||||
(dictRef dictExtended 2)
|
||||
))))
|
BIN
text.lisp.pdf
BIN
text.lisp.pdf
Binary file not shown.
Loading…
Reference in a new issue