add text measurement
This commit is contained in:
parent
64bd9bae26
commit
90fd2d0d5c
9 changed files with 393 additions and 63 deletions
BIN
3rdparty/harfbuzzjs/hb.wasm
vendored
Executable file
BIN
3rdparty/harfbuzzjs/hb.wasm
vendored
Executable file
Binary file not shown.
|
@ -14,4 +14,5 @@
|
|||
- [ ] close pdf
|
||||
- [v] add character
|
||||
- [ ] add path
|
||||
- [ ] basic typesetting format
|
||||
- [ ] basic typesetting format
|
||||
- [v] text measuring width in pt
|
11
package-lock.json
generated
11
package-lock.json
generated
|
@ -10,6 +10,7 @@
|
|||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"harfbuzzjs": "^0.3.5",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"typescript-parsec": "^0.3.4"
|
||||
},
|
||||
|
@ -1021,6 +1022,11 @@
|
|||
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/harfbuzzjs": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/harfbuzzjs/-/harfbuzzjs-0.3.5.tgz",
|
||||
"integrity": "sha512-SbNxmVAyhlUJTHdaxgK5S6Uqy4mXIu80Vl6KDn8d+ctPAF6W3DY2yehB4BwIC24I/Tk5HGLjaQkyny5gY0r41Q=="
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
|
@ -2464,6 +2470,11 @@
|
|||
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
|
||||
"dev": true
|
||||
},
|
||||
"harfbuzzjs": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/harfbuzzjs/-/harfbuzzjs-0.3.5.tgz",
|
||||
"integrity": "sha512-SbNxmVAyhlUJTHdaxgK5S6Uqy4mXIu80Vl6KDn8d+ctPAF6W3DY2yehB4BwIC24I/Tk5HGLjaQkyny5gY0r41Q=="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"harfbuzzjs": "^0.3.5",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"typescript-parsec": "^0.3.4"
|
||||
},
|
||||
|
|
147
src/index.js
147
src/index.js
|
@ -157,6 +157,43 @@ SINGLE.setPattern((0, typescript_parsec_2.alt)((0, typescript_parsec_2.apply)((0
|
|||
LISPS.setPattern((0, typescript_parsec_2.alt)((0, typescript_parsec_2.apply)((0, typescript_parsec_2.kmid)((0, typescript_parsec_2.seq)((0, typescript_parsec_2.str)("("), __), (0, typescript_parsec_2.rep_sc)(LISP), (0, typescript_parsec_2.str)(")")), applyList), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.str)("'"), (0, typescript_parsec_2.kmid)((0, typescript_parsec_2.seq)((0, typescript_parsec_2.str)("("), __), (0, typescript_parsec_2.rep_sc)(LISP), (0, typescript_parsec_2.str)(")"))), applyQuoted)));
|
||||
CON_STR_INNER.setPattern((0, typescript_parsec_2.alt)((0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Id), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Int), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Flo), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Str), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Other), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.SpaceNL), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.tok)(TokenKind.BSlash), (0, typescript_parsec_2.tok)(TokenKind.LParen)), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.tok)(TokenKind.BSlash), (0, typescript_parsec_2.tok)(TokenKind.RParen)), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.tok)(TokenKind.BSlash), (0, typescript_parsec_2.tok)(TokenKind.LBrack)), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.tok)(TokenKind.BSlash), (0, typescript_parsec_2.tok)(TokenKind.RBrack)), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.tok)(TokenKind.BSlash), (0, typescript_parsec_2.tok)(TokenKind.Apos)), tokenToStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.kright)((0, typescript_parsec_2.tok)(TokenKind.BSlash), (0, typescript_parsec_2.tok)(TokenKind.BSlash)), bSlashTokenToStr), LISPS));
|
||||
CON_STR.setPattern((0, typescript_parsec_2.apply)((0, typescript_parsec_2.kmid)((0, typescript_parsec_2.str)("["), (0, typescript_parsec_2.rep_sc)(CON_STR_INNER), (0, typescript_parsec_2.str)("]")), applyStrings));
|
||||
/**
|
||||
* measuer the width of a test in px
|
||||
* @param inputString the string to be measured
|
||||
* @param fontFamily font family name
|
||||
* @param fontSizePt font size in pt
|
||||
* @returns the width in px
|
||||
*/
|
||||
async function measureWidthPx(inputString, fontFamily, fontSizePt) {
|
||||
return await WebAssembly.instantiate(fs.readFileSync(__dirname + "/../3rdparty/harfbuzzjs/hb.wasm"))
|
||||
.then(function (wsm) {
|
||||
var hb = require('harfbuzzjs/hbjs');
|
||||
hb = hb(wsm.instance);
|
||||
let fontName = (0, child_process_1.spawnSync)('fc-match', ['--format=%{file}', fontFamily]);
|
||||
const fontPath = fontName.stdout.toString();
|
||||
let fontdata = fs.readFileSync(fontPath);
|
||||
var blob = hb.createBlob(fontdata); // Load the font data into something Harfbuzz can use
|
||||
var face = hb.createFace(blob, 0); // Select the first font in the file (there's normally only one!)
|
||||
var font = hb.createFont(face); // Create a Harfbuzz font object from the face
|
||||
font.setScale(fontSizePt * 4 / 3 * 1000, fontSizePt * 4 / 3 * 1000);
|
||||
var buffer = hb.createBuffer(); // Make a buffer to hold some text
|
||||
buffer.addText(inputString); // Fill it with some stuff
|
||||
buffer.guessSegmentProperties(); // Set script, language and direction
|
||||
hb.shape(font, buffer); // Shape the text, determining glyph IDs and positions
|
||||
var output = buffer.json();
|
||||
var totalX = 0;
|
||||
for (var glyph of output) {
|
||||
var xAdvance = glyph.ax;
|
||||
totalX += xAdvance;
|
||||
}
|
||||
// Release memory
|
||||
buffer.destroy();
|
||||
font.destroy();
|
||||
face.destroy();
|
||||
blob.destroy();
|
||||
return totalX / 1000;
|
||||
});
|
||||
}
|
||||
function astToString(ast, isInQuoted) {
|
||||
if (Array.isArray(ast)) {
|
||||
const ast2 = ast.map((x) => astToString(x, isInQuoted));
|
||||
|
@ -369,11 +406,8 @@ async function drawText(pageIndex, fontFamily, textSize, color, x, y, text) {
|
|||
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,
|
||||
|
@ -384,10 +418,10 @@ async function drawText(pageIndex, fontFamily, textSize, color, x, y, text) {
|
|||
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;
|
||||
let rgbHex = /[#]?([\dA-Fa-f]{2})([\dA-Fa-f]{2})([\dA-Fa-f]{2})/.exec(hex);
|
||||
let r = parseInt(rgbHex[1], 16) / 255.0;
|
||||
let g = parseInt(rgbHex[2], 16) / 255.0;
|
||||
let b = parseInt(rgbHex[3], 16) / 255.0;
|
||||
return (0, pdf_lib_1.rgb)(r, g, b);
|
||||
}
|
||||
function listRef(l, i) {
|
||||
|
@ -463,6 +497,24 @@ async function interp(prog, env) {
|
|||
};
|
||||
}
|
||||
}
|
||||
// define manipulation
|
||||
if (op.id === "define") {
|
||||
const vari = prog[1];
|
||||
const data = await interp(prog[2], env);
|
||||
if (prog.length !== 3) {
|
||||
throw invalidLengthException('define', 2);
|
||||
}
|
||||
else if (!isItem(vari) || !isItem(data)) {
|
||||
throw new Error("the type of replace and variable should be the same.");
|
||||
}
|
||||
else if (env[vari.id] !== undefined) {
|
||||
throw new Error("variable can't be duplicated defined.");
|
||||
}
|
||||
else {
|
||||
env = extendEnv(env, vari.id, true, data);
|
||||
return { type: ItemType.Unit };
|
||||
}
|
||||
}
|
||||
/** let function */
|
||||
else if (op.id === "let" || op.id === "letrec") {
|
||||
const bindings = prog[1];
|
||||
|
@ -531,9 +583,13 @@ async function interp(prog, env) {
|
|||
}
|
||||
}
|
||||
else {
|
||||
const argsMapped = await Promise.all(prog.slice(1).map(async (x) => {
|
||||
return interp(x, env);
|
||||
}));
|
||||
let argsMapped = [];
|
||||
for (var i = 1; i < prog.length; i++) {
|
||||
argsMapped.push(await interp(prog[i], env));
|
||||
}
|
||||
/* 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);
|
||||
|
@ -604,6 +660,73 @@ async function interp(prog, env) {
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (op.id === "and") {
|
||||
if (prog.length !== 3) {
|
||||
throw invalidLengthException('and', 2);
|
||||
}
|
||||
else if (!argsMapped[0].hasOwnProperty('type') || argsMapped[0].type !== ItemType.Bool
|
||||
|| !argsMapped[1].hasOwnProperty('type') || argsMapped[1].type !== ItemType.Bool) {
|
||||
throw new Error("the arg of 'and' is not valid boolean value");
|
||||
}
|
||||
else {
|
||||
let ret = {
|
||||
type: ItemType.Bool,
|
||||
bool: argsMapped[0].bool && argsMapped[1].bool
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else if (op.id === "or") {
|
||||
if (prog.length !== 3) {
|
||||
throw invalidLengthException('or', 2);
|
||||
}
|
||||
else if (!argsMapped[0].hasOwnProperty('type') || argsMapped[0].type !== ItemType.Bool
|
||||
|| !argsMapped[1].hasOwnProperty('type') || argsMapped[1].type !== ItemType.Bool) {
|
||||
throw new Error("the arg of 'or' is not valid boolean value");
|
||||
}
|
||||
else {
|
||||
let ret = {
|
||||
type: ItemType.Bool,
|
||||
bool: argsMapped[0].bool || argsMapped[1].bool
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
// measuring
|
||||
else if (op.id === "measureWidthPx") {
|
||||
if (prog.length !== 4) {
|
||||
throw invalidLengthException('measureWidthPx', 3);
|
||||
}
|
||||
else {
|
||||
let text = argsMapped[0].str;
|
||||
let fontfamily = argsMapped[1].str;
|
||||
let sizePt = argsMapped[2].flo;
|
||||
let returnValue = await measureWidthPx(text, fontfamily, sizePt);
|
||||
return {
|
||||
type: ItemType.Flo,
|
||||
flo: returnValue
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (op.id === "isList") {
|
||||
const arg = argsMapped[0];
|
||||
if (prog.length !== 2) {
|
||||
throw invalidLengthException('isList', 1);
|
||||
}
|
||||
else if (arg.type === ItemType.Ls) {
|
||||
let a = {
|
||||
type: ItemType.Bool,
|
||||
bool: true,
|
||||
};
|
||||
return a;
|
||||
}
|
||||
else {
|
||||
return {
|
||||
type: ItemType.Bool,
|
||||
bool: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (op.id === "car") {
|
||||
const arg = argsMapped[0];
|
||||
if (prog.length !== 2) {
|
||||
|
@ -631,7 +754,7 @@ async function interp(prog, env) {
|
|||
else if (op.id === "cons") {
|
||||
const arg = argsMapped;
|
||||
if (prog.length !== 3) {
|
||||
throw invalidLengthException('cdr', 2);
|
||||
throw invalidLengthException('cons', 2);
|
||||
}
|
||||
else if (!arg[1].hasOwnProperty('type') || arg[1].type !== ItemType.Ls) {
|
||||
throw new Error("the 2nd arg of 'cons' is not a list.");
|
||||
|
@ -693,7 +816,7 @@ async function interp(prog, env) {
|
|||
// set manipulations
|
||||
else if (op.id === "set!") {
|
||||
const vari = prog[1];
|
||||
const replacer = prog[2];
|
||||
const replacer = await interp(prog[2], env);
|
||||
if (prog.length !== 3) {
|
||||
throw invalidLengthException('set!', 2);
|
||||
}
|
||||
|
|
158
src/index.ts
158
src/index.ts
|
@ -20,7 +20,7 @@ import {
|
|||
tok,
|
||||
opt,
|
||||
} from "typescript-parsec";
|
||||
|
||||
import {inspect} from "node:util";
|
||||
|
||||
|
||||
/** input lisp file */
|
||||
|
@ -265,6 +265,55 @@ CON_STR.setPattern(
|
|||
apply(kmid(str("["), rep_sc(CON_STR_INNER), str("]")), applyStrings)
|
||||
);
|
||||
|
||||
/**
|
||||
* measuer the width of a test in px
|
||||
* @param inputString the string to be measured
|
||||
* @param fontFamily font family name
|
||||
* @param fontSizePt font size in pt
|
||||
* @returns the width in px
|
||||
*/
|
||||
async function measureWidthPx(inputString: string, fontFamily : string, fontSizePt: number): Promise<number>{
|
||||
return await WebAssembly.instantiate(fs.readFileSync(__dirname+"/../3rdparty/harfbuzzjs/hb.wasm"))
|
||||
.then(function (wsm) {
|
||||
var hb = require('harfbuzzjs/hbjs');
|
||||
hb = hb(wsm.instance);
|
||||
|
||||
let fontName = spawnSync('fc-match', ['--format=%{file}', fontFamily]);
|
||||
const fontPath = fontName.stdout.toString();
|
||||
let fontdata = fs.readFileSync(fontPath);
|
||||
|
||||
|
||||
var blob = hb.createBlob(fontdata); // Load the font data into something Harfbuzz can use
|
||||
var face = hb.createFace(blob, 0); // Select the first font in the file (there's normally only one!)
|
||||
var font = hb.createFont(face); // Create a Harfbuzz font object from the face
|
||||
font.setScale(fontSizePt * 4/3* 1000 , fontSizePt*4/3 * 1000 );
|
||||
var buffer = hb.createBuffer(); // Make a buffer to hold some text
|
||||
buffer.addText(inputString); // Fill it with some stuff
|
||||
buffer.guessSegmentProperties(); // Set script, language and direction
|
||||
hb.shape(font, buffer); // Shape the text, determining glyph IDs and positions
|
||||
var output : Array<{g : number,
|
||||
ax : number,
|
||||
dx : number,
|
||||
dy : number}> = buffer.json();
|
||||
|
||||
var totalX = 0;
|
||||
for (var glyph of output) {
|
||||
var xAdvance = glyph.ax;
|
||||
totalX += xAdvance;
|
||||
|
||||
}
|
||||
|
||||
// Release memory
|
||||
buffer.destroy();
|
||||
font.destroy();
|
||||
face.destroy();
|
||||
blob.destroy();
|
||||
|
||||
return totalX / 1000;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function astToString(ast: AST, isInQuoted? : boolean): string {
|
||||
if (Array.isArray(ast)) {
|
||||
const ast2 = ast.map((x)=>astToString(x, isInQuoted));
|
||||
|
@ -480,13 +529,10 @@ 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,
|
||||
|
@ -501,10 +547,10 @@ const path = fcMatch.stdout.toString();
|
|||
|
||||
|
||||
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;
|
||||
let rgbHex = /[#]?([\dA-Fa-f]{2})([\dA-Fa-f]{2})([\dA-Fa-f]{2})/.exec(hex);
|
||||
let r = parseInt((rgbHex as RegExpExecArray)[1], 16)/255.0;
|
||||
let g = parseInt((rgbHex as RegExpExecArray)[2], 16)/255.0;
|
||||
let b = parseInt((rgbHex as RegExpExecArray)[3], 16)/255.0;
|
||||
return rgb(r,g,b);
|
||||
}
|
||||
|
||||
|
@ -587,6 +633,22 @@ async function interp(prog: AST, env: Env): Promise<AST> {
|
|||
}
|
||||
}
|
||||
}
|
||||
// define manipulation
|
||||
if (op.id === "define") {
|
||||
const vari : ItemId = prog[1] as ItemId;
|
||||
const data = await interp(prog[2], env);
|
||||
if (prog.length !== 3){
|
||||
throw invalidLengthException('define', 2);
|
||||
}else if (!isItem(vari) || !isItem(data)){
|
||||
throw new Error("the type of replace and variable should be the same.")
|
||||
}else if(env[vari.id] !== undefined){
|
||||
throw new Error("variable can't be duplicated defined.")
|
||||
}else {
|
||||
env = extendEnv(env, vari.id, true, data);
|
||||
return {type:ItemType.Unit};
|
||||
}
|
||||
}
|
||||
|
||||
/** let function */
|
||||
else if (op.id === "let" || op.id === "letrec"){
|
||||
const bindings = prog[1];
|
||||
|
@ -647,10 +709,16 @@ async function interp(prog: AST, env: Env): Promise<AST> {
|
|||
}
|
||||
}
|
||||
else{
|
||||
let argsMapped = [];
|
||||
for (var i=1;i<prog.length;i++){
|
||||
argsMapped.push(await interp(prog[i], env));
|
||||
}
|
||||
|
||||
const argsMapped = await Promise.all( prog.slice(1).map(async (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);
|
||||
|
@ -706,7 +774,69 @@ async function interp(prog: AST, env: Env): Promise<AST> {
|
|||
};
|
||||
}
|
||||
}
|
||||
} else if (op.id === "car") {
|
||||
}else if (op.id === "and"){
|
||||
if (prog.length !== 3){
|
||||
throw invalidLengthException('and', 2);
|
||||
}else if (!argsMapped[0].hasOwnProperty('type') || (argsMapped[0] as Item).type !== ItemType.Bool
|
||||
|| !argsMapped[1].hasOwnProperty('type') || (argsMapped[1] as Item).type !== ItemType.Bool){
|
||||
throw new Error("the arg of 'and' is not valid boolean value")
|
||||
}else{
|
||||
let ret = {
|
||||
type : ItemType.Bool,
|
||||
bool: (argsMapped[0] as ItemBool).bool && (argsMapped[1] as ItemBool).bool
|
||||
};
|
||||
return ret as Item;
|
||||
}
|
||||
}else if (op.id === "or"){
|
||||
if (prog.length !== 3){
|
||||
throw invalidLengthException('or', 2);
|
||||
}else if (!argsMapped[0].hasOwnProperty('type') || (argsMapped[0] as Item).type !== ItemType.Bool
|
||||
|| !argsMapped[1].hasOwnProperty('type') || (argsMapped[1] as Item).type !== ItemType.Bool){
|
||||
throw new Error("the arg of 'or' is not valid boolean value")
|
||||
}else{
|
||||
let ret = {
|
||||
type : ItemType.Bool,
|
||||
bool: (argsMapped[0] as ItemBool).bool || (argsMapped[1] as ItemBool).bool
|
||||
};
|
||||
return ret as Item;
|
||||
}
|
||||
}
|
||||
|
||||
// measuring
|
||||
else if (op.id === "measureWidthPx"){
|
||||
if (prog.length !== 4){
|
||||
throw invalidLengthException('measureWidthPx', 3);
|
||||
}else{
|
||||
let text = (argsMapped[0] as ItemStr).str;
|
||||
let fontfamily = (argsMapped[1] as ItemStr).str;
|
||||
let sizePt = (argsMapped[2] as ItemFlo).flo;
|
||||
let returnValue = await measureWidthPx(text, fontfamily, sizePt);
|
||||
return {
|
||||
type: ItemType.Flo,
|
||||
flo: returnValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (op.id === "isList"){
|
||||
const arg = argsMapped[0];
|
||||
if (prog.length !== 2){
|
||||
throw invalidLengthException('isList', 1);
|
||||
}else if ((arg as Item).type === ItemType.Ls){
|
||||
|
||||
let a = {
|
||||
type: ItemType.Bool,
|
||||
bool: true as boolean,
|
||||
};
|
||||
return a as Item;
|
||||
}else{
|
||||
return {
|
||||
type: ItemType.Bool,
|
||||
bool: false as boolean,
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (op.id === "car") {
|
||||
const arg = argsMapped[0];
|
||||
if (prog.length !== 2){
|
||||
throw invalidLengthException('car', 1);
|
||||
|
@ -728,7 +858,7 @@ async function interp(prog: AST, env: Env): Promise<AST> {
|
|||
}else if (op.id === "cons") {
|
||||
const arg = argsMapped;
|
||||
if (prog.length !== 3){
|
||||
throw invalidLengthException('cdr', 2);
|
||||
throw invalidLengthException('cons', 2);
|
||||
}else if (!arg[1].hasOwnProperty('type') || (arg[1] as Item).type !== ItemType.Ls){
|
||||
throw new Error("the 2nd arg of 'cons' is not a list.")
|
||||
}else{
|
||||
|
@ -778,11 +908,10 @@ async function interp(prog: AST, env: Env): Promise<AST> {
|
|||
return subString(str, i as ItemInt, prog[3] as ItemInt);}
|
||||
}
|
||||
}
|
||||
|
||||
// set manipulations
|
||||
else if (op.id === "set!") {
|
||||
const vari : ItemId = prog[1] as ItemId;
|
||||
const replacer = prog[2];
|
||||
const replacer = await interp(prog[2], env);
|
||||
if (prog.length !== 3){
|
||||
throw invalidLengthException('set!', 2);
|
||||
}else if (!isItem(vari) || !isItem(replacer)
|
||||
|
@ -968,6 +1097,7 @@ async function run(){
|
|||
const prog = fs.readFileSync(filename, { encoding: 'utf8' });
|
||||
|
||||
console.log(await evaluate(prog));
|
||||
|
||||
|
||||
const pdfBytes = await pdfDoc.save();
|
||||
fs.writeFileSync(filename+'.pdf', pdfBytes, 'binary');
|
||||
|
|
98
text.lisp
98
text.lisp
|
@ -1,39 +1,65 @@
|
|||
(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"
|
||||
(define defaultFontFormat
|
||||
'(("fontFamily" "Gentium")
|
||||
("color" "#ff0000")
|
||||
("fontSize" 12)
|
||||
)
|
||||
)
|
||||
(addPDFPage '())
|
||||
|
||||
(map (lambda (x) (+ x 2)) '(8 9 10))
|
||||
(let ((dict emptyDict))
|
||||
(let ((dictExtended
|
||||
(extendDict
|
||||
(extendDict emptyDict 1 2) 2 4)))
|
||||
(dictRef dictExtended 2)
|
||||
))))
|
||||
|
||||
(define map (lambda (f l)
|
||||
(if (!= l '())
|
||||
(cons (f (car l)) (map f (cdr l)))
|
||||
'())))
|
||||
(define emptyDict '())
|
||||
(define extendDict (lambda (dict var data) (cons (cons var (cons data '())) dict)))
|
||||
(define dictRef (lambda (dict key)
|
||||
(if (= dict '()) false
|
||||
(if (= key (car (car dict))) (car (cdr (car dict))) (dictRef (cdr dict) key))
|
||||
)))
|
||||
(define setDictItem (lambda (dict key data)
|
||||
(if (= (dictRef dict key) false)
|
||||
false
|
||||
(setDictItemAux dict '() key data)
|
||||
)))
|
||||
|
||||
(define setDictItemAux (lambda (oldDict newDict key data)
|
||||
(if (= oldDict '()) newDict
|
||||
(if (= (car(car oldDict)) key)
|
||||
(setDictItemAux (cdr oldDict) (cons (cons key (cons data '())) newDict) key data)
|
||||
(setDictItemAux (cdr oldDict) (cons (car oldDict) newDict) key data)
|
||||
))))
|
||||
|
||||
(addPDFPage '())
|
||||
(addPDFPage '())
|
||||
(define text2boxAux2 (lambda (format text)
|
||||
(if (isList text)
|
||||
(if (= (listRef text 0) "fontSize")
|
||||
(let ((newFormat (setDictItem format "fontSize" (listRef text 1)))) (text2boxAux1 newFormat (listRef text 2)))
|
||||
text)
|
||||
(cons format (cons text '())))
|
||||
))
|
||||
|
||||
(define text2boxAux1 (lambda (format txt)
|
||||
(if (isList txt)
|
||||
(map (lambda (x) (text2boxAux2 format x)) txt)
|
||||
(cons format (cons txt '()))
|
||||
)))
|
||||
|
||||
(define text2box (lambda (txt) (text2boxAux1 defaultFontFormat txt)))
|
||||
|
||||
|
||||
|
||||
|
||||
(drawText
|
||||
(dictRef defaultFontFormat "fontFamily")
|
||||
(dictRef defaultFontFormat "fontSize")
|
||||
(dictRef defaultFontFormat "color")
|
||||
40.0
|
||||
50.0
|
||||
"blah"
|
||||
)
|
||||
(define text '("abracabra" ("fontSize" 18 "貓") "foo"))
|
||||
(text2box text)
|
||||
(measureWidthPx "1314abc" "Gentium" 12.0)
|
||||
)
|
BIN
text.lisp.pdf
BIN
text.lisp.pdf
Binary file not shown.
38
text2.lisp
Normal file
38
text2.lisp
Normal file
|
@ -0,0 +1,38 @@
|
|||
(letrec (
|
||||
(defaultFontFormat
|
||||
'(("fontFamily" "Gentium")
|
||||
("color" "#ff0000")
|
||||
("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
|
||||
"Gentium"
|
||||
"#ff0000"
|
||||
"12"
|
||||
40.0
|
||||
50.0
|
||||
"blah"
|
||||
)
|
||||
(addPDFPage '())
|
||||
|
||||
(map (lambda (x) (+ x 2)) '(8 9 10))
|
||||
(let ((dict emptyDict))
|
||||
(let ((dictExtended
|
||||
(extendDict
|
||||
(extendDict emptyDict 1 2) 2 4)))
|
||||
(dictRef dictExtended 2)
|
||||
))))
|
Loading…
Reference in a new issue