ai-nevermore 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,21 +29,32 @@ Install with `npm install -g ai-nevermore`
29
29
  nevermore [command]
30
30
 
31
31
  Commands:
32
- nevermore pseudotext [input-file] process the input
32
+ nevermore pseudotext [input-file] transform text to poison
33
+ nevermore pseudoimage [input-file] transform XOR image encoding
33
34
 
34
35
  Options:
35
- --version Show version number [boolean]
36
- -U, --unified-output File to generate html + css into [string]
37
- -C, --css-output File to generate css into [string]
38
- -H, --html-output File to generate html into [string]
39
- -r, --raw-output Do not wrap the ouput [boolean]
40
- -s, --size The size of the font in pixels [number] [default: 12]
41
- -f, --font The font in question (only webfonts are supported)
36
+ --version Show version number [boolean]
37
+ -K, --key key to use for decoding the image [string]
38
+ -U, --unified-output File to generate html + css into [string]
39
+ -C, --css-output File to generate css into [string]
40
+ -I, --image-output File to output image to [string]
41
+ -H, --html-output File to generate html into [string]
42
+ -r, --raw-output Do not wrap the ouput [boolean]
43
+ -E, --encode inline encoding [boolean]
44
+ -D, --decode inline decoding [boolean]
45
+ -m, --render-mode output mode
46
+ [string] [choices: "fixed-mode", "inline-mode"] [default: "inline-mode"]
47
+ -s, --size The size of the font in pixels (required for
48
+ fixed-width) [number] [default: 12]
49
+ -d, --custom-dictionary A json map of replacement words [string]
50
+ -f, --font The font in question (required for fixed-width; only
51
+ webfonts are supported)
42
52
  [string] [choices: "Andale Mono", "Arial", "Avenir", "Avenir Next", "Comic
43
53
  Sans MS", "Courier New", "Georgia", "Helvetica", "Impact", "Inter", "Times New
44
54
  Roman", "Trebuchet MS", "Verdana", "Webdings", "Open Sans", "Tahoma"]
45
55
  [default: "Arial"]
46
- --help Show help [boolean]
56
+ --help Show help [boolean]
57
+
47
58
  ```
48
59
 
49
60
  Roadmap
@@ -52,7 +63,10 @@ Roadmap
52
63
  - [x] raw output mode
53
64
  - [x] stdin, stdout support
54
65
  - [x] custom dictionary
66
+ - [x] image encoding
67
+ - [ ] web component decoder
55
68
  - [ ] self randomizing dictionary
69
+ - [ ] add a replacement mode (opposed to a tokenizer based solution)
56
70
 
57
71
  Development
58
72
  -----------
package/bin/nevermore CHANGED
@@ -2,18 +2,21 @@
2
2
  import yargs from 'yargs';
3
3
  import { hideBin } from 'yargs/helpers';
4
4
  import { computeIndexKeys, generateHTMLAndCSS } from '../src/index.mjs';
5
+ import { NevermoreImage } from '../src/image.mjs';
6
+ import { Canvas } from '@environment-safe/canvas';
5
7
  import { readFile, writeFile } from 'node:fs/promises';
6
8
  import { join } from 'node:path';
7
9
  const { cwd } = process;
10
+ const __dirname = import.meta.dirname;
8
11
 
9
12
 
10
13
  const formatHTML = (html, css, isRaw=false)=>{
11
14
  if(isRaw) return `${css?`<style>${css}</style>`:''}${html}`;
12
- return `<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >${css?`<style>${css}</style>`:''}</head><body>${html}</body></html>`;
15
+ return `<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >${css.indexOf('\n')===-1?`<link rel="stylesheet" href="${css}">`:`<style>${css}</style>`}</head><body>${html}</body></html>`;
13
16
  };
14
17
 
15
18
  yargs(hideBin(process.argv))
16
- .command('pseudotext [input-file]', 'process the input', (yargs) => {
19
+ .command('pseudotext [input-file]', 'transform text to poison', (yargs) => {
17
20
  return yargs
18
21
  .positional('input-file', {
19
22
  describe: 'file input'
@@ -45,7 +48,13 @@ yargs(hideBin(process.argv))
45
48
  idx = result.toString();
46
49
  }
47
50
  //const { index, root } = await computeIndexKeys(input);
48
- const { html, css } = await generateHTMLAndCSS(root, idx);
51
+ const { html, css } = await generateHTMLAndCSS(
52
+ root, idx, {
53
+ mode: argv['render-mode'],
54
+ font: argv['font'],
55
+ size: argv['size']
56
+ }
57
+ );
49
58
  if(argv['unified-output']){
50
59
  const output = formatHTML(html, css, argv['raw-output']);
51
60
  await writeFile(argv['unified-output'], output);
@@ -53,7 +62,11 @@ yargs(hideBin(process.argv))
53
62
  if(argv['css-output'] && argv['html-output']){
54
63
  const cssLocation = join(cwd(), argv['css-output']);
55
64
  const htmlLocation = join(cwd(), argv['html-output']);
56
- const output = formatHTML(html, null, argv['raw-output']);
65
+ const output = formatHTML(
66
+ html,
67
+ `./${argv['css-output']}`,
68
+ argv['raw-output']
69
+ );
57
70
  await writeFile(htmlLocation, output);
58
71
  await writeFile(cssLocation, css);
59
72
 
@@ -62,6 +75,43 @@ yargs(hideBin(process.argv))
62
75
  process.stdout.write(output);
63
76
  }
64
77
  }
78
+ }).command('pseudoimage [input-file]', 'transform XOR image encoding', (yargs) => {
79
+ return yargs
80
+ .positional('input-file', {
81
+ describe: 'file input'
82
+ })
83
+ }, async (argv) => {
84
+ let image = null;
85
+ if(!argv['input-file']){
86
+ //TODO: pipe support
87
+ throw new Error('pipe not yet supported');
88
+ }else{
89
+ const options = {
90
+ url: argv['input-file'],
91
+ maskDir: join(__dirname, '..', 'textures')
92
+ };
93
+ if(argv['key']){
94
+ options.key = argv['key']
95
+ }
96
+ image = new NevermoreImage(options);
97
+ await image.ready;
98
+ }
99
+ if(image && argv['image-output']){
100
+ let canvas = null;
101
+ if(argv['encode']){
102
+ canvas = await image.encode();
103
+ }
104
+ if(argv['decode']){
105
+ canvas = await image.decode();
106
+ }
107
+ Canvas.save(argv['image-output'], canvas);
108
+ }else{
109
+ throw new Error('piped output not yet supported');
110
+ }
111
+ }).option('key', {
112
+ alias: 'K',
113
+ type: 'string',
114
+ description: 'key to use for decoding the image'
65
115
  }).option('unified-output', {
66
116
  alias: 'U',
67
117
  type: 'string',
@@ -70,6 +120,10 @@ yargs(hideBin(process.argv))
70
120
  alias: 'C',
71
121
  type: 'string',
72
122
  description: 'File to generate css into'
123
+ }).option('image-output', {
124
+ alias: 'I',
125
+ type: 'string',
126
+ description: 'File to output image to'
73
127
  }).option('html-output', {
74
128
  alias: 'H',
75
129
  type: 'string',
@@ -78,11 +132,27 @@ yargs(hideBin(process.argv))
78
132
  alias: 'r',
79
133
  type: 'boolean',
80
134
  description: 'Do not wrap the ouput'
135
+ }).option('encode', {
136
+ alias: 'E',
137
+ type: 'boolean',
138
+ description: 'inline encoding'
139
+ }).option('decode', {
140
+ alias: 'D',
141
+ type: 'boolean',
142
+ description: 'inline decoding'
143
+ }).option('render-mode', {
144
+ alias: 'm',
145
+ type: 'string',
146
+ description: 'output mode',
147
+ default: 'inline-mode',
148
+ choices: [
149
+ 'fixed-mode', 'inline-mode'
150
+ ],
81
151
  }).option('size', {
82
152
  alias: 's',
83
153
  type: 'number',
84
154
  default: 12,
85
- description: 'The size of the font in pixels'
155
+ description: 'The size of the font in pixels (required for fixed-width)'
86
156
  }).option('custom-dictionary', {
87
157
  alias: 'd',
88
158
  type: 'string',
@@ -97,5 +167,5 @@ yargs(hideBin(process.argv))
97
167
  'Open Sans', 'Tahoma'
98
168
  ],
99
169
  default: 'Arial',
100
- description: 'The font in question (only webfonts are supported)'
170
+ description: 'The font in question (required for fixed-width; only webfonts are supported)'
101
171
  }).help().parse();
package/demo.mjs ADDED
@@ -0,0 +1,29 @@
1
+ import { channelXOR, NevermoreImage } from './src/image.mjs';
2
+ import { Canvas, Image } from '@environment-safe/canvas';
3
+
4
+ (async ()=>{
5
+ const image = new NevermoreImage({
6
+ url:'./test.jpg',
7
+ maskDir: './textures'
8
+ });
9
+ await image.ready;
10
+ const canvas = image.encode();
11
+ await Canvas.save('./encoded.jpg', canvas);
12
+ const decoded = image.decode();
13
+ await Canvas.save('./decoded.jpg', decoded);
14
+ /*
15
+ const canvas = await Canvas.load('./test.jpg');
16
+ const texture = await Canvas.load('./textures/A.jpg');
17
+ await Canvas.save('./t.jpg', canvas);
18
+ await Canvas.save('./x.jpg', texture);
19
+ const pixels = channelXOR(canvas, texture, 'red', 1);
20
+ const context = canvas.getContext('2d');
21
+ console.log(pixels, 0, 0, canvas.width, canvas.height)
22
+ context.putImageData(pixels, 0, 0, 0, 0, canvas.width, canvas.height);
23
+ await Canvas.save('./out.jpg', canvas);
24
+ const resetPixels = channelXOR(canvas, texture, 'red', -1);
25
+ context.putImageData(resetPixels, 0, 0, 0, 0, canvas.width, canvas.height);
26
+ await Canvas.save('./reset.jpg', canvas);
27
+ //*/
28
+
29
+ })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-nevermore",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "keywords": [
5
5
  "adversarial",
6
6
  "content",
@@ -18,6 +18,12 @@
18
18
  ".": {
19
19
  "import": "./src/index.mjs"
20
20
  },
21
+ "./image": {
22
+ "import": "./src/image.mjs"
23
+ },
24
+ "./src/image.mjs": {
25
+ "import": "./src/image.mjs"
26
+ },
21
27
  "./package.json": {
22
28
  "import": "./package.json"
23
29
  },
@@ -25,16 +31,20 @@
25
31
  "import": "./src/index.mjs"
26
32
  }
27
33
  },
28
- "bin":{
34
+ "bin": {
29
35
  "nevermore": "./bin/nevermore"
30
36
  },
31
- "scripts":{
37
+ "scripts": {
32
38
  "single-file-demo": "./bin/nevermore pseudotext -U output.html nevermore.txt",
33
39
  "pipe-demo": "cat nevermore.txt | ./bin/nevermore pseudotext",
34
- "demo": "./bin/nevermore pseudotext -H out.html -C out.css nevermore.txt"
40
+ "demo": "./bin/nevermore pseudotext -H out.html -C out.css nevermore.txt",
41
+ "image-encode": "./bin/nevermore pseudoimage ./test.jpg --image-output out.jpg --encode",
42
+ "image-decode": "./bin/nevermore pseudoimage ./out.jpg --image-output decoded.jpg --decode",
43
+ "help": "./bin/nevermore --help"
35
44
  },
36
45
  "dependencies": {
37
- "@environment-safe/file": "^0.4.0",
46
+ "@environment-safe/canvas": "^4.2.3",
47
+ "@environment-safe/file": "^0.4.3",
38
48
  "object-hash": "^3.0.0",
39
49
  "parse-english": "^7.0.0",
40
50
  "self-dict": "^1.0.0",
package/src/image.mjs ADDED
@@ -0,0 +1,186 @@
1
+ import { Canvas, Image } from '@environment-safe/canvas';
2
+ import { File, Path } from '@environment-safe/file';
3
+
4
+ export const channelXOR = (imageCanvas, textureCanvas, chn='', direction= 1)=>{
5
+ const imageContext = imageCanvas.getContext('2d', { willReadFrequently: true });
6
+ const textureContext = textureCanvas.getContext('2d', { willReadFrequently: true });
7
+ const texturePixels = textureContext.getImageData(0,0, textureCanvas.width, textureCanvas.height);
8
+ const newPixels = imageContext.getImageData(0,0, imageCanvas.width, imageCanvas.height);
9
+ const sx = imageCanvas.width; //getx
10
+ const sy = imageCanvas.height; //gety
11
+ const tx = textureCanvas.width; //getx
12
+ const ty = textureCanvas.height; //gety
13
+ let x = null;
14
+ let y = null;
15
+ let pxl = null;
16
+ let tex_r, tex_g, tex_b, mask_offset, pixel_offset, value = null;
17
+ let channel = '';
18
+ switch(chn.toLowerCase()){
19
+ case 'red':
20
+ case 'r':
21
+ channel = 'r';
22
+ break;
23
+ case 'green':
24
+ case 'g':
25
+ channel = 'g';
26
+ break;
27
+ case 'blue':
28
+ case 'b':
29
+ channel = 'b';
30
+ break;
31
+ }
32
+ //kernel_size = filter.length; //coming soon
33
+ const intervalWidth = 256;
34
+ for(y = 0; y < sy; y++){
35
+ for(x = 0; x < sx; x++){
36
+ mask_offset = (((y%ty)*((sx%tx)*4)) + ((x%tx)*4));
37
+ pixel_offset = ((y*(sx*4)) + (x*4));
38
+ tex_r = texturePixels.data[mask_offset ];
39
+ tex_g = texturePixels.data[mask_offset + 1];
40
+ tex_b = texturePixels.data[mask_offset + 2];
41
+ if(!(tex_r === tex_g && tex_g === tex_b)){
42
+ value = Math.floor((tex_r + tex_g + tex_b) / 3);
43
+ }else{
44
+ value = tex_r;
45
+ }
46
+ if(channel === 'r'){
47
+ newPixels.data[pixel_offset ] = (
48
+ newPixels.data[pixel_offset ] + direction*value + intervalWidth
49
+ )%intervalWidth;
50
+ }
51
+ if(channel === 'g'){
52
+ newPixels.data[pixel_offset+1] = (
53
+ newPixels.data[pixel_offset+1] + direction*value + intervalWidth
54
+ )%intervalWidth;
55
+ }
56
+ if(channel === 'b'){
57
+ newPixels.data[pixel_offset + 2] = (
58
+ newPixels.data[pixel_offset+2] + direction*value + intervalWidth
59
+ )%intervalWidth;
60
+ }
61
+ }
62
+ }
63
+ return newPixels;
64
+ };
65
+
66
+ const makeKey = (len, alpha)=>{
67
+ const alphabet = alpha || 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
68
+ let key = '';
69
+ for(let lcv=0; lcv < len; lcv++){
70
+ key += alphabet[Math.floor(Math.random()*alphabet.length)];
71
+ }
72
+ return key;
73
+ }
74
+
75
+ export class NevermoreImage{
76
+ constructor(options={}){
77
+ if(!(
78
+ options.masks || options.maskDir
79
+ )) throw new Error('a set of masks are required');
80
+ this.ready = new Promise(async (resolve, reject)=>{
81
+ // load all masks
82
+ // TODO: load from cache
83
+ let masks = options.masks;
84
+ const textureWork = [];
85
+ const textureDictionary = {};
86
+ if(options.maskDir){
87
+ const types = ['jpg', 'jpeg', 'gif', 'png'];
88
+ const files = (await File.list(options.maskDir)).filter((name)=>{
89
+ const parts = name.split('.');
90
+ const ext = parts.pop().toLowerCase();
91
+ return types.indexOf(ext) !== -1;
92
+ // TODO: support mime through magic numbers
93
+ });
94
+ const fileLoads = [];
95
+ for(let lcv=0; lcv<files.length; lcv++){
96
+ const pth = new Path(files[lcv]);
97
+ if(!pth.parsed) throw new Error('path not parsed');
98
+ const parsed = pth.parsed.posix || pth.parsed.win32;
99
+ const canvasLoad = Canvas.load(
100
+ Path.join(options.maskDir, files[lcv])
101
+ );
102
+ textureWork.push(new Promise(async (resolve)=>{
103
+ resolve({
104
+ name: parsed.name,
105
+ canvas: await canvasLoad
106
+ });
107
+ }))
108
+ fileLoads.push(Canvas.load(Path.join(options.maskDir, files[lcv])));
109
+ }
110
+ masks = await Promise.all(fileLoads);
111
+ const texturesLoaded = await Promise.all(textureWork);
112
+ for(let lcv=0; lcv<texturesLoaded.length; lcv++){
113
+ textureDictionary[
114
+ texturesLoaded[lcv].name
115
+ ] = texturesLoaded[lcv].canvas;
116
+ };
117
+ this.textureDictionary = textureDictionary;
118
+ }
119
+ this.masks = masks;
120
+ this.canvas = options.image;
121
+ // masks loaded, now load the base image
122
+ if(options.url){
123
+ this.canvas = await Canvas.load(options.url);
124
+ }
125
+ if(!this.canvas){
126
+ return reject(new Error(
127
+ 'no base image provided to Image File'
128
+ ));
129
+ }
130
+ //make an id
131
+ this.key = options.key || `${makeKey(5)}-${makeKey(5)}-${makeKey(5)}`;
132
+ console.log('KEY:', this.key);
133
+ //we're ready to do work
134
+ resolve();
135
+ });
136
+ }
137
+
138
+ transform(options={}){
139
+ const { channelKeys, direction, key } = options;
140
+ let pixels = null;
141
+ let channelKey = null;
142
+ const canvas = this.canvas;
143
+ for(let lcv=0; lcv < channelKeys.r.length; lcv++){
144
+ const texture = this.textureDictionary[channelKeys.r[lcv]];
145
+ const pixels = channelXOR(canvas, texture, 'red', direction);
146
+ const context = canvas.getContext('2d');
147
+ context.putImageData(pixels, 0, 0, 0, 0, canvas.width, canvas.height);
148
+ }
149
+ for(let lcv=0; lcv < channelKeys.r.length; lcv++){
150
+ const texture = this.textureDictionary[channelKeys.r[lcv]];
151
+ const pixels = channelXOR(canvas, texture, 'green', direction);
152
+ const context = canvas.getContext('2d');
153
+ context.putImageData(pixels, 0, 0, 0, 0, canvas.width, canvas.height);
154
+ }
155
+ for(let lcv=0; lcv < channelKeys.r.length; lcv++){
156
+ const texture = this.textureDictionary[channelKeys.r[lcv]];
157
+ const pixels = channelXOR(canvas, texture, 'blue', direction);
158
+ const context = canvas.getContext('2d');
159
+ context.putImageData(pixels, 0, 0, 0, 0, canvas.width, canvas.height);
160
+ }
161
+ // should this mutate? current answer: yes
162
+ return canvas;
163
+ }
164
+
165
+ encode(options={}){
166
+ const key = options.key || this.key;
167
+ const parts = key.split('-')
168
+ const channelKeys = {
169
+ r: parts[0].split(''),
170
+ g: parts[1].split(''),
171
+ b: parts[2].split('')
172
+ }
173
+ return this.transform({ channelKeys, key, direction: 1 });
174
+ }
175
+
176
+ decode(options={}){
177
+ const key = options.key || this.key;
178
+ const parts = key.split('-');
179
+ const channelKeys = {
180
+ r: parts[0].split('').reverse(),
181
+ g: parts[1].split('').reverse(),
182
+ b: parts[2].split('').reverse()
183
+ }
184
+ return this.transform({ channelKeys, key, direction: -1 });
185
+ }
186
+ }
package/src/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  import { ParseEnglish } from 'parse-english'
2
2
  import { inspect } from 'unist-util-inspect'
3
3
  import { define } from 'self-dict';
4
+ import pixelWidth from 'string-pixel-width';
4
5
  import { thesaurus } from './thesaurus.mjs';
5
6
  import { hash } from './hash.mjs';
6
- import pixelWidth from 'string-pixel-width';
7
-
7
+ import { modes } from './render-modes.mjs';
8
8
 
9
9
  let wordlist = null;
10
10
 
@@ -24,8 +24,6 @@ const randomWord = async ()=>{
24
24
  while((!word) || word.split(' ').length > 1){
25
25
  word = wordlist[Math.floor(wordlist.length * Math.random())];
26
26
  }
27
- //console.log(word);
28
- //process.exit();
29
27
  return word;
30
28
  }
31
29
 
@@ -40,97 +38,30 @@ const traverse = async (node, handler)=>{
40
38
  }
41
39
  };
42
40
 
43
- const render = async (node, index, fnt, sz)=>{
41
+ const render = async (node, index, modeName, fnt, sz)=>{
42
+ const mode = modes[modeName];
44
43
  let html = '';
45
44
  let css = '';
46
45
  let className = null;
47
46
  let width = null;
48
47
  const font = fnt || 'Arial';
49
48
  const size = sz || 12;
50
- switch(node.type){
51
- case 'RootNode':
52
- css += `span{
53
- font-family: ${font};
54
- font-size: ${size}px;
55
- display:inline-block;
56
- vertical-align: baseline;
57
- }\n`;
58
- break;
59
- case 'ParagraphNode':
60
- html += '<p>';
61
- break;
62
- case 'SentenceNode': break;
63
- case 'WordNode': break;
64
- case 'TextNode':
65
- if(node.replacement){
66
- width = pixelWidth(node.word, {
67
- size: size,
68
- font
69
- });
70
- className = 'R'+hash(node.word);
71
- css += `.${className}{
72
- visibility: hidden;
73
- position: relative;
74
- overflow:none;
75
- vertical-align: text-bottom;
76
- display:inline-block;
77
- font-size: ${size}px;
78
- height: ${size}px;
79
- width: ${width}px;
80
- }
81
- `;
82
- css += `.${className}::after{
83
- visibility: visible;
84
- position: absolute;
85
- vertical-align: text-bottom;
86
- font-family: ${font};
87
- font-size: ${size}px;
88
- display:inline-block;
89
- margin-top: -${Math.round(size/4)}px;
90
- top: 0;
91
- left: 0;
92
- width: ${width}px;
93
- height: ${size}px;
94
- content : '${node.value}'
95
- }
96
- `;
97
- html += `<span class="guarded ${
98
- className
99
- } ">${node.replacement}</span>`;
100
- }else{
101
- //console.log(node); process.exit();
102
- html += `<span>${node.value}</span>`;
103
- }
104
- break;
105
- case 'WhiteSpaceNode':
106
- break;
107
- case 'PunctuationNode':
108
- break;
109
- default: {console.log(node); process.exit()}
49
+ if(mode[node.type] && mode[node.type].pre){
50
+ html += mode[node.type].pre({font, size, node});
51
+ }
52
+ if(mode[node.type] && mode[node.type].style){
53
+ css += mode[node.type].style({font, size, node});
110
54
  }
