加上量字功能

This commit is contained in:
Tan, Kian-ting 2024-04-21 19:09:52 +08:00
parent 90fd2d0d5c
commit 0ccf7a2fa2
4 changed files with 143 additions and 42 deletions

View file

@ -44,7 +44,8 @@ var TokenKind;
TokenKind[TokenKind["SpaceNL"] = 9] = "SpaceNL";
TokenKind[TokenKind["BSlash"] = 10] = "BSlash";
TokenKind[TokenKind["Apos"] = 11] = "Apos";
TokenKind[TokenKind["Other"] = 12] = "Other";
TokenKind[TokenKind["Cmt"] = 12] = "Cmt";
TokenKind[TokenKind["Other"] = 13] = "Other";
})(TokenKind || (TokenKind = {}));
var ItemType;
(function (ItemType) {
@ -71,16 +72,16 @@ const tokenizer = (0, typescript_parsec_1.buildLexer)([
[true, /^'/g, TokenKind.Apos],
[true, /^(\s|\t|\r?\n)+/g, TokenKind.SpaceNL],
[true, /^\\/g, TokenKind.BSlash],
[false, /^\/\/[^\/\r\n]+(\r?\n)/g, TokenKind.Cmt],
[true, /^([^+\-*/a-zA-Z_0-9\[\]()'\s\t\r\n\\]+)/g, TokenKind.Other],
]);
/*
* ## BNF
LISP = UNIT | LISPS | CON_STR
LISP = SINGLE | LISPS | CON_STR
LISPS = "(" LISP ")" | "'" "(" LISP ")"
SINGLE = "{" CONSTR_INNR | LISP "}"
UNIT = INT | NUMBER | STRING | ID
CONSTR = "[" (CONSTR_INNER "]"
CONSTR_INNER = ([^\\\[\][]] | [\\][{}\[\]]) | LISPS)*
SINGLE = INT | NUMBER | STRING | ID
CON_STR = "[" CONSTR_INNER "]"
CONSTR_INNER = ([^\\\[\]{}] | [\\][{}\[\]]) | LISPS)*
*/
const SINGLE = (0, typescript_parsec_1.rule)();
const LISPS = (0, typescript_parsec_1.rule)();
@ -151,7 +152,7 @@ function applyStrings(value) {
return merged;
}
/** for convinence to omit the spaces and newlines */
const __ = (0, typescript_parsec_2.opt)((0, typescript_parsec_2.tok)(TokenKind.SpaceNL));
const __ = (0, typescript_parsec_2.rep_sc)((0, typescript_parsec_2.tok)(TokenKind.SpaceNL));
LISP.setPattern((0, typescript_parsec_2.alt)((0, typescript_parsec_2.kleft)(SINGLE, __), (0, typescript_parsec_2.kleft)(LISPS, __), (0, typescript_parsec_2.kleft)(CON_STR, __)));
SINGLE.setPattern((0, typescript_parsec_2.alt)((0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Id), applyId), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Int), applyInt), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Flo), applyFlo), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Str), applyStr), (0, typescript_parsec_2.apply)((0, typescript_parsec_2.tok)(TokenKind.Bool), applyBool)));
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)));
@ -175,7 +176,7 @@ async function measureWidthPx(inputString, fontFamily, fontSizePt) {
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);
font.setScale(fontSizePt * 1000, fontSizePt * 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
@ -401,21 +402,21 @@ function cons(h, t) {
}
/* PDF manipulation */
async function drawText(pageIndex, fontFamily, textSize, color, x, y, text) {
let currentPage = pdfDoc.getPages()[0];
let pdfPages = pdfDoc.getPages();
let currentPage = pdfPages[pageIndex];
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);
const customFont = await pdfDoc.embedFont(fontBytes);
const rgbColor = await hexColorToRGB(color);
let a = await pdfDoc.getPage(0).drawText(text, {
let _ = await currentPage.drawText(text, {
x: x,
y: y,
size: textSize,
font: customFont,
color: rgbColor,
});
await pdfDoc.save();
}
async function hexColorToRGB(hex) {
let rgbHex = /[#]?([\dA-Fa-f]{2})([\dA-Fa-f]{2})([\dA-Fa-f]{2})/.exec(hex);
@ -825,7 +826,7 @@ async function interp(prog, env) {
throw new Error("the type of replace and variable should be the same.");
}
else {
env[vari.id][0].value = prog[2];
env[vari.id][0].value = replacer;
return { type: ItemType.Unit };
}
}
@ -850,7 +851,7 @@ async function interp(prog, env) {
}
else {
const fontFamily = argsMapped[0].str;
const textSize = argsMapped[1].int;
const textSize = argsMapped[1].flo;
const color = argsMapped[2].str;
const x = argsMapped[3].flo;
const y = argsMapped[4].flo;
@ -861,6 +862,26 @@ async function interp(prog, env) {
};
}
}
// print to the console
else if (op.id === "print") {
if (prog.length !== 2) {
throw invalidLengthException('print', 1);
}
else {
let data = argsMapped[0];
let dataString;
if (data.hasOwnProperty("list")) {
dataString = astToString(data, true);
}
else {
dataString = astToString(data, false);
}
console.log(dataString);
return {
type: ItemType.Unit,
};
}
}
// procedures returning the last called command
else if (op.id === "begin") {
const rtn = argsMapped[argsMapped.length - 1];

View file

@ -39,7 +39,8 @@ enum TokenKind {
RBrack,
SpaceNL,
BSlash,
Apos,
Apos, // Apostrophe
Cmt, // comment
Other,
}
@ -124,17 +125,17 @@ const tokenizer = buildLexer([
[true, /^'/g, TokenKind.Apos],
[true, /^(\s|\t|\r?\n)+/g, TokenKind.SpaceNL],
[true, /^\\/g, TokenKind.BSlash],
[false, /^\/\/[^\/\r\n]+(\r?\n)/g, TokenKind.Cmt], // omit the comment
[true, /^([^+\-*/a-zA-Z_0-9\[\]()'\s\t\r\n\\]+)/g, TokenKind.Other],
]);
/*
* ## BNF
LISP = UNIT | LISPS | CON_STR
LISP = SINGLE | LISPS | CON_STR
LISPS = "(" LISP ")" | "'" "(" LISP ")"
SINGLE = "{" CONSTR_INNR | LISP "}"
UNIT = INT | NUMBER | STRING | ID
CONSTR = "[" (CONSTR_INNER "]"
CONSTR_INNER = ([^\\\[\][]] | [\\][{}\[\]]) | LISPS)*
SINGLE = INT | NUMBER | STRING | ID
CON_STR = "[" CONSTR_INNER "]"
CONSTR_INNER = ([^\\\[\]{}] | [\\][{}\[\]]) | LISPS)*
*/
const SINGLE = rule<TokenKind, AST>();
@ -216,7 +217,7 @@ function applyStrings(value: AST[]): AST {
}
/** for convinence to omit the spaces and newlines */
const __ = opt(tok(TokenKind.SpaceNL));
const __ = rep_sc(tok(TokenKind.SpaceNL));
LISP.setPattern(alt(kleft(SINGLE, __), kleft(LISPS, __), kleft(CON_STR, __)));
@ -286,7 +287,7 @@ async function measureWidthPx(inputString: string, fontFamily : string, fontSize
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 );
font.setScale(fontSizePt * 1000 , fontSizePt * 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
@ -309,7 +310,7 @@ async function measureWidthPx(inputString: string, fontFamily : string, fontSize
face.destroy();
blob.destroy();
return totalX / 1000;
return totalX / 1000;
});
}
@ -523,7 +524,8 @@ async function drawText(pageIndex : number,
x : number,
y : number,
text : string){
let currentPage = pdfDoc.getPages()[0];
let pdfPages = pdfDoc.getPages();
let currentPage = pdfPages[pageIndex];
const fcMatch = await spawnSync('fc-match', ['--format=%{file}', fontFamily]);
const path = fcMatch.stdout.toString();
@ -534,14 +536,14 @@ const path = fcMatch.stdout.toString();
const rgbColor = await hexColorToRGB(color);
let a = await pdfDoc.getPage(0).drawText(text, {
let _ = await currentPage.drawText(text, {
x: x,
y: y,
size: textSize,
font: customFont,
color: rgbColor,
});
await pdfDoc.save();
}
@ -918,7 +920,7 @@ async function interp(prog: AST, env: Env): Promise<AST> {
|| (env[vari.id][0].value as Item).type != replacer.type ){
throw new Error("the type of replace and variable should be the same.")
}else{
env[vari.id][0].value = prog[2];
env[vari.id][0].value = replacer;
return {type:ItemType.Unit};
}
}
@ -940,7 +942,7 @@ async function interp(prog: AST, env: Env): Promise<AST> {
throw invalidLengthException('drawText', 6);
}else{
const fontFamily = (argsMapped[0] as ItemStr).str;
const textSize = (argsMapped[1] as ItemInt).int;
const textSize = (argsMapped[1] as ItemFlo).flo;
const color = (argsMapped[2] as ItemStr).str;
const x = (argsMapped[3] as ItemFlo).flo;
const y = (argsMapped[4] as ItemFlo).flo;
@ -958,6 +960,24 @@ async function interp(prog: AST, env: Env): Promise<AST> {
}
}
}
// print to the console
else if (op.id === "print"){
if (prog.length !== 2){
throw invalidLengthException('print', 1);
}else{
let data = argsMapped[0];
let dataString;
if (data.hasOwnProperty("list")){
dataString = astToString(data, true);
}else{
dataString = astToString(data, false);
}
console.log(dataString);
return {
type:ItemType.Unit,
}
}
}
// procedures returning the last called command
else if (op.id === "begin"){
const rtn = argsMapped[argsMapped.length-1];

View file

@ -1,8 +1,8 @@
(begin
(define defaultFontFormat
'(("fontFamily" "Gentium")
'(("fontFamily" "FreeSerif")
("color" "#ff0000")
("fontSize" 12)
("fontSize" 12.0)
)
)
@ -23,6 +23,13 @@
(setDictItemAux dict '() key data)
)))
(define addDictItem (lambda (dict key data)
(if (= (dictRef dict key) false)
(extendDict dict key data)
dict
)
))
(define setDictItemAux (lambda (oldDict newDict key data)
(if (= oldDict '()) newDict
(if (= (car(car oldDict)) key)
@ -30,36 +37,89 @@
(setDictItemAux (cdr oldDict) (cons (car oldDict) newDict) key data)
))))
(addPDFPage '())
(addPDFPage '())
(define text2boxAux2 (lambda (format text)
(if (isList text)
// set font size
(if (= (listRef text 0) "fontSize")
(let ((newFormat (setDictItem format "fontSize" (listRef text 1)))) (text2boxAux1 newFormat (listRef text 2)))
text)
(cons format (cons text '())))
(cons (cons "content" (cons text '())) format)
)
))
(define text2boxAux1 (lambda (format txt)
(if (isList txt)
(map (lambda (x) (text2boxAux2 format x)) txt)
(cons format (cons txt '()))
(cons (cons "content" (cons txt '())) format)
)))
(define text2box (lambda (txt) (text2boxAux1 defaultFontFormat txt)))
// (measureWidthPx "1314abc" "Gentium" 12.0)
(define measureBoxItem (lambda (boxList) (map measureBoxItemAux boxList)))
(define measureBoxItemAux (lambda (boxDict)
(let
((fontFamily (dictRef boxDict "fontFamily"))
(fontSize (dictRef boxDict "fontSize"))
(content (dictRef boxDict "content")))
(let
((measuredWidthPx (measureWidthPx content fontFamily fontSize)))
(addDictItem boxDict "widthPx" measuredWidthPx)
)
)))
(define text2box (lambda (txt)
(let ((tmp1 (text2boxAux1 defaultFontFormat txt)))
(let ((res (measureBoxItem tmp1))) res)
)))
(drawText
(dictRef defaultFontFormat "fontFamily")
(dictRef defaultFontFormat "fontSize")
(dictRef defaultFontFormat "color")
40.0
50.0
"blah"
//(drawText
//(dictRef defaultFontFormat "fontFamily")
//(dictRef defaultFontFormat "fontSize")
//(dictRef defaultFontFormat "color")
//40.0
//50.0
//"blah"
//)
(define x 0.0)
(define text '("abracabra" ("fontSize" 18.0 "cat") "foo"))
(define putOnChar (lambda (boxDict x)
(let
((content (dictRef boxDict "content"))
(fontFamily (dictRef boxDict "fontFamily"))
(fontSize (dictRef boxDict "fontSize"))
(color (dictRef boxDict "color"))
(widthPx (dictRef boxDict "widthPx"))
)
(begin
(drawText fontFamily fontSize color x 100.0 content)
(+ x widthPx)) // return an updated x
)))
(define putOnChars (lambda (ls x)
(if (= ls '())
'()
(let
((newX (putOnChar (car ls) x)))
(putOnChars (cdr ls) newX))
))
)
(define text '("abracabra" ("fontSize" 18 "貓") "foo"))
(text2box text)
(measureWidthPx "1314abc" "Gentium" 12.0)
(let ((boxOfText (text2box text)))
(putOnChars boxOfText 0.0)
)
(text2box text)
// (measureWidthPx "1314abc" "Gentium" 12.0)
)

Binary file not shown.