]> git.kianting.info Git - anotherTypesetter/commitdiff
add text measurement
authorTan Kian-ting <chenjt30@gmail.com>
Thu, 18 Apr 2024 17:25:07 +0000 (01:25 +0800)
committerTan Kian-ting <chenjt30@gmail.com>
Thu, 18 Apr 2024 17:25:07 +0000 (01:25 +0800)
3rdparty/harfbuzzjs/hb.wasm [new file with mode: 0755]
README.md
package-lock.json
package.json
src/index.js
src/index.ts
text.lisp
text.lisp.pdf
text2.lisp [new file with mode: 0644]

diff --git a/3rdparty/harfbuzzjs/hb.wasm b/3rdparty/harfbuzzjs/hb.wasm
new file mode 100755 (executable)
index 0000000..427871a
Binary files /dev/null and b/3rdparty/harfbuzzjs/hb.wasm differ
index 6801888b815b2d652bfa57ef689c8dee13f5d4fd..db3f025eff5a33655ae17045e063e61892ec250e 100644 (file)
--- a/README.md
+++ b/README.md
@@ -14,4 +14,5 @@
  - [ ] close pdf
  - [v] add character
  - [ ] add path
- - [ ] basic typesetting format
\ No newline at end of file
+ - [ ] basic typesetting format
+ - [v] text measuring width in pt
\ No newline at end of file
index 54984020d60a307c83ad6d38caa652958cfe4281..f83f9f3c8a060780c34e3c6424782045471e5058 100644 (file)
@@ -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"
       },
       "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",
       "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",
index f01ade4a65a93eb0c0fde59358614591c67b2dff..f1cfa2114226c0a577de142e317c6dd7d55270a9 100644 (file)
@@ -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"
   },
index 3b0d71890ec0c30bcabb3c53b5b85cff9b4394fb..dae4421b0389d28f0d616d49add0a6cb063a71e6 100644 (file)
@@ -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);
                         }
index 2b266a30f63bede7e41ce4bf7540176bd4f94688..9260f7f9e12467d2eb3cf58efc6e2e8be1e3dbae 100644 (file)
@@ -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');
index 1d38465507d90193436906f29f30fee4803f2a0a..3023dbdafb52eebf83c7799f7284e16c47706589 100644 (file)
--- a/text.lisp
+++ b/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
+(define defaultFontFormat
+    '(("fontFamily" "Gentium")
+      ("color" "#ff0000")
+      ("fontSize" 12)        
     )
+)
+
+
+(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)
+))))
 
-(begin
 (addPDFPage '())
-(drawText
-    (dictRef defaultFontFormat "fontFamily")
-    (dictRef defaultFontFormat "size")
-    (dictRef defaultFontFormat "color")
-    40.0
-    50.0
-    "blah"
-)
 (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)))
+
 
-(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
+
+
+(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)
+)
\ No newline at end of file
index 08e08686487b894715567faaf5aac7d89e0e3554..335d5699028c25694bdf15c52c3e0319b518bde1 100644 (file)
Binary files a/text.lisp.pdf and b/text.lisp.pdf differ
diff --git a/text2.lisp b/text2.lisp
new file mode 100644 (file)
index 0000000..d222fea
--- /dev/null
@@ -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)
+))))
\ No newline at end of file