111
55
  if(node.children){
112
56
  let vals = null;
113
57
  for(let lcv=0; lcv< node.children.length; lcv++){
114
- vals = await render(node.children[lcv]);
58
+ vals = await render(node.children[lcv], index, modeName, fnt, sz);
115
59
  html += vals.html;
116
60
  css += vals.css;
117
61
  }
118
62
  }
119
- switch(node.type){
120
- case 'RootNode': break;
121
- case 'ParagraphNode':
122
- html += '</p>';
123
- break;
124
- case 'SentenceNode':
125
- break;
126
- case 'WordNode': break;
127
- case 'TextNode': break;
128
- case 'WhiteSpaceNode':
129
- html += `${node.value}`;
130
- break;
131
- case 'PunctuationNode':
132
- html += `<span>${node.value}</span>`;
133
- break;
63
+ if(mode[node.type] && mode[node.type].post){
64
+ html += mode[node.type].post({font, size, node});
134
65
  }
135
66
  return {html, css};
136
67
  };
@@ -166,8 +97,10 @@ export const computeIndexKeys = async (textBody)=>{
166
97
  return { index, root: node };
167
98
  };
168
99
 
169
- export const generateHTMLAndCSS = async (node, index, fnt, sz)=>{
100
+ export const generateHTMLAndCSS = async (node, index, options)=>{
170
101
  await checkThesaurusInit();
171
- const { html, css } = await render(node, index, fnt, sz);
102
+ const { html, css } = await render(
103
+ node, index, options.mode, options.font, options.size
104
+ );
172
105
  return { html, css };
173
106
  };
@@ -0,0 +1,120 @@
1
+ import pixelWidth from 'string-pixel-width';
2
+ import { thesaurus } from './thesaurus.mjs';
3
+ import { hash } from './hash.mjs';
4
+
5
+ export const fixedMode = {
6
+ RootNode : {
7
+ style : ({font, size, node})=>{
8
+ return `span{
9
+ font-family: ${font};
10
+ font-size: ${size}px;
11
+ display:inline-block;
12
+ vertical-align: baseline;
13
+ }\n`;
14
+ }
15
+ },
16
+ ParagraphNode : {
17
+ pre : ({font, size, node})=>{
18
+ return `<p>`;
19
+ },
20
+ post : ({font, size, node})=>{
21
+ return `</p>`;
22
+ }
23
+ },
24
+ TextNode: {
25
+ style : ({font, size, node})=>{
26
+ if(node.replacement){
27
+ const width = pixelWidth(node.word, {
28
+ size: size,
29
+ font
30
+ });
31
+ const className = 'R'+hash(node.word);
32
+ return `.${className}{
33
+ visibility: hidden;
34
+ position: relative;
35
+ overflow:none;
36
+ vertical-align: text-bottom;
37
+ display:inline-block;
38
+ font-size: ${size}px;
39
+ height: ${size}px;
40
+ width: ${width}px;
41
+ }
42
+ .${className}::after{
43
+ content : '${node.value}'
44
+ }
45
+ `;
46
+ }else{
47
+ return '';
48
+ }
49
+ },
50
+ pre : ({font, size, node})=>{
51
+ if(node.replacement){
52
+ const className = 'R'+hash(node.word);
53
+ return `<span class="guarded ${
54
+ className
55
+ } ">${node.replacement}</span>`;
56
+ }else{
57
+ return `<span>${node.value}</span>`;
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ export const inlineMode = {
64
+ RootNode : {
65
+ style : ({font, size, node})=>{
66
+ return `.guarded{
67
+ visibility: hidden;
68
+ font-size: 0;
69
+ }
70
+ .guarded::after{
71
+ visibility: visible;
72
+ display: inline-block;
73
+ font-size: 1.0rem;
74
+ }
75
+ \n`;
76
+ }
77
+ },
78
+ ParagraphNode : {
79
+ pre : ({font, size, node})=>{
80
+ return `<p>`;
81
+ },
82
+ post : ({font, size, node})=>{
83
+ return `</p>`;
84
+ }
85
+ },
86
+ WhiteSpaceNode :{
87
+ post : ({font, size, node})=>{
88
+ return `${node.value}`;
89
+ }
90
+ },
91
+ TextNode: {
92
+ style : ({font, size, node})=>{
93
+ if(node.replacement){
94
+ const width = pixelWidth(node.word, {
95
+ size: size,
96
+ font
97
+ });
98
+ const className = 'R'+hash(node.word);
99
+ return `.${className}::after{ content : '${node.value}'; }`;
100
+ }else{
101
+ return '';
102
+ }
103
+ },
104
+ pre : ({font, size, node})=>{
105
+ if(node.replacement){
106
+ const className = 'R'+hash(node.word);
107
+ return `<span class="guarded ${
108
+ className
109
+ } ">${node.replacement}</span>`;
110
+ }else{
111
+ return `<span>${node.value}</span>`;
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ export const modes = {
118
+ 'fixed-mode' : fixedMode,
119
+ 'inline-mode' : inlineMode
120
+ }
package/test.jpg ADDED
Binary file
package/textures/A.jpg ADDED
Binary file
package/textures/B.jpg ADDED
Binary file
package/textures/C.jpg ADDED
Binary file
package/textures/D.jpg ADDED
Binary file
package/textures/E.jpg ADDED
Binary file
package/textures/F.jpg ADDED
Binary file
package/textures/G.jpg ADDED
Binary file
package/textures/H.jpg ADDED
Binary file
package/textures/I.jpg ADDED
Binary file
package/textures/J.jpg ADDED
Binary file
package/textures/K.jpg ADDED
Binary file
package/textures/L.jpg ADDED
Binary file
package/textures/M.jpg ADDED
Binary file
package/textures/N.jpg ADDED
Binary file
package/textures/O.jpg ADDED
Binary file
package/textures/P.jpg ADDED
Binary file
package/textures/Q.jpg ADDED
Binary file
package/textures/R.jpg ADDED
Binary file
package/textures/S.jpg ADDED
Binary file
package/textures/T.jpg ADDED
Binary file
package/textures/U.jpg ADDED
Binary file
package/textures/V.jpg ADDED
Binary file
package/textures/W.jpg ADDED
Binary file
package/textures/X.jpg ADDED
Binary file
package/textures/Y.jpg ADDED
Binary file
package/textures/Z.jpg ADDED
Binary file