Merge branch 'master' into patch-1

This commit is contained in:
GCHQDeveloper581 2026-03-07 08:01:45 +00:00 committed by GitHub
commit f941f0a0d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
69 changed files with 6197 additions and 5689 deletions

View File

@ -1,18 +1,19 @@
name: "Master Build, Test & Deploy" name: "Master Build, Test & Deploy"
permissions:
contents: read
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: branches:
- master - master
permissions:
contents: read
jobs: jobs:
main: main:
permissions: permissions:
contents: write contents: write
pages: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6

View File

@ -7,7 +7,6 @@ on:
- "v*" - "v*"
permissions: permissions:
id-token: write
contents: read contents: read
env: env:
@ -18,6 +17,9 @@ env:
jobs: jobs:
main: main:
permissions:
packages: write
contents: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
@ -93,5 +95,32 @@ jobs:
file_glob: true file_glob: true
body: "See the [CHANGELOG](https://github.com/gchq/CyberChef/blob/master/CHANGELOG.md) and [commit messages](https://github.com/gchq/CyberChef/commits/master) for details." body: "See the [CHANGELOG](https://github.com/gchq/CyberChef/blob/master/CHANGELOG.md) and [commit messages](https://github.com/gchq/CyberChef/commits/master) for details."
npm-publish:
permissions:
id-token: write
contents: read
needs: main
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set node version
uses: actions/setup-node@v6
with:
node-version: 18
registry-url: "https://registry.npmjs.org"
- name: Install
run: npm ci
- name: Create machine generated files
run: npm run node
- name: Reset node version ready for publish
uses: actions/setup-node@v6
with:
node-version: ^24.5
registry-url: "https://registry.npmjs.org"
- name: Publish to NPM - name: Publish to NPM
run: npm publish run: npm publish

View File

@ -13,6 +13,35 @@ All major and minor version changes will be documented in this file. Details of
## Details ## Details
### [10.22.0] - 2026-02-11
- Separate npm publish out into separate job and run with Node 24.5 [@GCHQDeveloper581] | [#2188]
- Fixed Percent delimiter for hex encoding [@beneri] [@C85297] | [#2137]
- Added the ability to paste one or more Images from the Clipboard [@t-martine] [@a3957273] [@C85297] | [#1876]
- Quoted Printable - consistent reference to 'email' [@wesinator] | [#2186]
- Fix freeze when output text decoding fails [@Raka-loah] | [#1573]
- Update Browserslist DB [@C85297] | [#2183]
- Add contents write permission to releases workflow [@C85297] | [#2182]
- Fix release workflow permissions [@C85297] | [#2181]
### [10.21.0] - 2026-02-05
- Fix import operations with special chars in them [@d98762625] [@jg42526] | [#1040]
- Remove custom CodeQL workflow [@C85297] | [#2176]
- Fix code scanning warnings in workflows [@GCHQDeveloper581] | [#2177]
- Use NPM trusted publishing [@C85297] [@GCHQDeveloper581] | [#2174]
- Fix: Correctly parse xxd odd byte hexdumps [@ThomasNotTom] [@GCHQDeveloper581] | [#2058]
- Update Sitemap URLs to Use Valid Paths in sitemap.mjs [@rbpi] [@C85297] | [#1861]
- Use recommended GitHub Actions to build image [@AlexGustafsson] [@C85297] | [#2055]
- Remove version 10 message from banner [@C85297] | [#2169]
- Bump form-data from 4.0.1 to 4.0.5 | [#2175]
- Bump node-forge from 1.3.1 to 1.3.3 | [#2173]
- Update crypto browserify [@C85297] | [#2172]
- Update kbpgp package (resolves #2135) [@GCHQDeveloper581] | [#2136]
- Fix the processing of ALPNs for JA4 to align with new specification update [@tuliperis] | [#2165]
- Add Bech32 and Bech32m encoding/decoding operations [@thomasxm] | [#2159]
- Exclude Delete character from hex dump output [@mikecat] [@C85297] | [#2086]
- Tiny typo fix in "To Base85" operation [@twostraws] | [#2118]
- Bump jsonpath-plus [@C85297] | [#2166]
### [10.20.0] - 2026-01-28 ### [10.20.0] - 2026-01-28
- Fixed Optical Character Recognition and added tests [@n1474335] | [ab37c1e] - Fixed Optical Character Recognition and added tests [@n1474335] | [ab37c1e]
- Fixed JA4 version fallback value [@n1474335] | [7a5225c] - Fixed JA4 version fallback value [@n1474335] | [7a5225c]
@ -509,6 +538,8 @@ All major and minor version changes will be documented in this file. Details of
## [4.0.0] - 2016-11-28 ## [4.0.0] - 2016-11-28
- Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306) - Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306)
[10.22.0]: https://github.com/gchq/CyberChef/releases/tag/v10.22.0
[10.21.0]: https://github.com/gchq/CyberChef/releases/tag/v10.21.0
[10.20.0]: https://github.com/gchq/CyberChef/releases/tag/v10.20.0 [10.20.0]: https://github.com/gchq/CyberChef/releases/tag/v10.20.0
[10.19.0]: https://github.com/gchq/CyberChef/releases/tag/v10.19.0 [10.19.0]: https://github.com/gchq/CyberChef/releases/tag/v10.19.0
[10.18.0]: https://github.com/gchq/CyberChef/releases/tag/v10.18.0 [10.18.0]: https://github.com/gchq/CyberChef/releases/tag/v10.18.0
@ -754,6 +785,18 @@ All major and minor version changes will be documented in this file. Details of
[@remingtr]: https://github.com/remingtr [@remingtr]: https://github.com/remingtr
[@0xff1ce]: https://github.com/0xff1ce [@0xff1ce]: https://github.com/0xff1ce
[@starplanet]: https://github.com/starplanet [@starplanet]: https://github.com/starplanet
[@C85297]: https://github.com/C85297
[@GCHQDeveloper581]: https://github.com/GCHQDeveloper581
[@ThomasNotTom]: https://github.com/ThomasNotTom
[@rbpi]: https://github.com/rbpi
[@AlexGustafsson]: https://github.com/AlexGustafsson
[@tuliperis]: https://github.com/tuliperis
[@thomasxm]: https://github.com/thomasxm
[@twostraws]: https://github.com/twostraws
[@beneri]: https://github.com/beneri
[@t-martine]: https://github.com/t-martine
[@wesinator]: https://github.com/wesinator
[@Raka-loah]: https://github.com/Raka-loah
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7 [8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7
@ -942,3 +985,29 @@ All major and minor version changes will be documented in this file. Details of
[#512]: https://github.com/gchq/CyberChef/issues/512 [#512]: https://github.com/gchq/CyberChef/issues/512
[#1732]: https://github.com/gchq/CyberChef/issues/1732 [#1732]: https://github.com/gchq/CyberChef/issues/1732
[#1789]: https://github.com/gchq/CyberChef/issues/1789 [#1789]: https://github.com/gchq/CyberChef/issues/1789
[#1040]: https://github.com/gchq/CyberChef/pull/1040
[#2176]: https://github.com/gchq/CyberChef/pull/2176
[#2177]: https://github.com/gchq/CyberChef/pull/2177
[#2174]: https://github.com/gchq/CyberChef/pull/2174
[#2058]: https://github.com/gchq/CyberChef/pull/2058
[#1861]: https://github.com/gchq/CyberChef/pull/1861
[#2055]: https://github.com/gchq/CyberChef/pull/2055
[#2169]: https://github.com/gchq/CyberChef/pull/2169
[#2175]: https://github.com/gchq/CyberChef/pull/2175
[#2173]: https://github.com/gchq/CyberChef/pull/2173
[#2172]: https://github.com/gchq/CyberChef/pull/2172
[#2136]: https://github.com/gchq/CyberChef/pull/2136
[#2165]: https://github.com/gchq/CyberChef/pull/2165
[#2159]: https://github.com/gchq/CyberChef/pull/2159
[#2086]: https://github.com/gchq/CyberChef/pull/2086
[#2118]: https://github.com/gchq/CyberChef/pull/2118
[#2166]: https://github.com/gchq/CyberChef/pull/2166
[#2188]: https://github.com/gchq/CyberChef/pull/2188
[#2137]: https://github.com/gchq/CyberChef/pull/2137
[#1876]: https://github.com/gchq/CyberChef/pull/1876
[#2186]: https://github.com/gchq/CyberChef/pull/2186
[#1573]: https://github.com/gchq/CyberChef/pull/1573
[#2183]: https://github.com/gchq/CyberChef/pull/2183
[#2182]: https://github.com/gchq/CyberChef/pull/2182
[#2181]: https://github.com/gchq/CyberChef/pull/2181

View File

@ -29,4 +29,6 @@ RUN npm run build
######################################### #########################################
FROM nginx:stable-alpine AS cyberchef FROM nginx:stable-alpine AS cyberchef
LABEL maintainer="GCHQ <oss@gchq.gov.uk>"
COPY --from=builder /app/build/prod /usr/share/nginx/html/ COPY --from=builder /app/build/prod /usr/share/nginx/html/

View File

@ -432,18 +432,6 @@ module.exports = function (grunt) {
}, },
stdout: false stdout: false
}, },
fixJimpModule: {
command: function () {
switch (process.platform) {
case "darwin":
// Space added before comma to prevent multiple modifications
return `sed -i '' 's/"es\\/index.js",/"es\\/index.js" ,\\n "type": "module",/' ./node_modules/jimp/package.json`;
default:
return `sed -i 's/"es\\/index.js",/"es\\/index.js" ,\\n "type": "module",/' ./node_modules/jimp/package.json`;
}
},
stdout: false
}
}, },
}); });
}; };

View File

@ -10,13 +10,7 @@ module.exports = function(api) {
}] }]
], ],
"plugins": [ "plugins": [
"dynamic-import-node",
"@babel/plugin-syntax-import-assertions", "@babel/plugin-syntax-import-assertions",
[
"babel-plugin-transform-builtin-extend", {
"globals": ["Error"]
}
],
[ [
"@babel/plugin-transform-runtime", { "@babel/plugin-transform-runtime", {
"regenerator": true "regenerator": true

5277
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "cyberchef", "name": "cyberchef",
"version": "10.20.0", "version": "10.22.1",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>", "author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef", "homepage": "https://gchq.github.io/CyberChef",
@ -39,57 +39,54 @@
"node >= 16" "node >= 16"
], ],
"devDependencies": { "devDependencies": {
"@babel/core": "^7.24.7", "@babel/eslint-parser": "^7.28.6",
"@babel/eslint-parser": "^7.24.7", "@babel/plugin-syntax-import-assertions": "^7.28.6",
"@babel/plugin-syntax-import-assertions": "^7.24.7", "@babel/plugin-transform-runtime": "^7.29.0",
"@babel/plugin-transform-runtime": "^7.24.7", "@babel/preset-env": "^7.29.0",
"@babel/preset-env": "^7.24.7", "@babel/runtime": "^7.28.6",
"@babel/runtime": "^7.24.7", "@codemirror/commands": "^6.10.2",
"@codemirror/commands": "^6.6.0", "@codemirror/language": "^6.12.1",
"@codemirror/language": "^6.10.2", "@codemirror/search": "^6.6.0",
"@codemirror/search": "^6.5.6", "@codemirror/state": "^6.5.4",
"@codemirror/state": "^6.4.1", "@codemirror/view": "^6.39.15",
"@codemirror/view": "^6.28.0", "autoprefixer": "^10.4.24",
"autoprefixer": "^10.4.19", "babel-loader": "^10.0.0",
"babel-loader": "^9.1.3",
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-transform-builtin-extend": "1.1.2",
"base64-loader": "^1.0.0", "base64-loader": "^1.0.0",
"chromedriver": "^130.0.0", "chromedriver": "^130.0.4",
"cli-progress": "^3.12.0", "cli-progress": "^3.12.0",
"colors": "^1.4.0", "colors": "^1.4.0",
"compression-webpack-plugin": "^11.1.0", "compression-webpack-plugin": "^11.1.0",
"copy-webpack-plugin": "^12.0.2", "copy-webpack-plugin": "^13.0.1",
"core-js": "^3.37.1", "core-js": "^3.48.0",
"cspell": "^8.17.3", "cspell": "^8.19.4",
"css-loader": "7.1.2", "css-loader": "7.1.4",
"eslint": "^9.4.0", "eslint": "^9.39.3",
"eslint-plugin-jsdoc": "^48.2.9", "eslint-plugin-jsdoc": "^50.8.0",
"globals": "^15.4.0", "globals": "^15.15.0",
"grunt": "^1.6.1", "grunt": "^1.6.1",
"grunt-chmod": "~1.1.1", "grunt-chmod": "~1.1.1",
"grunt-concurrent": "^3.0.0", "grunt-concurrent": "^3.0.0",
"grunt-contrib-clean": "~2.0.1", "grunt-contrib-clean": "~2.0.1",
"grunt-contrib-connect": "^4.0.0", "grunt-contrib-connect": "^5.0.1",
"grunt-contrib-copy": "~1.0.0", "grunt-contrib-copy": "~1.0.0",
"grunt-contrib-watch": "^1.1.0", "grunt-contrib-watch": "^1.1.0",
"grunt-eslint": "^25.0.0", "grunt-eslint": "^25.0.0",
"grunt-exec": "~3.0.0", "grunt-exec": "~3.0.0",
"grunt-webpack": "^6.0.0", "grunt-webpack": "^6.0.0",
"grunt-zip": "^1.0.0", "grunt-zip": "^1.0.0",
"html-webpack-plugin": "^5.6.0", "html-webpack-plugin": "^5.6.6",
"imports-loader": "^5.0.0", "imports-loader": "^5.0.0",
"mini-css-extract-plugin": "2.9.0", "mini-css-extract-plugin": "2.10.0",
"modify-source-webpack-plugin": "^4.1.0", "modify-source-webpack-plugin": "^4.1.0",
"nightwatch": "^3.6.3", "nightwatch": "^3.15.0",
"postcss": "^8.4.38", "postcss": "^8.5.6",
"postcss-css-variables": "^0.19.0", "postcss-css-variables": "^0.19.0",
"postcss-import": "^16.1.0", "postcss-import": "^16.1.1",
"postcss-loader": "^8.1.1", "postcss-loader": "^8.2.1",
"prompt": "^1.3.0", "prompt": "^1.3.0",
"sitemap": "^8.0.0", "sitemap": "^8.0.2",
"terser": "^5.31.1", "terser": "^5.46.0",
"webpack": "^5.91.0", "webpack": "^5.105.2",
"webpack-bundle-analyzer": "^4.10.2", "webpack-bundle-analyzer": "^4.10.2",
"webpack-dev-server": "5.0.4", "webpack-dev-server": "5.0.4",
"webpack-node-externals": "^3.0.0", "webpack-node-externals": "^3.0.0",
@ -97,15 +94,15 @@
}, },
"dependencies": { "dependencies": {
"@astronautlabs/amf": "^0.0.6", "@astronautlabs/amf": "^0.0.6",
"@babel/polyfill": "^7.12.1",
"@blu3r4y/lzma": "^2.3.3", "@blu3r4y/lzma": "^2.3.3",
"@wavesenterprise/crypto-gost-js": "^2.1.0-RC1", "@wavesenterprise/crypto-gost-js": "^2.1.0-RC1",
"@xmldom/xmldom": "^0.8.10", "@xmldom/xmldom": "^0.8.11",
"argon2-browser": "^1.18.0", "argon2-browser": "^1.18.0",
"arrive": "^2.4.1", "arrive": "^2.5.2",
"avsc": "^5.7.7", "assert": "^2.1.0",
"avsc": "^5.7.9",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"bignumber.js": "^9.1.2", "bignumber.js": "^9.3.1",
"blakejs": "^1.2.1", "blakejs": "^1.2.1",
"bootstrap": "4.6.2", "bootstrap": "4.6.2",
"bootstrap-colorpicker": "^3.4.0", "bootstrap-colorpicker": "^3.4.0",
@ -122,44 +119,45 @@
"ctph.js": "0.0.5", "ctph.js": "0.0.5",
"d3": "7.9.0", "d3": "7.9.0",
"d3-hexbin": "^0.2.2", "d3-hexbin": "^0.2.2",
"diff": "^5.2.0", "diff": "^5.2.2",
"dompurify": "^3.2.5", "dompurify": "^3.3.1",
"es6-promisify": "^7.0.0", "es6-promisify": "^7.0.0",
"escodegen": "^2.1.0", "escodegen": "^2.1.0",
"esprima": "^4.0.1", "esprima": "^4.0.1",
"events": "^3.3.0",
"exif-parser": "^0.1.12", "exif-parser": "^0.1.12",
"fernet": "^0.4.0", "fernet": "^0.3.3",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"flat": "^6.0.1", "flat": "^6.0.1",
"geodesy": "1.1.3", "geodesy": "1.1.3",
"handlebars": "^4.7.8", "handlebars": "^4.7.8",
"hash-wasm": "^4.12.0", "hash-wasm": "^4.12.0",
"highlight.js": "^11.9.0", "highlight.js": "^11.11.1",
"ieee754": "^1.2.1", "ieee754": "^1.2.1",
"jimp": "^0.22.12", "jimp": "^1.6.0",
"jq-web": "^0.5.1", "jq-wasm": "^1.1.0-jq-1.8.1",
"jquery": "3.7.1", "jquery": "3.7.1",
"js-sha3": "^0.9.3", "js-sha3": "^0.9.3",
"jsesc": "^3.0.2", "jsesc": "^3.1.0",
"json5": "^2.2.3", "json5": "^2.2.3",
"jsonata": "^2.0.3", "jsonata": "^2.1.0",
"jsonpath-plus": "^10.3.0", "jsonpath-plus": "^10.4.0",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "9.0.0",
"jsqr": "^1.4.0", "jsqr": "^1.4.0",
"jsrsasign": "^11.1.0", "jsrsasign": "^11.1.1",
"kbpgp": "^2.1.17", "kbpgp": "^2.1.17",
"libbzip2-wasm": "0.0.4", "libbzip2-wasm": "0.0.4",
"libyara-wasm": "^1.2.1", "libyara-wasm": "^1.2.1",
"lodash": "^4.17.21", "lodash": "^4.17.23",
"loglevel": "^1.9.1", "loglevel": "^1.9.2",
"loglevel-message-prefix": "^3.0.0", "loglevel-message-prefix": "^3.0.0",
"lz-string": "^1.5.0", "lz-string": "^1.5.0",
"lz4js": "^0.2.0", "lz4js": "^0.2.0",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.1",
"moment": "^2.30.1", "moment": "^2.30.1",
"moment-timezone": "^0.5.45", "moment-timezone": "^0.6.0",
"ngeohash": "^0.6.3", "ngeohash": "^0.6.3",
"node-forge": "^1.3.1", "node-forge": "^1.3.3",
"node-md6": "^0.1.0", "node-md6": "^0.1.0",
"nodom": "^2.4.0", "nodom": "^2.4.0",
"notepack.io": "^3.0.1", "notepack.io": "^3.0.1",
@ -169,24 +167,26 @@
"path": "^0.12.7", "path": "^0.12.7",
"popper.js": "^1.16.1", "popper.js": "^1.16.1",
"process": "^0.11.10", "process": "^0.11.10",
"protobufjs": "^7.3.1", "protobufjs": "^7.5.4",
"qr-image": "^3.2.0", "qr-image": "^3.2.0",
"reflect-metadata": "^0.2.2", "reflect-metadata": "^0.2.2",
"rison": "^0.1.1", "rison": "^0.1.1",
"scryptsy": "^2.1.0", "scryptsy": "^2.1.0",
"snackbarjs": "^1.1.0", "snackbarjs": "^1.1.0",
"sortablejs": "^1.15.2", "sortablejs": "^1.15.7",
"split.js": "^1.6.5", "split.js": "^1.6.5",
"sql-formatter": "^15.6.5",
"ssdeep.js": "0.0.3", "ssdeep.js": "0.0.3",
"stream-browserify": "^3.0.0", "stream-browserify": "^3.0.0",
"tesseract.js": "5.1.0", "tesseract.js": "^6.0.1",
"ua-parser-js": "^1.0.38", "ua-parser-js": "^1.0.41",
"unorm": "^1.6.0", "unorm": "^1.6.0",
"url": "^0.11.4",
"utf8": "^3.0.0", "utf8": "^3.0.0",
"uuid": "^11.1.0", "uuid": "^13.0.0",
"vkbeautify": "^0.99.3", "vkbeautify": "^0.99.3",
"xpath": "0.0.34", "xpath": "0.0.34",
"xregexp": "^5.1.1", "xregexp": "^5.1.2",
"zlibjs": "^0.3.1" "zlibjs": "^0.3.1"
}, },
"scripts": { "scripts": {
@ -200,9 +200,10 @@
"testuidev": "npx nightwatch --env=dev", "testuidev": "npx nightwatch --env=dev",
"lint": "npx grunt lint", "lint": "npx grunt lint",
"lint:grammar": "cspell ./src", "lint:grammar": "cspell ./src",
"postinstall": "npx grunt exec:fixCryptoApiImports && npx grunt exec:fixSnackbarMarkup && npx grunt exec:fixJimpModule", "postinstall": "npx grunt exec:fixCryptoApiImports && npx grunt exec:fixSnackbarMarkup",
"newop": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newOperation.mjs", "newop": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newOperation.mjs",
"minor": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newMinorVersion.mjs", "minor": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newMinorVersion.mjs && npm version minor --git-tag-version=false && echo \"Updated to version v$(npm pkg get version | xargs), please create a pull request and once merged use 'npm run tag'\"",
"tag": "git tag -s \"v$(npm pkg get version | xargs)\" -m \"$(npm pkg get version | xargs)\" && echo \"Created v$(npm pkg get version | xargs), now check and push the tag\"",
"getheapsize": "node -e 'console.log(`node heap limit = ${require(\"v8\").getHeapStatistics().heap_size_limit / (1024 * 1024)} Mb`)'", "getheapsize": "node -e 'console.log(`node heap limit = ${require(\"v8\").getHeapStatistics().heap_size_limit / (1024 * 1024)} Mb`)'",
"setheapsize": "export NODE_OPTIONS=--max_old_space_size=2048" "setheapsize": "export NODE_OPTIONS=--max_old_space_size=2048"
} }

View File

@ -229,6 +229,7 @@ class Recipe {
} }
this.lastRunOp = op; this.lastRunOp = op;
} catch (err) { } catch (err) {
log.error(err);
// Return expected errors as output // Return expected errors as output
if (err instanceof OperationError || err?.type === "OperationError") { if (err instanceof OperationError || err?.type === "OperationError") {
// Cannot rely on `err instanceof OperationError` here as extending // Cannot rely on `err instanceof OperationError` here as extending

View File

@ -164,7 +164,10 @@
"Typex", "Typex",
"Lorenz", "Lorenz",
"Colossus", "Colossus",
"SIGABA" "SIGABA",
"Flask Session Decode",
"Flask Session Sign",
"Flask Session Verify"
] ]
}, },
{ {
@ -222,6 +225,9 @@
"Subtract", "Subtract",
"Multiply", "Multiply",
"Divide", "Divide",
"Modular Exponentiation",
"Modular Inverse",
"Extended GCD",
"Mean", "Mean",
"Median", "Median",
"Standard Deviation", "Standard Deviation",
@ -551,6 +557,7 @@
"P-list Viewer", "P-list Viewer",
"Disassemble x86", "Disassemble x86",
"Pseudo-Random Number Generator", "Pseudo-Random Number Generator",
"Pseudo-Random Integer Generator",
"Generate De Bruijn Sequence", "Generate De Bruijn Sequence",
"Generate UUID", "Generate UUID",
"Analyse UUID", "Analyse UUID",

View File

@ -7,138 +7,197 @@
*/ */
/* eslint no-console: ["off"] */ /* eslint no-console: ["off"] */
/* eslint jsdoc/require-jsdoc: ["off"] */
import prompt from "prompt";
import colors from "colors";
import path from "path"; import path from "path";
import fs from "fs"; import fs from "fs";
import process from "process"; import process from "process";
import { execSync } from "child_process";
const dir = path.join(process.cwd() + "/src/core/config/"); const ignoredAuthors = ["github-advanced-security[bot]", "dependabot[bot]"];
if (!fs.existsSync(dir)) {
async function main() {
const dir = path.join(process.cwd() + "/src/core/config/");
if (!fs.existsSync(dir)) {
console.log("\nCWD: " + process.cwd()); console.log("\nCWD: " + process.cwd());
console.log("Error: newMinorVersion.mjs should be run from the project root"); console.log(
console.log("Example> node --experimental-modules src/core/config/scripts/newMinorVersion.mjs"); "Error: newMinorVersion.mjs should be run from the project root",
);
console.log(
"Example> node --experimental-modules src/core/config/scripts/newMinorVersion.mjs",
);
process.exit(1); process.exit(1);
} }
let changelogData = fs.readFileSync(path.join(process.cwd(), "CHANGELOG.md"), "utf8"); let changelogData = fs.readFileSync(
const lastVersion = changelogData.match(/## Details\s+### \[(\d+)\.(\d+)\.(\d+)\]/); path.join(process.cwd(), "CHANGELOG.md"),
const newVersion = [ "utf8",
);
const lastVersion = changelogData.match(
/## Details\s+### \[(\d+)\.(\d+)\.(\d+)\]/,
);
const newVersion = [
parseInt(lastVersion[1], 10), parseInt(lastVersion[1], 10),
parseInt(lastVersion[2], 10) + 1, parseInt(lastVersion[2], 10) + 1,
0 0,
]; ];
let knownContributors = changelogData.match(/^\[@([^\]]+)\]/gm); let knownContributors = changelogData.match(/^\[@([^\]]+)\]/gm);
knownContributors = knownContributors.map(c => c.slice(2, -1)); knownContributors = knownContributors.map((c) => c.slice(2, -1));
const date = (new Date()).toISOString().split("T")[0]; const date = new Date().toISOString().split("T")[0];
const schema = { const lastVersionSha = execSync(
properties: { `git rev-list -n 1 v${lastVersion[1]}.${lastVersion[2]}.${lastVersion[3]}`,
message: { {
description: "A short but descriptive summary of a feature in this version", encoding: "utf8",
example: "Added 'Op name' operation",
prompt: "Feature description",
type: "string",
required: true,
}, },
author: { ).trim();
description: "The author of the feature (only one supported, edit manually to add more)", if (lastVersionSha.length !== 40) {
example: "n1474335", throw new Error(
prompt: "Author", `Unexpected output from git rev-list: ${lastVersionSha}`,
type: "string", );
default: "n1474335"
},
id: {
description: "The PR number or full commit hash for this feature.",
example: "1200",
prompt: "Pull request or commit ID",
type: "string"
},
another: {
description: "y/n",
example: "y",
prompt: "Add another feature?",
type: "string",
pattern: /^[yn]$/,
}
}
};
// Build schema
for (const prop in schema.properties) {
const p = schema.properties[prop];
p.description = "\n" + colors.white(p.description) + colors.cyan("\nExample: " + p.example) + "\n" + colors.green(p.prompt);
}
prompt.message = "";
prompt.delimiter = ":".green;
const features = [];
const authors = [];
const prIDs = [];
const commitIDs = [];
prompt.start();
const getFeature = function() {
prompt.get(schema, (err, result) => {
if (err) {
console.log("\nExiting script.");
process.exit(0);
} }
features.push(result); const features = [];
if (result.another === "y") { const commits = await (
getFeature(); await fetch(`https://api.github.com/repos/gchq/cyberchef/commits`)
).json();
let foundLast = false;
for (const commit of commits) {
if (commit.sha === lastVersionSha) {
foundLast = true;
break;
} else { } else {
const feature = {
message: "",
authors: [],
id: "",
};
const msgparts = commit.commit.message.split("\n\n");
feature.message = msgparts[0];
const prIdMatch = feature.message.match(/\(#(\d+)\)$/);
if (prIdMatch !== null) {
feature.message = feature.message
.replace(prIdMatch[0], "")
.trim();
feature.id = prIdMatch[1];
}
if (!ignoredAuthors.includes(commit.author.login)) {
feature.authors.push(commit.author.login);
}
if (msgparts.length > 1) {
msgparts[1]
.split("\n")
.filter((line) => line.startsWith("Co-authored-by: "))
.forEach((line) => {
let coAuthor = line.slice("Co-authored-by: ".length);
if (coAuthor.indexOf(">") !== -1) {
const email = coAuthor.slice(
coAuthor.indexOf("<") + 1,
coAuthor.indexOf(">"),
);
if (email.endsWith("@users.noreply.github.com")) {
coAuthor = email.slice(
email.indexOf("+") + 1,
-"@users.noreply.github.com".length,
);
} else {
throw new Error(
"Could not get ID of co-author: " +
coAuthor,
);
}
} else {
throw new Error(
"Could not get email of co-author: " + coAuthor,
);
}
if (!ignoredAuthors.includes(coAuthor)) {
feature.authors.push(coAuthor);
}
});
}
features.push(feature);
}
}
if (!foundLast) {
throw new Error(
`Could not find last version commit: ${lastVersionSha} - need to add paging functionality`,
);
}
let message = `### [${newVersion[0]}.${newVersion[1]}.${newVersion[2]}] - ${date}\n`; let message = `### [${newVersion[0]}.${newVersion[1]}.${newVersion[2]}] - ${date}\n`;
features.forEach(feature => { const authors = [];
const id = feature.id.length > 10 ? feature.id.slice(0, 7) : "#" + feature.id; const prIDs = [];
message += `- ${feature.message} [@${feature.author}] | [${id}]\n`; const commitIDs = [];
if (!knownContributors.includes(feature.author)) { features.forEach((feature) => {
authors.push(`[@${feature.author}]: https://github.com/${feature.author}`); const id =
feature.id.length > 10 ? feature.id.slice(0, 7) : "#" + feature.id;
message += `- ${feature.message} ${feature.authors.map((a) => `[@${a}]`).join(" ")} | [${id}]\n`;
feature.authors.forEach((author) => {
if (!knownContributors.includes(author)) {
knownContributors.push(author);
authors.push(`[@${author}]: https://github.com/${author}`);
} }
});
if (feature.id.length > 10) { if (feature.id.length > 10) {
commitIDs.push(`[${id}]: https://github.com/gchq/CyberChef/commit/${feature.id}`); commitIDs.push(
`[${id}]: https://github.com/gchq/CyberChef/commit/${feature.id}`,
);
} else { } else {
prIDs.push(`[#${feature.id}]: https://github.com/gchq/CyberChef/pull/${feature.id}`); prIDs.push(
`[#${feature.id}]: https://github.com/gchq/CyberChef/pull/${feature.id}`,
);
} }
}); });
// Message // Message
changelogData = changelogData.replace(/## Details\n\n/, "## Details\n\n" + message + "\n"); changelogData = changelogData.replace(
/## Details\n\n/,
"## Details\n\n" + message + "\n",
);
// Tag // Tag
const newTag = `[${newVersion[0]}.${newVersion[1]}.${newVersion[2]}]: https://github.com/gchq/CyberChef/releases/tag/v${newVersion[0]}.${newVersion[1]}.${newVersion[2]}\n`; const newTag = `[${newVersion[0]}.${newVersion[1]}.${newVersion[2]}]: https://github.com/gchq/CyberChef/releases/tag/v${newVersion[0]}.${newVersion[1]}.${newVersion[2]}\n`;
changelogData = changelogData.replace(/\n\n(\[\d+\.\d+\.\d+\]: https)/, "\n\n" + newTag + "$1"); changelogData = changelogData.replace(
/\n\n(\[\d+\.\d+\.\d+\]: https)/,
"\n\n" + newTag + "$1",
);
// Author // Author
authors.forEach(author => { authors.forEach((author) => {
changelogData = changelogData.replace(/(\n\[@[^\]]+\]: https:\/\/github\.com\/[^\n]+\n)\n/, "$1" + author + "\n\n"); changelogData = changelogData.replace(
/(\n\[@[^\]]+\]: https:\/\/github\.com\/[^\n]+\n)\n/,
"$1" + author + "\n\n",
);
}); });
// Commit IDs // Commit IDs
commitIDs.forEach(commitID => { commitIDs.forEach((commitID) => {
changelogData = changelogData.replace(/(\n\[[^\].]+\]: https:\/\/github.com\/gchq\/CyberChef\/commit\/[^\n]+\n)\n/, "$1" + commitID + "\n\n"); changelogData = changelogData.replace(
/(\n\[[^\].]+\]: https:\/\/github.com\/gchq\/CyberChef\/commit\/[^\n]+\n)\n/,
"$1" + commitID + "\n\n",
);
}); });
// PR IDs // PR IDs
prIDs.forEach(prID => { prIDs.forEach((prID) => {
changelogData = changelogData.replace(/(\n\[#[^\]]+\]: https:\/\/github.com\/gchq\/CyberChef\/pull\/[^\n]+\n)\n*$/, "$1" + prID + "\n\n"); changelogData = changelogData.replace(
/(\n\[#[^\]]+\]: https:\/\/github.com\/gchq\/CyberChef\/(?:pull|issues)\/[^\n]+\n)\n*$/,
"$1" + prID + "\n\n",
);
}); });
fs.writeFileSync(path.join(process.cwd(), "CHANGELOG.md"), changelogData); fs.writeFileSync(path.join(process.cwd(), "CHANGELOG.md"), changelogData);
}
console.log("Written CHANGELOG.md\nCommit changes and then run `npm version minor`."); main().catch(console.error);
}
});
};
getFeature();

View File

@ -0,0 +1,73 @@
/**
* @author p-leriche [philip.leriche@cantab.net]
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
/**
* Number theory utilities used by cryptographic operations.
*
* Currently provides:
* - parseBigInt
* - Extended Euclidean Algorithm
* - Modular Exponentiation
*
* Additional algorithms may be added as required.
*/
/**
* parseBigInt helper operation
*/
export function parseBigInt(value, param) {
const v = (value ?? "").trim();
if (/^0x[0-9a-f]+$/i.test(v)) return BigInt(v);
if (/^[+-]?[0-9]+$/.test(v)) return BigInt(v);
throw new OperationError(param + " must be decimal or hex (0x...)");
}
/**
* Extended Euclidean Algorithm
*
* Returns [g, x, y] such that:
* a*x + b*y = g = gcd(a, b)
*
* (Uses an iterative algorithm to avoid possible stack overflow)
*/
export function egcd(a, b) {
let oldR = a, r = b;
let oldS = 1n, s = 0n;
let oldT = 0n, t = 1n;
while (r !== 0n) {
const quotient = oldR / r;
[oldR, r] = [r, oldR - quotient * r];
[oldS, s] = [s, oldS - quotient * s];
[oldT, t] = [t, oldT - quotient * t];
}
// oldR is the gcd
// oldS and oldT are the Bézout coefficients
return [oldR, oldS, oldT];
}
/**
* Modular exponentiation
*/
export function modPow(base, exponent, modulus) {
let result = 1n;
base %= modulus;
while (exponent > 0n) {
if (exponent & 1n) {
result = (result * base) % modulus;
}
base = (base * base) % modulus;
exponent >>= 1n;
}
return result;
}

View File

@ -33,7 +33,7 @@ export function toHex(data, delim=" ", padding=2, extraDelim="", lineSize=0) {
if (data instanceof ArrayBuffer) data = new Uint8Array(data); if (data instanceof ArrayBuffer) data = new Uint8Array(data);
let output = ""; let output = "";
const prepend = (delim === "0x" || delim === "\\x"); const prepend = (delim === "0x" || delim === "\\x" || delim === "%");
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
const hex = data[i].toString(16).padStart(padding, "0"); const hex = data[i].toString(16).padStart(padding, "0");

View File

@ -1,251 +0,0 @@
/**
* Image manipulation resources
*
* @author j433866 [j433866@gmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
/**
* Gaussian blurs an image.
*
* @param {jimp} input
* @param {number} radius
* @param {boolean} fast
* @returns {jimp}
*/
export function gaussianBlur (input, radius) {
try {
// From http://blog.ivank.net/fastest-gaussian-blur.html
const boxes = boxesForGauss(radius, 3);
for (let i = 0; i < 3; i++) {
input = boxBlur(input, (boxes[i] - 1) / 2);
}
} catch (err) {
throw new OperationError(`Error blurring image. (${err})`);
}
return input;
}
/**
*
* @param {number} radius
* @param {number} numBoxes
* @returns {Array}
*/
function boxesForGauss(radius, numBoxes) {
const idealWidth = Math.sqrt((12 * radius * radius / numBoxes) + 1);
let wl = Math.floor(idealWidth);
if (wl % 2 === 0) {
wl--;
}
const wu = wl + 2;
const mIdeal = (12 * radius * radius - numBoxes * wl * wl - 4 * numBoxes * wl - 3 * numBoxes) / (-4 * wl - 4);
const m = Math.round(mIdeal);
const sizes = [];
for (let i = 0; i < numBoxes; i++) {
sizes.push(i < m ? wl : wu);
}
return sizes;
}
/**
* Applies a box blur effect to the image
*
* @param {jimp} source
* @param {number} radius
* @returns {jimp}
*/
function boxBlur (source, radius) {
const width = source.bitmap.width;
const height = source.bitmap.height;
let output = source.clone();
output = boxBlurH(source, output, width, height, radius);
source = boxBlurV(output, source, width, height, radius);
return source;
}
/**
* Applies the horizontal blur
*
* @param {jimp} source
* @param {jimp} output
* @param {number} width
* @param {number} height
* @param {number} radius
* @returns {jimp}
*/
function boxBlurH (source, output, width, height, radius) {
const iarr = 1 / (radius + radius + 1);
for (let i = 0; i < height; i++) {
let ti = 0,
li = ti,
ri = ti + radius;
const idx = source.getPixelIndex(ti, i);
const firstValRed = source.bitmap.data[idx],
firstValGreen = source.bitmap.data[idx + 1],
firstValBlue = source.bitmap.data[idx + 2],
firstValAlpha = source.bitmap.data[idx + 3];
const lastIdx = source.getPixelIndex(width - 1, i),
lastValRed = source.bitmap.data[lastIdx],
lastValGreen = source.bitmap.data[lastIdx + 1],
lastValBlue = source.bitmap.data[lastIdx + 2],
lastValAlpha = source.bitmap.data[lastIdx + 3];
let red = (radius + 1) * firstValRed;
let green = (radius + 1) * firstValGreen;
let blue = (radius + 1) * firstValBlue;
let alpha = (radius + 1) * firstValAlpha;
for (let j = 0; j < radius; j++) {
const jIdx = source.getPixelIndex(ti + j, i);
red += source.bitmap.data[jIdx];
green += source.bitmap.data[jIdx + 1];
blue += source.bitmap.data[jIdx + 2];
alpha += source.bitmap.data[jIdx + 3];
}
for (let j = 0; j <= radius; j++) {
const jIdx = source.getPixelIndex(ri++, i);
red += source.bitmap.data[jIdx] - firstValRed;
green += source.bitmap.data[jIdx + 1] - firstValGreen;
blue += source.bitmap.data[jIdx + 2] - firstValBlue;
alpha += source.bitmap.data[jIdx + 3] - firstValAlpha;
const tiIdx = source.getPixelIndex(ti++, i);
output.bitmap.data[tiIdx] = Math.round(red * iarr);
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
}
for (let j = radius + 1; j < width - radius; j++) {
const riIdx = source.getPixelIndex(ri++, i);
const liIdx = source.getPixelIndex(li++, i);
red += source.bitmap.data[riIdx] - source.bitmap.data[liIdx];
green += source.bitmap.data[riIdx + 1] - source.bitmap.data[liIdx + 1];
blue += source.bitmap.data[riIdx + 2] - source.bitmap.data[liIdx + 2];
alpha += source.bitmap.data[riIdx + 3] - source.bitmap.data[liIdx + 3];
const tiIdx = source.getPixelIndex(ti++, i);
output.bitmap.data[tiIdx] = Math.round(red * iarr);
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
}
for (let j = width - radius; j < width; j++) {
const liIdx = source.getPixelIndex(li++, i);
red += lastValRed - source.bitmap.data[liIdx];
green += lastValGreen - source.bitmap.data[liIdx + 1];
blue += lastValBlue - source.bitmap.data[liIdx + 2];
alpha += lastValAlpha - source.bitmap.data[liIdx + 3];
const tiIdx = source.getPixelIndex(ti++, i);
output.bitmap.data[tiIdx] = Math.round(red * iarr);
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
}
}
return output;
}
/**
* Applies the vertical blur
*
* @param {jimp} source
* @param {jimp} output
* @param {number} width
* @param {number} height
* @param {number} radius
* @returns {jimp}
*/
function boxBlurV (source, output, width, height, radius) {
const iarr = 1 / (radius + radius + 1);
for (let i = 0; i < width; i++) {
let ti = 0,
li = ti,
ri = ti + radius;
const idx = source.getPixelIndex(i, ti);
const firstValRed = source.bitmap.data[idx],
firstValGreen = source.bitmap.data[idx + 1],
firstValBlue = source.bitmap.data[idx + 2],
firstValAlpha = source.bitmap.data[idx + 3];
const lastIdx = source.getPixelIndex(i, height - 1),
lastValRed = source.bitmap.data[lastIdx],
lastValGreen = source.bitmap.data[lastIdx + 1],
lastValBlue = source.bitmap.data[lastIdx + 2],
lastValAlpha = source.bitmap.data[lastIdx + 3];
let red = (radius + 1) * firstValRed;
let green = (radius + 1) * firstValGreen;
let blue = (radius + 1) * firstValBlue;
let alpha = (radius + 1) * firstValAlpha;
for (let j = 0; j < radius; j++) {
const jIdx = source.getPixelIndex(i, ti + j);
red += source.bitmap.data[jIdx];
green += source.bitmap.data[jIdx + 1];
blue += source.bitmap.data[jIdx + 2];
alpha += source.bitmap.data[jIdx + 3];
}
for (let j = 0; j <= radius; j++) {
const riIdx = source.getPixelIndex(i, ri++);
red += source.bitmap.data[riIdx] - firstValRed;
green += source.bitmap.data[riIdx + 1] - firstValGreen;
blue += source.bitmap.data[riIdx + 2] - firstValBlue;
alpha += source.bitmap.data[riIdx + 3] - firstValAlpha;
const tiIdx = source.getPixelIndex(i, ti++);
output.bitmap.data[tiIdx] = Math.round(red * iarr);
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
}
for (let j = radius + 1; j < height - radius; j++) {
const riIdx = source.getPixelIndex(i, ri++);
const liIdx = source.getPixelIndex(i, li++);
red += source.bitmap.data[riIdx] - source.bitmap.data[liIdx];
green += source.bitmap.data[riIdx + 1] - source.bitmap.data[liIdx + 1];
blue += source.bitmap.data[riIdx + 2] - source.bitmap.data[liIdx + 2];
alpha += source.bitmap.data[riIdx + 3] - source.bitmap.data[liIdx + 3];
const tiIdx = source.getPixelIndex(i, ti++);
output.bitmap.data[tiIdx] = Math.round(red * iarr);
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
}
for (let j = height - radius; j < height; j++) {
const liIdx = source.getPixelIndex(i, li++);
red += lastValRed - source.bitmap.data[liIdx];
green += lastValGreen - source.bitmap.data[liIdx + 1];
blue += lastValBlue - source.bitmap.data[liIdx + 2];
alpha += lastValAlpha - source.bitmap.data[liIdx + 3];
const tiIdx = source.getPixelIndex(i, ti++);
output.bitmap.data[tiIdx] = Math.round(red * iarr);
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
}
}
return output;
}

View File

@ -10,7 +10,7 @@ import OperationError from "../errors/OperationError.mjs";
import jsQR from "jsqr"; import jsQR from "jsqr";
import qr from "qr-image"; import qr from "qr-image";
import Utils from "../Utils.mjs"; import Utils from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Parses a QR code image from an image * Parses a QR code image from an image
@ -29,18 +29,31 @@ export async function parseQrCode(input, normalise) {
try { try {
if (normalise) { if (normalise) {
image.rgba(false);
image.background(0xFFFFFFFF);
image.normalize();
image.greyscale(); image.greyscale();
image = await image.getBufferAsync(Jimp.MIME_JPEG); image.normalize();
image = await Jimp.read(image);
} }
} catch (err) { } catch (err) {
throw new OperationError(`Error normalising image. (${err})`); throw new OperationError(`Error normalising image. (${err})`);
} }
const qrData = jsQR(image.bitmap.data, image.getWidth(), image.getHeight()); // Remove transparency which jsQR cannot handle
image.scan((x, y, idx) => {
// If pixel is fully transparent, make it opaque white
if (image.bitmap.data[idx + 3] === 0x00) {
image.bitmap.data[idx + 0] = 0xff;
image.bitmap.data[idx + 1] = 0xff;
image.bitmap.data[idx + 2] = 0xff;
}
// Otherwise, make it fully opaque at its existing colour
image.bitmap.data[idx + 3] = 0xff;
});
image = await Jimp.read(await image.getBuffer(JimpMime.jpeg));
const qrData = jsQR(
new Uint8ClampedArray(image.bitmap.data),
image.width,
image.height,
);
if (qrData) { if (qrData) {
return qrData.data; return qrData.data;
} else { } else {
@ -58,7 +71,13 @@ export async function parseQrCode(input, normalise) {
* @param {string} errorCorrection * @param {string} errorCorrection
* @returns {ArrayBuffer} * @returns {ArrayBuffer}
*/ */
export function generateQrCode(input, format, moduleSize, margin, errorCorrection) { export function generateQrCode(
input,
format,
moduleSize,
margin,
errorCorrection,
) {
const formats = ["SVG", "EPS", "PDF", "PNG"]; const formats = ["SVG", "EPS", "PDF", "PNG"];
if (!formats.includes(format.toUpperCase())) { if (!formats.includes(format.toUpperCase())) {
throw new OperationError("Unsupported QR code format."); throw new OperationError("Unsupported QR code format.");
@ -70,7 +89,8 @@ export function generateQrCode(input, format, moduleSize, margin, errorCorrectio
type: format, type: format,
size: moduleSize, size: moduleSize,
margin: margin, margin: margin,
"ec_level": errorCorrection.charAt(0).toUpperCase() // eslint-disable-next-line camelcase
ec_level: errorCorrection.charAt(0).toUpperCase(),
}); });
} catch (err) { } catch (err) {
throw new OperationError(`Error generating QR code. (${err})`); throw new OperationError(`Error generating QR code. (${err})`);
@ -86,7 +106,7 @@ export function generateQrCode(input, format, moduleSize, margin, errorCorrectio
case "PDF": case "PDF":
return Utils.strToArrayBuffer(qrImage); return Utils.strToArrayBuffer(qrImage);
case "PNG": case "PNG":
return qrImage.buffer; return qrImage.buffer.slice(qrImage.byteOffset, qrImage.byteLength + qrImage.byteOffset);
default: default:
throw new OperationError("Unsupported QR code format."); throw new OperationError("Unsupported QR code format.");
} }

View File

@ -9,13 +9,19 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import {
Jimp,
JimpMime,
ResizeStrategy,
measureText,
measureTextHeight,
loadFont,
} from "jimp";
/** /**
* Add Text To Image operation * Add Text To Image operation
*/ */
class AddTextToImage extends Operation { class AddTextToImage extends Operation {
/** /**
* AddTextToImage constructor * AddTextToImage constructor
*/ */
@ -24,7 +30,8 @@ class AddTextToImage extends Operation {
this.name = "Add Text To Image"; this.name = "Add Text To Image";
this.module = "Image"; this.module = "Image";
this.description = "Adds text onto an image.<br><br>Text can be horizontally or vertically aligned, or the position can be manually specified.<br>Variants of the Roboto font face are available in any size or colour."; this.description =
"Adds text onto an image.<br><br>Text can be horizontally or vertically aligned, or the position can be manually specified.<br>Variants of the Roboto font face are available in any size or colour.";
this.infoURL = ""; this.infoURL = "";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
@ -33,72 +40,67 @@ class AddTextToImage extends Operation {
{ {
name: "Text", name: "Text",
type: "string", type: "string",
value: "" value: "",
}, },
{ {
name: "Horizontal align", name: "Horizontal align",
type: "option", type: "option",
value: ["None", "Left", "Center", "Right"] value: ["None", "Left", "Center", "Right"],
}, },
{ {
name: "Vertical align", name: "Vertical align",
type: "option", type: "option",
value: ["None", "Top", "Middle", "Bottom"] value: ["None", "Top", "Middle", "Bottom"],
}, },
{ {
name: "X position", name: "X position",
type: "number", type: "number",
value: 0 value: 0,
}, },
{ {
name: "Y position", name: "Y position",
type: "number", type: "number",
value: 0 value: 0,
}, },
{ {
name: "Size", name: "Size",
type: "number", type: "number",
value: 32, value: 32,
min: 8 min: 8,
}, },
{ {
name: "Font face", name: "Font face",
type: "option", type: "option",
value: [ value: ["Roboto", "Roboto Black", "Roboto Mono", "Roboto Slab"],
"Roboto",
"Roboto Black",
"Roboto Mono",
"Roboto Slab"
]
}, },
{ {
name: "Red", name: "Red",
type: "number", type: "number",
value: 255, value: 255,
min: 0, min: 0,
max: 255 max: 255,
}, },
{ {
name: "Green", name: "Green",
type: "number", type: "number",
value: 255, value: 255,
min: 0, min: 0,
max: 255 max: 255,
}, },
{ {
name: "Blue", name: "Blue",
type: "number", type: "number",
value: 255, value: 255,
min: 0, min: 0,
max: 255 max: 255,
}, },
{ {
name: "Alpha", name: "Alpha",
type: "number", type: "number",
value: 255, value: 255,
min: 0, min: 0,
max: 255 max: 255,
} },
]; ];
} }
@ -131,41 +133,60 @@ class AddTextToImage extends Operation {
} catch (err) { } catch (err) {
throw new OperationError(`Error loading image. (${err})`); throw new OperationError(`Error loading image. (${err})`);
} }
try {
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Adding text to image..."); self.sendStatusMessage("Adding text to image...");
const fontsMap = {}; const fontsMap = {};
try {
const fonts = [ const fonts = [
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.fnt"), import(
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoBlack72White.fnt"), /* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.fnt"
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoMono72White.fnt"), ),
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoSlab72White.fnt") import(
/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoBlack72White.fnt"
),
import(
/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoMono72White.fnt"
),
import(
/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoSlab72White.fnt"
),
]; ];
await Promise.all(fonts) await Promise.all(fonts).then((fonts) => {
.then(fonts => {
fontsMap.Roboto = fonts[0]; fontsMap.Roboto = fonts[0];
fontsMap["Roboto Black"] = fonts[1]; fontsMap["Roboto Black"] = fonts[1];
fontsMap["Roboto Mono"] = fonts[2]; fontsMap["Roboto Mono"] = fonts[2];
fontsMap["Roboto Slab"] = fonts[3]; fontsMap["Roboto Slab"] = fonts[3];
}); });
// Make Webpack load the png font images // Make Webpack load the png font images
await Promise.all([ await Promise.all([
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.png"), import(
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoSlab72White.png"), /* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.png"
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoMono72White.png"), ),
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoBlack72White.png") import(
/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoSlab72White.png"
),
import(
/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoMono72White.png"
),
import(
/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoBlack72White.png"
),
]); ]);
} catch (err) {
throw new OperationError(`Error preparing fonts. (${err})`);
}
let jimpFont;
try {
const font = fontsMap[fontFace]; const font = fontsMap[fontFace];
// LoadFont needs an absolute url, so append the font name to self.docURL // LoadFont needs an absolute url, so append the font name to self.docURL
const jimpFont = await Jimp.loadFont(self.docURL + "/" + font.default); jimpFont = await loadFont(self.docURL + "/" + font.default);
jimpFont.pages.forEach(function(page) { jimpFont.pages.forEach(function (page) {
if (page.bitmap) { if (page.bitmap) {
// Adjust the RGB values of the image pages to change the font colour. // Adjust the RGB values of the image pages to change the font colour.
const pageWidth = page.bitmap.width; const pageWidth = page.bitmap.width;
@ -175,32 +196,56 @@ class AddTextToImage extends Operation {
const idx = (iy * pageWidth + ix) << 2; const idx = (iy * pageWidth + ix) << 2;
const newRed = page.bitmap.data[idx] - (255 - red); const newRed = page.bitmap.data[idx] - (255 - red);
const newGreen = page.bitmap.data[idx + 1] - (255 - green); const newGreen =
const newBlue = page.bitmap.data[idx + 2] - (255 - blue); page.bitmap.data[idx + 1] - (255 - green);
const newAlpha = page.bitmap.data[idx + 3] - (255 - alpha); const newBlue =
page.bitmap.data[idx + 2] - (255 - blue);
const newAlpha =
page.bitmap.data[idx + 3] - (255 - alpha);
// Make sure the bitmap values don't go below 0 as that makes jimp very unhappy // Make sure the bitmap values don't go below 0 as that makes jimp very unhappy
page.bitmap.data[idx] = (newRed > 0) ? newRed : 0; page.bitmap.data[idx] = newRed > 0 ? newRed : 0;
page.bitmap.data[idx + 1] = (newGreen > 0) ? newGreen : 0; page.bitmap.data[idx + 1] =
page.bitmap.data[idx + 2] = (newBlue > 0) ? newBlue : 0; newGreen > 0 ? newGreen : 0;
page.bitmap.data[idx + 3] = (newAlpha > 0) ? newAlpha : 0; page.bitmap.data[idx + 2] =
newBlue > 0 ? newBlue : 0;
page.bitmap.data[idx + 3] =
newAlpha > 0 ? newAlpha : 0;
} }
} }
} }
}); });
} catch (err) {
throw new OperationError(`Error loading font. (${err})`);
}
try {
// Create a temporary image to hold the rendered text // Create a temporary image to hold the rendered text
const textImage = new Jimp(Jimp.measureText(jimpFont, text), Jimp.measureTextHeight(jimpFont, text)); const textImage = new Jimp({
textImage.print(jimpFont, 0, 0, text); width: measureText(jimpFont, text),
height: measureTextHeight(jimpFont, text),
});
textImage.print({
font: jimpFont,
x: 0,
y: 0,
text,
});
// Scale the rendered text image to the correct size // Scale the rendered text image to the correct size
const scaleFactor = size / 72; const scaleFactor = size / 72;
if (size !== 1) { if (size !== 1) {
// Use bicubic for decreasing size // Use bicubic for decreasing size
if (size > 1) { if (size > 1) {
textImage.scale(scaleFactor, Jimp.RESIZE_BICUBIC); textImage.scale({
f: scaleFactor,
mode: ResizeStrategy.BICUBIC,
});
} else { } else {
textImage.scale(scaleFactor, Jimp.RESIZE_BILINEAR); textImage.scale({
f: scaleFactor,
mode: ResizeStrategy.BILINEAR,
});
} }
} }
@ -210,10 +255,10 @@ class AddTextToImage extends Operation {
xPos = 0; xPos = 0;
break; break;
case "Center": case "Center":
xPos = (image.getWidth() / 2) - (textImage.getWidth() / 2); xPos = image.width / 2 - textImage.width / 2;
break; break;
case "Right": case "Right":
xPos = image.getWidth() - textImage.getWidth(); xPos = image.width - textImage.width;
break; break;
} }
@ -222,25 +267,33 @@ class AddTextToImage extends Operation {
yPos = 0; yPos = 0;
break; break;
case "Middle": case "Middle":
yPos = (image.getHeight() / 2) - (textImage.getHeight() / 2); yPos = image.height / 2 - textImage.height / 2;
break; break;
case "Bottom": case "Bottom":
yPos = image.getHeight() - textImage.getHeight(); yPos = image.height - textImage.height;
break; break;
} }
// Blit the rendered text image onto the original source image // Blit the rendered text image onto the original source image
image.blit(textImage, xPos, yPos); image.blit({
src: textImage,
x: xPos,
y: yPos,
});
} catch (err) {
throw new OperationError(`Error adding text to image. (${err})`);
}
try {
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
throw new OperationError(`Error adding text to image. (${err})`); throw new OperationError(`Error exporting image. (${err})`);
} }
} }
@ -261,7 +314,6 @@ class AddTextToImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default AddTextToImage; export default AddTextToImage;

View File

@ -9,14 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { gaussianBlur } from "../lib/ImageManipulation.mjs"; import { Jimp, JimpMime } from "jimp";
import Jimp from "jimp/es/index.js";
/** /**
* Blur Image operation * Blur Image operation
*/ */
class BlurImage extends Operation { class BlurImage extends Operation {
/** /**
* BlurImage constructor * BlurImage constructor
*/ */
@ -25,7 +23,8 @@ class BlurImage extends Operation {
this.name = "Blur Image"; this.name = "Blur Image";
this.module = "Image"; this.module = "Image";
this.description = "Applies a blur effect to the image.<br><br>Gaussian blur is much slower than fast blur, but produces better results."; this.description =
"Applies a blur effect to the image.<br><br>Gaussian blur is much slower than fast blur, but produces better results.";
this.infoURL = "https://wikipedia.org/wiki/Gaussian_blur"; this.infoURL = "https://wikipedia.org/wiki/Gaussian_blur";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
@ -35,13 +34,13 @@ class BlurImage extends Operation {
name: "Amount", name: "Amount",
type: "number", type: "number",
value: 5, value: 5,
min: 1 min: 1,
}, },
{ {
name: "Type", name: "Type",
type: "option", type: "option",
value: ["Fast", "Gaussian"] value: ["Fast", "Gaussian"],
} },
]; ];
} }
@ -73,15 +72,15 @@ class BlurImage extends Operation {
case "Gaussian": case "Gaussian":
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Gaussian blurring image..."); self.sendStatusMessage("Gaussian blurring image...");
image = gaussianBlur(image, blurAmount); image.gaussian(blurAmount);
break; break;
} }
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
@ -106,7 +105,6 @@ class BlurImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default BlurImage; export default BlurImage;

View File

@ -9,13 +9,18 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import {
Jimp,
JimpMime,
ResizeStrategy,
HorizontalAlign,
VerticalAlign,
} from "jimp";
/** /**
* Contain Image operation * Contain Image operation
*/ */
class ContainImage extends Operation { class ContainImage extends Operation {
/** /**
* ContainImage constructor * ContainImage constructor
*/ */
@ -24,7 +29,8 @@ class ContainImage extends Operation {
this.name = "Contain Image"; this.name = "Contain Image";
this.module = "Image"; this.module = "Image";
this.description = "Scales an image to the specified width and height, maintaining the aspect ratio. The image may be letterboxed."; this.description =
"Scales an image to the specified width and height, maintaining the aspect ratio. The image may be letterboxed.";
this.infoURL = ""; this.infoURL = "";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
@ -34,33 +40,25 @@ class ContainImage extends Operation {
name: "Width", name: "Width",
type: "number", type: "number",
value: 100, value: 100,
min: 1 min: 1,
}, },
{ {
name: "Height", name: "Height",
type: "number", type: "number",
value: 100, value: 100,
min: 1 min: 1,
}, },
{ {
name: "Horizontal align", name: "Horizontal align",
type: "option", type: "option",
value: [ value: ["Left", "Center", "Right"],
"Left", defaultIndex: 1,
"Center",
"Right"
],
defaultIndex: 1
}, },
{ {
name: "Vertical align", name: "Vertical align",
type: "option", type: "option",
value: [ value: ["Top", "Middle", "Bottom"],
"Top", defaultIndex: 1,
"Middle",
"Bottom"
],
defaultIndex: 1
}, },
{ {
name: "Resizing algorithm", name: "Resizing algorithm",
@ -70,15 +68,15 @@ class ContainImage extends Operation {
"Bilinear", "Bilinear",
"Bicubic", "Bicubic",
"Hermite", "Hermite",
"Bezier" "Bezier",
], ],
defaultIndex: 1 defaultIndex: 1,
}, },
{ {
name: "Opaque background", name: "Opaque background",
type: "boolean", type: "boolean",
value: true value: true,
} },
]; ];
} }
@ -91,20 +89,20 @@ class ContainImage extends Operation {
const [width, height, hAlign, vAlign, alg, opaqueBg] = args; const [width, height, hAlign, vAlign, alg, opaqueBg] = args;
const resizeMap = { const resizeMap = {
"Nearest Neighbour": Jimp.RESIZE_NEAREST_NEIGHBOR, "Nearest Neighbour": ResizeStrategy.NEAREST_NEIGHBOR,
"Bilinear": Jimp.RESIZE_BILINEAR, Bilinear: ResizeStrategy.BILINEAR,
"Bicubic": Jimp.RESIZE_BICUBIC, Bicubic: ResizeStrategy.BICUBIC,
"Hermite": Jimp.RESIZE_HERMITE, Hermite: ResizeStrategy.HERMITE,
"Bezier": Jimp.RESIZE_BEZIER Bezier: ResizeStrategy.BEZIER,
}; };
const alignMap = { const alignMap = {
"Left": Jimp.HORIZONTAL_ALIGN_LEFT, Left: HorizontalAlign.LEFT,
"Center": Jimp.HORIZONTAL_ALIGN_CENTER, Center: HorizontalAlign.CENTER,
"Right": Jimp.HORIZONTAL_ALIGN_RIGHT, Right: HorizontalAlign.RIGHT,
"Top": Jimp.VERTICAL_ALIGN_TOP, Top: VerticalAlign.TOP,
"Middle": Jimp.VERTICAL_ALIGN_MIDDLE, Middle: VerticalAlign.MIDDLE,
"Bottom": Jimp.VERTICAL_ALIGN_BOTTOM Bottom: VerticalAlign.BOTTOM,
}; };
if (!isImage(input)) { if (!isImage(input)) {
@ -117,22 +115,35 @@ class ContainImage extends Operation {
} catch (err) { } catch (err) {
throw new OperationError(`Error loading image. (${err})`); throw new OperationError(`Error loading image. (${err})`);
} }
const originalMime = image.mime;
try { try {
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Containing image..."); self.sendStatusMessage("Containing image...");
image.contain(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]); image.contain({
w: width,
h: height,
align: alignMap[hAlign] | alignMap[vAlign],
mode: resizeMap[alg],
});
if (opaqueBg) { if (opaqueBg) {
const newImage = await Jimp.read(width, height, 0x000000FF); const newImage = new Jimp({
newImage.blit(image, 0, 0); width,
image = newImage; height,
color: 0x000000ff,
});
image = newImage.blit({
src: image,
x: 0,
y: 0,
});
} }
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (originalMime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(originalMime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
@ -156,7 +167,6 @@ class ContainImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default ContainImage; export default ContainImage;

View File

@ -8,13 +8,12 @@ import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs"; import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime, PNGFilterType } from "jimp";
/** /**
* Convert Image Format operation * Convert Image Format operation
*/ */
class ConvertImageFormat extends Operation { class ConvertImageFormat extends Operation {
/** /**
* ConvertImageFormat constructor * ConvertImageFormat constructor
*/ */
@ -23,7 +22,8 @@ class ConvertImageFormat extends Operation {
this.name = "Convert Image Format"; this.name = "Convert Image Format";
this.module = "Image"; this.module = "Image";
this.description = "Converts an image between different formats. Supported formats:<br><ul><li>Joint Photographic Experts Group (JPEG)</li><li>Portable Network Graphics (PNG)</li><li>Bitmap (BMP)</li><li>Tagged Image File Format (TIFF)</li></ul><br>Note: GIF files are supported for input, but cannot be outputted."; this.description =
"Converts an image between different formats. Supported formats:<br><ul><li>Joint Photographic Experts Group (JPEG)</li><li>Portable Network Graphics (PNG)</li><li>Bitmap (BMP)</li><li>Tagged Image File Format (TIFF)</li></ul><br>Note: GIF files are supported for input, but cannot be outputted.";
this.infoURL = "https://wikipedia.org/wiki/Image_file_formats"; this.infoURL = "https://wikipedia.org/wiki/Image_file_formats";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
@ -32,39 +32,27 @@ class ConvertImageFormat extends Operation {
{ {
name: "Output Format", name: "Output Format",
type: "option", type: "option",
value: [ value: ["JPEG", "PNG", "BMP", "TIFF"],
"JPEG",
"PNG",
"BMP",
"TIFF"
]
}, },
{ {
name: "JPEG Quality", name: "JPEG Quality",
type: "number", type: "number",
value: 80, value: 80,
min: 1, min: 1,
max: 100 max: 100,
}, },
{ {
name: "PNG Filter Type", name: "PNG Filter Type",
type: "option", type: "option",
value: [ value: ["Auto", "None", "Sub", "Up", "Average", "Paeth"],
"Auto",
"None",
"Sub",
"Up",
"Average",
"Paeth"
]
}, },
{ {
name: "PNG Deflate Level", name: "PNG Deflate Level",
type: "number", type: "number",
value: 9, value: 9,
min: 0, min: 0,
max: 9 max: 9,
} },
]; ];
} }
@ -76,19 +64,19 @@ class ConvertImageFormat extends Operation {
async run(input, args) { async run(input, args) {
const [format, jpegQuality, pngFilterType, pngDeflateLevel] = args; const [format, jpegQuality, pngFilterType, pngDeflateLevel] = args;
const formatMap = { const formatMap = {
"JPEG": Jimp.MIME_JPEG, JPEG: JimpMime.jpeg,
"PNG": Jimp.MIME_PNG, PNG: JimpMime.png,
"BMP": Jimp.MIME_BMP, BMP: JimpMime.bmp,
"TIFF": Jimp.MIME_TIFF TIFF: JimpMime.tiff,
}; };
const pngFilterMap = { const pngFilterMap = {
"Auto": Jimp.PNG_FILTER_AUTO, Auto: PNGFilterType.AUTO,
"None": Jimp.PNG_FILTER_NONE, None: PNGFilterType.NONE,
"Sub": Jimp.PNG_FILTER_SUB, Sub: PNGFilterType.SUB,
"Up": Jimp.PNG_FILTER_UP, Up: PNGFilterType.UP,
"Average": Jimp.PNG_FILTER_AVERAGE, Average: PNGFilterType.AVERAGE,
"Paeth": Jimp.PNG_FILTER_PATH Paeth: PNGFilterType.PATH,
}; };
const mime = formatMap[format]; const mime = formatMap[format];
@ -103,18 +91,25 @@ class ConvertImageFormat extends Operation {
throw new OperationError(`Error opening image file. (${err})`); throw new OperationError(`Error opening image file. (${err})`);
} }
try { try {
switch (format) { let buffer;
case "JPEG": switch (mime) {
image.quality(jpegQuality); case JimpMime.jpeg:
buffer = await image.getBuffer(mime, {
quality: jpegQuality,
});
break; break;
case "PNG": case JimpMime.png:
image.filterType(pngFilterMap[pngFilterType]); buffer = await image.getBuffer(mime, {
image.deflateLevel(pngDeflateLevel); filterType: pngFilterMap[pngFilterType],
deflateLevel: pngDeflateLevel,
});
break;
default:
buffer = await image.getBuffer(mime);
break; break;
} }
const imageBuffer = await image.getBufferAsync(mime); return buffer.buffer;
return imageBuffer.buffer;
} catch (err) { } catch (err) {
throw new OperationError(`Error converting image format. (${err})`); throw new OperationError(`Error converting image format. (${err})`);
} }
@ -137,7 +132,6 @@ class ConvertImageFormat extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default ConvertImageFormat; export default ConvertImageFormat;

View File

@ -9,13 +9,18 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp/es/index.js"; import {
Jimp,
JimpMime,
ResizeStrategy,
HorizontalAlign,
VerticalAlign,
} from "jimp";
/** /**
* Cover Image operation * Cover Image operation
*/ */
class CoverImage extends Operation { class CoverImage extends Operation {
/** /**
* CoverImage constructor * CoverImage constructor
*/ */
@ -24,7 +29,8 @@ class CoverImage extends Operation {
this.name = "Cover Image"; this.name = "Cover Image";
this.module = "Image"; this.module = "Image";
this.description = "Scales the image to the given width and height, keeping the aspect ratio. The image may be clipped."; this.description =
"Scales the image to the given width and height, keeping the aspect ratio. The image may be clipped.";
this.infoURL = ""; this.infoURL = "";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
@ -34,33 +40,25 @@ class CoverImage extends Operation {
name: "Width", name: "Width",
type: "number", type: "number",
value: 100, value: 100,
min: 1 min: 1,
}, },
{ {
name: "Height", name: "Height",
type: "number", type: "number",
value: 100, value: 100,
min: 1 min: 1,
}, },
{ {
name: "Horizontal align", name: "Horizontal align",
type: "option", type: "option",
value: [ value: ["Left", "Center", "Right"],
"Left", defaultIndex: 1,
"Center",
"Right"
],
defaultIndex: 1
}, },
{ {
name: "Vertical align", name: "Vertical align",
type: "option", type: "option",
value: [ value: ["Top", "Middle", "Bottom"],
"Top", defaultIndex: 1,
"Middle",
"Bottom"
],
defaultIndex: 1
}, },
{ {
name: "Resizing algorithm", name: "Resizing algorithm",
@ -70,10 +68,10 @@ class CoverImage extends Operation {
"Bilinear", "Bilinear",
"Bicubic", "Bicubic",
"Hermite", "Hermite",
"Bezier" "Bezier",
], ],
defaultIndex: 1 defaultIndex: 1,
} },
]; ];
} }
@ -86,20 +84,20 @@ class CoverImage extends Operation {
const [width, height, hAlign, vAlign, alg] = args; const [width, height, hAlign, vAlign, alg] = args;
const resizeMap = { const resizeMap = {
"Nearest Neighbour": jimp.RESIZE_NEAREST_NEIGHBOR, "Nearest Neighbour": ResizeStrategy.NEAREST_NEIGHBOR,
"Bilinear": jimp.RESIZE_BILINEAR, Bilinear: ResizeStrategy.BILINEAR,
"Bicubic": jimp.RESIZE_BICUBIC, Bicubic: ResizeStrategy.BICUBIC,
"Hermite": jimp.RESIZE_HERMITE, Hermite: ResizeStrategy.HERMITE,
"Bezier": jimp.RESIZE_BEZIER Bezier: ResizeStrategy.BEZIER,
}; };
const alignMap = { const alignMap = {
"Left": jimp.HORIZONTAL_ALIGN_LEFT, Left: HorizontalAlign.LEFT,
"Center": jimp.HORIZONTAL_ALIGN_CENTER, Center: HorizontalAlign.CENTER,
"Right": jimp.HORIZONTAL_ALIGN_RIGHT, Right: HorizontalAlign.RIGHT,
"Top": jimp.VERTICAL_ALIGN_TOP, Top: VerticalAlign.TOP,
"Middle": jimp.VERTICAL_ALIGN_MIDDLE, Middle: VerticalAlign.MIDDLE,
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM Bottom: VerticalAlign.BOTTOM,
}; };
if (!isImage(input)) { if (!isImage(input)) {
@ -108,19 +106,24 @@ class CoverImage extends Operation {
let image; let image;
try { try {
image = await jimp.read(input); image = await Jimp.read(input);
} catch (err) { } catch (err) {
throw new OperationError(`Error loading image. (${err})`); throw new OperationError(`Error loading image. (${err})`);
} }
try { try {
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Covering image..."); self.sendStatusMessage("Covering image...");
image.cover(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]); image.cover({
w: width,
h: height,
align: alignMap[hAlign] | alignMap[vAlign],
mode: resizeMap[alg],
});
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
@ -144,7 +147,6 @@ class CoverImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default CoverImage; export default CoverImage;

View File

@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Crop Image operation * Crop Image operation
*/ */
class CropImage extends Operation { class CropImage extends Operation {
/** /**
* CropImage constructor * CropImage constructor
*/ */
@ -24,7 +23,8 @@ class CropImage extends Operation {
this.name = "Crop Image"; this.name = "Crop Image";
this.module = "Image"; this.module = "Image";
this.description = "Crops an image to the specified region, or automatically crops edges.<br><br><b><u>Autocrop</u></b><br>Automatically crops same-colour borders from the image.<br><br><u>Autocrop tolerance</u><br>A percentage value for the tolerance of colour difference between pixels.<br><br><u>Only autocrop frames</u><br>Only crop real frames (all sides must have the same border)<br><br><u>Symmetric autocrop</u><br>Force autocrop to be symmetric (top/bottom and left/right are cropped by the same amount)<br><br><u>Autocrop keep border</u><br>The number of pixels of border to leave around the image."; this.description =
"Crops an image to the specified region, or automatically crops edges.<br><br><b><u>Autocrop</u></b><br>Automatically crops same-colour borders from the image.<br><br><u>Autocrop tolerance</u><br>A percentage value for the tolerance of colour difference between pixels.<br><br><u>Only autocrop frames</u><br>Only crop real frames (all sides must have the same border)<br><br><u>Symmetric autocrop</u><br>Force autocrop to be symmetric (top/bottom and left/right are cropped by the same amount)<br><br><u>Autocrop keep border</u><br>The number of pixels of border to leave around the image.";
this.infoURL = "https://wikipedia.org/wiki/Cropping_(image)"; this.infoURL = "https://wikipedia.org/wiki/Cropping_(image)";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
@ -34,30 +34,30 @@ class CropImage extends Operation {
name: "X Position", name: "X Position",
type: "number", type: "number",
value: 0, value: 0,
min: 0 min: 0,
}, },
{ {
name: "Y Position", name: "Y Position",
type: "number", type: "number",
value: 0, value: 0,
min: 0 min: 0,
}, },
{ {
name: "Width", name: "Width",
type: "number", type: "number",
value: 10, value: 10,
min: 1 min: 1,
}, },
{ {
name: "Height", name: "Height",
type: "number", type: "number",
value: 10, value: 10,
min: 1 min: 1,
}, },
{ {
name: "Autocrop", name: "Autocrop",
type: "boolean", type: "boolean",
value: false value: false,
}, },
{ {
name: "Autocrop tolerance (%)", name: "Autocrop tolerance (%)",
@ -65,24 +65,24 @@ class CropImage extends Operation {
value: 0.02, value: 0.02,
min: 0, min: 0,
max: 100, max: 100,
step: 0.01 step: 0.01,
}, },
{ {
name: "Only autocrop frames", name: "Only autocrop frames",
type: "boolean", type: "boolean",
value: true value: true,
}, },
{ {
name: "Symmetric autocrop", name: "Symmetric autocrop",
type: "boolean", type: "boolean",
value: false value: false,
}, },
{ {
name: "Autocrop keep border (px)", name: "Autocrop keep border (px)",
type: "number", type: "number",
value: 0, value: 0,
min: 0 min: 0,
} },
]; ];
} }
@ -92,7 +92,17 @@ class CropImage extends Operation {
* @returns {byteArray} * @returns {byteArray}
*/ */
async run(input, args) { async run(input, args) {
const [xPos, yPos, width, height, autocrop, autoTolerance, autoFrames, autoSymmetric, autoBorder] = args; const [
xPos,
yPos,
width,
height,
autocrop,
autoTolerance,
autoFrames,
autoSymmetric,
autoBorder,
] = args;
if (!isImage(input)) { if (!isImage(input)) {
throw new OperationError("Invalid file type."); throw new OperationError("Invalid file type.");
} }
@ -108,20 +118,25 @@ class CropImage extends Operation {
self.sendStatusMessage("Cropping image..."); self.sendStatusMessage("Cropping image...");
if (autocrop) { if (autocrop) {
image.autocrop({ image.autocrop({
tolerance: (autoTolerance / 100), tolerance: autoTolerance / 100,
cropOnlyFrames: autoFrames, cropOnlyFrames: autoFrames,
cropSymmetric: autoSymmetric, cropSymmetric: autoSymmetric,
leaveBorder: autoBorder leaveBorder: autoBorder,
}); });
} else { } else {
image.crop(xPos, yPos, width, height); image.crop({
x: xPos,
y: yPos,
w: width,
h: height,
});
} }
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
@ -145,7 +160,6 @@ class CropImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default CropImage; export default CropImage;

View File

@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Image Dither operation * Image Dither operation
*/ */
class DitherImage extends Operation { class DitherImage extends Operation {
/** /**
* DitherImage constructor * DitherImage constructor
*/ */
@ -51,17 +50,19 @@ class DitherImage extends Operation {
try { try {
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Applying dither to image..."); self.sendStatusMessage("Applying dither to image...");
image.dither565(); image.dither();
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
throw new OperationError(`Error applying dither to image. (${err})`); throw new OperationError(
`Error applying dither to image. (${err})`,
);
} }
} }
@ -81,7 +82,6 @@ class DitherImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default DitherImage; export default DitherImage;

View File

@ -44,23 +44,6 @@ class EscapeUnicodeCharacters extends Operation {
"value": true "value": true
} }
]; ];
this.checks = [
{
pattern: "\\\\u(?:[\\da-f]{4,6})",
flags: "i",
args: ["\\u"]
},
{
pattern: "%u(?:[\\da-f]{4,6})",
flags: "i",
args: ["%u"]
},
{
pattern: "U\\+(?:[\\da-f]{4,6})",
flags: "i",
args: ["U+"]
}
];
} }
/** /**

View File

@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs"; import Utils from "../Utils.mjs";
import { fromBinary } from "../lib/Binary.mjs"; import { fromBinary } from "../lib/Binary.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp } from "jimp";
/** /**
* Extract LSB operation * Extract LSB operation
*/ */
class ExtractLSB extends Operation { class ExtractLSB extends Operation {
/** /**
* ExtractLSB constructor * ExtractLSB constructor
*/ */
@ -24,8 +23,10 @@ class ExtractLSB extends Operation {
this.name = "Extract LSB"; this.name = "Extract LSB";
this.module = "Image"; this.module = "Image";
this.description = "Extracts the Least Significant Bit data from each pixel in an image. This is a common way to hide data in Steganography."; this.description =
this.infoURL = "https://wikipedia.org/wiki/Bit_numbering#Least_significant_bit_in_digital_steganography"; "Extracts the Least Significant Bit data from each pixel in an image. This is a common way to hide data in Steganography.";
this.infoURL =
"https://wikipedia.org/wiki/Bit_numbering#Least_significant_bit_in_digital_steganography";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "byteArray"; this.outputType = "byteArray";
this.args = [ this.args = [
@ -57,8 +58,8 @@ class ExtractLSB extends Operation {
{ {
name: "Bit", name: "Bit",
type: "number", type: "number",
value: 0 value: 0,
} },
]; ];
} }
@ -68,21 +69,27 @@ class ExtractLSB extends Operation {
* @returns {byteArray} * @returns {byteArray}
*/ */
async run(input, args) { async run(input, args) {
if (!isImage(input)) throw new OperationError("Please enter a valid image file."); if (!isImage(input))
throw new OperationError("Please enter a valid image file.");
const bit = 7 - args.pop(), const bit = 7 - args.pop(),
pixelOrder = args.pop(), pixelOrder = args.pop(),
colours = args.filter(option => option !== "").map(option => COLOUR_OPTIONS.indexOf(option)), colours = args
.filter((option) => option !== "")
.map((option) => COLOUR_OPTIONS.indexOf(option)),
parsedImage = await Jimp.read(input), parsedImage = await Jimp.read(input),
width = parsedImage.bitmap.width, width = parsedImage.bitmap.width,
height = parsedImage.bitmap.height, height = parsedImage.bitmap.height,
rgba = parsedImage.bitmap.data; rgba = parsedImage.bitmap.data;
if (bit < 0 || bit > 7) { if (bit < 0 || bit > 7) {
throw new OperationError("Error: Bit argument must be between 0 and 7"); throw new OperationError(
"Error: Bit argument must be between 0 and 7",
);
} }
let i, combinedBinary = ""; let i,
combinedBinary = "";
if (pixelOrder === "Row") { if (pixelOrder === "Row") {
for (i = 0; i < rgba.length; i += 4) { for (i = 0; i < rgba.length; i += 4) {
@ -106,7 +113,6 @@ class ExtractLSB extends Operation {
return fromBinary(combinedBinary); return fromBinary(combinedBinary);
} }
} }
const COLOUR_OPTIONS = ["R", "G", "B", "A"]; const COLOUR_OPTIONS = ["R", "G", "B", "A"];

View File

@ -7,15 +7,14 @@
import Operation from "../Operation.mjs"; import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs"; import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp } from "jimp";
import {RGBA_DELIM_OPTIONS} from "../lib/Delim.mjs"; import { RGBA_DELIM_OPTIONS } from "../lib/Delim.mjs";
/** /**
* Extract RGBA operation * Extract RGBA operation
*/ */
class ExtractRGBA extends Operation { class ExtractRGBA extends Operation {
/** /**
* ExtractRGBA constructor * ExtractRGBA constructor
*/ */
@ -24,7 +23,8 @@ class ExtractRGBA extends Operation {
this.name = "Extract RGBA"; this.name = "Extract RGBA";
this.module = "Image"; this.module = "Image";
this.description = "Extracts each pixel's RGBA value in an image. These are sometimes used in Steganography to hide text or data."; this.description =
"Extracts each pixel's RGBA value in an image. These are sometimes used in Steganography to hide text or data.";
this.infoURL = "https://wikipedia.org/wiki/RGBA_color_space"; this.infoURL = "https://wikipedia.org/wiki/RGBA_color_space";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "string"; this.outputType = "string";
@ -32,13 +32,13 @@ class ExtractRGBA extends Operation {
{ {
name: "Delimiter", name: "Delimiter",
type: "editableOption", type: "editableOption",
value: RGBA_DELIM_OPTIONS value: RGBA_DELIM_OPTIONS,
}, },
{ {
name: "Include Alpha", name: "Include Alpha",
type: "boolean", type: "boolean",
value: true value: true,
} },
]; ];
} }
@ -48,18 +48,20 @@ class ExtractRGBA extends Operation {
* @returns {string} * @returns {string}
*/ */
async run(input, args) { async run(input, args) {
if (!isImage(input)) throw new OperationError("Please enter a valid image file."); if (!isImage(input))
throw new OperationError("Please enter a valid image file.");
const delimiter = args[0], const delimiter = args[0],
includeAlpha = args[1], includeAlpha = args[1],
parsedImage = await Jimp.read(input); parsedImage = await Jimp.read(input);
let bitmap = parsedImage.bitmap.data; let bitmap = parsedImage.bitmap.data;
bitmap = includeAlpha ? bitmap : bitmap.filter((val, idx) => idx % 4 !== 3); bitmap = includeAlpha ?
bitmap :
bitmap.filter((val, idx) => idx % 4 !== 3);
return bitmap.join(delimiter); return bitmap.join(delimiter);
} }
} }
export default ExtractRGBA; export default ExtractRGBA;

View File

@ -0,0 +1,80 @@
/**
* @author ThePlayer372-FR []
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { fromBase64 } from "../lib/Base64.mjs";
/**
* Flask Session Decode operation
*/
class FlaskSessionDecode extends Operation {
/**
* FlaskSessionDecode constructor
*/
constructor() {
super();
this.name = "Flask Session Decode";
this.module = "Crypto";
this.description = "Decodes the payload of a Flask session cookie (itsdangerous) into JSON.";
this.inputType = "string";
this.outputType = "JSON";
this.args = [
{
name: "View TimeStamp",
type: "boolean",
value: false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {Object[]}
*/
run(input, args) {
input = input.trim();
const parts = input.split(".");
if (parts.length !== 3) {
throw new OperationError("Invalid Flask token format. Expected payload.timestamp.signature");
}
const payloadB64 = parts[0];
const time = parts[1];
const timeB64 = time.replace(/-/g, "+").replace(/_/g, "/");
const binary = fromBase64(timeB64);
const bytes = new Uint8Array(4);
for (let i = 0; i < 4; i++) {
bytes[i] = binary.charCodeAt(i);
}
const view = new DataView(bytes.buffer);
const timestamp = view.getInt32(0, false);
const base64 = payloadB64.replace(/-/g, "+").replace(/_/g, "/");
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
let payloadJson;
try {
payloadJson = fromBase64(padded);
} catch (e) {
throw new OperationError("Invalid Base64 payload");
}
try {
let data = JSON.parse(payloadJson);
if (args[0]) {
data = {payload: data, timestamp: timestamp};
}
return data;
} catch (e) {
throw new OperationError("Unable to decode JSON payload: " + e.message);
}
}
}
export default FlaskSessionDecode;

View File

@ -0,0 +1,89 @@
/**
* @author ThePlayer372-FR []
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import CryptoApi from "crypto-api/src/crypto-api.mjs";
import Utils from "../Utils.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Flask Session Sign operation
*/
class FlaskSessionSign extends Operation {
/**
* FlaskSessionSign constructor
*/
constructor() {
super();
this.name = "Flask Session Sign";
this.module = "Crypto";
this.description = "Signs a JSON payload to produce a Flask session cookie (itsdangerous HMAC).";
this.inputType = "JSON";
this.outputType = "string";
this.args = [
{
name: "Key",
type: "toggleString",
value: "",
toggleValues: ["Hex", "Decimal", "Binary", "Base64", "UTF8", "Latin1"]
},
{
name: "Salt",
type: "toggleString",
value: "cookie-session",
toggleValues: ["UTF8", "Hex", "Decimal", "Binary", "Base64", "Latin1"]
},
{
name: "Algorithm",
type: "option",
value: ["sha1", "sha256"],
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!args[0].string) {
throw new OperationError("Secret key required");
}
const key = Utils.convertToByteString(args[0].string, args[0].option);
const salt = Utils.convertToByteString(args[1].string || "cookie-session", args[1].option);
const algorithm = args[2] || "sha1";
const payloadB64 = toBase64(Utils.strToByteArray(JSON.stringify(input)));
const payload = payloadB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
const derivedKey = CryptoApi.getHmac(key, CryptoApi.getHasher(algorithm));
derivedKey.update(salt);
const currentTimeStamp = Math.ceil(Date.now() / 1000);
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setInt32(0, currentTimeStamp, false);
const bytes = new Uint8Array(buffer);
let binary = "";
bytes.forEach(b => binary += String.fromCharCode(b));
const timeB64 = toBase64(Utils.strToByteArray(binary));
const time = timeB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
const data = Utils.convertToByteString(payload + "." + time, "utf8");
const sign = CryptoApi.getHmac(derivedKey.finalize(), CryptoApi.getHasher(algorithm));
sign.update(data);
const signB64 = toBase64(sign.finalize());
const sign64 = signB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
return payload + "." + time + "." + sign64;
}
}
export default FlaskSessionSign;

View File

@ -0,0 +1,136 @@
/**
* @author ThePlayer372-FR []
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import CryptoApi from "crypto-api/src/crypto-api.mjs";
import Utils from "../Utils.mjs";
import { toBase64, fromBase64 } from "../lib/Base64.mjs";
/**
* Flask Session Verify operation
*/
class FlaskSessionVerify extends Operation {
/**
* FlaskSessionVerify constructor
*/
constructor() {
super();
this.name = "Flask Session Verify";
this.module = "Crypto";
this.description = "Verifies the HMAC signature of a Flask session cookie (itsdangerous) generated.";
this.inputType = "string";
this.outputType = "JSON";
this.args = [
{
name: "Key",
type: "toggleString",
value: "",
toggleValues: ["Hex", "Decimal", "Binary", "Base64", "UTF8", "Latin1"]
},
{
name: "Salt",
type: "toggleString",
value: "cookie-session",
toggleValues: ["UTF8", "Hex", "Decimal", "Binary", "Base64", "Latin1"]
},
{
name: "Algorithm",
type: "option",
value: ["sha1", "sha256"],
},
{
name: "View TimeStamp",
type: "boolean",
value: true
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!args[0].string) {
throw new OperationError("Secret key required");
}
const key = Utils.convertToByteString(args[0].string, args[0].option);
const salt = Utils.convertToByteString(args[1].string || "cookie-session", args[1].option);
const algorithm = args[2] || "sha1";
input = input.trim();
const parts = input.split(".");
if (parts.length !== 3) {
throw new OperationError("Invalid Flask token format. Expected payload.timestamp.signature");
}
const data = Utils.convertToByteString(parts[0] + "." + parts[1], "utf8");
const derivedKey = CryptoApi.getHmac(key, CryptoApi.getHasher(algorithm));
derivedKey.update(salt);
const sign = CryptoApi.getHmac(derivedKey.finalize(), CryptoApi.getHasher(algorithm));
sign.update(data);
const payloadB64 = parts[0];
const base64 = payloadB64.replace(/-/g, "+").replace(/_/g, "/");
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
const time = parts[1];
const timeB64 = time.replace(/-/g, "+").replace(/_/g, "/");
const binary = fromBase64(timeB64);
const bytes = new Uint8Array(4);
for (let i = 0; i < 4; i++) {
bytes[i] = binary.charCodeAt(i);
}
const view = new DataView(bytes.buffer);
const timestamp = view.getInt32(0, false);
let payloadJson;
try {
payloadJson = fromBase64(padded);
} catch (e) {
throw new OperationError("Invalid Base64 payload");
}
const signB64 = toBase64(sign.finalize());
const sign64 = signB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
if (sign64 !== parts[2]) {
throw new OperationError("Invalid signature!");
}
try {
const decoded = JSON.parse(payloadJson);
if (!args[3]) {
return {
valid: true,
payload: decoded,
};
} else {
return {
valid: true,
payload: decoded,
timestamp: timestamp
};
}
} catch (e) {
throw new OperationError("Unable to decode JSON payload: " + e.message);
}
}
}
export default FlaskSessionVerify;

View File

@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Flip Image operation * Flip Image operation
*/ */
class FlipImage extends Operation { class FlipImage extends Operation {
/** /**
* FlipImage constructor * FlipImage constructor
*/ */
@ -33,8 +32,8 @@ class FlipImage extends Operation {
{ {
name: "Axis", name: "Axis",
type: "option", type: "option",
value: ["Horizontal", "Vertical"] value: ["Horizontal", "Vertical"],
} },
]; ];
} }
@ -60,18 +59,24 @@ class FlipImage extends Operation {
self.sendStatusMessage("Flipping image..."); self.sendStatusMessage("Flipping image...");
switch (flipAxis) { switch (flipAxis) {
case "Horizontal": case "Horizontal":
image.flip(true, false); image.flip({
horizontal: true,
vertical: false,
});
break; break;
case "Vertical": case "Vertical":
image.flip(false, true); image.flip({
horizontal: false,
vertical: true,
});
break; break;
} }
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
@ -95,7 +100,6 @@ class FlipImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default FlipImage; export default FlipImage;

View File

@ -23,7 +23,7 @@ class FromQuotedPrintable extends Operation {
this.name = "From Quoted Printable"; this.name = "From Quoted Printable";
this.module = "Default"; this.module = "Default";
this.description = "Converts QP-encoded text back to standard text.<br><br>e.g. The quoted-printable encoded string <code>hello=20world</code> becomes <code>hello world</code>"; this.description = "Converts QP-encoded text back to standard text. This format is a content transfer encoding common in email messages.<br><br>e.g. The quoted-printable encoded string <code>hello=20world</code> becomes <code>hello world</code>";
this.infoURL = "https://wikipedia.org/wiki/Quoted-printable"; this.infoURL = "https://wikipedia.org/wiki/Quoted-printable";
this.inputType = "string"; this.inputType = "string";
this.outputType = "byteArray"; this.outputType = "byteArray";

View File

@ -7,16 +7,15 @@
import Operation from "../Operation.mjs"; import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs"; import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs"; import Utils from "../Utils.mjs";
import {isImage} from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import {toBase64} from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import {isWorkerEnvironment} from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime, ResizeStrategy, rgbaToInt } from "jimp";
/** /**
* Generate Image operation * Generate Image operation
*/ */
class GenerateImage extends Operation { class GenerateImage extends Operation {
/** /**
* GenerateImage constructor * GenerateImage constructor
*/ */
@ -25,27 +24,28 @@ class GenerateImage extends Operation {
this.name = "Generate Image"; this.name = "Generate Image";
this.module = "Image"; this.module = "Image";
this.description = "Generates an image using the input as pixel values."; this.description =
"Generates an image using the input as pixel values.";
this.infoURL = ""; this.infoURL = "";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
this.presentType = "html"; this.presentType = "html";
this.args = [ this.args = [
{ {
"name": "Mode", name: "Mode",
"type": "option", type: "option",
"value": ["Greyscale", "RG", "RGB", "RGBA", "Bits"] value: ["Greyscale", "RG", "RGB", "RGBA", "Bits"],
}, },
{ {
"name": "Pixel Scale Factor", name: "Pixel Scale Factor",
"type": "number", type: "number",
"value": 8, value: 8,
}, },
{ {
"name": "Pixels per row", name: "Pixels per row",
"type": "number", type: "number",
"value": 64, value: 64,
} },
]; ];
} }
@ -67,21 +67,23 @@ class GenerateImage extends Operation {
} }
const bytePerPixelMap = { const bytePerPixelMap = {
"Greyscale": 1, Greyscale: 1,
"RG": 2, RG: 2,
"RGB": 3, RGB: 3,
"RGBA": 4, RGBA: 4,
"Bits": 1/8, Bits: 1 / 8,
}; };
const bytesPerPixel = bytePerPixelMap[mode]; const bytesPerPixel = bytePerPixelMap[mode];
if (bytesPerPixel > 0 && input.length % bytesPerPixel !== 0) { if (bytesPerPixel > 0 && input.length % bytesPerPixel !== 0) {
throw new OperationError(`Number of bytes is not a divisor of ${bytesPerPixel}`); throw new OperationError(
`Number of bytes is not a divisor of ${bytesPerPixel}`,
);
} }
const height = Math.ceil(input.length / bytesPerPixel / width); const height = Math.ceil(input.length / bytesPerPixel / width);
const image = await new Jimp(width, height, (err, image) => {}); const image = new Jimp({ width, height });
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Generating image from data..."); self.sendStatusMessage("Generating image from data...");
@ -94,8 +96,8 @@ class GenerateImage extends Operation {
const x = index % width; const x = index % width;
const y = Math.floor(index / width); const y = Math.floor(index / width);
const value = curByte[k] === "0" ? 0xFF : 0x00; const value = curByte[k] === "0" ? 0xff : 0x00;
const pixel = Jimp.rgbaToInt(value, value, value, 0xFF); const pixel = rgbaToInt(value, value, value, 0xff);
image.setPixelColor(pixel, x, y); image.setPixelColor(pixel, x, y);
} }
} }
@ -109,7 +111,7 @@ class GenerateImage extends Operation {
let red = 0x00; let red = 0x00;
let green = 0x00; let green = 0x00;
let blue = 0x00; let blue = 0x00;
let alpha = 0xFF; let alpha = 0xff;
switch (mode) { switch (mode) {
case "Greyscale": case "Greyscale":
@ -139,10 +141,12 @@ class GenerateImage extends Operation {
} }
try { try {
const pixel = Jimp.rgbaToInt(red, green, blue, alpha); const pixel = rgbaToInt(red, green, blue, alpha);
image.setPixelColor(pixel, x, y); image.setPixelColor(pixel, x, y);
} catch (err) { } catch (err) {
throw new OperationError(`Error while generating image from pixel values. (${err})`); throw new OperationError(
`Error while generating image from pixel values. (${err})`,
);
} }
} }
} }
@ -151,11 +155,15 @@ class GenerateImage extends Operation {
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Scaling image..."); self.sendStatusMessage("Scaling image...");
image.scaleToFit(width*scale, height*scale, Jimp.RESIZE_NEAREST_NEIGHBOR); image.scaleToFit({
w: width * scale,
h: height * scale,
mode: ResizeStrategy.NEAREST_NEIGHBOR,
});
} }
try { try {
const imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); const imageBuffer = await image.getBuffer(JimpMime.png);
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
throw new OperationError(`Error generating image. (${err})`); throw new OperationError(`Error generating image. (${err})`);
@ -178,7 +186,6 @@ class GenerateImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default GenerateImage; export default GenerateImage;

View File

@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Image Brightness / Contrast operation * Image Brightness / Contrast operation
*/ */
class ImageBrightnessContrast extends Operation { class ImageBrightnessContrast extends Operation {
/** /**
* ImageBrightnessContrast constructor * ImageBrightnessContrast constructor
*/ */
@ -35,15 +34,15 @@ class ImageBrightnessContrast extends Operation {
type: "number", type: "number",
value: 0, value: 0,
min: -100, min: -100,
max: 100 max: 100,
}, },
{ {
name: "Contrast", name: "Contrast",
type: "number", type: "number",
value: 0, value: 0,
min: -100, min: -100,
max: 100 max: 100,
} },
]; ];
} }
@ -77,14 +76,16 @@ class ImageBrightnessContrast extends Operation {
} }
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
throw new OperationError(`Error adjusting image brightness or contrast. (${err})`); throw new OperationError(
`Error adjusting image brightness or contrast. (${err})`,
);
} }
} }
@ -104,7 +105,6 @@ class ImageBrightnessContrast extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default ImageBrightnessContrast; export default ImageBrightnessContrast;

View File

@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Image Filter operation * Image Filter operation
*/ */
class ImageFilter extends Operation { class ImageFilter extends Operation {
/** /**
* ImageFilter constructor * ImageFilter constructor
*/ */
@ -33,11 +32,8 @@ class ImageFilter extends Operation {
{ {
name: "Filter type", name: "Filter type",
type: "option", type: "option",
value: [ value: ["Greyscale", "Sepia"],
"Greyscale", },
"Sepia"
]
}
]; ];
} }
@ -60,7 +56,11 @@ class ImageFilter extends Operation {
} }
try { try {
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Applying " + filterType.toLowerCase() + " filter to image..."); self.sendStatusMessage(
"Applying " +
filterType.toLowerCase() +
" filter to image...",
);
if (filterType === "Greyscale") { if (filterType === "Greyscale") {
image.greyscale(); image.greyscale();
} else { } else {
@ -68,14 +68,16 @@ class ImageFilter extends Operation {
} }
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
throw new OperationError(`Error applying filter to image. (${err})`); throw new OperationError(
`Error applying filter to image. (${err})`,
);
} }
} }
@ -95,7 +97,6 @@ class ImageFilter extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default ImageFilter; export default ImageFilter;

View File

@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Image Hue/Saturation/Lightness operation * Image Hue/Saturation/Lightness operation
*/ */
class ImageHueSaturationLightness extends Operation { class ImageHueSaturationLightness extends Operation {
/** /**
* ImageHueSaturationLightness constructor * ImageHueSaturationLightness constructor
*/ */
@ -24,7 +23,8 @@ class ImageHueSaturationLightness extends Operation {
this.name = "Image Hue/Saturation/Lightness"; this.name = "Image Hue/Saturation/Lightness";
this.module = "Image"; this.module = "Image";
this.description = "Adjusts the hue / saturation / lightness (HSL) values of an image."; this.description =
"Adjusts the hue / saturation / lightness (HSL) values of an image.";
this.infoURL = ""; this.infoURL = "";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
@ -35,22 +35,22 @@ class ImageHueSaturationLightness extends Operation {
type: "number", type: "number",
value: 0, value: 0,
min: -360, min: -360,
max: 360 max: 360,
}, },
{ {
name: "Saturation", name: "Saturation",
type: "number", type: "number",
value: 0, value: 0,
min: -100, min: -100,
max: 100 max: 100,
}, },
{ {
name: "Lightness", name: "Lightness",
type: "number", type: "number",
value: 0, value: 0,
min: -100, min: -100,
max: 100 max: 100,
} },
]; ];
} }
@ -76,43 +76,45 @@ class ImageHueSaturationLightness extends Operation {
if (hue !== 0) { if (hue !== 0) {
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Changing image hue..."); self.sendStatusMessage("Changing image hue...");
image.colour([ image.color([
{ {
apply: "hue", apply: "hue",
params: [hue] params: [hue],
} },
]); ]);
} }
if (saturation !== 0) { if (saturation !== 0) {
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Changing image saturation..."); self.sendStatusMessage("Changing image saturation...");
image.colour([ image.color([
{ {
apply: "saturate", apply: "saturate",
params: [saturation] params: [saturation],
} },
]); ]);
} }
if (lightness !== 0) { if (lightness !== 0) {
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Changing image lightness..."); self.sendStatusMessage("Changing image lightness...");
image.colour([ image.color([
{ {
apply: "lighten", apply: "lighten",
params: [lightness] params: [lightness],
} },
]); ]);
} }
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
throw new OperationError(`Error adjusting image hue / saturation / lightness. (${err})`); throw new OperationError(
`Error adjusting image hue / saturation / lightness. (${err})`,
);
} }
} }

View File

@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Image Opacity operation * Image Opacity operation
*/ */
class ImageOpacity extends Operation { class ImageOpacity extends Operation {
/** /**
* ImageOpacity constructor * ImageOpacity constructor
*/ */
@ -35,8 +34,8 @@ class ImageOpacity extends Operation {
type: "number", type: "number",
value: 100, value: 100,
min: 0, min: 0,
max: 100 max: 100,
} },
]; ];
} }
@ -63,10 +62,10 @@ class ImageOpacity extends Operation {
image.opacity(opacity / 100); image.opacity(opacity / 100);
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
@ -90,7 +89,6 @@ class ImageOpacity extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default ImageOpacity; export default ImageOpacity;

View File

@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Invert Image operation * Invert Image operation
*/ */
class InvertImage extends Operation { class InvertImage extends Operation {
/** /**
* InvertImage constructor * InvertImage constructor
*/ */
@ -54,10 +53,10 @@ class InvertImage extends Operation {
image.invert(); image.invert();
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
@ -81,7 +80,6 @@ class InvertImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default InvertImage; export default InvertImage;

View File

@ -6,7 +6,7 @@
import Operation from "../Operation.mjs"; import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs"; import OperationError from "../errors/OperationError.mjs";
import jq from "jq-web"; import * as jq from "jq-wasm";
/** /**
* jq operation * jq operation
@ -40,16 +40,15 @@ class Jq extends Operation {
* @returns {string} * @returns {string}
*/ */
run(input, args) { run(input, args) {
return (async () => {
const [query] = args; const [query] = args;
let result;
try { try {
result = jq.json(input, query); const result = await jq.json(input, query);
return JSON.stringify(result);
} catch (err) { } catch (err) {
throw new OperationError(`Invalid jq expression: ${err.message}`); throw new OperationError(`Invalid jq expression: ${err.message}`);
} }
})();
return JSON.stringify(result);
} }
} }

View File

@ -8,13 +8,12 @@ import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs"; import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Normalise Image operation * Normalise Image operation
*/ */
class NormaliseImage extends Operation { class NormaliseImage extends Operation {
/** /**
* NormaliseImage constructor * NormaliseImage constructor
*/ */
@ -27,7 +26,7 @@ class NormaliseImage extends Operation {
this.infoURL = ""; this.infoURL = "";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
this.presentType= "html"; this.presentType = "html";
this.args = []; this.args = [];
} }
@ -52,10 +51,10 @@ class NormaliseImage extends Operation {
image.normalize(); image.normalize();
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
@ -79,7 +78,6 @@ class NormaliseImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default NormaliseImage; export default NormaliseImage;

View File

@ -13,7 +13,6 @@ import { parseQrCode } from "../lib/QRCode.mjs";
* Parse QR Code operation * Parse QR Code operation
*/ */
class ParseQRCode extends Operation { class ParseQRCode extends Operation {
/** /**
* ParseQRCode constructor * ParseQRCode constructor
*/ */
@ -22,24 +21,26 @@ class ParseQRCode extends Operation {
this.name = "Parse QR Code"; this.name = "Parse QR Code";
this.module = "Image"; this.module = "Image";
this.description = "Reads an image file and attempts to detect and read a Quick Response (QR) code from the image.<br><br><u>Normalise Image</u><br>Attempts to normalise the image before parsing it to improve detection of a QR code."; this.description =
"Reads an image file and attempts to detect and read a Quick Response (QR) code from the image.<br><br><u>Normalise Image</u><br>Attempts to normalise the image before parsing it to improve detection of a QR code.";
this.infoURL = "https://wikipedia.org/wiki/QR_code"; this.infoURL = "https://wikipedia.org/wiki/QR_code";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "string"; this.outputType = "string";
this.args = [ this.args = [
{ {
"name": "Normalise image", name: "Normalise image",
"type": "boolean", type: "boolean",
"value": false value: false,
} },
]; ];
this.checks = [ this.checks = [
{ {
"pattern": "^(?:\\xff\\xd8\\xff|\\x89\\x50\\x4e\\x47|\\x47\\x49\\x46|.{8}\\x57\\x45\\x42\\x50|\\x42\\x4d)", pattern:
"flags": "", "^(?:\\xff\\xd8\\xff|\\x89\\x50\\x4e\\x47|\\x47\\x49\\x46|.{8}\\x57\\x45\\x42\\x50|\\x42\\x4d)",
"args": [false], flags: "",
"useful": true args: [false],
} useful: true,
},
]; ];
} }
@ -54,9 +55,8 @@ class ParseQRCode extends Operation {
if (!isImage(input)) { if (!isImage(input)) {
throw new OperationError("Invalid file type."); throw new OperationError("Invalid file type.");
} }
return await parseQrCode(input, normalise); return parseQrCode(input, normalise);
} }
} }
export default ParseQRCode; export default ParseQRCode;

View File

@ -0,0 +1,164 @@
/**
* @author cktgh [chankaitung@gmail.com]
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import forge from "node-forge";
import Utils, { isWorkerEnvironment } from "../Utils.mjs";
import { DELIM_OPTIONS } from "../lib/Delim.mjs";
/**
* Pseudo-Random Integer Generator operation
*/
class PseudoRandomIntegerGenerator extends Operation {
// in theory 2**53 is the max range, but we use Number.MAX_SAFE_INTEGER (2**53 - 1) as it is more consistent.
static MAX_RANGE = Number.MAX_SAFE_INTEGER;
// arbitrary choice
static BUFFER_SIZE = 1024;
/**
* PseudoRandomIntegerGenerator constructor
*/
constructor() {
super();
this.name = "Pseudo-Random Integer Generator";
this.module = "Ciphers";
this.description = "A cryptographically-secure pseudo-random number generator (PRNG).<br><br>Generates random integers within a specified range using the browser's built-in <code>crypto.getRandomValues()</code> method if available.<br><br>The supported range of integers is from <code>-(2^53 - 1)</code> to <code>(2^53 - 1)</code>.";
this.infoURL = "https://wikipedia.org/wiki/Pseudorandom_number_generator";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Number of Integers",
"type": "number",
"value": 1,
"min": 1
},
{
"name": "Min Value",
"type": "number",
"value": 0,
"min": Number.MIN_SAFE_INTEGER,
"max": Number.MAX_SAFE_INTEGER
},
{
"name": "Max Value",
"type": "number",
"value": 99,
"min": Number.MIN_SAFE_INTEGER,
"max": Number.MAX_SAFE_INTEGER
},
{
"name": "Delimiter",
"type": "option",
"value": DELIM_OPTIONS
},
{
"name": "Output",
"type": "option",
"value": ["Raw", "Hex", "Decimal"]
}
];
// not using BigUint64Array to avoid BigInt handling overhead
this.randomBuffer = new Uint32Array(PseudoRandomIntegerGenerator.BUFFER_SIZE);
this.randomBufferOffset = PseudoRandomIntegerGenerator.BUFFER_SIZE;
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [numInts, minInt, maxInt, delimiter, outputType] = args;
if (minInt === null || maxInt === null) return "";
const min = Math.ceil(minInt);
const max = Math.floor(maxInt);
const delim = Utils.charRep(delimiter || "Space");
if (!Number.isSafeInteger(min) || !Number.isSafeInteger(max)) {
throw new OperationError("Min and Max must be between `-(2^53 - 1)` and `2^53 - 1`.");
}
if (min > max) {
throw new OperationError("Min cannot be larger than Max.");
}
const range = max - min + 1; // inclusive range
if (range > PseudoRandomIntegerGenerator.MAX_RANGE) {
throw new OperationError("Range between Min and Max cannot be larger than `2^53`");
}
// as large as possible while divisible by range
const rejectionThreshold = PseudoRandomIntegerGenerator.MAX_RANGE - (PseudoRandomIntegerGenerator.MAX_RANGE % range);
const output = [];
for (let i = 0; i < numInts; i++) {
const result = this._generateRandomValue(rejectionThreshold);
const intValue = min + (result % range);
switch (outputType) {
case "Hex":
output.push(intValue.toString(16));
break;
case "Decimal":
output.push(intValue.toString(10));
break;
case "Raw":
default:
output.push(Utils.chr(intValue));
}
}
if (outputType === "Raw") {
return output.join("");
}
return output.join(delim);
}
/**
* Generate a random value, result will be less than the rejection threshold (exclusive).
*
* @param {number} rejectionThreshold
* @returns {number}
*/
_generateRandomValue(rejectionThreshold) {
let result;
do {
if (this.randomBufferOffset + 2 > this.randomBuffer.length) {
this._resetRandomBuffer();
}
// stitching a 53 bit number; not using BigUint64Array to avoid BigInt handling overhead
result = (this.randomBuffer[this.randomBufferOffset++] & 0x1f_ffff) * 0x1_0000_0000 +
this.randomBuffer[this.randomBufferOffset++];
} while (result >= rejectionThreshold);
return result;
}
/**
* Fill random buffer with new random values and rseet the offset.
*/
_resetRandomBuffer() {
if (isWorkerEnvironment() && self.crypto) {
self.crypto.getRandomValues(this.randomBuffer);
} else {
const bytes = forge.random.getBytesSync(this.randomBuffer.length * 4);
for (let j = 0; j < this.randomBuffer.length; j++) {
this.randomBuffer[j] = (bytes.charCodeAt(j * 4) << 24) |
(bytes.charCodeAt(j * 4 + 1) << 16) |
(bytes.charCodeAt(j * 4 + 2) << 8) |
bytes.charCodeAt(j * 4 + 3);
}
}
this.randomBufferOffset = 0;
}
}
export default PseudoRandomIntegerGenerator;

View File

@ -10,13 +10,12 @@ import Utils from "../Utils.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { runHash } from "../lib/Hash.mjs"; import { runHash } from "../lib/Hash.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp } from "jimp";
/** /**
* Randomize Colour Palette operation * Randomize Colour Palette operation
*/ */
class RandomizeColourPalette extends Operation { class RandomizeColourPalette extends Operation {
/** /**
* RandomizeColourPalette constructor * RandomizeColourPalette constructor
*/ */
@ -25,7 +24,8 @@ class RandomizeColourPalette extends Operation {
this.name = "Randomize Colour Palette"; this.name = "Randomize Colour Palette";
this.module = "Image"; this.module = "Image";
this.description = "Randomizes each colour in an image's colour palette. This can often reveal text or symbols that were previously a very similar colour to their surroundings, a technique sometimes used in Steganography."; this.description =
"Randomizes each colour in an image's colour palette. This can often reveal text or symbols that were previously a very similar colour to their surroundings, a technique sometimes used in Steganography.";
this.infoURL = "https://wikipedia.org/wiki/Indexed_color"; this.infoURL = "https://wikipedia.org/wiki/Indexed_color";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
@ -34,8 +34,8 @@ class RandomizeColourPalette extends Operation {
{ {
name: "Seed", name: "Seed",
type: "string", type: "string",
value: "" value: "",
} },
]; ];
} }
@ -45,23 +45,24 @@ class RandomizeColourPalette extends Operation {
* @returns {ArrayBuffer} * @returns {ArrayBuffer}
*/ */
async run(input, args) { async run(input, args) {
if (!isImage(input)) throw new OperationError("Please enter a valid image file."); if (!isImage(input))
throw new OperationError("Please enter a valid image file.");
const seed = args[0] || (Math.random().toString().substr(2)), const seed = args[0] || Math.random().toString().substr(2),
parsedImage = await Jimp.read(input), parsedImage = await Jimp.read(input),
width = parsedImage.bitmap.width, width = parsedImage.bitmap.width,
height = parsedImage.bitmap.height; height = parsedImage.bitmap.height;
let rgbString, rgbHash, rgbHex; let rgbString, rgbHash, rgbHex;
parsedImage.scan(0, 0, width, height, function(x, y, idx) { parsedImage.scan(0, 0, width, height, function (x, y, idx) {
rgbString = this.bitmap.data.slice(idx, idx+3).join("."); rgbString = this.bitmap.data.slice(idx, idx + 3).join(".");
rgbHash = runHash("md5", Utils.strToArrayBuffer(seed + rgbString)); rgbHash = runHash("md5", Utils.strToArrayBuffer(seed + rgbString));
rgbHex = rgbHash.substr(0, 6) + "ff"; rgbHex = rgbHash.substr(0, 6) + "ff";
parsedImage.setPixelColor(parseInt(rgbHex, 16), x, y); parsedImage.setPixelColor(parseInt(rgbHex, 16), x, y);
}); });
const imageBuffer = await parsedImage.getBufferAsync(Jimp.AUTO); const imageBuffer = await parsedImage.getBuffer(parsedImage.mime);
return new Uint8Array(imageBuffer).buffer; return new Uint8Array(imageBuffer).buffer;
} }
@ -77,7 +78,6 @@ class RandomizeColourPalette extends Operation {
return `<img src="data:${type};base64,${toBase64(data)}">`; return `<img src="data:${type};base64,${toBase64(data)}">`;
} }
} }
export default RandomizeColourPalette; export default RandomizeColourPalette;

View File

@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime, ResizeStrategy } from "jimp";
/** /**
* Resize Image operation * Resize Image operation
*/ */
class ResizeImage extends Operation { class ResizeImage extends Operation {
/** /**
* ResizeImage constructor * ResizeImage constructor
*/ */
@ -24,7 +23,8 @@ class ResizeImage extends Operation {
this.name = "Resize Image"; this.name = "Resize Image";
this.module = "Image"; this.module = "Image";
this.description = "Resizes an image to the specified width and height values."; this.description =
"Resizes an image to the specified width and height values.";
this.infoURL = "https://wikipedia.org/wiki/Image_scaling"; this.infoURL = "https://wikipedia.org/wiki/Image_scaling";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
@ -34,23 +34,23 @@ class ResizeImage extends Operation {
name: "Width", name: "Width",
type: "number", type: "number",
value: 100, value: 100,
min: 1 min: 1,
}, },
{ {
name: "Height", name: "Height",
type: "number", type: "number",
value: 100, value: 100,
min: 1 min: 1,
}, },
{ {
name: "Unit type", name: "Unit type",
type: "option", type: "option",
value: ["Pixels", "Percent"] value: ["Pixels", "Percent"],
}, },
{ {
name: "Maintain aspect ratio", name: "Maintain aspect ratio",
type: "boolean", type: "boolean",
value: false value: false,
}, },
{ {
name: "Resizing algorithm", name: "Resizing algorithm",
@ -60,10 +60,10 @@ class ResizeImage extends Operation {
"Bilinear", "Bilinear",
"Bicubic", "Bicubic",
"Hermite", "Hermite",
"Bezier" "Bezier",
], ],
defaultIndex: 1 defaultIndex: 1,
} },
]; ];
} }
@ -80,11 +80,11 @@ class ResizeImage extends Operation {
resizeAlg = args[4]; resizeAlg = args[4];
const resizeMap = { const resizeMap = {
"Nearest Neighbour": Jimp.RESIZE_NEAREST_NEIGHBOR, "Nearest Neighbour": ResizeStrategy.NEAREST_NEIGHBOR,
"Bilinear": Jimp.RESIZE_BILINEAR, Bilinear: ResizeStrategy.BILINEAR,
"Bicubic": Jimp.RESIZE_BICUBIC, Bicubic: ResizeStrategy.BICUBIC,
"Hermite": Jimp.RESIZE_HERMITE, Hermite: ResizeStrategy.HERMITE,
"Bezier": Jimp.RESIZE_BEZIER Bezier: ResizeStrategy.BEZIER,
}; };
if (!isImage(input)) { if (!isImage(input)) {
@ -99,23 +99,31 @@ class ResizeImage extends Operation {
} }
try { try {
if (unit === "Percent") { if (unit === "Percent") {
width = image.getWidth() * (width / 100); width = image.width * (width / 100);
height = image.getHeight() * (height / 100); height = image.height * (height / 100);
} }
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Resizing image..."); self.sendStatusMessage("Resizing image...");
if (aspect) { if (aspect) {
image.scaleToFit(width, height, resizeMap[resizeAlg]); image.scaleToFit({
w: width,
h: height,
mode: resizeMap[resizeAlg],
});
} else { } else {
image.resize(width, height, resizeMap[resizeAlg]); image.resize({
w: width,
h: height,
mode: resizeMap[resizeAlg],
});
} }
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
@ -139,7 +147,6 @@ class ResizeImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default ResizeImage; export default ResizeImage;

View File

@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Rotate Image operation * Rotate Image operation
*/ */
class RotateImage extends Operation { class RotateImage extends Operation {
/** /**
* RotateImage constructor * RotateImage constructor
*/ */
@ -24,7 +23,8 @@ class RotateImage extends Operation {
this.name = "Rotate Image"; this.name = "Rotate Image";
this.module = "Image"; this.module = "Image";
this.description = "Rotates an image by the specified number of degrees."; this.description =
"Rotates an image by the specified number of degrees.";
this.infoURL = ""; this.infoURL = "";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
@ -33,8 +33,8 @@ class RotateImage extends Operation {
{ {
name: "Rotation amount (degrees)", name: "Rotation amount (degrees)",
type: "number", type: "number",
value: 90 value: 90,
} },
]; ];
} }
@ -62,10 +62,10 @@ class RotateImage extends Operation {
image.rotate(degrees); image.rotate(degrees);
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
@ -89,7 +89,6 @@ class RotateImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default RotateImage; export default RotateImage;

View File

@ -3,8 +3,7 @@
* @copyright Crown Copyright 2016 * @copyright Crown Copyright 2016
* @license Apache-2.0 * @license Apache-2.0
*/ */
import { format } from "sql-formatter";
import vkbeautify from "vkbeautify";
import Operation from "../Operation.mjs"; import Operation from "../Operation.mjs";
/** /**
@ -39,7 +38,26 @@ class SQLBeautify extends Operation {
*/ */
run(input, args) { run(input, args) {
const indentStr = args[0]; const indentStr = args[0];
return vkbeautify.sql(input, indentStr); // Extract and replace bind variables like :Bind1 with __BIND_0__
const bindRegex = /:\w+/g;
const bindMap = {};
let bindCounter=0;
const placeholderInput = input.replace(bindRegex, (match) => {
const placeholder = `__BIND_${bindCounter++}__`;
bindMap[placeholder] = match;
return placeholder;
});
// Format the SQL with chosen options
let formatted= format(placeholderInput, {
language: "mysql", // Use MySQL as the default dialect for better compatibility with real-world SQL
useTabs: indentStr==="\t", // true if tab, false if spaces
tabWidth: indentStr.length || 4, // fallback if empty
indentStyle: "standard" // fine for most SQL
});
// Replace placeholders back with original bind variables
formatted = formatted.replace(/__BIND_\d+__/g, match => bindMap[match] || match);
return formatted;
} }
} }

View File

@ -8,15 +8,13 @@ import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs"; import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import { gaussianBlur } from "../lib/ImageManipulation.mjs";
import { isWorkerEnvironment } from "../Utils.mjs"; import { isWorkerEnvironment } from "../Utils.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Sharpen Image operation * Sharpen Image operation
*/ */
class SharpenImage extends Operation { class SharpenImage extends Operation {
/** /**
* SharpenImage constructor * SharpenImage constructor
*/ */
@ -35,22 +33,22 @@ class SharpenImage extends Operation {
name: "Radius", name: "Radius",
type: "number", type: "number",
value: 2, value: 2,
min: 1 min: 1,
}, },
{ {
name: "Amount", name: "Amount",
type: "number", type: "number",
value: 1, value: 1,
min: 0, min: 0,
step: 0.1 step: 0.1,
}, },
{ {
name: "Threshold", name: "Threshold",
type: "number", type: "number",
value: 10, value: 10,
min: 0, min: 0,
max: 100 max: 100,
} },
]; ];
} }
@ -79,13 +77,21 @@ class SharpenImage extends Operation {
const blurMask = image.clone(); const blurMask = image.clone();
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Sharpening image... (Blurring cloned image)"); self.sendStatusMessage(
const blurImage = gaussianBlur(image.clone(), radius); "Sharpening image... (Blurring cloned image)",
);
const blurImage = image.clone().gaussian(radius);
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Sharpening image... (Creating unsharp mask)"); self.sendStatusMessage(
blurMask.scan(0, 0, blurMask.bitmap.width, blurMask.bitmap.height, function(x, y, idx) { "Sharpening image... (Creating unsharp mask)",
);
blurMask.scan(
0,
0,
blurMask.bitmap.width,
blurMask.bitmap.height,
function (x, y, idx) {
const blurRed = blurImage.bitmap.data[idx]; const blurRed = blurImage.bitmap.data[idx];
const blurGreen = blurImage.bitmap.data[idx + 1]; const blurGreen = blurImage.bitmap.data[idx + 1];
const blurBlue = blurImage.bitmap.data[idx + 2]; const blurBlue = blurImage.bitmap.data[idx + 2];
@ -95,14 +101,25 @@ class SharpenImage extends Operation {
const normalBlue = this.bitmap.data[idx + 2]; const normalBlue = this.bitmap.data[idx + 2];
// Subtract blurred pixel value from normal image // Subtract blurred pixel value from normal image
this.bitmap.data[idx] = (normalRed > blurRed) ? normalRed - blurRed : 0; this.bitmap.data[idx] =
this.bitmap.data[idx + 1] = (normalGreen > blurGreen) ? normalGreen - blurGreen : 0; normalRed > blurRed ? normalRed - blurRed : 0;
this.bitmap.data[idx + 2] = (normalBlue > blurBlue) ? normalBlue - blurBlue : 0; this.bitmap.data[idx + 1] =
}); normalGreen > blurGreen ? normalGreen - blurGreen : 0;
this.bitmap.data[idx + 2] =
normalBlue > blurBlue ? normalBlue - blurBlue : 0;
},
);
if (isWorkerEnvironment()) if (isWorkerEnvironment())
self.sendStatusMessage("Sharpening image... (Merging with unsharp mask)"); self.sendStatusMessage(
image.scan(0, 0, image.bitmap.width, image.bitmap.height, function(x, y, idx) { "Sharpening image... (Merging with unsharp mask)",
);
image.scan(
0,
0,
image.bitmap.width,
image.bitmap.height,
function (x, y, idx) {
let maskRed = blurMask.bitmap.data[idx]; let maskRed = blurMask.bitmap.data[idx];
let maskGreen = blurMask.bitmap.data[idx + 1]; let maskGreen = blurMask.bitmap.data[idx + 1];
let maskBlue = blurMask.bitmap.data[idx + 2]; let maskBlue = blurMask.bitmap.data[idx + 2];
@ -112,8 +129,14 @@ class SharpenImage extends Operation {
const normalBlue = this.bitmap.data[idx + 2]; const normalBlue = this.bitmap.data[idx + 2];
// Calculate luminance // Calculate luminance
const maskLuminance = (0.2126 * maskRed + 0.7152 * maskGreen + 0.0722 * maskBlue); const maskLuminance =
const normalLuminance = (0.2126 * normalRed + 0.7152 * normalGreen + 0.0722 * normalBlue); 0.2126 * maskRed +
0.7152 * maskGreen +
0.0722 * maskBlue;
const normalLuminance =
0.2126 * normalRed +
0.7152 * normalGreen +
0.0722 * normalBlue;
let luminanceDiff; let luminanceDiff;
if (maskLuminance > normalLuminance) { if (maskLuminance > normalLuminance) {
@ -129,17 +152,27 @@ class SharpenImage extends Operation {
// Only change pixel value if the difference is higher than threshold // Only change pixel value if the difference is higher than threshold
if ((luminanceDiff / 255) * 100 >= threshold) { if ((luminanceDiff / 255) * 100 >= threshold) {
this.bitmap.data[idx] = (normalRed + maskRed) <= 255 ? normalRed + maskRed : 255; this.bitmap.data[idx] =
this.bitmap.data[idx + 1] = (normalGreen + maskGreen) <= 255 ? normalGreen + maskGreen : 255; normalRed + maskRed <= 255 ?
this.bitmap.data[idx + 2] = (normalBlue + maskBlue) <= 255 ? normalBlue + maskBlue : 255; normalRed + maskRed :
255;
this.bitmap.data[idx + 1] =
normalGreen + maskGreen <= 255 ?
normalGreen + maskGreen :
255;
this.bitmap.data[idx + 2] =
normalBlue + maskBlue <= 255 ?
normalBlue + maskBlue :
255;
} }
}); },
);
let imageBuffer; let imageBuffer;
if (image.getMIME() === "image/gif") { if (image.mime === "image/gif") {
imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG); imageBuffer = await image.getBuffer(JimpMime.png);
} else { } else {
imageBuffer = await image.getBufferAsync(Jimp.AUTO); imageBuffer = await image.getBuffer(image.mime);
} }
return imageBuffer.buffer; return imageBuffer.buffer;
} catch (err) { } catch (err) {
@ -163,7 +196,6 @@ class SharpenImage extends Operation {
return `<img src="data:${type};base64,${toBase64(dataArray)}">`; return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
} }
} }
export default SharpenImage; export default SharpenImage;

View File

@ -7,14 +7,13 @@
import Operation from "../Operation.mjs"; import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs"; import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs"; import Utils from "../Utils.mjs";
import {isImage} from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp, JimpMime } from "jimp";
/** /**
* Split Colour Channels operation * Split Colour Channels operation
*/ */
class SplitColourChannels extends Operation { class SplitColourChannels extends Operation {
/** /**
* SplitColourChannels constructor * SplitColourChannels constructor
*/ */
@ -23,7 +22,8 @@ class SplitColourChannels extends Operation {
this.name = "Split Colour Channels"; this.name = "Split Colour Channels";
this.module = "Image"; this.module = "Image";
this.description = "Splits the given image into its red, green and blue colour channels."; this.description =
"Splits the given image into its red, green and blue colour channels.";
this.infoURL = "https://wikipedia.org/wiki/Channel_(digital_image)"; this.infoURL = "https://wikipedia.org/wiki/Channel_(digital_image)";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "List<File>"; this.outputType = "List<File>";
@ -48,26 +48,44 @@ class SplitColourChannels extends Operation {
const split = parsedImage const split = parsedImage
.clone() .clone()
.color([ .color([
{apply: "blue", params: [-255]}, { apply: "blue", params: [-255] },
{apply: "green", params: [-255]} { apply: "green", params: [-255] },
]) ])
.getBufferAsync(Jimp.MIME_PNG); .getBuffer(JimpMime.png);
resolve(new File([new Uint8Array((await split).values())], "red.png", {type: "image/png"})); resolve(
new File(
[new Uint8Array((await split).values())],
"red.png",
{ type: "image/png" },
),
);
} catch (err) { } catch (err) {
reject(new OperationError(`Could not split red channel: ${err}`)); reject(
new OperationError(`Could not split red channel: ${err}`),
);
} }
}); });
const green = new Promise(async (resolve, reject) => { const green = new Promise(async (resolve, reject) => {
try { try {
const split = parsedImage.clone() const split = parsedImage
.clone()
.color([ .color([
{apply: "red", params: [-255]}, { apply: "red", params: [-255] },
{apply: "blue", params: [-255]}, { apply: "blue", params: [-255] },
]).getBufferAsync(Jimp.MIME_PNG); ])
resolve(new File([new Uint8Array((await split).values())], "green.png", {type: "image/png"})); .getBuffer(JimpMime.png);
resolve(
new File(
[new Uint8Array((await split).values())],
"green.png",
{ type: "image/png" },
),
);
} catch (err) { } catch (err) {
reject(new OperationError(`Could not split green channel: ${err}`)); reject(
new OperationError(`Could not split green channel: ${err}`),
);
} }
}); });
@ -75,12 +93,21 @@ class SplitColourChannels extends Operation {
try { try {
const split = parsedImage const split = parsedImage
.color([ .color([
{apply: "red", params: [-255]}, { apply: "red", params: [-255] },
{apply: "green", params: [-255]}, { apply: "green", params: [-255] },
]).getBufferAsync(Jimp.MIME_PNG); ])
resolve(new File([new Uint8Array((await split).values())], "blue.png", {type: "image/png"})); .getBuffer(JimpMime.png);
resolve(
new File(
[new Uint8Array((await split).values())],
"blue.png",
{ type: "image/png" },
),
);
} catch (err) { } catch (err) {
reject(new OperationError(`Could not split blue channel: ${err}`)); reject(
new OperationError(`Could not split blue channel: ${err}`),
);
} }
}); });
@ -96,7 +123,6 @@ class SplitColourChannels extends Operation {
async present(files) { async present(files) {
return await Utils.displayFilesAsHTML(files); return await Utils.displayFilesAsHTML(files);
} }
} }
export default SplitColourChannels; export default SplitColourChannels;

View File

@ -23,7 +23,7 @@ class ToQuotedPrintable extends Operation {
this.name = "To Quoted Printable"; this.name = "To Quoted Printable";
this.module = "Default"; this.module = "Default";
this.description = "Quoted-Printable, or QP encoding, is an encoding using printable ASCII characters (alphanumeric and the equals sign '=') to transmit 8-bit data over a 7-bit data path or, generally, over a medium which is not 8-bit clean. It is defined as a MIME content transfer encoding for use in e-mail.<br><br>QP works by using the equals sign '=' as an escape character. It also limits line length to 76, as some software has limits on line length."; this.description = "Quoted-Printable, or QP encoding, is an encoding using printable ASCII characters (alphanumeric and the equals sign '=') to transmit 8-bit data over a 7-bit data path or, generally, over a medium which is not 8-bit clean. It is defined as a MIME content transfer encoding for use in email.<br><br>QP works by using the equals sign '=' as an escape character. It also limits line length to 76, as some software has limits on line length.";
this.infoURL = "https://wikipedia.org/wiki/Quoted-printable"; this.infoURL = "https://wikipedia.org/wiki/Quoted-printable";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "string"; this.outputType = "string";

View File

@ -30,6 +30,23 @@ class UnescapeUnicodeCharacters extends Operation {
"value": ["\\u", "%u", "U+"] "value": ["\\u", "%u", "U+"]
} }
]; ];
this.checks = [
{
pattern: "\\\\u(?:[\\da-f]{4,6})",
flags: "i",
args: ["\\u"]
},
{
pattern: "%u(?:[\\da-f]{4,6})",
flags: "i",
args: ["%u"]
},
{
pattern: "U\\+(?:[\\da-f]{4,6})",
flags: "i",
args: ["U+"]
}
];
} }
/** /**

View File

@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs"; import Utils from "../Utils.mjs";
import { isImage } from "../lib/FileType.mjs"; import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs"; import { toBase64 } from "../lib/Base64.mjs";
import Jimp from "jimp/es/index.js"; import { Jimp } from "jimp";
/** /**
* View Bit Plane operation * View Bit Plane operation
*/ */
class ViewBitPlane extends Operation { class ViewBitPlane extends Operation {
/** /**
* ViewBitPlane constructor * ViewBitPlane constructor
*/ */
@ -24,7 +23,8 @@ class ViewBitPlane extends Operation {
this.name = "View Bit Plane"; this.name = "View Bit Plane";
this.module = "Image"; this.module = "Image";
this.description = "Extracts and displays a bit plane of any given image. These show only a single bit from each pixel, and can be used to hide messages in Steganography."; this.description =
"Extracts and displays a bit plane of any given image. These show only a single bit from each pixel, and can be used to hide messages in Steganography.";
this.infoURL = "https://wikipedia.org/wiki/Bit_plane"; this.infoURL = "https://wikipedia.org/wiki/Bit_plane";
this.inputType = "ArrayBuffer"; this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer"; this.outputType = "ArrayBuffer";
@ -33,13 +33,13 @@ class ViewBitPlane extends Operation {
{ {
name: "Colour", name: "Colour",
type: "option", type: "option",
value: COLOUR_OPTIONS value: COLOUR_OPTIONS,
}, },
{ {
name: "Bit", name: "Bit",
type: "number", type: "number",
value: 0 value: 0,
} },
]; ];
} }
@ -49,36 +49,38 @@ class ViewBitPlane extends Operation {
* @returns {ArrayBuffer} * @returns {ArrayBuffer}
*/ */
async run(input, args) { async run(input, args) {
if (!isImage(input)) throw new OperationError("Please enter a valid image file."); if (!isImage(input))
throw new OperationError("Please enter a valid image file.");
const [colour, bit] = args, const [colour, bit] = args,
parsedImage = await Jimp.read(input), parsedImage = await Jimp.read(input),
width = parsedImage.bitmap.width, width = parsedImage.bitmap.width,
height = parsedImage.bitmap.height, height = parsedImage.bitmap.height,
colourIndex = COLOUR_OPTIONS.indexOf(colour), colourIndex = COLOUR_OPTIONS.indexOf(colour),
bitIndex = 7-bit; bitIndex = 7 - bit;
if (bit < 0 || bit > 7) { if (bit < 0 || bit > 7) {
throw new OperationError("Error: Bit argument must be between 0 and 7"); throw new OperationError(
"Error: Bit argument must be between 0 and 7",
);
} }
let pixel, bin, newPixelValue; let pixel, bin, newPixelValue;
parsedImage.scan(0, 0, width, height, function(x, y, idx) { parsedImage.scan(0, 0, width, height, function (x, y, idx) {
pixel = this.bitmap.data[idx + colourIndex]; pixel = this.bitmap.data[idx + colourIndex];
bin = Utils.bin(pixel); bin = Utils.bin(pixel);
newPixelValue = 255; newPixelValue = 255;
if (bin.charAt(bitIndex) === "1") newPixelValue = 0; if (bin.charAt(bitIndex) === "1") newPixelValue = 0;
for (let i=0; i < 3; i++) { for (let i = 0; i < 3; i++) {
this.bitmap.data[idx + i] = newPixelValue; this.bitmap.data[idx + i] = newPixelValue;
} }
this.bitmap.data[idx + 3] = 255; this.bitmap.data[idx + 3] = 255;
}); });
const imageBuffer = await parsedImage.getBufferAsync(Jimp.AUTO); const imageBuffer = await parsedImage.getBuffer(parsedImage.mime);
return new Uint8Array(imageBuffer).buffer; return new Uint8Array(imageBuffer).buffer;
} }
@ -94,14 +96,8 @@ class ViewBitPlane extends Operation {
return `<img src="data:${type};base64,${toBase64(data)}">`; return `<img src="data:${type};base64,${toBase64(data)}">`;
} }
} }
const COLOUR_OPTIONS = [ const COLOUR_OPTIONS = ["Red", "Green", "Blue", "Alpha"];
"Red",
"Green",
"Blue",
"Alpha"
];
export default ViewBitPlane; export default ViewBitPlane;

View File

@ -49,15 +49,14 @@ class HTMLIngredient {
toHtml() { toHtml() {
let html = "", let html = "",
i, m, eventFn; i, m, eventFn;
const hintHtml = this.hint ? `data-toggle="tooltip" title="${this.hint}"` : "";
switch (this.type) { switch (this.type) {
case "string": case "string":
case "binaryString": case "binaryString":
case "byteArray": case "byteArray":
html += `<div class="form-group ing-wide"> html += `<div class="form-group ing-wide" ${hintHtml}>
<label for="${this.id}" <label for="${this.id}" class="bmd-label-floating">${this.name}</label>
${this.hint ? `data-toggle="tooltip" title="${this.hint}"` : ""}
class="bmd-label-floating">${this.name}</label>
<input type="text" <input type="text"
class="form-control arg" class="form-control arg"
id="${this.id}" id="${this.id}"
@ -70,10 +69,8 @@ class HTMLIngredient {
break; break;
case "shortString": case "shortString":
case "binaryShortString": case "binaryShortString":
html += `<div class="form-group ing-short"> html += `<div class="form-group ing-short" ${hintHtml}>
<label for="${this.id}" <label for="${this.id}" class="bmd-label-floating inline">${this.name}</label>
${this.hint ? `data-toggle="tooltip" title="${this.hint}"` : ""}
class="bmd-label-floating inline">${this.name}</label>
<input type="text" <input type="text"
class="form-control arg inline" class="form-control arg inline"
id="${this.id}" id="${this.id}"
@ -85,11 +82,9 @@ class HTMLIngredient {
</div>`; </div>`;
break; break;
case "toggleString": case "toggleString":
html += `<div class="form-group input-group ing-wide" data-help-title="Multi-type ingredients" data-help="Selecting a data type from the dropdown will change how the ingredient is interpreted by the operation."> html += `<div class="form-group input-group ing-wide" data-help-title="Multi-type ingredients" data-help="Selecting a data type from the dropdown will change how the ingredient is interpreted by the operation." ${hintHtml}>
<div class="toggle-string"> <div class="toggle-string">
<label for="${this.id}" <label for="${this.id}" class="bmd-label-floating toggle-string">${this.name}</label>
${this.hint ? `data-toggle="tooltip" title="${this.hint}"` : ""}
class="bmd-label-floating toggle-string">${this.name}</label>
<input type="text" <input type="text"
class="form-control arg toggle-string" class="form-control arg toggle-string"
id="${this.id}" id="${this.id}"
@ -111,10 +106,8 @@ class HTMLIngredient {
</div>`; </div>`;
break; break;
case "number": case "number":
html += `<div class="form-group inline ing-medium"> html += `<div class="form-group inline ing-medium" ${hintHtml}>
<label for="${this.id}" <label for="${this.id}" class="bmd-label-floating inline">${this.name}</label>
${this.hint ? `data-toggle="tooltip" title="${this.hint}"` : ""}
class="bmd-label-floating inline">${this.name}</label>
<input type="number" <input type="number"
class="form-control arg inline" class="form-control arg inline"
id="${this.id}" id="${this.id}"
@ -128,9 +121,9 @@ class HTMLIngredient {
</div>`; </div>`;
break; break;
case "boolean": case "boolean":
html += `<div class="form-group inline boolean-arg ing-flexible"> html += `<div class="form-group inline boolean-arg ing-flexible" ${hintHtml}>
<div class="checkbox"> <div class="checkbox">
<label ${this.hint ? `data-toggle="tooltip" title="${this.hint}"` : ""}> <label>
<input type="checkbox" <input type="checkbox"
class="arg" class="arg"
id="${this.id}" id="${this.id}"
@ -144,10 +137,8 @@ class HTMLIngredient {
</div>`; </div>`;
break; break;
case "option": case "option":
html += `<div class="form-group ing-medium"> html += `<div class="form-group ing-medium" ${hintHtml}>
<label for="${this.id}" <label for="${this.id}" class="bmd-label-floating inline">${this.name}</label>
${this.hint ? `data-toggle="tooltip" title="${this.hint}"` : ""}
class="bmd-label-floating inline">${this.name}</label>
<select <select
class="form-control arg inline" class="form-control arg inline"
id="${this.id}" id="${this.id}"
@ -168,10 +159,8 @@ class HTMLIngredient {
break; break;
case "populateOption": case "populateOption":
case "populateMultiOption": case "populateMultiOption":
html += `<div class="form-group ing-medium" data-help-title="Population dropdowns" data-help="Selecting a value from this dropdown will populate some of the other ingredients for this operation with pre-canned values."> html += `<div class="form-group ing-medium" data-help-title="Population dropdowns" data-help="Selecting a value from this dropdown will populate some of the other ingredients for this operation with pre-canned values." ${hintHtml}>
<label for="${this.id}" <label for="${this.id}" class="bmd-label-floating">${this.name}</label>
${this.hint ? `data-toggle="tooltip" title="${this.hint}"` : ""}
class="bmd-label-floating">${this.name}</label>
<select <select
class="form-control arg no-state-change populate-option" class="form-control arg no-state-change populate-option"
id="${this.id}" id="${this.id}"
@ -199,10 +188,8 @@ class HTMLIngredient {
this.manager.addDynamicListener("#" + this.id, "change", eventFn, this); this.manager.addDynamicListener("#" + this.id, "change", eventFn, this);
break; break;
case "editableOption": case "editableOption":
html += `<div class="form-group input-group ing-wide"> html += `<div class="form-group input-group ing-wide" ${hintHtml}>
<label for="${this.id}" <label for="${this.id}" class="bmd-label-floating">${this.name}</label>
${this.hint ? `data-toggle="tooltip" title="${this.hint}"` : ""}
class="bmd-label-floating">${this.name}</label>
<input type="text" <input type="text"
class="form-control arg" class="form-control arg"
id="${this.id}" id="${this.id}"
@ -230,10 +217,8 @@ class HTMLIngredient {
this.manager.addDynamicListener(".editable-option-menu a", "click", this.editableOptionClick, this); this.manager.addDynamicListener(".editable-option-menu a", "click", this.editableOptionClick, this);
break; break;
case "editableOptionShort": case "editableOptionShort":
html += `<div class="form-group input-group ing-short"> html += `<div class="form-group input-group ing-short" ${hintHtml}>
<label for="${this.id}" <label for="${this.id}" class="bmd-label-floating inline">${this.name}</label>
${this.hint ? `data-toggle="tooltip" title="${this.hint}"` : ""}
class="bmd-label-floating inline">${this.name}</label>
<input type="text" <input type="text"
class="form-control arg inline" class="form-control arg inline"
id="${this.id}" id="${this.id}"
@ -261,10 +246,8 @@ class HTMLIngredient {
this.manager.addDynamicListener(".editable-option-menu a", "click", this.editableOptionClick, this); this.manager.addDynamicListener(".editable-option-menu a", "click", this.editableOptionClick, this);
break; break;
case "text": case "text":
html += `<div class="form-group ing-very-wide"> html += `<div class="form-group ing-very-wide" ${hintHtml}>
<label for="${this.id}" <label for="${this.id}" class="bmd-label-floating">${this.name}</label>
${this.hint ? `data-toggle="tooltip" title="${this.hint}"` : ""}
class="bmd-label-floating">${this.name}</label>
<textarea <textarea
class="form-control arg" class="form-control arg"
id="${this.id}" id="${this.id}"
@ -275,10 +258,8 @@ class HTMLIngredient {
</div>`; </div>`;
break; break;
case "argSelector": case "argSelector":
html += `<div class="form-group inline ing-medium" data-help-title="Ingredient selector" data-help="Selecting options in this dropdown will configure which operation ingredients are visible."> html += `<div class="form-group inline ing-medium" data-help-title="Ingredient selector" data-help="Selecting options in this dropdown will configure which operation ingredients are visible." ${hintHtml}>
<label for="${this.id}" <label for="${this.id}" class="bmd-label-floating inline">${this.name}</label>
${this.hint ? `data-toggle="tooltip" title="${this.hint}"` : ""}
class="bmd-label-floating inline">${this.name}</label>
<select <select
class="form-control arg inline arg-selector" class="form-control arg inline arg-selector"
id="${this.id}" id="${this.id}"
@ -298,7 +279,7 @@ class HTMLIngredient {
this.manager.addDynamicListener(".arg-selector", "change", this.argSelectorChange, this); this.manager.addDynamicListener(".arg-selector", "change", this.argSelectorChange, this);
break; break;
case "label": case "label":
html += `<div class="form-group ing-flexible"> html += `<div class="form-group ing-flexible" ${hintHtml}>
<label>${this.name}</label> <label>${this.name}</label>
<input type="hidden" <input type="hidden"
class="form-control arg" class="form-control arg"

View File

@ -1,485 +1,491 @@
info face="Roboto" size=72 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2 <?xml version="1.0"?>
common lineHeight=85 base=67 scaleW=512 scaleH=512 pages=1 packed=0 <font>
page id=0 file="Roboto72White.png" <info face="Roboto" size="72" bold="0" italic="0" charset="" unicode="0" stretchH="100" smooth="1" aa="1" padding="1,1,1,1" spacing="-2,-2" outline="0" />
chars count=98 <common lineHeight="85" base="67" scaleW="512" scaleH="512" pages="1" packed="0" alphaChnl="0" redChnl="0" greenChnl="0" blueChnl="0" />
char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=0 page=0 chnl=0 <pages>
char id=10 x=0 y=0 width=70 height=99 xoffset=2 yoffset=-11 xadvance=74 page=0 chnl=0 <page id="0" file="Roboto72White.png" /> </pages>
char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=18 page=0 chnl=0 <chars count="98">
char id=33 x=493 y=99 width=10 height=55 xoffset=5 yoffset=14 xadvance=19 page=0 chnl=0 <char id="0" x="0" y="0" width="0" height="0" xoffset="-1" yoffset="66" xadvance="0" page="0" chnl="0" />
char id=34 x=446 y=319 width=16 height=19 xoffset=4 yoffset=12 xadvance=23 page=0 chnl=0 <char id="10" x="0" y="0" width="70" height="99" xoffset="2" yoffset="-11" xadvance="74" page="0" chnl="0" />
char id=35 x=204 y=265 width=41 height=54 xoffset=3 yoffset=14 xadvance=44 page=0 chnl=0 <char id="32" x="0" y="0" width="0" height="0" xoffset="-1" yoffset="66" xadvance="18" page="0" chnl="0" />
char id=36 x=269 y=0 width=35 height=69 xoffset=3 yoffset=6 xadvance=40 page=0 chnl=0 <char id="33" x="493" y="99" width="10" height="55" xoffset="5" yoffset="14" xadvance="19" page="0" chnl="0" />
char id=37 x=31 y=155 width=48 height=56 xoffset=3 yoffset=13 xadvance=53 page=0 chnl=0 <char id="34" x="446" y="319" width="16" height="19" xoffset="4" yoffset="12" xadvance="23" page="0" chnl="0" />
char id=38 x=79 y=155 width=43 height=56 xoffset=3 yoffset=13 xadvance=45 page=0 chnl=0 <char id="35" x="204" y="265" width="41" height="54" xoffset="3" yoffset="14" xadvance="44" page="0" chnl="0" />
char id=39 x=503 y=99 width=7 height=19 xoffset=3 yoffset=12 xadvance=13 page=0 chnl=0 <char id="36" x="269" y="0" width="35" height="69" xoffset="3" yoffset="6" xadvance="40" page="0" chnl="0" />
char id=40 x=70 y=0 width=21 height=78 xoffset=4 yoffset=7 xadvance=25 page=0 chnl=0 <char id="37" x="31" y="155" width="48" height="56" xoffset="3" yoffset="13" xadvance="53" page="0" chnl="0" />
char id=41 x=91 y=0 width=22 height=78 xoffset=-1 yoffset=7 xadvance=25 page=0 chnl=0 <char id="38" x="79" y="155" width="43" height="56" xoffset="3" yoffset="13" xadvance="45" page="0" chnl="0" />
char id=42 x=342 y=319 width=32 height=32 xoffset=-1 yoffset=14 xadvance=31 page=0 chnl=0 <char id="39" x="503" y="99" width="7" height="19" xoffset="3" yoffset="12" xadvance="13" page="0" chnl="0" />
char id=43 x=242 y=319 width=37 height=40 xoffset=2 yoffset=23 xadvance=41 page=0 chnl=0 <char id="40" x="70" y="0" width="21" height="78" xoffset="4" yoffset="7" xadvance="25" page="0" chnl="0" />
char id=44 x=433 y=319 width=13 height=21 xoffset=-1 yoffset=58 xadvance=14 page=0 chnl=0 <char id="41" x="91" y="0" width="22" height="78" xoffset="-1" yoffset="7" xadvance="25" page="0" chnl="0" />
char id=45 x=27 y=360 width=19 height=8 xoffset=0 yoffset=41 xadvance=19 page=0 chnl=0 <char id="42" x="342" y="319" width="32" height="32" xoffset="-1" yoffset="14" xadvance="31" page="0" chnl="0" />
char id=46 x=17 y=360 width=10 height=11 xoffset=4 yoffset=58 xadvance=19 page=0 chnl=0 <char id="43" x="242" y="319" width="37" height="40" xoffset="2" yoffset="23" xadvance="41" page="0" chnl="0" />
char id=47 x=355 y=0 width=30 height=58 xoffset=-1 yoffset=14 xadvance=30 page=0 chnl=0 <char id="44" x="433" y="319" width="13" height="21" xoffset="-1" yoffset="58" xadvance="14" page="0" chnl="0" />
char id=48 x=449 y=99 width=34 height=56 xoffset=3 yoffset=13 xadvance=40 page=0 chnl=0 <char id="45" x="27" y="360" width="19" height="8" xoffset="0" yoffset="41" xadvance="19" page="0" chnl="0" />
char id=49 x=474 y=211 width=22 height=54 xoffset=5 yoffset=14 xadvance=40 page=0 chnl=0 <char id="46" x="17" y="360" width="10" height="11" xoffset="4" yoffset="58" xadvance="19" page="0" chnl="0" />
char id=50 x=195 y=155 width=37 height=55 xoffset=2 yoffset=13 xadvance=41 page=0 chnl=0 <char id="47" x="355" y="0" width="30" height="58" xoffset="-1" yoffset="14" xadvance="30" page="0" chnl="0" />
char id=51 x=379 y=99 width=35 height=56 xoffset=2 yoffset=13 xadvance=40 page=0 chnl=0 <char id="48" x="449" y="99" width="34" height="56" xoffset="3" yoffset="13" xadvance="40" page="0" chnl="0" />
char id=52 x=128 y=265 width=39 height=54 xoffset=1 yoffset=14 xadvance=41 page=0 chnl=0 <char id="49" x="474" y="211" width="22" height="54" xoffset="5" yoffset="14" xadvance="40" page="0" chnl="0" />
char id=53 x=232 y=155 width=35 height=55 xoffset=4 yoffset=14 xadvance=40 page=0 chnl=0 <char id="50" x="195" y="155" width="37" height="55" xoffset="2" yoffset="13" xadvance="41" page="0" chnl="0" />
char id=54 x=267 y=155 width=35 height=55 xoffset=4 yoffset=14 xadvance=41 page=0 chnl=0 <char id="51" x="379" y="99" width="35" height="56" xoffset="2" yoffset="13" xadvance="40" page="0" chnl="0" />
char id=55 x=167 y=265 width=37 height=54 xoffset=2 yoffset=14 xadvance=41 page=0 chnl=0 <char id="52" x="128" y="265" width="39" height="54" xoffset="1" yoffset="14" xadvance="41" page="0" chnl="0" />
char id=56 x=414 y=99 width=35 height=56 xoffset=3 yoffset=13 xadvance=40 page=0 chnl=0 <char id="53" x="232" y="155" width="35" height="55" xoffset="4" yoffset="14" xadvance="40" page="0" chnl="0" />
char id=57 x=302 y=155 width=34 height=55 xoffset=3 yoffset=13 xadvance=41 page=0 chnl=0 <char id="54" x="267" y="155" width="35" height="55" xoffset="4" yoffset="14" xadvance="41" page="0" chnl="0" />
char id=58 x=495 y=265 width=10 height=41 xoffset=4 yoffset=28 xadvance=18 page=0 chnl=0 <char id="55" x="167" y="265" width="37" height="54" xoffset="2" yoffset="14" xadvance="41" page="0" chnl="0" />
char id=59 x=496 y=211 width=13 height=52 xoffset=0 yoffset=28 xadvance=15 page=0 chnl=0 <char id="56" x="414" y="99" width="35" height="56" xoffset="3" yoffset="13" xadvance="40" page="0" chnl="0" />
char id=60 x=279 y=319 width=31 height=35 xoffset=2 yoffset=27 xadvance=37 page=0 chnl=0 <char id="57" x="302" y="155" width="34" height="55" xoffset="3" yoffset="13" xadvance="41" page="0" chnl="0" />
char id=61 x=402 y=319 width=31 height=23 xoffset=4 yoffset=31 xadvance=39 page=0 chnl=0 <char id="58" x="495" y="265" width="10" height="41" xoffset="4" yoffset="28" xadvance="18" page="0" chnl="0" />
char id=62 x=310 y=319 width=32 height=35 xoffset=4 yoffset=27 xadvance=38 page=0 chnl=0 <char id="59" x="496" y="211" width="13" height="52" xoffset="0" yoffset="28" xadvance="15" page="0" chnl="0" />
char id=63 x=0 y=155 width=31 height=56 xoffset=2 yoffset=13 xadvance=34 page=0 chnl=0 <char id="60" x="279" y="319" width="31" height="35" xoffset="2" yoffset="27" xadvance="37" page="0" chnl="0" />
char id=64 x=210 y=0 width=59 height=69 xoffset=3 yoffset=15 xadvance=65 page=0 chnl=0 <char id="61" x="402" y="319" width="31" height="23" xoffset="4" yoffset="31" xadvance="39" page="0" chnl="0" />
char id=65 x=336 y=155 width=49 height=54 xoffset=-1 yoffset=14 xadvance=47 page=0 chnl=0 <char id="62" x="310" y="319" width="32" height="35" xoffset="4" yoffset="27" xadvance="38" page="0" chnl="0" />
char id=66 x=385 y=155 width=37 height=54 xoffset=5 yoffset=14 xadvance=45 page=0 chnl=0 <char id="63" x="0" y="155" width="31" height="56" xoffset="2" yoffset="13" xadvance="34" page="0" chnl="0" />
char id=67 x=0 y=99 width=42 height=56 xoffset=3 yoffset=13 xadvance=46 page=0 chnl=0 <char id="64" x="210" y="0" width="59" height="69" xoffset="3" yoffset="15" xadvance="65" page="0" chnl="0" />
char id=68 x=422 y=155 width=39 height=54 xoffset=5 yoffset=14 xadvance=47 page=0 chnl=0 <char id="65" x="336" y="155" width="49" height="54" xoffset="-1" yoffset="14" xadvance="47" page="0" chnl="0" />
char id=69 x=461 y=155 width=35 height=54 xoffset=5 yoffset=14 xadvance=41 page=0 chnl=0 <char id="66" x="385" y="155" width="37" height="54" xoffset="5" yoffset="14" xadvance="45" page="0" chnl="0" />
char id=70 x=0 y=211 width=34 height=54 xoffset=5 yoffset=14 xadvance=40 page=0 chnl=0 <char id="67" x="0" y="99" width="42" height="56" xoffset="3" yoffset="13" xadvance="46" page="0" chnl="0" />
char id=71 x=42 y=99 width=42 height=56 xoffset=3 yoffset=13 xadvance=49 page=0 chnl=0 <char id="68" x="422" y="155" width="39" height="54" xoffset="5" yoffset="14" xadvance="47" page="0" chnl="0" />
char id=72 x=34 y=211 width=41 height=54 xoffset=5 yoffset=14 xadvance=51 page=0 chnl=0 <char id="69" x="461" y="155" width="35" height="54" xoffset="5" yoffset="14" xadvance="41" page="0" chnl="0" />
char id=73 x=496 y=155 width=9 height=54 xoffset=5 yoffset=14 xadvance=19 page=0 chnl=0 <char id="70" x="0" y="211" width="34" height="54" xoffset="5" yoffset="14" xadvance="40" page="0" chnl="0" />
char id=74 x=122 y=155 width=34 height=55 xoffset=1 yoffset=14 xadvance=40 page=0 chnl=0 <char id="71" x="42" y="99" width="42" height="56" xoffset="3" yoffset="13" xadvance="49" page="0" chnl="0" />
char id=75 x=75 y=211 width=41 height=54 xoffset=5 yoffset=14 xadvance=45 page=0 chnl=0 <char id="72" x="34" y="211" width="41" height="54" xoffset="5" yoffset="14" xadvance="51" page="0" chnl="0" />
char id=76 x=116 y=211 width=33 height=54 xoffset=5 yoffset=14 xadvance=39 page=0 chnl=0 <char id="73" x="496" y="155" width="9" height="54" xoffset="5" yoffset="14" xadvance="19" page="0" chnl="0" />
char id=77 x=149 y=211 width=53 height=54 xoffset=5 yoffset=14 xadvance=63 page=0 chnl=0 <char id="74" x="122" y="155" width="34" height="55" xoffset="1" yoffset="14" xadvance="40" page="0" chnl="0" />
char id=78 x=202 y=211 width=41 height=54 xoffset=5 yoffset=14 xadvance=51 page=0 chnl=0 <char id="75" x="75" y="211" width="41" height="54" xoffset="5" yoffset="14" xadvance="45" page="0" chnl="0" />
char id=79 x=84 y=99 width=43 height=56 xoffset=3 yoffset=13 xadvance=49 page=0 chnl=0 <char id="76" x="116" y="211" width="33" height="54" xoffset="5" yoffset="14" xadvance="39" page="0" chnl="0" />
char id=80 x=243 y=211 width=39 height=54 xoffset=5 yoffset=14 xadvance=45 page=0 chnl=0 <char id="77" x="149" y="211" width="53" height="54" xoffset="5" yoffset="14" xadvance="63" page="0" chnl="0" />
char id=81 x=304 y=0 width=44 height=64 xoffset=3 yoffset=13 xadvance=49 page=0 chnl=0 <char id="78" x="202" y="211" width="41" height="54" xoffset="5" yoffset="14" xadvance="51" page="0" chnl="0" />
char id=82 x=282 y=211 width=40 height=54 xoffset=5 yoffset=14 xadvance=45 page=0 chnl=0 <char id="79" x="84" y="99" width="43" height="56" xoffset="3" yoffset="13" xadvance="49" page="0" chnl="0" />
char id=83 x=127 y=99 width=39 height=56 xoffset=2 yoffset=13 xadvance=43 page=0 chnl=0 <char id="80" x="243" y="211" width="39" height="54" xoffset="5" yoffset="14" xadvance="45" page="0" chnl="0" />
char id=84 x=322 y=211 width=42 height=54 xoffset=1 yoffset=14 xadvance=44 page=0 chnl=0 <char id="81" x="304" y="0" width="44" height="64" xoffset="3" yoffset="13" xadvance="49" page="0" chnl="0" />
char id=85 x=156 y=155 width=39 height=55 xoffset=4 yoffset=14 xadvance=47 page=0 chnl=0 <char id="82" x="282" y="211" width="40" height="54" xoffset="5" yoffset="14" xadvance="45" page="0" chnl="0" />
char id=86 x=364 y=211 width=47 height=54 xoffset=-1 yoffset=14 xadvance=46 page=0 chnl=0 <char id="83" x="127" y="99" width="39" height="56" xoffset="2" yoffset="13" xadvance="43" page="0" chnl="0" />
char id=87 x=411 y=211 width=63 height=54 xoffset=1 yoffset=14 xadvance=64 page=0 chnl=0 <char id="84" x="322" y="211" width="42" height="54" xoffset="1" yoffset="14" xadvance="44" page="0" chnl="0" />
char id=88 x=0 y=265 width=44 height=54 xoffset=1 yoffset=14 xadvance=45 page=0 chnl=0 <char id="85" x="156" y="155" width="39" height="55" xoffset="4" yoffset="14" xadvance="47" page="0" chnl="0" />
char id=89 x=44 y=265 width=45 height=54 xoffset=-1 yoffset=14 xadvance=43 page=0 chnl=0 <char id="86" x="364" y="211" width="47" height="54" xoffset="-1" yoffset="14" xadvance="46" page="0" chnl="0" />
char id=90 x=89 y=265 width=39 height=54 xoffset=2 yoffset=14 xadvance=43 page=0 chnl=0 <char id="87" x="411" y="211" width="63" height="54" xoffset="1" yoffset="14" xadvance="64" page="0" chnl="0" />
char id=91 x=161 y=0 width=16 height=72 xoffset=4 yoffset=7 xadvance=19 page=0 chnl=0 <char id="88" x="0" y="265" width="44" height="54" xoffset="1" yoffset="14" xadvance="45" page="0" chnl="0" />
char id=92 x=385 y=0 width=30 height=58 xoffset=0 yoffset=14 xadvance=30 page=0 chnl=0 <char id="89" x="44" y="265" width="45" height="54" xoffset="-1" yoffset="14" xadvance="43" page="0" chnl="0" />
char id=93 x=177 y=0 width=16 height=72 xoffset=0 yoffset=7 xadvance=20 page=0 chnl=0 <char id="90" x="89" y="265" width="39" height="54" xoffset="2" yoffset="14" xadvance="43" page="0" chnl="0" />
char id=94 x=374 y=319 width=28 height=28 xoffset=1 yoffset=14 xadvance=30 page=0 chnl=0 <char id="91" x="161" y="0" width="16" height="72" xoffset="4" yoffset="7" xadvance="19" page="0" chnl="0" />
char id=95 x=46 y=360 width=34 height=8 xoffset=0 yoffset=65 xadvance=34 page=0 chnl=0 <char id="92" x="385" y="0" width="30" height="58" xoffset="0" yoffset="14" xadvance="30" page="0" chnl="0" />
char id=96 x=0 y=360 width=17 height=13 xoffset=1 yoffset=11 xadvance=22 page=0 chnl=0 <char id="93" x="177" y="0" width="16" height="72" xoffset="0" yoffset="7" xadvance="20" page="0" chnl="0" />
char id=97 x=268 y=265 width=34 height=42 xoffset=3 yoffset=27 xadvance=39 page=0 chnl=0 <char id="94" x="374" y="319" width="28" height="28" xoffset="1" yoffset="14" xadvance="30" page="0" chnl="0" />
char id=98 x=415 y=0 width=34 height=57 xoffset=4 yoffset=12 xadvance=40 page=0 chnl=0 <char id="95" x="46" y="360" width="34" height="8" xoffset="0" yoffset="65" xadvance="34" page="0" chnl="0" />
char id=99 x=302 y=265 width=34 height=42 xoffset=2 yoffset=27 xadvance=38 page=0 chnl=0 <char id="96" x="0" y="360" width="17" height="13" xoffset="1" yoffset="11" xadvance="22" page="0" chnl="0" />
char id=100 x=449 y=0 width=34 height=57 xoffset=2 yoffset=12 xadvance=40 page=0 chnl=0 <char id="97" x="268" y="265" width="34" height="42" xoffset="3" yoffset="27" xadvance="39" page="0" chnl="0" />
char id=101 x=336 y=265 width=34 height=42 xoffset=2 yoffset=27 xadvance=38 page=0 chnl=0 <char id="98" x="415" y="0" width="34" height="57" xoffset="4" yoffset="12" xadvance="40" page="0" chnl="0" />
char id=102 x=483 y=0 width=25 height=57 xoffset=1 yoffset=11 xadvance=26 page=0 chnl=0 <char id="99" x="302" y="265" width="34" height="42" xoffset="2" yoffset="27" xadvance="38" page="0" chnl="0" />
char id=103 x=166 y=99 width=34 height=56 xoffset=2 yoffset=27 xadvance=40 page=0 chnl=0 <char id="100" x="449" y="0" width="34" height="57" xoffset="2" yoffset="12" xadvance="40" page="0" chnl="0" />
char id=104 x=200 y=99 width=32 height=56 xoffset=4 yoffset=12 xadvance=40 page=0 chnl=0 <char id="101" x="336" y="265" width="34" height="42" xoffset="2" yoffset="27" xadvance="38" page="0" chnl="0" />
char id=105 x=483 y=99 width=10 height=55 xoffset=4 yoffset=13 xadvance=18 page=0 chnl=0 <char id="102" x="483" y="0" width="25" height="57" xoffset="1" yoffset="11" xadvance="26" page="0" chnl="0" />
char id=106 x=193 y=0 width=17 height=71 xoffset=-4 yoffset=13 xadvance=17 page=0 chnl=0 <char id="103" x="166" y="99" width="34" height="56" xoffset="2" yoffset="27" xadvance="40" page="0" chnl="0" />
char id=107 x=232 y=99 width=34 height=56 xoffset=4 yoffset=12 xadvance=37 page=0 chnl=0 <char id="104" x="200" y="99" width="32" height="56" xoffset="4" yoffset="12" xadvance="40" page="0" chnl="0" />
char id=108 x=266 y=99 width=9 height=56 xoffset=4 yoffset=12 xadvance=17 page=0 chnl=0 <char id="105" x="483" y="99" width="10" height="55" xoffset="4" yoffset="13" xadvance="18" page="0" chnl="0" />
char id=109 x=439 y=265 width=56 height=41 xoffset=4 yoffset=27 xadvance=64 page=0 chnl=0 <char id="106" x="193" y="0" width="17" height="71" xoffset="-4" yoffset="13" xadvance="17" page="0" chnl="0" />
char id=110 x=0 y=319 width=32 height=41 xoffset=4 yoffset=27 xadvance=40 page=0 chnl=0 <char id="107" x="232" y="99" width="34" height="56" xoffset="4" yoffset="12" xadvance="37" page="0" chnl="0" />
char id=111 x=370 y=265 width=37 height=42 xoffset=2 yoffset=27 xadvance=41 page=0 chnl=0 <char id="108" x="266" y="99" width="9" height="56" xoffset="4" yoffset="12" xadvance="17" page="0" chnl="0" />
char id=112 x=275 y=99 width=34 height=56 xoffset=4 yoffset=27 xadvance=40 page=0 chnl=0 <char id="109" x="439" y="265" width="56" height="41" xoffset="4" yoffset="27" xadvance="64" page="0" chnl="0" />
char id=113 x=309 y=99 width=34 height=56 xoffset=2 yoffset=27 xadvance=41 page=0 chnl=0 <char id="110" x="0" y="319" width="32" height="41" xoffset="4" yoffset="27" xadvance="40" page="0" chnl="0" />
char id=114 x=32 y=319 width=21 height=41 xoffset=4 yoffset=27 xadvance=25 page=0 chnl=0 <char id="111" x="370" y="265" width="37" height="42" xoffset="2" yoffset="27" xadvance="41" page="0" chnl="0" />
char id=115 x=407 y=265 width=32 height=42 xoffset=2 yoffset=27 xadvance=37 page=0 chnl=0 <char id="112" x="275" y="99" width="34" height="56" xoffset="4" yoffset="27" xadvance="40" page="0" chnl="0" />
char id=116 x=245 y=265 width=23 height=51 xoffset=0 yoffset=18 xadvance=25 page=0 chnl=0 <char id="113" x="309" y="99" width="34" height="56" xoffset="2" yoffset="27" xadvance="41" page="0" chnl="0" />
char id=117 x=53 y=319 width=32 height=41 xoffset=4 yoffset=28 xadvance=40 page=0 chnl=0 <char id="114" x="32" y="319" width="21" height="41" xoffset="4" yoffset="27" xadvance="25" page="0" chnl="0" />
char id=118 x=85 y=319 width=35 height=40 xoffset=0 yoffset=28 xadvance=35 page=0 chnl=0 <char id="115" x="407" y="265" width="32" height="42" xoffset="2" yoffset="27" xadvance="37" page="0" chnl="0" />
char id=119 x=120 y=319 width=54 height=40 xoffset=0 yoffset=28 xadvance=54 page=0 chnl=0 <char id="116" x="245" y="265" width="23" height="51" xoffset="0" yoffset="18" xadvance="25" page="0" chnl="0" />
char id=120 x=174 y=319 width=36 height=40 xoffset=0 yoffset=28 xadvance=36 page=0 chnl=0 <char id="117" x="53" y="319" width="32" height="41" xoffset="4" yoffset="28" xadvance="40" page="0" chnl="0" />
char id=121 x=343 y=99 width=36 height=56 xoffset=-1 yoffset=28 xadvance=34 page=0 chnl=0 <char id="118" x="85" y="319" width="35" height="40" xoffset="0" yoffset="28" xadvance="35" page="0" chnl="0" />
char id=122 x=210 y=319 width=32 height=40 xoffset=2 yoffset=28 xadvance=35 page=0 chnl=0 <char id="119" x="120" y="319" width="54" height="40" xoffset="0" yoffset="28" xadvance="54" page="0" chnl="0" />
char id=123 x=113 y=0 width=24 height=73 xoffset=1 yoffset=9 xadvance=25 page=0 chnl=0 <char id="120" x="174" y="319" width="36" height="40" xoffset="0" yoffset="28" xadvance="36" page="0" chnl="0" />
char id=124 x=348 y=0 width=7 height=63 xoffset=5 yoffset=14 xadvance=17 page=0 chnl=0 <char id="121" x="343" y="99" width="36" height="56" xoffset="-1" yoffset="28" xadvance="34" page="0" chnl="0" />
char id=125 x=137 y=0 width=24 height=73 xoffset=-1 yoffset=9 xadvance=24 page=0 chnl=0 <char id="122" x="210" y="319" width="32" height="40" xoffset="2" yoffset="28" xadvance="35" page="0" chnl="0" />
char id=126 x=462 y=319 width=42 height=16 xoffset=4 yoffset=38 xadvance=50 page=0 chnl=0 <char id="123" x="113" y="0" width="24" height="73" xoffset="1" yoffset="9" xadvance="25" page="0" chnl="0" />
char id=127 x=0 y=0 width=70 height=99 xoffset=2 yoffset=-11 xadvance=74 page=0 chnl=0 <char id="124" x="348" y="0" width="7" height="63" xoffset="5" yoffset="14" xadvance="17" page="0" chnl="0" />
kernings count=382 <char id="125" x="137" y="0" width="24" height="73" xoffset="-1" yoffset="9" xadvance="24" page="0" chnl="0" />
kerning first=70 second=74 amount=-9 <char id="126" x="462" y="319" width="42" height="16" xoffset="4" yoffset="38" xadvance="50" page="0" chnl="0" />
kerning first=34 second=97 amount=-2 <char id="127" x="0" y="0" width="70" height="99" xoffset="2" yoffset="-11" xadvance="74" page="0" chnl="0" />
kerning first=34 second=101 amount=-2 </chars>
kerning first=34 second=113 amount=-2 <kernings count="382">
kerning first=34 second=99 amount=-2 <kerning first="70" second="74" amount="-9" />
kerning first=70 second=99 amount=-1 <kerning first="34" second="97" amount="-2" />
kerning first=88 second=113 amount=-1 <kerning first="34" second="101" amount="-2" />
kerning first=84 second=46 amount=-8 <kerning first="34" second="113" amount="-2" />
kerning first=84 second=119 amount=-2 <kerning first="34" second="99" amount="-2" />
kerning first=87 second=97 amount=-1 <kerning first="70" second="99" amount="-1" />
kerning first=90 second=117 amount=-1 <kerning first="88" second="113" amount="-1" />
kerning first=39 second=97 amount=-2 <kerning first="84" second="46" amount="-8" />
kerning first=69 second=111 amount=-1 <kerning first="84" second="119" amount="-2" />
kerning first=87 second=41 amount=1 <kerning first="87" second="97" amount="-1" />
kerning first=76 second=86 amount=-6 <kerning first="90" second="117" amount="-1" />
kerning first=121 second=34 amount=1 <kerning first="39" second="97" amount="-2" />
kerning first=40 second=86 amount=1 <kerning first="69" second="111" amount="-1" />
kerning first=85 second=65 amount=-1 <kerning first="87" second="41" amount="1" />
kerning first=89 second=89 amount=1 <kerning first="76" second="86" amount="-6" />
kerning first=72 second=65 amount=1 <kerning first="121" second="34" amount="1" />
kerning first=104 second=39 amount=-4 <kerning first="40" second="86" amount="1" />
kerning first=114 second=102 amount=1 <kerning first="85" second="65" amount="-1" />
kerning first=89 second=42 amount=-2 <kerning first="89" second="89" amount="1" />
kerning first=114 second=34 amount=1 <kerning first="72" second="65" amount="1" />
kerning first=84 second=115 amount=-4 <kerning first="104" second="39" amount="-4" />
kerning first=84 second=71 amount=-1 <kerning first="114" second="102" amount="1" />
kerning first=89 second=101 amount=-2 <kerning first="89" second="42" amount="-2" />
kerning first=89 second=45 amount=-2 <kerning first="114" second="34" amount="1" />
kerning first=122 second=99 amount=-1 <kerning first="84" second="115" amount="-4" />
kerning first=78 second=88 amount=1 <kerning first="84" second="71" amount="-1" />
kerning first=68 second=89 amount=-2 <kerning first="89" second="101" amount="-2" />
kerning first=122 second=103 amount=-1 <kerning first="89" second="45" amount="-2" />
kerning first=78 second=84 amount=-1 <kerning first="122" second="99" amount="-1" />
kerning first=86 second=103 amount=-2 <kerning first="78" second="88" amount="1" />
kerning first=89 second=67 amount=-1 <kerning first="68" second="89" amount="-2" />
kerning first=89 second=79 amount=-1 <kerning first="122" second="103" amount="-1" />
kerning first=75 second=111 amount=-1 <kerning first="78" second="84" amount="-1" />
kerning first=111 second=120 amount=-1 <kerning first="86" second="103" amount="-2" />
kerning first=87 second=44 amount=-4 <kerning first="89" second="67" amount="-1" />
kerning first=91 second=74 amount=-1 <kerning first="89" second="79" amount="-1" />
kerning first=120 second=111 amount=-1 <kerning first="75" second="111" amount="-1" />
kerning first=84 second=111 amount=-3 <kerning first="111" second="120" amount="-1" />
kerning first=102 second=113 amount=-1 <kerning first="87" second="44" amount="-4" />
kerning first=80 second=88 amount=-1 <kerning first="91" second="74" amount="-1" />
kerning first=66 second=84 amount=-1 <kerning first="120" second="111" amount="-1" />
kerning first=65 second=87 amount=-2 <kerning first="84" second="111" amount="-3" />
kerning first=86 second=100 amount=-2 <kerning first="102" second="113" amount="-1" />
kerning first=122 second=100 amount=-1 <kerning first="80" second="88" amount="-1" />
kerning first=75 second=118 amount=-1 <kerning first="66" second="84" amount="-1" />
kerning first=70 second=118 amount=-1 <kerning first="65" second="87" amount="-2" />
kerning first=73 second=88 amount=1 <kerning first="86" second="100" amount="-2" />
kerning first=70 second=121 amount=-1 <kerning first="122" second="100" amount="-1" />
kerning first=65 second=34 amount=-4 <kerning first="75" second="118" amount="-1" />
kerning first=39 second=101 amount=-2 <kerning first="70" second="118" amount="-1" />
kerning first=75 second=101 amount=-1 <kerning first="73" second="88" amount="1" />
kerning first=84 second=99 amount=-3 <kerning first="70" second="121" amount="-1" />
kerning first=84 second=65 amount=-3 <kerning first="65" second="34" amount="-4" />
kerning first=112 second=39 amount=-1 <kerning first="39" second="101" amount="-2" />
kerning first=76 second=39 amount=-12 <kerning first="75" second="101" amount="-1" />
kerning first=78 second=65 amount=1 <kerning first="84" second="99" amount="-3" />
kerning first=88 second=45 amount=-2 <kerning first="84" second="65" amount="-3" />
kerning first=65 second=121 amount=-2 <kerning first="112" second="39" amount="-1" />
kerning first=34 second=111 amount=-2 <kerning first="76" second="39" amount="-12" />
kerning first=89 second=85 amount=-3 <kerning first="78" second="65" amount="1" />
kerning first=114 second=99 amount=-1 <kerning first="88" second="45" amount="-2" />
kerning first=86 second=125 amount=1 <kerning first="65" second="121" amount="-2" />
kerning first=70 second=111 amount=-1 <kerning first="34" second="111" amount="-2" />
kerning first=89 second=120 amount=-1 <kerning first="89" second="85" amount="-3" />
kerning first=90 second=119 amount=-1 <kerning first="114" second="99" amount="-1" />
kerning first=120 second=99 amount=-1 <kerning first="86" second="125" amount="1" />
kerning first=89 second=117 amount=-1 <kerning first="70" second="111" amount="-1" />
kerning first=82 second=89 amount=-2 <kerning first="89" second="120" amount="-1" />
kerning first=75 second=117 amount=-1 <kerning first="90" second="119" amount="-1" />
kerning first=34 second=34 amount=-4 <kerning first="120" second="99" amount="-1" />
kerning first=89 second=110 amount=-1 <kerning first="89" second="117" amount="-1" />
kerning first=88 second=101 amount=-1 <kerning first="82" second="89" amount="-2" />
kerning first=107 second=103 amount=-1 <kerning first="75" second="117" amount="-1" />
kerning first=34 second=115 amount=-3 <kerning first="34" second="34" amount="-4" />
kerning first=98 second=39 amount=-1 <kerning first="89" second="110" amount="-1" />
kerning first=70 second=65 amount=-6 <kerning first="88" second="101" amount="-1" />
kerning first=70 second=46 amount=-8 <kerning first="107" second="103" amount="-1" />
kerning first=98 second=34 amount=-1 <kerning first="34" second="115" amount="-3" />
kerning first=70 second=84 amount=1 <kerning first="98" second="39" amount="-1" />
kerning first=114 second=100 amount=-1 <kerning first="70" second="65" amount="-6" />
kerning first=88 second=79 amount=-1 <kerning first="70" second="46" amount="-8" />
kerning first=39 second=113 amount=-2 <kerning first="98" second="34" amount="-1" />
kerning first=114 second=103 amount=-1 <kerning first="70" second="84" amount="1" />
kerning first=77 second=65 amount=1 <kerning first="114" second="100" amount="-1" />
kerning first=120 second=103 amount=-1 <kerning first="88" second="79" amount="-1" />
kerning first=114 second=121 amount=1 <kerning first="39" second="113" amount="-2" />
kerning first=89 second=100 amount=-2 <kerning first="114" second="103" amount="-1" />
kerning first=80 second=65 amount=-5 <kerning first="77" second="65" amount="1" />
kerning first=121 second=111 amount=-1 <kerning first="120" second="103" amount="-1" />
kerning first=84 second=74 amount=-8 <kerning first="114" second="121" amount="1" />
kerning first=122 second=111 amount=-1 <kerning first="89" second="100" amount="-2" />
kerning first=114 second=118 amount=1 <kerning first="80" second="65" amount="-5" />
kerning first=102 second=41 amount=1 <kerning first="121" second="111" amount="-1" />
kerning first=122 second=113 amount=-1 <kerning first="84" second="74" amount="-8" />
kerning first=89 second=122 amount=-1 <kerning first="122" second="111" amount="-1" />
kerning first=89 second=38 amount=-1 <kerning first="114" second="118" amount="1" />
kerning first=81 second=89 amount=-1 <kerning first="102" second="41" amount="1" />
kerning first=114 second=111 amount=-1 <kerning first="122" second="113" amount="-1" />
kerning first=46 second=34 amount=-6 <kerning first="89" second="122" amount="-1" />
kerning first=84 second=112 amount=-4 <kerning first="89" second="38" amount="-1" />
kerning first=112 second=34 amount=-1 <kerning first="81" second="89" amount="-1" />
kerning first=76 second=34 amount=-12 <kerning first="114" second="111" amount="-1" />
kerning first=102 second=125 amount=1 <kerning first="46" second="34" amount="-6" />
kerning first=39 second=115 amount=-3 <kerning first="84" second="112" amount="-4" />
kerning first=76 second=118 amount=-5 <kerning first="112" second="34" amount="-1" />
kerning first=86 second=99 amount=-2 <kerning first="76" second="34" amount="-12" />
kerning first=84 second=84 amount=1 <kerning first="102" second="125" amount="1" />
kerning first=86 second=65 amount=-3 <kerning first="39" second="115" amount="-3" />
kerning first=87 second=101 amount=-1 <kerning first="76" second="118" amount="-5" />
kerning first=67 second=125 amount=-1 <kerning first="86" second="99" amount="-2" />
kerning first=120 second=113 amount=-1 <kerning first="84" second="84" amount="1" />
kerning first=118 second=46 amount=-4 <kerning first="86" second="65" amount="-3" />
kerning first=88 second=103 amount=-1 <kerning first="87" second="101" amount="-1" />
kerning first=111 second=122 amount=-1 <kerning first="67" second="125" amount="-1" />
kerning first=77 second=84 amount=-1 <kerning first="120" second="113" amount="-1" />
kerning first=114 second=46 amount=-4 <kerning first="118" second="46" amount="-4" />
kerning first=34 second=39 amount=-4 <kerning first="88" second="103" amount="-1" />
kerning first=114 second=44 amount=-4 <kerning first="111" second="122" amount="-1" />
kerning first=69 second=84 amount=1 <kerning first="77" second="84" amount="-1" />
kerning first=89 second=46 amount=-7 <kerning first="114" second="46" amount="-4" />
kerning first=97 second=39 amount=-2 <kerning first="34" second="39" amount="-4" />
kerning first=34 second=100 amount=-2 <kerning first="114" second="44" amount="-4" />
kerning first=70 second=100 amount=-1 <kerning first="69" second="84" amount="1" />
kerning first=84 second=120 amount=-3 <kerning first="89" second="46" amount="-7" />
kerning first=90 second=118 amount=-1 <kerning first="97" second="39" amount="-2" />
kerning first=70 second=114 amount=-1 <kerning first="34" second="100" amount="-2" />
kerning first=34 second=112 amount=-1 <kerning first="70" second="100" amount="-1" />
kerning first=109 second=34 amount=-4 <kerning first="84" second="120" amount="-3" />
kerning first=86 second=113 amount=-2 <kerning first="90" second="118" amount="-1" />
kerning first=88 second=71 amount=-1 <kerning first="70" second="114" amount="-1" />
kerning first=66 second=89 amount=-2 <kerning first="34" second="112" amount="-1" />
kerning first=102 second=103 amount=-1 <kerning first="109" second="34" amount="-4" />
kerning first=88 second=67 amount=-1 <kerning first="86" second="113" amount="-2" />
kerning first=39 second=110 amount=-1 <kerning first="88" second="71" amount="-1" />
kerning first=75 second=110 amount=-1 <kerning first="66" second="89" amount="-2" />
kerning first=88 second=117 amount=-1 <kerning first="102" second="103" amount="-1" />
kerning first=89 second=118 amount=-1 <kerning first="88" second="67" amount="-1" />
kerning first=97 second=118 amount=-1 <kerning first="39" second="110" amount="-1" />
kerning first=87 second=65 amount=-2 <kerning first="75" second="110" amount="-1" />
kerning first=73 second=89 amount=-1 <kerning first="88" second="117" amount="-1" />
kerning first=89 second=74 amount=-3 <kerning first="89" second="118" amount="-1" />
kerning first=102 second=101 amount=-1 <kerning first="97" second="118" amount="-1" />
kerning first=86 second=111 amount=-2 <kerning first="87" second="65" amount="-2" />
kerning first=65 second=119 amount=-1 <kerning first="73" second="89" amount="-1" />
kerning first=84 second=100 amount=-3 <kerning first="89" second="74" amount="-3" />
kerning first=104 second=34 amount=-4 <kerning first="102" second="101" amount="-1" />
kerning first=86 second=41 amount=1 <kerning first="86" second="111" amount="-2" />
kerning first=111 second=34 amount=-5 <kerning first="65" second="119" amount="-1" />
kerning first=40 second=89 amount=1 <kerning first="84" second="100" amount="-3" />
kerning first=121 second=39 amount=1 <kerning first="104" second="34" amount="-4" />
kerning first=68 second=90 amount=-1 <kerning first="86" second="41" amount="1" />
kerning first=114 second=113 amount=-1 <kerning first="111" second="34" amount="-5" />
kerning first=68 second=88 amount=-1 <kerning first="40" second="89" amount="1" />
kerning first=98 second=120 amount=-1 <kerning first="121" second="39" amount="1" />
kerning first=110 second=34 amount=-4 <kerning first="68" second="90" amount="-1" />
kerning first=119 second=44 amount=-4 <kerning first="114" second="113" amount="-1" />
kerning first=119 second=46 amount=-4 <kerning first="68" second="88" amount="-1" />
kerning first=118 second=44 amount=-4 <kerning first="98" second="120" amount="-1" />
kerning first=84 second=114 amount=-3 <kerning first="110" second="34" amount="-4" />
kerning first=86 second=97 amount=-2 <kerning first="119" second="44" amount="-4" />
kerning first=68 second=86 amount=-1 <kerning first="119" second="46" amount="-4" />
kerning first=86 second=93 amount=1 <kerning first="118" second="44" amount="-4" />
kerning first=97 second=34 amount=-2 <kerning first="84" second="114" amount="-3" />
kerning first=34 second=65 amount=-4 <kerning first="86" second="97" amount="-2" />
kerning first=84 second=118 amount=-3 <kerning first="68" second="86" amount="-1" />
kerning first=76 second=84 amount=-10 <kerning first="86" second="93" amount="1" />
kerning first=107 second=99 amount=-1 <kerning first="97" second="34" amount="-2" />
kerning first=121 second=46 amount=-4 <kerning first="34" second="65" amount="-4" />
kerning first=123 second=85 amount=-1 <kerning first="84" second="118" amount="-3" />
kerning first=65 second=63 amount=-2 <kerning first="76" second="84" amount="-10" />
kerning first=89 second=44 amount=-7 <kerning first="107" second="99" amount="-1" />
kerning first=80 second=118 amount=1 <kerning first="121" second="46" amount="-4" />
kerning first=112 second=122 amount=-1 <kerning first="123" second="85" amount="-1" />
kerning first=79 second=65 amount=-1 <kerning first="65" second="63" amount="-2" />
kerning first=80 second=121 amount=1 <kerning first="89" second="44" amount="-7" />
kerning first=118 second=34 amount=1 <kerning first="80" second="118" amount="1" />
kerning first=87 second=45 amount=-2 <kerning first="112" second="122" amount="-1" />
kerning first=69 second=100 amount=-1 <kerning first="79" second="65" amount="-1" />
kerning first=87 second=103 amount=-1 <kerning first="80" second="121" amount="1" />
kerning first=112 second=120 amount=-1 <kerning first="118" second="34" amount="1" />
kerning first=68 second=44 amount=-4 <kerning first="87" second="45" amount="-2" />
kerning first=86 second=45 amount=-1 <kerning first="69" second="100" amount="-1" />
kerning first=39 second=34 amount=-4 <kerning first="87" second="103" amount="-1" />
kerning first=68 second=46 amount=-4 <kerning first="112" second="120" amount="-1" />
kerning first=65 second=89 amount=-3 <kerning first="68" second="44" amount="-4" />
kerning first=69 second=118 amount=-1 <kerning first="86" second="45" amount="-1" />
kerning first=88 second=99 amount=-1 <kerning first="39" second="34" amount="-4" />
kerning first=87 second=46 amount=-4 <kerning first="68" second="46" amount="-4" />
kerning first=47 second=47 amount=-8 <kerning first="65" second="89" amount="-3" />
kerning first=73 second=65 amount=1 <kerning first="69" second="118" amount="-1" />
kerning first=123 second=74 amount=-1 <kerning first="88" second="99" amount="-1" />
kerning first=69 second=102 amount=-1 <kerning first="87" second="46" amount="-4" />
kerning first=87 second=111 amount=-1 <kerning first="47" second="47" amount="-8" />
kerning first=39 second=112 amount=-1 <kerning first="73" second="65" amount="1" />
kerning first=89 second=116 amount=-1 <kerning first="123" second="74" amount="-1" />
kerning first=70 second=113 amount=-1 <kerning first="69" second="102" amount="-1" />
kerning first=77 second=88 amount=1 <kerning first="87" second="111" amount="-1" />
kerning first=84 second=32 amount=-1 <kerning first="39" second="112" amount="-1" />
kerning first=90 second=103 amount=-1 <kerning first="89" second="116" amount="-1" />
kerning first=65 second=86 amount=-3 <kerning first="70" second="113" amount="-1" />
kerning first=75 second=112 amount=-1 <kerning first="77" second="88" amount="1" />
kerning first=39 second=109 amount=-1 <kerning first="84" second="32" amount="-1" />
kerning first=75 second=81 amount=-1 <kerning first="90" second="103" amount="-1" />
kerning first=89 second=115 amount=-2 <kerning first="65" second="86" amount="-3" />
kerning first=84 second=83 amount=-1 <kerning first="75" second="112" amount="-1" />
kerning first=89 second=87 amount=1 <kerning first="39" second="109" amount="-1" />
kerning first=114 second=101 amount=-1 <kerning first="75" second="81" amount="-1" />
kerning first=116 second=111 amount=-1 <kerning first="89" second="115" amount="-2" />
kerning first=90 second=100 amount=-1 <kerning first="84" second="83" amount="-1" />
kerning first=84 second=122 amount=-2 <kerning first="89" second="87" amount="1" />
kerning first=68 second=84 amount=-1 <kerning first="114" second="101" amount="-1" />
kerning first=32 second=84 amount=-1 <kerning first="116" second="111" amount="-1" />
kerning first=84 second=117 amount=-3 <kerning first="90" second="100" amount="-1" />
kerning first=74 second=65 amount=-1 <kerning first="84" second="122" amount="-2" />
kerning first=107 second=101 amount=-1 <kerning first="68" second="84" amount="-1" />
kerning first=75 second=109 amount=-1 <kerning first="32" second="84" amount="-1" />
kerning first=80 second=46 amount=-11 <kerning first="84" second="117" amount="-3" />
kerning first=89 second=93 amount=1 <kerning first="74" second="65" amount="-1" />
kerning first=89 second=65 amount=-3 <kerning first="107" second="101" amount="-1" />
kerning first=87 second=117 amount=-1 <kerning first="75" second="109" amount="-1" />
kerning first=89 second=81 amount=-1 <kerning first="80" second="46" amount="-11" />
kerning first=39 second=103 amount=-2 <kerning first="89" second="93" amount="1" />
kerning first=86 second=101 amount=-2 <kerning first="89" second="65" amount="-3" />
kerning first=86 second=117 amount=-1 <kerning first="87" second="117" amount="-1" />
kerning first=84 second=113 amount=-3 <kerning first="89" second="81" amount="-1" />
kerning first=34 second=110 amount=-1 <kerning first="39" second="103" amount="-2" />
kerning first=89 second=84 amount=1 <kerning first="86" second="101" amount="-2" />
kerning first=84 second=110 amount=-4 <kerning first="86" second="117" amount="-1" />
kerning first=39 second=99 amount=-2 <kerning first="84" second="113" amount="-3" />
kerning first=88 second=121 amount=-1 <kerning first="34" second="110" amount="-1" />
kerning first=65 second=39 amount=-4 <kerning first="89" second="84" amount="1" />
kerning first=110 second=39 amount=-4 <kerning first="84" second="110" amount="-4" />
kerning first=75 second=67 amount=-1 <kerning first="39" second="99" amount="-2" />
kerning first=88 second=118 amount=-1 <kerning first="88" second="121" amount="-1" />
kerning first=86 second=114 amount=-1 <kerning first="65" second="39" amount="-4" />
kerning first=80 second=74 amount=-7 <kerning first="110" second="39" amount="-4" />
kerning first=84 second=97 amount=-4 <kerning first="75" second="67" amount="-1" />
kerning first=82 second=84 amount=-3 <kerning first="88" second="118" amount="-1" />
kerning first=91 second=85 amount=-1 <kerning first="86" second="114" amount="-1" />
kerning first=102 second=99 amount=-1 <kerning first="80" second="74" amount="-7" />
kerning first=66 second=86 amount=-1 <kerning first="84" second="97" amount="-4" />
kerning first=120 second=101 amount=-1 <kerning first="82" second="84" amount="-3" />
kerning first=102 second=93 amount=1 <kerning first="91" second="85" amount="-1" />
kerning first=75 second=100 amount=-1 <kerning first="102" second="99" amount="-1" />
kerning first=84 second=79 amount=-1 <kerning first="66" second="86" amount="-1" />
kerning first=111 second=121 amount=-1 <kerning first="120" second="101" amount="-1" />
kerning first=75 second=121 amount=-1 <kerning first="102" second="93" amount="1" />
kerning first=81 second=87 amount=-1 <kerning first="75" second="100" amount="-1" />
kerning first=107 second=113 amount=-1 <kerning first="84" second="79" amount="-1" />
kerning first=120 second=100 amount=-1 <kerning first="111" second="121" amount="-1" />
kerning first=90 second=79 amount=-1 <kerning first="75" second="121" amount="-1" />
kerning first=89 second=114 amount=-1 <kerning first="81" second="87" amount="-1" />
kerning first=122 second=101 amount=-1 <kerning first="107" second="113" amount="-1" />
kerning first=111 second=118 amount=-1 <kerning first="120" second="100" amount="-1" />
kerning first=82 second=86 amount=-1 <kerning first="90" second="79" amount="-1" />
kerning first=67 second=84 amount=-1 <kerning first="89" second="114" amount="-1" />
kerning first=70 second=101 amount=-1 <kerning first="122" second="101" amount="-1" />
kerning first=89 second=83 amount=-1 <kerning first="111" second="118" amount="-1" />
kerning first=114 second=97 amount=-1 <kerning first="82" second="86" amount="-1" />
kerning first=70 second=97 amount=-1 <kerning first="67" second="84" amount="-1" />
kerning first=89 second=102 amount=-1 <kerning first="70" second="101" amount="-1" />
kerning first=78 second=89 amount=-1 <kerning first="89" second="83" amount="-1" />
kerning first=70 second=44 amount=-8 <kerning first="114" second="97" amount="-1" />
kerning first=44 second=39 amount=-6 <kerning first="70" second="97" amount="-1" />
kerning first=84 second=45 amount=-8 <kerning first="89" second="102" amount="-1" />
kerning first=89 second=121 amount=-1 <kerning first="78" second="89" amount="-1" />
kerning first=84 second=86 amount=1 <kerning first="70" second="44" amount="-8" />
kerning first=87 second=99 amount=-1 <kerning first="44" second="39" amount="-6" />
kerning first=98 second=122 amount=-1 <kerning first="84" second="45" amount="-8" />
kerning first=89 second=112 amount=-1 <kerning first="89" second="121" amount="-1" />
kerning first=89 second=103 amount=-2 <kerning first="84" second="86" amount="1" />
kerning first=88 second=81 amount=-1 <kerning first="87" second="99" amount="-1" />
kerning first=102 second=34 amount=1 <kerning first="98" second="122" amount="-1" />
kerning first=109 second=39 amount=-4 <kerning first="89" second="112" amount="-1" />
kerning first=81 second=84 amount=-2 <kerning first="89" second="103" amount="-2" />
kerning first=121 second=97 amount=-1 <kerning first="88" second="81" amount="-1" />
kerning first=89 second=99 amount=-2 <kerning first="102" second="34" amount="1" />
kerning first=89 second=125 amount=1 <kerning first="109" second="39" amount="-4" />
kerning first=81 second=86 amount=-1 <kerning first="81" second="84" amount="-2" />
kerning first=114 second=116 amount=2 <kerning first="121" second="97" amount="-1" />
kerning first=114 second=119 amount=1 <kerning first="89" second="99" amount="-2" />
kerning first=84 second=44 amount=-8 <kerning first="89" second="125" amount="1" />
kerning first=102 second=39 amount=1 <kerning first="81" second="86" amount="-1" />
kerning first=44 second=34 amount=-6 <kerning first="114" second="116" amount="2" />
kerning first=34 second=109 amount=-1 <kerning first="114" second="119" amount="1" />
kerning first=75 second=119 amount=-2 <kerning first="84" second="44" amount="-8" />
kerning first=76 second=65 amount=1 <kerning first="102" second="39" amount="1" />
kerning first=84 second=81 amount=-1 <kerning first="44" second="34" amount="-6" />
kerning first=76 second=121 amount=-5 <kerning first="34" second="109" amount="-1" />
kerning first=69 second=101 amount=-1 <kerning first="75" second="119" amount="-2" />
kerning first=89 second=111 amount=-2 <kerning first="76" second="65" amount="1" />
kerning first=80 second=90 amount=-1 <kerning first="84" second="81" amount="-1" />
kerning first=89 second=97 amount=-3 <kerning first="76" second="121" amount="-5" />
kerning first=89 second=109 amount=-1 <kerning first="69" second="101" amount="-1" />
kerning first=90 second=99 amount=-1 <kerning first="89" second="111" amount="-2" />
kerning first=89 second=86 amount=1 <kerning first="80" second="90" amount="-1" />
kerning first=79 second=88 amount=-1 <kerning first="89" second="97" amount="-3" />
kerning first=70 second=103 amount=-1 <kerning first="89" second="109" amount="-1" />
kerning first=34 second=103 amount=-2 <kerning first="90" second="99" amount="-1" />
kerning first=84 second=67 amount=-1 <kerning first="89" second="86" amount="1" />
kerning first=76 second=79 amount=-2 <kerning first="79" second="88" amount="-1" />
kerning first=89 second=41 amount=1 <kerning first="70" second="103" amount="-1" />
kerning first=65 second=118 amount=-2 <kerning first="34" second="103" amount="-2" />
kerning first=75 second=71 amount=-1 <kerning first="84" second="67" amount="-1" />
kerning first=76 second=87 amount=-5 <kerning first="76" second="79" amount="-2" />
kerning first=77 second=89 amount=-1 <kerning first="89" second="41" amount="1" />
kerning first=90 second=113 amount=-1 <kerning first="65" second="118" amount="-2" />
kerning first=79 second=89 amount=-2 <kerning first="75" second="71" amount="-1" />
kerning first=118 second=111 amount=-1 <kerning first="76" second="87" amount="-5" />
kerning first=118 second=97 amount=-1 <kerning first="77" second="89" amount="-1" />
kerning first=88 second=100 amount=-1 <kerning first="90" second="113" amount="-1" />
kerning first=90 second=121 amount=-1 <kerning first="79" second="89" amount="-2" />
kerning first=89 second=113 amount=-2 <kerning first="118" second="111" amount="-1" />
kerning first=84 second=87 amount=1 <kerning first="118" second="97" amount="-1" />
kerning first=39 second=111 amount=-2 <kerning first="88" second="100" amount="-1" />
kerning first=80 second=44 amount=-11 <kerning first="90" second="121" amount="-1" />
kerning first=39 second=100 amount=-2 <kerning first="89" second="113" amount="-2" />
kerning first=75 second=113 amount=-1 <kerning first="84" second="87" amount="1" />
kerning first=88 second=111 amount=-1 <kerning first="39" second="111" amount="-2" />
kerning first=84 second=89 amount=1 <kerning first="80" second="44" amount="-11" />
kerning first=84 second=103 amount=-3 <kerning first="39" second="100" amount="-2" />
kerning first=70 second=117 amount=-1 <kerning first="75" second="113" amount="-1" />
kerning first=67 second=41 amount=-1 <kerning first="88" second="111" amount="-1" />
kerning first=89 second=71 amount=-1 <kerning first="84" second="89" amount="1" />
kerning first=121 second=44 amount=-4 <kerning first="84" second="103" amount="-3" />
kerning first=97 second=121 amount=-1 <kerning first="70" second="117" amount="-1" />
kerning first=87 second=113 amount=-1 <kerning first="67" second="41" amount="-1" />
kerning first=73 second=84 amount=-1 <kerning first="89" second="71" amount="-1" />
kerning first=84 second=101 amount=-3 <kerning first="121" second="44" amount="-4" />
kerning first=75 second=99 amount=-1 <kerning first="97" second="121" amount="-1" />
kerning first=65 second=85 amount=-1 <kerning first="87" second="113" amount="-1" />
kerning first=76 second=67 amount=-2 <kerning first="73" second="84" amount="-1" />
kerning first=76 second=81 amount=-2 <kerning first="84" second="101" amount="-3" />
kerning first=75 second=79 amount=-1 <kerning first="75" second="99" amount="-1" />
kerning first=39 second=65 amount=-4 <kerning first="65" second="85" amount="-1" />
kerning first=76 second=117 amount=-2 <kerning first="76" second="67" amount="-2" />
kerning first=65 second=84 amount=-5 <kerning first="76" second="81" amount="-2" />
kerning first=90 second=101 amount=-1 <kerning first="75" second="79" amount="-1" />
kerning first=84 second=121 amount=-3 <kerning first="39" second="65" amount="-4" />
kerning first=69 second=99 amount=-1 <kerning first="76" second="117" amount="-2" />
kerning first=114 second=39 amount=1 <kerning first="65" second="84" amount="-5" />
kerning first=84 second=109 amount=-4 <kerning first="90" second="101" amount="-1" />
kerning first=76 second=119 amount=-3 <kerning first="84" second="121" amount="-3" />
kerning first=76 second=85 amount=-2 <kerning first="69" second="99" amount="-1" />
kerning first=65 second=116 amount=-1 <kerning first="114" second="39" amount="1" />
kerning first=76 second=71 amount=-2 <kerning first="84" second="109" amount="-4" />
kerning first=79 second=90 amount=-1 <kerning first="76" second="119" amount="-3" />
kerning first=107 second=100 amount=-1 <kerning first="76" second="85" amount="-2" />
kerning first=90 second=111 amount=-1 <kerning first="65" second="116" amount="-1" />
kerning first=79 second=44 amount=-4 <kerning first="76" second="71" amount="-2" />
kerning first=75 second=45 amount=-2 <kerning first="79" second="90" amount="-1" />
kerning first=40 second=87 amount=1 <kerning first="107" second="100" amount="-1" />
kerning first=79 second=86 amount=-1 <kerning first="90" second="111" amount="-1" />
kerning first=102 second=100 amount=-1 <kerning first="79" second="44" amount="-4" />
kerning first=72 second=89 amount=-1 <kerning first="75" second="45" amount="-2" />
kerning first=72 second=88 amount=1 <kerning first="40" second="87" amount="1" />
kerning first=79 second=46 amount=-4 <kerning first="79" second="86" amount="-1" />
kerning first=76 second=89 amount=-8 <kerning first="102" second="100" amount="-1" />
kerning first=68 second=65 amount=-1 <kerning first="72" second="89" amount="-1" />
kerning first=79 second=84 amount=-1 <kerning first="72" second="88" amount="1" />
kerning first=87 second=100 amount=-1 <kerning first="79" second="46" amount="-4" />
kerning first=75 second=103 amount=-1 <kerning first="76" second="89" amount="-8" />
kerning first=90 second=67 amount=-1 <kerning first="68" second="65" amount="-1" />
kerning first=69 second=103 amount=-1 <kerning first="79" second="84" amount="-1" />
kerning first=90 second=71 amount=-1 <kerning first="87" second="100" amount="-1" />
kerning first=86 second=44 amount=-8 <kerning first="75" second="103" amount="-1" />
kerning first=69 second=121 amount=-1 <kerning first="90" second="67" amount="-1" />
kerning first=87 second=114 amount=-1 <kerning first="69" second="103" amount="-1" />
kerning first=118 second=39 amount=1 <kerning first="90" second="71" amount="-1" />
kerning first=46 second=39 amount=-6 <kerning first="86" second="44" amount="-8" />
kerning first=72 second=84 amount=-1 <kerning first="69" second="121" amount="-1" />
kerning first=86 second=46 amount=-8 <kerning first="87" second="114" amount="-1" />
kerning first=69 second=113 amount=-1 <kerning first="118" second="39" amount="1" />
kerning first=69 second=119 amount=-1 <kerning first="46" second="39" amount="-6" />
kerning first=39 second=39 amount=-4 <kerning first="72" second="84" amount="-1" />
kerning first=69 second=117 amount=-1 <kerning first="86" second="46" amount="-8" />
kerning first=111 second=39 amount=-5 <kerning first="69" second="113" amount="-1" />
kerning first=90 second=81 amount=-1 <kerning first="69" second="119" amount="-1" />
<kerning first="39" second="39" amount="-4" />
<kerning first="69" second="117" amount="-1" />
<kerning first="111" second="39" amount="-5" />
<kerning first="90" second="81" amount="-1" />
</kernings>
</font>

View File

@ -1,488 +1,494 @@
info face="Roboto Black" size=72 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2 <?xml version="1.0"?>
common lineHeight=85 base=67 scaleW=512 scaleH=512 pages=1 packed=0 <font>
page id=0 file="RobotoBlack72White.png" <info face="Roboto Black" size="72" bold="0" italic="0" charset="" unicode="0" stretchH="100" smooth="1" aa="1" padding="1,1,1,1" spacing="-2,-2" outline="0" />
chars count=98 <common lineHeight="85" base="67" scaleW="512" scaleH="512" pages="1" packed="0" alphaChnl="0" redChnl="0" greenChnl="0" blueChnl="0" />
char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=0 page=0 chnl=0 <pages>
char id=10 x=0 y=0 width=70 height=99 xoffset=2 yoffset=-11 xadvance=74 page=0 chnl=0 <page id="0" file="RobotoBlack72White.png" /> </pages>
char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=18 page=0 chnl=0 <chars count="98">
char id=33 x=460 y=156 width=15 height=55 xoffset=3 yoffset=14 xadvance=20 page=0 chnl=0 <char id="0" x="0" y="0" width="0" height="0" xoffset="-1" yoffset="66" xadvance="0" page="0" chnl="0" />
char id=34 x=207 y=362 width=22 height=22 xoffset=0 yoffset=12 xadvance=23 page=0 chnl=0 <char id="10" x="0" y="0" width="70" height="99" xoffset="2" yoffset="-11" xadvance="74" page="0" chnl="0" />
char id=35 x=404 y=266 width=41 height=54 xoffset=0 yoffset=14 xadvance=42 page=0 chnl=0 <char id="32" x="0" y="0" width="0" height="0" xoffset="-1" yoffset="66" xadvance="18" page="0" chnl="0" />
char id=36 x=220 y=0 width=38 height=69 xoffset=2 yoffset=7 xadvance=42 page=0 chnl=0 <char id="33" x="460" y="156" width="15" height="55" xoffset="3" yoffset="14" xadvance="20" page="0" chnl="0" />
char id=37 x=167 y=156 width=49 height=56 xoffset=2 yoffset=13 xadvance=53 page=0 chnl=0 <char id="34" x="207" y="362" width="22" height="22" xoffset="0" yoffset="12" xadvance="23" page="0" chnl="0" />
char id=38 x=216 y=156 width=48 height=56 xoffset=1 yoffset=13 xadvance=48 page=0 chnl=0 <char id="35" x="404" y="266" width="41" height="54" xoffset="0" yoffset="14" xadvance="42" page="0" chnl="0" />
char id=39 x=499 y=320 width=10 height=22 xoffset=1 yoffset=12 xadvance=11 page=0 chnl=0 <char id="36" x="220" y="0" width="38" height="69" xoffset="2" yoffset="7" xadvance="42" page="0" chnl="0" />
char id=40 x=70 y=0 width=22 height=75 xoffset=3 yoffset=9 xadvance=25 page=0 chnl=0 <char id="37" x="167" y="156" width="49" height="56" xoffset="2" yoffset="13" xadvance="53" page="0" chnl="0" />
char id=41 x=92 y=0 width=23 height=75 xoffset=0 yoffset=9 xadvance=25 page=0 chnl=0 <char id="38" x="216" y="156" width="48" height="56" xoffset="1" yoffset="13" xadvance="48" page="0" chnl="0" />
char id=42 x=103 y=362 width=36 height=34 xoffset=-1 yoffset=14 xadvance=33 page=0 chnl=0 <char id="39" x="499" y="320" width="10" height="22" xoffset="1" yoffset="12" xadvance="11" page="0" chnl="0" />
char id=43 x=0 y=362 width=37 height=40 xoffset=1 yoffset=23 xadvance=39 page=0 chnl=0 <char id="40" x="70" y="0" width="22" height="75" xoffset="3" yoffset="9" xadvance="25" page="0" chnl="0" />
char id=44 x=483 y=320 width=16 height=25 xoffset=0 yoffset=57 xadvance=20 page=0 chnl=0 <char id="41" x="92" y="0" width="23" height="75" xoffset="0" yoffset="9" xadvance="25" page="0" chnl="0" />
char id=45 x=308 y=362 width=23 height=12 xoffset=4 yoffset=38 xadvance=32 page=0 chnl=0 <char id="42" x="103" y="362" width="36" height="34" xoffset="-1" yoffset="14" xadvance="33" page="0" chnl="0" />
char id=46 x=270 y=362 width=15 height=15 xoffset=3 yoffset=54 xadvance=22 page=0 chnl=0 <char id="43" x="0" y="362" width="37" height="40" xoffset="1" yoffset="23" xadvance="39" page="0" chnl="0" />
char id=47 x=374 y=0 width=29 height=58 xoffset=-3 yoffset=14 xadvance=25 page=0 chnl=0 <char id="44" x="483" y="320" width="16" height="25" xoffset="0" yoffset="57" xadvance="20" page="0" chnl="0" />
char id=48 x=77 y=156 width=38 height=56 xoffset=2 yoffset=13 xadvance=42 page=0 chnl=0 <char id="45" x="308" y="362" width="23" height="12" xoffset="4" yoffset="38" xadvance="32" page="0" chnl="0" />
char id=49 x=299 y=266 width=26 height=54 xoffset=4 yoffset=14 xadvance=41 page=0 chnl=0 <char id="46" x="270" y="362" width="15" height="15" xoffset="3" yoffset="54" xadvance="22" page="0" chnl="0" />
char id=50 x=383 y=156 width=39 height=55 xoffset=1 yoffset=13 xadvance=42 page=0 chnl=0 <char id="47" x="374" y="0" width="29" height="58" xoffset="-3" yoffset="14" xadvance="25" page="0" chnl="0" />
char id=51 x=434 y=99 width=39 height=56 xoffset=1 yoffset=13 xadvance=42 page=0 chnl=0 <char id="48" x="77" y="156" width="38" height="56" xoffset="2" yoffset="13" xadvance="42" page="0" chnl="0" />
char id=52 x=325 y=266 width=40 height=54 xoffset=1 yoffset=14 xadvance=42 page=0 chnl=0 <char id="49" x="299" y="266" width="26" height="54" xoffset="4" yoffset="14" xadvance="41" page="0" chnl="0" />
char id=53 x=422 y=156 width=38 height=55 xoffset=2 yoffset=14 xadvance=42 page=0 chnl=0 <char id="50" x="383" y="156" width="39" height="55" xoffset="1" yoffset="13" xadvance="42" page="0" chnl="0" />
char id=54 x=0 y=156 width=39 height=56 xoffset=2 yoffset=13 xadvance=42 page=0 chnl=0 <char id="51" x="434" y="99" width="39" height="56" xoffset="1" yoffset="13" xadvance="42" page="0" chnl="0" />
char id=55 x=365 y=266 width=39 height=54 xoffset=1 yoffset=14 xadvance=42 page=0 chnl=0 <char id="52" x="325" y="266" width="40" height="54" xoffset="1" yoffset="14" xadvance="42" page="0" chnl="0" />
char id=56 x=473 y=99 width=38 height=56 xoffset=2 yoffset=13 xadvance=42 page=0 chnl=0 <char id="53" x="422" y="156" width="38" height="55" xoffset="2" yoffset="14" xadvance="42" page="0" chnl="0" />
char id=57 x=39 y=156 width=38 height=56 xoffset=2 yoffset=13 xadvance=42 page=0 chnl=0 <char id="54" x="0" y="156" width="39" height="56" xoffset="2" yoffset="13" xadvance="42" page="0" chnl="0" />
char id=58 x=471 y=266 width=15 height=43 xoffset=3 yoffset=26 xadvance=21 page=0 chnl=0 <char id="55" x="365" y="266" width="39" height="54" xoffset="1" yoffset="14" xadvance="42" page="0" chnl="0" />
char id=59 x=150 y=156 width=17 height=56 xoffset=1 yoffset=26 xadvance=21 page=0 chnl=0 <char id="56" x="473" y="99" width="38" height="56" xoffset="2" yoffset="13" xadvance="42" page="0" chnl="0" />
char id=60 x=37 y=362 width=33 height=38 xoffset=1 yoffset=26 xadvance=37 page=0 chnl=0 <char id="57" x="39" y="156" width="38" height="56" xoffset="2" yoffset="13" xadvance="42" page="0" chnl="0" />
char id=61 x=172 y=362 width=35 height=27 xoffset=3 yoffset=31 xadvance=42 page=0 chnl=0 <char id="58" x="471" y="266" width="15" height="43" xoffset="3" yoffset="26" xadvance="21" page="0" chnl="0" />
char id=62 x=70 y=362 width=33 height=38 xoffset=3 yoffset=26 xadvance=37 page=0 chnl=0 <char id="59" x="150" y="156" width="17" height="56" xoffset="1" yoffset="26" xadvance="21" page="0" chnl="0" />
char id=63 x=115 y=156 width=35 height=56 xoffset=0 yoffset=13 xadvance=36 page=0 chnl=0 <char id="60" x="37" y="362" width="33" height="38" xoffset="1" yoffset="26" xadvance="37" page="0" chnl="0" />
char id=64 x=258 y=0 width=61 height=68 xoffset=1 yoffset=16 xadvance=64 page=0 chnl=0 <char id="61" x="172" y="362" width="35" height="27" xoffset="3" yoffset="31" xadvance="42" page="0" chnl="0" />
char id=65 x=0 y=212 width=53 height=54 xoffset=-2 yoffset=14 xadvance=49 page=0 chnl=0 <char id="62" x="70" y="362" width="33" height="38" xoffset="3" yoffset="26" xadvance="37" page="0" chnl="0" />
char id=66 x=53 y=212 width=42 height=54 xoffset=3 yoffset=14 xadvance=47 page=0 chnl=0 <char id="63" x="115" y="156" width="35" height="56" xoffset="0" yoffset="13" xadvance="36" page="0" chnl="0" />
char id=67 x=37 y=99 width=46 height=56 xoffset=1 yoffset=13 xadvance=47 page=0 chnl=0 <char id="64" x="258" y="0" width="61" height="68" xoffset="1" yoffset="16" xadvance="64" page="0" chnl="0" />
char id=68 x=95 y=212 width=42 height=54 xoffset=3 yoffset=14 xadvance=47 page=0 chnl=0 <char id="65" x="0" y="212" width="53" height="54" xoffset="-2" yoffset="14" xadvance="49" page="0" chnl="0" />
char id=69 x=137 y=212 width=38 height=54 xoffset=3 yoffset=14 xadvance=41 page=0 chnl=0 <char id="66" x="53" y="212" width="42" height="54" xoffset="3" yoffset="14" xadvance="47" page="0" chnl="0" />
char id=70 x=475 y=156 width=36 height=54 xoffset=3 yoffset=14 xadvance=39 page=0 chnl=0 <char id="67" x="37" y="99" width="46" height="56" xoffset="1" yoffset="13" xadvance="47" page="0" chnl="0" />
char id=71 x=83 y=99 width=45 height=56 xoffset=2 yoffset=13 xadvance=49 page=0 chnl=0 <char id="68" x="95" y="212" width="42" height="54" xoffset="3" yoffset="14" xadvance="47" page="0" chnl="0" />
char id=72 x=175 y=212 width=45 height=54 xoffset=3 yoffset=14 xadvance=51 page=0 chnl=0 <char id="69" x="137" y="212" width="38" height="54" xoffset="3" yoffset="14" xadvance="41" page="0" chnl="0" />
char id=73 x=220 y=212 width=14 height=54 xoffset=4 yoffset=14 xadvance=22 page=0 chnl=0 <char id="70" x="475" y="156" width="36" height="54" xoffset="3" yoffset="14" xadvance="39" page="0" chnl="0" />
char id=74 x=264 y=156 width=37 height=55 xoffset=0 yoffset=14 xadvance=40 page=0 chnl=0 <char id="71" x="83" y="99" width="45" height="56" xoffset="2" yoffset="13" xadvance="49" page="0" chnl="0" />
char id=75 x=234 y=212 width=45 height=54 xoffset=3 yoffset=14 xadvance=46 page=0 chnl=0 <char id="72" x="175" y="212" width="45" height="54" xoffset="3" yoffset="14" xadvance="51" page="0" chnl="0" />
char id=76 x=279 y=212 width=36 height=54 xoffset=3 yoffset=14 xadvance=39 page=0 chnl=0 <char id="73" x="220" y="212" width="14" height="54" xoffset="4" yoffset="14" xadvance="22" page="0" chnl="0" />
char id=77 x=315 y=212 width=58 height=54 xoffset=3 yoffset=14 xadvance=63 page=0 chnl=0 <char id="74" x="264" y="156" width="37" height="55" xoffset="0" yoffset="14" xadvance="40" page="0" chnl="0" />
char id=78 x=373 y=212 width=45 height=54 xoffset=3 yoffset=14 xadvance=51 page=0 chnl=0 <char id="75" x="234" y="212" width="45" height="54" xoffset="3" yoffset="14" xadvance="46" page="0" chnl="0" />
char id=79 x=128 y=99 width=47 height=56 xoffset=1 yoffset=13 xadvance=50 page=0 chnl=0 <char id="76" x="279" y="212" width="36" height="54" xoffset="3" yoffset="14" xadvance="39" page="0" chnl="0" />
char id=80 x=418 y=212 width=43 height=54 xoffset=3 yoffset=14 xadvance=48 page=0 chnl=0 <char id="77" x="315" y="212" width="58" height="54" xoffset="3" yoffset="14" xadvance="63" page="0" chnl="0" />
char id=81 x=319 y=0 width=47 height=65 xoffset=2 yoffset=13 xadvance=50 page=0 chnl=0 <char id="78" x="373" y="212" width="45" height="54" xoffset="3" yoffset="14" xadvance="51" page="0" chnl="0" />
char id=82 x=461 y=212 width=43 height=54 xoffset=3 yoffset=14 xadvance=46 page=0 chnl=0 <char id="79" x="128" y="99" width="47" height="56" xoffset="1" yoffset="13" xadvance="50" page="0" chnl="0" />
char id=83 x=175 y=99 width=42 height=56 xoffset=1 yoffset=13 xadvance=44 page=0 chnl=0 <char id="80" x="418" y="212" width="43" height="54" xoffset="3" yoffset="14" xadvance="48" page="0" chnl="0" />
char id=84 x=0 y=266 width=45 height=54 xoffset=0 yoffset=14 xadvance=45 page=0 chnl=0 <char id="81" x="319" y="0" width="47" height="65" xoffset="2" yoffset="13" xadvance="50" page="0" chnl="0" />
char id=85 x=301 y=156 width=42 height=55 xoffset=3 yoffset=14 xadvance=48 page=0 chnl=0 <char id="82" x="461" y="212" width="43" height="54" xoffset="3" yoffset="14" xadvance="46" page="0" chnl="0" />
char id=86 x=45 y=266 width=51 height=54 xoffset=-2 yoffset=14 xadvance=48 page=0 chnl=0 <char id="83" x="175" y="99" width="42" height="56" xoffset="1" yoffset="13" xadvance="44" page="0" chnl="0" />
char id=87 x=96 y=266 width=64 height=54 xoffset=-1 yoffset=14 xadvance=63 page=0 chnl=0 <char id="84" x="0" y="266" width="45" height="54" xoffset="0" yoffset="14" xadvance="45" page="0" chnl="0" />
char id=88 x=160 y=266 width=48 height=54 xoffset=-1 yoffset=14 xadvance=46 page=0 chnl=0 <char id="85" x="301" y="156" width="42" height="55" xoffset="3" yoffset="14" xadvance="48" page="0" chnl="0" />
char id=89 x=208 y=266 width=49 height=54 xoffset=-2 yoffset=14 xadvance=45 page=0 chnl=0 <char id="86" x="45" y="266" width="51" height="54" xoffset="-2" yoffset="14" xadvance="48" page="0" chnl="0" />
char id=90 x=257 y=266 width=42 height=54 xoffset=1 yoffset=14 xadvance=44 page=0 chnl=0 <char id="87" x="96" y="266" width="64" height="54" xoffset="-1" yoffset="14" xadvance="63" page="0" chnl="0" />
char id=91 x=115 y=0 width=18 height=75 xoffset=3 yoffset=5 xadvance=21 page=0 chnl=0 <char id="88" x="160" y="266" width="48" height="54" xoffset="-1" yoffset="14" xadvance="46" page="0" chnl="0" />
char id=92 x=403 y=0 width=37 height=58 xoffset=-2 yoffset=14 xadvance=31 page=0 chnl=0 <char id="89" x="208" y="266" width="49" height="54" xoffset="-2" yoffset="14" xadvance="45" page="0" chnl="0" />
char id=93 x=133 y=0 width=18 height=75 xoffset=0 yoffset=5 xadvance=21 page=0 chnl=0 <char id="90" x="257" y="266" width="42" height="54" xoffset="1" yoffset="14" xadvance="44" page="0" chnl="0" />
char id=94 x=139 y=362 width=33 height=28 xoffset=0 yoffset=14 xadvance=32 page=0 chnl=0 <char id="91" x="115" y="0" width="18" height="75" xoffset="3" yoffset="5" xadvance="21" page="0" chnl="0" />
char id=95 x=331 y=362 width=34 height=12 xoffset=-1 yoffset=65 xadvance=33 page=0 chnl=0 <char id="92" x="403" y="0" width="37" height="58" xoffset="-2" yoffset="14" xadvance="31" page="0" chnl="0" />
char id=96 x=285 y=362 width=23 height=13 xoffset=0 yoffset=12 xadvance=24 page=0 chnl=0 <char id="93" x="133" y="0" width="18" height="75" xoffset="0" yoffset="5" xadvance="21" page="0" chnl="0" />
char id=97 x=0 y=320 width=37 height=42 xoffset=1 yoffset=27 xadvance=38 page=0 chnl=0 <char id="94" x="139" y="362" width="33" height="28" xoffset="0" yoffset="14" xadvance="32" page="0" chnl="0" />
char id=98 x=440 y=0 width=37 height=57 xoffset=2 yoffset=12 xadvance=40 page=0 chnl=0 <char id="95" x="331" y="362" width="34" height="12" xoffset="-1" yoffset="65" xadvance="33" page="0" chnl="0" />
char id=99 x=37 y=320 width=36 height=42 xoffset=1 yoffset=27 xadvance=38 page=0 chnl=0 <char id="96" x="285" y="362" width="23" height="13" xoffset="0" yoffset="12" xadvance="24" page="0" chnl="0" />
char id=100 x=0 y=99 width=37 height=57 xoffset=1 yoffset=12 xadvance=40 page=0 chnl=0 <char id="97" x="0" y="320" width="37" height="42" xoffset="1" yoffset="27" xadvance="38" page="0" chnl="0" />
char id=101 x=73 y=320 width=38 height=42 xoffset=1 yoffset=27 xadvance=39 page=0 chnl=0 <char id="98" x="440" y="0" width="37" height="57" xoffset="2" yoffset="12" xadvance="40" page="0" chnl="0" />
char id=102 x=477 y=0 width=28 height=57 xoffset=0 yoffset=11 xadvance=27 page=0 chnl=0 <char id="99" x="37" y="320" width="36" height="42" xoffset="1" yoffset="27" xadvance="38" page="0" chnl="0" />
char id=103 x=217 y=99 width=38 height=56 xoffset=1 yoffset=27 xadvance=41 page=0 chnl=0 <char id="100" x="0" y="99" width="37" height="57" xoffset="1" yoffset="12" xadvance="40" page="0" chnl="0" />
char id=104 x=255 y=99 width=36 height=56 xoffset=2 yoffset=12 xadvance=40 page=0 chnl=0 <char id="101" x="73" y="320" width="38" height="42" xoffset="1" yoffset="27" xadvance="39" page="0" chnl="0" />
char id=105 x=291 y=99 width=15 height=56 xoffset=2 yoffset=12 xadvance=19 page=0 chnl=0 <char id="102" x="477" y="0" width="28" height="57" xoffset="0" yoffset="11" xadvance="27" page="0" chnl="0" />
char id=106 x=197 y=0 width=23 height=71 xoffset=-5 yoffset=12 xadvance=20 page=0 chnl=0 <char id="103" x="217" y="99" width="38" height="56" xoffset="1" yoffset="27" xadvance="41" page="0" chnl="0" />
char id=107 x=306 y=99 width=40 height=56 xoffset=2 yoffset=12 xadvance=39 page=0 chnl=0 <char id="104" x="255" y="99" width="36" height="56" xoffset="2" yoffset="12" xadvance="40" page="0" chnl="0" />
char id=108 x=346 y=99 width=14 height=56 xoffset=3 yoffset=12 xadvance=20 page=0 chnl=0 <char id="105" x="291" y="99" width="15" height="56" xoffset="2" yoffset="12" xadvance="19" page="0" chnl="0" />
char id=109 x=186 y=320 width=58 height=41 xoffset=2 yoffset=27 xadvance=63 page=0 chnl=0 <char id="106" x="197" y="0" width="23" height="71" xoffset="-5" yoffset="12" xadvance="20" page="0" chnl="0" />
char id=110 x=244 y=320 width=36 height=41 xoffset=2 yoffset=27 xadvance=40 page=0 chnl=0 <char id="107" x="306" y="99" width="40" height="56" xoffset="2" yoffset="12" xadvance="39" page="0" chnl="0" />
char id=111 x=111 y=320 width=39 height=42 xoffset=1 yoffset=27 xadvance=41 page=0 chnl=0 <char id="108" x="346" y="99" width="14" height="56" xoffset="3" yoffset="12" xadvance="20" page="0" chnl="0" />
char id=112 x=360 y=99 width=37 height=56 xoffset=2 yoffset=27 xadvance=40 page=0 chnl=0 <char id="109" x="186" y="320" width="58" height="41" xoffset="2" yoffset="27" xadvance="63" page="0" chnl="0" />
char id=113 x=397 y=99 width=37 height=56 xoffset=1 yoffset=27 xadvance=40 page=0 chnl=0 <char id="110" x="244" y="320" width="36" height="41" xoffset="2" yoffset="27" xadvance="40" page="0" chnl="0" />
char id=114 x=486 y=266 width=25 height=41 xoffset=2 yoffset=27 xadvance=27 page=0 chnl=0 <char id="111" x="111" y="320" width="39" height="42" xoffset="1" yoffset="27" xadvance="41" page="0" chnl="0" />
char id=115 x=150 y=320 width=36 height=42 xoffset=0 yoffset=27 xadvance=37 page=0 chnl=0 <char id="112" x="360" y="99" width="37" height="56" xoffset="2" yoffset="27" xadvance="40" page="0" chnl="0" />
char id=116 x=445 y=266 width=26 height=51 xoffset=0 yoffset=18 xadvance=25 page=0 chnl=0 <char id="113" x="397" y="99" width="37" height="56" xoffset="1" yoffset="27" xadvance="40" page="0" chnl="0" />
char id=117 x=280 y=320 width=36 height=41 xoffset=2 yoffset=28 xadvance=40 page=0 chnl=0 <char id="114" x="486" y="266" width="25" height="41" xoffset="2" yoffset="27" xadvance="27" page="0" chnl="0" />
char id=118 x=316 y=320 width=39 height=40 xoffset=-1 yoffset=28 xadvance=37 page=0 chnl=0 <char id="115" x="150" y="320" width="36" height="42" xoffset="0" yoffset="27" xadvance="37" page="0" chnl="0" />
char id=119 x=355 y=320 width=54 height=40 xoffset=-1 yoffset=28 xadvance=52 page=0 chnl=0 <char id="116" x="445" y="266" width="26" height="51" xoffset="0" yoffset="18" xadvance="25" page="0" chnl="0" />
char id=120 x=409 y=320 width=40 height=40 xoffset=-1 yoffset=28 xadvance=37 page=0 chnl=0 <char id="117" x="280" y="320" width="36" height="41" xoffset="2" yoffset="28" xadvance="40" page="0" chnl="0" />
char id=121 x=343 y=156 width=40 height=55 xoffset=-1 yoffset=28 xadvance=37 page=0 chnl=0 <char id="118" x="316" y="320" width="39" height="40" xoffset="-1" yoffset="28" xadvance="37" page="0" chnl="0" />
char id=122 x=449 y=320 width=34 height=40 xoffset=1 yoffset=28 xadvance=36 page=0 chnl=0 <char id="119" x="355" y="320" width="54" height="40" xoffset="-1" yoffset="28" xadvance="52" page="0" chnl="0" />
char id=123 x=151 y=0 width=23 height=72 xoffset=0 yoffset=9 xadvance=23 page=0 chnl=0 <char id="120" x="409" y="320" width="40" height="40" xoffset="-1" yoffset="28" xadvance="37" page="0" chnl="0" />
char id=124 x=366 y=0 width=8 height=63 xoffset=5 yoffset=14 xadvance=18 page=0 chnl=0 <char id="121" x="343" y="156" width="40" height="55" xoffset="-1" yoffset="28" xadvance="37" page="0" chnl="0" />
char id=125 x=174 y=0 width=23 height=72 xoffset=0 yoffset=9 xadvance=23 page=0 chnl=0 <char id="122" x="449" y="320" width="34" height="40" xoffset="1" yoffset="28" xadvance="36" page="0" chnl="0" />
char id=126 x=229 y=362 width=41 height=19 xoffset=2 yoffset=36 xadvance=45 page=0 chnl=0 <char id="123" x="151" y="0" width="23" height="72" xoffset="0" yoffset="9" xadvance="23" page="0" chnl="0" />
char id=127 x=0 y=0 width=70 height=99 xoffset=2 yoffset=-11 xadvance=74 page=0 chnl=0 <char id="124" x="366" y="0" width="8" height="63" xoffset="5" yoffset="14" xadvance="18" page="0" chnl="0" />
kernings count=385 <char id="125" x="174" y="0" width="23" height="72" xoffset="0" yoffset="9" xadvance="23" page="0" chnl="0" />
kerning first=84 second=74 amount=-8 <char id="126" x="229" y="362" width="41" height="19" xoffset="2" yoffset="36" xadvance="45" page="0" chnl="0" />
kerning first=86 second=100 amount=-2 <char id="127" x="0" y="0" width="70" height="99" xoffset="2" yoffset="-11" xadvance="74" page="0" chnl="0" />
kerning first=114 second=113 amount=-1 </chars>
kerning first=70 second=121 amount=-1 <kernings count="385">
kerning first=34 second=99 amount=-2 <kerning first="84" second="74" amount="-8" />
kerning first=70 second=99 amount=-1 <kerning first="86" second="100" amount="-2" />
kerning first=69 second=99 amount=-1 <kerning first="114" second="113" amount="-1" />
kerning first=88 second=113 amount=-1 <kerning first="70" second="121" amount="-1" />
kerning first=84 second=46 amount=-9 <kerning first="34" second="99" amount="-2" />
kerning first=87 second=97 amount=-1 <kerning first="70" second="99" amount="-1" />
kerning first=90 second=117 amount=-1 <kerning first="69" second="99" amount="-1" />
kerning first=39 second=97 amount=-2 <kerning first="88" second="113" amount="-1" />
kerning first=69 second=111 amount=-1 <kerning first="84" second="46" amount="-9" />
kerning first=87 second=41 amount=1 <kerning first="87" second="97" amount="-1" />
kerning first=121 second=34 amount=1 <kerning first="90" second="117" amount="-1" />
kerning first=40 second=86 amount=1 <kerning first="39" second="97" amount="-2" />
kerning first=85 second=65 amount=-1 <kerning first="69" second="111" amount="-1" />
kerning first=72 second=65 amount=1 <kerning first="87" second="41" amount="1" />
kerning first=114 second=102 amount=1 <kerning first="121" second="34" amount="1" />
kerning first=89 second=42 amount=-2 <kerning first="40" second="86" amount="1" />
kerning first=114 second=34 amount=1 <kerning first="85" second="65" amount="-1" />
kerning first=75 second=67 amount=-1 <kerning first="72" second="65" amount="1" />
kerning first=89 second=85 amount=-3 <kerning first="114" second="102" amount="1" />
kerning first=77 second=88 amount=1 <kerning first="89" second="42" amount="-2" />
kerning first=84 second=115 amount=-3 <kerning first="114" second="34" amount="1" />
kerning first=84 second=71 amount=-1 <kerning first="75" second="67" amount="-1" />
kerning first=89 second=101 amount=-2 <kerning first="89" second="85" amount="-3" />
kerning first=89 second=45 amount=-5 <kerning first="77" second="88" amount="1" />
kerning first=78 second=88 amount=1 <kerning first="84" second="115" amount="-3" />
kerning first=68 second=89 amount=-2 <kerning first="84" second="71" amount="-1" />
kerning first=122 second=103 amount=-1 <kerning first="89" second="101" amount="-2" />
kerning first=78 second=84 amount=-1 <kerning first="89" second="45" amount="-5" />
kerning first=86 second=103 amount=-2 <kerning first="78" second="88" amount="1" />
kerning first=89 second=79 amount=-1 <kerning first="68" second="89" amount="-2" />
kerning first=75 second=111 amount=-1 <kerning first="122" second="103" amount="-1" />
kerning first=111 second=120 amount=-1 <kerning first="78" second="84" amount="-1" />
kerning first=87 second=44 amount=-5 <kerning first="86" second="103" amount="-2" />
kerning first=67 second=84 amount=-1 <kerning first="89" second="79" amount="-1" />
kerning first=84 second=111 amount=-7 <kerning first="75" second="111" amount="-1" />
kerning first=84 second=83 amount=-1 <kerning first="111" second="120" amount="-1" />
kerning first=102 second=113 amount=-1 <kerning first="87" second="44" amount="-5" />
kerning first=39 second=101 amount=-2 <kerning first="67" second="84" amount="-1" />
kerning first=80 second=88 amount=-2 <kerning first="84" second="111" amount="-7" />
kerning first=66 second=84 amount=-1 <kerning first="84" second="83" amount="-1" />
kerning first=65 second=87 amount=-1 <kerning first="102" second="113" amount="-1" />
kerning first=122 second=100 amount=-1 <kerning first="39" second="101" amount="-2" />
kerning first=75 second=118 amount=-1 <kerning first="80" second="88" amount="-2" />
kerning first=73 second=65 amount=1 <kerning first="66" second="84" amount="-1" />
kerning first=70 second=118 amount=-1 <kerning first="65" second="87" amount="-1" />
kerning first=73 second=88 amount=1 <kerning first="122" second="100" amount="-1" />
kerning first=82 second=89 amount=-2 <kerning first="75" second="118" amount="-1" />
kerning first=65 second=34 amount=-4 <kerning first="73" second="65" amount="1" />
kerning first=120 second=99 amount=-1 <kerning first="70" second="118" amount="-1" />
kerning first=84 second=99 amount=-3 <kerning first="73" second="88" amount="1" />
kerning first=84 second=65 amount=-4 <kerning first="82" second="89" amount="-2" />
kerning first=112 second=39 amount=-1 <kerning first="65" second="34" amount="-4" />
kerning first=76 second=39 amount=-10 <kerning first="120" second="99" amount="-1" />
kerning first=78 second=65 amount=1 <kerning first="84" second="99" amount="-3" />
kerning first=88 second=45 amount=-5 <kerning first="84" second="65" amount="-4" />
kerning first=34 second=111 amount=-3 <kerning first="112" second="39" amount="-1" />
kerning first=114 second=99 amount=-1 <kerning first="76" second="39" amount="-10" />
kerning first=86 second=125 amount=1 <kerning first="78" second="65" amount="1" />
kerning first=70 second=111 amount=-1 <kerning first="88" second="45" amount="-5" />
kerning first=89 second=120 amount=-1 <kerning first="34" second="111" amount="-3" />
kerning first=90 second=119 amount=-1 <kerning first="114" second="99" amount="-1" />
kerning first=89 second=89 amount=1 <kerning first="86" second="125" amount="1" />
kerning first=89 second=117 amount=-1 <kerning first="70" second="111" amount="-1" />
kerning first=75 second=117 amount=-1 <kerning first="89" second="120" amount="-1" />
kerning first=76 second=65 amount=1 <kerning first="90" second="119" amount="-1" />
kerning first=34 second=34 amount=-1 <kerning first="89" second="89" amount="1" />
kerning first=89 second=110 amount=-1 <kerning first="89" second="117" amount="-1" />
kerning first=88 second=101 amount=-1 <kerning first="75" second="117" amount="-1" />
kerning first=107 second=103 amount=-1 <kerning first="76" second="65" amount="1" />
kerning first=34 second=115 amount=-3 <kerning first="34" second="34" amount="-1" />
kerning first=80 second=44 amount=-14 <kerning first="89" second="110" amount="-1" />
kerning first=98 second=39 amount=-1 <kerning first="88" second="101" amount="-1" />
kerning first=70 second=65 amount=-7 <kerning first="107" second="103" amount="-1" />
kerning first=89 second=116 amount=-1 <kerning first="34" second="115" amount="-3" />
kerning first=70 second=46 amount=-10 <kerning first="80" second="44" amount="-14" />
kerning first=98 second=34 amount=-1 <kerning first="98" second="39" amount="-1" />
kerning first=70 second=84 amount=1 <kerning first="70" second="65" amount="-7" />
kerning first=114 second=100 amount=-1 <kerning first="89" second="116" amount="-1" />
kerning first=88 second=79 amount=-1 <kerning first="70" second="46" amount="-10" />
kerning first=39 second=113 amount=-2 <kerning first="98" second="34" amount="-1" />
kerning first=65 second=118 amount=-2 <kerning first="70" second="84" amount="1" />
kerning first=114 second=103 amount=-1 <kerning first="114" second="100" amount="-1" />
kerning first=77 second=65 amount=1 <kerning first="88" second="79" amount="-1" />
kerning first=120 second=103 amount=-1 <kerning first="39" second="113" amount="-2" />
kerning first=65 second=110 amount=-2 <kerning first="65" second="118" amount="-2" />
kerning first=114 second=121 amount=1 <kerning first="114" second="103" amount="-1" />
kerning first=89 second=100 amount=-2 <kerning first="77" second="65" amount="1" />
kerning first=80 second=65 amount=-6 <kerning first="120" second="103" amount="-1" />
kerning first=121 second=111 amount=-1 <kerning first="65" second="110" amount="-2" />
kerning first=34 second=101 amount=-2 <kerning first="114" second="121" amount="1" />
kerning first=122 second=111 amount=-1 <kerning first="89" second="100" amount="-2" />
kerning first=114 second=118 amount=1 <kerning first="80" second="65" amount="-6" />
kerning first=102 second=41 amount=1 <kerning first="121" second="111" amount="-1" />
kerning first=122 second=113 amount=-1 <kerning first="34" second="101" amount="-2" />
kerning first=89 second=122 amount=-1 <kerning first="122" second="111" amount="-1" />
kerning first=68 second=88 amount=-1 <kerning first="114" second="118" amount="1" />
kerning first=81 second=89 amount=-1 <kerning first="102" second="41" amount="1" />
kerning first=114 second=111 amount=-1 <kerning first="122" second="113" amount="-1" />
kerning first=46 second=34 amount=-10 <kerning first="89" second="122" amount="-1" />
kerning first=84 second=112 amount=-3 <kerning first="68" second="88" amount="-1" />
kerning first=76 second=34 amount=-10 <kerning first="81" second="89" amount="-1" />
kerning first=39 second=115 amount=-3 <kerning first="114" second="111" amount="-1" />
kerning first=76 second=118 amount=-4 <kerning first="46" second="34" amount="-10" />
kerning first=86 second=99 amount=-2 <kerning first="84" second="112" amount="-3" />
kerning first=84 second=84 amount=1 <kerning first="76" second="34" amount="-10" />
kerning first=120 second=111 amount=-1 <kerning first="39" second="115" amount="-3" />
kerning first=65 second=79 amount=-1 <kerning first="76" second="118" amount="-4" />
kerning first=87 second=101 amount=-1 <kerning first="86" second="99" amount="-2" />
kerning first=67 second=125 amount=-1 <kerning first="84" second="84" amount="1" />
kerning first=120 second=113 amount=-1 <kerning first="120" second="111" amount="-1" />
kerning first=118 second=46 amount=-6 <kerning first="65" second="79" amount="-1" />
kerning first=88 second=103 amount=-1 <kerning first="87" second="101" amount="-1" />
kerning first=111 second=122 amount=-1 <kerning first="67" second="125" amount="-1" />
kerning first=77 second=84 amount=-1 <kerning first="120" second="113" amount="-1" />
kerning first=114 second=46 amount=-6 <kerning first="118" second="46" amount="-6" />
kerning first=34 second=39 amount=-1 <kerning first="88" second="103" amount="-1" />
kerning first=65 second=121 amount=-2 <kerning first="111" second="122" amount="-1" />
kerning first=114 second=44 amount=-6 <kerning first="77" second="84" amount="-1" />
kerning first=69 second=84 amount=1 <kerning first="114" second="46" amount="-6" />
kerning first=89 second=46 amount=-8 <kerning first="34" second="39" amount="-1" />
kerning first=97 second=39 amount=-1 <kerning first="65" second="121" amount="-2" />
kerning first=34 second=100 amount=-2 <kerning first="114" second="44" amount="-6" />
kerning first=70 second=100 amount=-1 <kerning first="69" second="84" amount="1" />
kerning first=84 second=120 amount=-3 <kerning first="89" second="46" amount="-8" />
kerning first=90 second=118 amount=-1 <kerning first="97" second="39" amount="-1" />
kerning first=70 second=114 amount=-1 <kerning first="34" second="100" amount="-2" />
kerning first=34 second=112 amount=-1 <kerning first="70" second="100" amount="-1" />
kerning first=89 second=86 amount=1 <kerning first="84" second="120" amount="-3" />
kerning first=86 second=113 amount=-2 <kerning first="90" second="118" amount="-1" />
kerning first=88 second=71 amount=-1 <kerning first="70" second="114" amount="-1" />
kerning first=122 second=99 amount=-1 <kerning first="34" second="112" amount="-1" />
kerning first=66 second=89 amount=-2 <kerning first="89" second="86" amount="1" />
kerning first=102 second=103 amount=-1 <kerning first="86" second="113" amount="-2" />
kerning first=88 second=67 amount=-1 <kerning first="88" second="71" amount="-1" />
kerning first=39 second=110 amount=-1 <kerning first="122" second="99" amount="-1" />
kerning first=88 second=117 amount=-1 <kerning first="66" second="89" amount="-2" />
kerning first=89 second=118 amount=-1 <kerning first="102" second="103" amount="-1" />
kerning first=97 second=118 amount=-1 <kerning first="88" second="67" amount="-1" />
kerning first=87 second=65 amount=-2 <kerning first="39" second="110" amount="-1" />
kerning first=89 second=67 amount=-1 <kerning first="88" second="117" amount="-1" />
kerning first=89 second=74 amount=-3 <kerning first="89" second="118" amount="-1" />
kerning first=102 second=101 amount=-1 <kerning first="97" second="118" amount="-1" />
kerning first=86 second=111 amount=-2 <kerning first="87" second="65" amount="-2" />
kerning first=65 second=119 amount=-1 <kerning first="89" second="67" amount="-1" />
kerning first=84 second=100 amount=-3 <kerning first="89" second="74" amount="-3" />
kerning first=120 second=100 amount=-1 <kerning first="102" second="101" amount="-1" />
kerning first=104 second=34 amount=-3 <kerning first="86" second="111" amount="-2" />
kerning first=86 second=41 amount=1 <kerning first="65" second="119" amount="-1" />
kerning first=111 second=34 amount=-3 <kerning first="84" second="100" amount="-3" />
kerning first=40 second=89 amount=1 <kerning first="120" second="100" amount="-1" />
kerning first=121 second=39 amount=1 <kerning first="104" second="34" amount="-3" />
kerning first=70 second=74 amount=-7 <kerning first="86" second="41" amount="1" />
kerning first=68 second=90 amount=-1 <kerning first="111" second="34" amount="-3" />
kerning first=98 second=120 amount=-1 <kerning first="40" second="89" amount="1" />
kerning first=110 second=34 amount=-3 <kerning first="121" second="39" amount="1" />
kerning first=119 second=46 amount=-4 <kerning first="70" second="74" amount="-7" />
kerning first=69 second=102 amount=-1 <kerning first="68" second="90" amount="-1" />
kerning first=118 second=44 amount=-6 <kerning first="98" second="120" amount="-1" />
kerning first=84 second=114 amount=-2 <kerning first="110" second="34" amount="-3" />
kerning first=86 second=97 amount=-2 <kerning first="119" second="46" amount="-4" />
kerning first=40 second=87 amount=1 <kerning first="69" second="102" amount="-1" />
kerning first=65 second=109 amount=-2 <kerning first="118" second="44" amount="-6" />
kerning first=68 second=86 amount=-1 <kerning first="84" second="114" amount="-2" />
kerning first=86 second=93 amount=1 <kerning first="86" second="97" amount="-2" />
kerning first=65 second=67 amount=-1 <kerning first="40" second="87" amount="1" />
kerning first=97 second=34 amount=-1 <kerning first="65" second="109" amount="-2" />
kerning first=34 second=65 amount=-4 <kerning first="68" second="86" amount="-1" />
kerning first=84 second=118 amount=-3 <kerning first="86" second="93" amount="1" />
kerning first=112 second=34 amount=-1 <kerning first="65" second="67" amount="-1" />
kerning first=76 second=84 amount=-7 <kerning first="97" second="34" amount="-1" />
kerning first=107 second=99 amount=-1 <kerning first="34" second="65" amount="-4" />
kerning first=123 second=85 amount=-1 <kerning first="84" second="118" amount="-3" />
kerning first=102 second=125 amount=1 <kerning first="112" second="34" amount="-1" />
kerning first=65 second=63 amount=-3 <kerning first="76" second="84" amount="-7" />
kerning first=89 second=44 amount=-8 <kerning first="107" second="99" amount="-1" />
kerning first=80 second=118 amount=1 <kerning first="123" second="85" amount="-1" />
kerning first=112 second=122 amount=-1 <kerning first="102" second="125" amount="1" />
kerning first=79 second=65 amount=-1 <kerning first="65" second="63" amount="-3" />
kerning first=80 second=121 amount=1 <kerning first="89" second="44" amount="-8" />
kerning first=118 second=34 amount=1 <kerning first="80" second="118" amount="1" />
kerning first=87 second=45 amount=-2 <kerning first="112" second="122" amount="-1" />
kerning first=69 second=100 amount=-1 <kerning first="79" second="65" amount="-1" />
kerning first=87 second=103 amount=-1 <kerning first="80" second="121" amount="1" />
kerning first=112 second=120 amount=-1 <kerning first="118" second="34" amount="1" />
kerning first=86 second=65 amount=-3 <kerning first="87" second="45" amount="-2" />
kerning first=65 second=81 amount=-1 <kerning first="69" second="100" amount="-1" />
kerning first=68 second=44 amount=-4 <kerning first="87" second="103" amount="-1" />
kerning first=86 second=45 amount=-6 <kerning first="112" second="120" amount="-1" />
kerning first=39 second=34 amount=-1 <kerning first="86" second="65" amount="-3" />
kerning first=72 second=88 amount=1 <kerning first="65" second="81" amount="-1" />
kerning first=68 second=46 amount=-4 <kerning first="68" second="44" amount="-4" />
kerning first=65 second=89 amount=-5 <kerning first="86" second="45" amount="-6" />
kerning first=69 second=118 amount=-1 <kerning first="39" second="34" amount="-1" />
kerning first=89 second=38 amount=-1 <kerning first="72" second="88" amount="1" />
kerning first=88 second=99 amount=-1 <kerning first="68" second="46" amount="-4" />
kerning first=65 second=71 amount=-1 <kerning first="65" second="89" amount="-5" />
kerning first=91 second=74 amount=-1 <kerning first="69" second="118" amount="-1" />
kerning first=75 second=101 amount=-1 <kerning first="89" second="38" amount="-1" />
kerning first=39 second=112 amount=-1 <kerning first="88" second="99" amount="-1" />
kerning first=70 second=113 amount=-1 <kerning first="65" second="71" amount="-1" />
kerning first=119 second=44 amount=-4 <kerning first="91" second="74" amount="-1" />
kerning first=72 second=89 amount=-1 <kerning first="75" second="101" amount="-1" />
kerning first=90 second=103 amount=-1 <kerning first="39" second="112" amount="-1" />
kerning first=65 second=86 amount=-3 <kerning first="70" second="113" amount="-1" />
kerning first=84 second=119 amount=-2 <kerning first="119" second="44" amount="-4" />
kerning first=34 second=110 amount=-1 <kerning first="72" second="89" amount="-1" />
kerning first=39 second=109 amount=-1 <kerning first="90" second="103" amount="-1" />
kerning first=75 second=81 amount=-1 <kerning first="65" second="86" amount="-3" />
kerning first=89 second=115 amount=-2 <kerning first="84" second="119" amount="-2" />
kerning first=89 second=87 amount=1 <kerning first="34" second="110" amount="-1" />
kerning first=114 second=101 amount=-1 <kerning first="39" second="109" amount="-1" />
kerning first=116 second=111 amount=-1 <kerning first="75" second="81" amount="-1" />
kerning first=90 second=100 amount=-1 <kerning first="89" second="115" amount="-2" />
kerning first=79 second=89 amount=-2 <kerning first="89" second="87" amount="1" />
kerning first=84 second=122 amount=-2 <kerning first="114" second="101" amount="-1" />
kerning first=68 second=84 amount=-3 <kerning first="116" second="111" amount="-1" />
kerning first=76 second=86 amount=-7 <kerning first="90" second="100" amount="-1" />
kerning first=74 second=65 amount=-1 <kerning first="79" second="89" amount="-2" />
kerning first=107 second=101 amount=-1 <kerning first="84" second="122" amount="-2" />
kerning first=80 second=46 amount=-14 <kerning first="68" second="84" amount="-3" />
kerning first=89 second=93 amount=1 <kerning first="76" second="86" amount="-7" />
kerning first=89 second=65 amount=-5 <kerning first="74" second="65" amount="-1" />
kerning first=87 second=117 amount=-1 <kerning first="107" second="101" amount="-1" />
kerning first=89 second=81 amount=-1 <kerning first="80" second="46" amount="-14" />
kerning first=39 second=103 amount=-2 <kerning first="89" second="93" amount="1" />
kerning first=86 second=101 amount=-2 <kerning first="89" second="65" amount="-5" />
kerning first=86 second=117 amount=-1 <kerning first="87" second="117" amount="-1" />
kerning first=84 second=113 amount=-3 <kerning first="89" second="81" amount="-1" />
kerning first=87 second=46 amount=-5 <kerning first="39" second="103" amount="-2" />
kerning first=47 second=47 amount=-9 <kerning first="86" second="101" amount="-2" />
kerning first=75 second=103 amount=-1 <kerning first="86" second="117" amount="-1" />
kerning first=89 second=84 amount=1 <kerning first="84" second="113" amount="-3" />
kerning first=84 second=110 amount=-3 <kerning first="87" second="46" amount="-5" />
kerning first=39 second=99 amount=-2 <kerning first="47" second="47" amount="-9" />
kerning first=88 second=121 amount=-1 <kerning first="75" second="103" amount="-1" />
kerning first=65 second=39 amount=-4 <kerning first="89" second="84" amount="1" />
kerning first=110 second=39 amount=-3 <kerning first="84" second="110" amount="-3" />
kerning first=88 second=118 amount=-1 <kerning first="39" second="99" amount="-2" />
kerning first=86 second=114 amount=-1 <kerning first="88" second="121" amount="-1" />
kerning first=80 second=74 amount=-6 <kerning first="65" second="39" amount="-4" />
kerning first=84 second=97 amount=-6 <kerning first="110" second="39" amount="-3" />
kerning first=82 second=84 amount=-2 <kerning first="88" second="118" amount="-1" />
kerning first=91 second=85 amount=-1 <kerning first="86" second="114" amount="-1" />
kerning first=102 second=99 amount=-1 <kerning first="80" second="74" amount="-6" />
kerning first=66 second=86 amount=-1 <kerning first="84" second="97" amount="-6" />
kerning first=120 second=101 amount=-1 <kerning first="82" second="84" amount="-2" />
kerning first=102 second=93 amount=1 <kerning first="91" second="85" amount="-1" />
kerning first=75 second=100 amount=-1 <kerning first="102" second="99" amount="-1" />
kerning first=84 second=79 amount=-1 <kerning first="66" second="86" amount="-1" />
kerning first=44 second=39 amount=-10 <kerning first="120" second="101" amount="-1" />
kerning first=111 second=121 amount=-1 <kerning first="102" second="93" amount="1" />
kerning first=75 second=121 amount=-1 <kerning first="75" second="100" amount="-1" />
kerning first=81 second=87 amount=-1 <kerning first="84" second="79" amount="-1" />
kerning first=107 second=113 amount=-1 <kerning first="44" second="39" amount="-10" />
kerning first=90 second=79 amount=-1 <kerning first="111" second="121" amount="-1" />
kerning first=89 second=114 amount=-1 <kerning first="75" second="121" amount="-1" />
kerning first=122 second=101 amount=-1 <kerning first="81" second="87" amount="-1" />
kerning first=111 second=118 amount=-1 <kerning first="107" second="113" amount="-1" />
kerning first=82 second=86 amount=-1 <kerning first="90" second="79" amount="-1" />
kerning first=70 second=101 amount=-1 <kerning first="89" second="114" amount="-1" />
kerning first=114 second=97 amount=-1 <kerning first="122" second="101" amount="-1" />
kerning first=70 second=97 amount=-1 <kerning first="111" second="118" amount="-1" />
kerning first=34 second=97 amount=-2 <kerning first="82" second="86" amount="-1" />
kerning first=89 second=102 amount=-1 <kerning first="70" second="101" amount="-1" />
kerning first=78 second=89 amount=-1 <kerning first="114" second="97" amount="-1" />
kerning first=70 second=44 amount=-10 <kerning first="70" second="97" amount="-1" />
kerning first=104 second=39 amount=-3 <kerning first="34" second="97" amount="-2" />
kerning first=84 second=45 amount=-10 <kerning first="89" second="102" amount="-1" />
kerning first=89 second=121 amount=-1 <kerning first="78" second="89" amount="-1" />
kerning first=109 second=34 amount=-3 <kerning first="70" second="44" amount="-10" />
kerning first=84 second=86 amount=1 <kerning first="104" second="39" amount="-3" />
kerning first=87 second=99 amount=-1 <kerning first="84" second="45" amount="-10" />
kerning first=32 second=84 amount=-2 <kerning first="89" second="121" amount="-1" />
kerning first=98 second=122 amount=-1 <kerning first="109" second="34" amount="-3" />
kerning first=89 second=112 amount=-1 <kerning first="84" second="86" amount="1" />
kerning first=89 second=103 amount=-2 <kerning first="87" second="99" amount="-1" />
kerning first=65 second=116 amount=-1 <kerning first="32" second="84" amount="-2" />
kerning first=88 second=81 amount=-1 <kerning first="98" second="122" amount="-1" />
kerning first=102 second=34 amount=1 <kerning first="89" second="112" amount="-1" />
kerning first=109 second=39 amount=-3 <kerning first="89" second="103" amount="-2" />
kerning first=81 second=84 amount=-1 <kerning first="65" second="116" amount="-1" />
kerning first=121 second=97 amount=-1 <kerning first="88" second="81" amount="-1" />
kerning first=89 second=99 amount=-2 <kerning first="102" second="34" amount="1" />
kerning first=89 second=125 amount=1 <kerning first="109" second="39" amount="-3" />
kerning first=81 second=86 amount=-1 <kerning first="81" second="84" amount="-1" />
kerning first=114 second=116 amount=2 <kerning first="121" second="97" amount="-1" />
kerning first=114 second=119 amount=1 <kerning first="89" second="99" amount="-2" />
kerning first=84 second=44 amount=-9 <kerning first="89" second="125" amount="1" />
kerning first=102 second=39 amount=1 <kerning first="81" second="86" amount="-1" />
kerning first=44 second=34 amount=-10 <kerning first="114" second="116" amount="2" />
kerning first=34 second=109 amount=-1 <kerning first="114" second="119" amount="1" />
kerning first=84 second=101 amount=-3 <kerning first="84" second="44" amount="-9" />
kerning first=75 second=119 amount=-2 <kerning first="102" second="39" amount="1" />
kerning first=84 second=81 amount=-1 <kerning first="44" second="34" amount="-10" />
kerning first=76 second=121 amount=-4 <kerning first="34" second="109" amount="-1" />
kerning first=69 second=101 amount=-1 <kerning first="84" second="101" amount="-3" />
kerning first=80 second=90 amount=-1 <kerning first="75" second="119" amount="-2" />
kerning first=89 second=97 amount=-2 <kerning first="84" second="81" amount="-1" />
kerning first=89 second=109 amount=-1 <kerning first="76" second="121" amount="-4" />
kerning first=90 second=99 amount=-1 <kerning first="69" second="101" amount="-1" />
kerning first=79 second=88 amount=-1 <kerning first="80" second="90" amount="-1" />
kerning first=70 second=103 amount=-1 <kerning first="89" second="97" amount="-2" />
kerning first=34 second=103 amount=-2 <kerning first="89" second="109" amount="-1" />
kerning first=84 second=67 amount=-1 <kerning first="90" second="99" amount="-1" />
kerning first=76 second=79 amount=-2 <kerning first="79" second="88" amount="-1" />
kerning first=34 second=113 amount=-2 <kerning first="70" second="103" amount="-1" />
kerning first=89 second=41 amount=1 <kerning first="34" second="103" amount="-2" />
kerning first=75 second=71 amount=-1 <kerning first="84" second="67" amount="-1" />
kerning first=76 second=87 amount=-3 <kerning first="76" second="79" amount="-2" />
kerning first=77 second=89 amount=-1 <kerning first="34" second="113" amount="-2" />
kerning first=90 second=113 amount=-1 <kerning first="89" second="41" amount="1" />
kerning first=118 second=111 amount=-1 <kerning first="75" second="71" amount="-1" />
kerning first=118 second=97 amount=-1 <kerning first="76" second="87" amount="-3" />
kerning first=88 second=100 amount=-1 <kerning first="77" second="89" amount="-1" />
kerning first=89 second=111 amount=-2 <kerning first="90" second="113" amount="-1" />
kerning first=90 second=121 amount=-1 <kerning first="118" second="111" amount="-1" />
kerning first=89 second=113 amount=-2 <kerning first="118" second="97" amount="-1" />
kerning first=84 second=87 amount=1 <kerning first="88" second="100" amount="-1" />
kerning first=39 second=111 amount=-3 <kerning first="89" second="111" amount="-2" />
kerning first=39 second=100 amount=-2 <kerning first="90" second="121" amount="-1" />
kerning first=75 second=113 amount=-1 <kerning first="89" second="113" amount="-2" />
kerning first=88 second=111 amount=-1 <kerning first="84" second="87" amount="1" />
kerning first=87 second=111 amount=-1 <kerning first="39" second="111" amount="-3" />
kerning first=89 second=83 amount=-1 <kerning first="39" second="100" amount="-2" />
kerning first=84 second=89 amount=1 <kerning first="75" second="113" amount="-1" />
kerning first=84 second=103 amount=-3 <kerning first="88" second="111" amount="-1" />
kerning first=70 second=117 amount=-1 <kerning first="87" second="111" amount="-1" />
kerning first=67 second=41 amount=-1 <kerning first="89" second="83" amount="-1" />
kerning first=89 second=71 amount=-1 <kerning first="84" second="89" amount="1" />
kerning first=121 second=44 amount=-6 <kerning first="84" second="103" amount="-3" />
kerning first=97 second=121 amount=-1 <kerning first="70" second="117" amount="-1" />
kerning first=87 second=113 amount=-1 <kerning first="67" second="41" amount="-1" />
kerning first=73 second=84 amount=-1 <kerning first="89" second="71" amount="-1" />
kerning first=121 second=46 amount=-6 <kerning first="121" second="44" amount="-6" />
kerning first=75 second=99 amount=-1 <kerning first="97" second="121" amount="-1" />
kerning first=65 second=112 amount=-2 <kerning first="87" second="113" amount="-1" />
kerning first=65 second=85 amount=-1 <kerning first="73" second="84" amount="-1" />
kerning first=76 second=67 amount=-2 <kerning first="121" second="46" amount="-6" />
kerning first=76 second=81 amount=-2 <kerning first="75" second="99" amount="-1" />
kerning first=102 second=100 amount=-1 <kerning first="65" second="112" amount="-2" />
kerning first=75 second=79 amount=-1 <kerning first="65" second="85" amount="-1" />
kerning first=39 second=65 amount=-4 <kerning first="76" second="67" amount="-2" />
kerning first=65 second=84 amount=-4 <kerning first="76" second="81" amount="-2" />
kerning first=90 second=101 amount=-1 <kerning first="102" second="100" amount="-1" />
kerning first=84 second=121 amount=-3 <kerning first="75" second="79" amount="-1" />
kerning first=114 second=39 amount=1 <kerning first="39" second="65" amount="-4" />
kerning first=84 second=109 amount=-3 <kerning first="65" second="84" amount="-4" />
kerning first=123 second=74 amount=-1 <kerning first="90" second="101" amount="-1" />
kerning first=76 second=119 amount=-2 <kerning first="84" second="121" amount="-3" />
kerning first=84 second=117 amount=-2 <kerning first="114" second="39" amount="1" />
kerning first=76 second=85 amount=-1 <kerning first="84" second="109" amount="-3" />
kerning first=76 second=71 amount=-2 <kerning first="123" second="74" amount="-1" />
kerning first=79 second=90 amount=-1 <kerning first="76" second="119" amount="-2" />
kerning first=107 second=100 amount=-1 <kerning first="84" second="117" amount="-2" />
kerning first=90 second=111 amount=-1 <kerning first="76" second="85" amount="-1" />
kerning first=79 second=44 amount=-4 <kerning first="76" second="71" amount="-2" />
kerning first=75 second=45 amount=-6 <kerning first="79" second="90" amount="-1" />
kerning first=79 second=86 amount=-1 <kerning first="107" second="100" amount="-1" />
kerning first=79 second=46 amount=-4 <kerning first="90" second="111" amount="-1" />
kerning first=76 second=89 amount=-10 <kerning first="79" second="44" amount="-4" />
kerning first=68 second=65 amount=-1 <kerning first="75" second="45" amount="-6" />
kerning first=79 second=84 amount=-3 <kerning first="79" second="86" amount="-1" />
kerning first=87 second=100 amount=-1 <kerning first="79" second="46" amount="-4" />
kerning first=84 second=32 amount=-2 <kerning first="76" second="89" amount="-10" />
kerning first=90 second=67 amount=-1 <kerning first="68" second="65" amount="-1" />
kerning first=69 second=103 amount=-1 <kerning first="79" second="84" amount="-3" />
kerning first=90 second=71 amount=-1 <kerning first="87" second="100" amount="-1" />
kerning first=86 second=44 amount=-8 <kerning first="84" second="32" amount="-2" />
kerning first=69 second=121 amount=-1 <kerning first="90" second="67" amount="-1" />
kerning first=87 second=114 amount=-1 <kerning first="69" second="103" amount="-1" />
kerning first=118 second=39 amount=1 <kerning first="90" second="71" amount="-1" />
kerning first=46 second=39 amount=-10 <kerning first="86" second="44" amount="-8" />
kerning first=72 second=84 amount=-1 <kerning first="69" second="121" amount="-1" />
kerning first=86 second=46 amount=-8 <kerning first="87" second="114" amount="-1" />
kerning first=69 second=113 amount=-1 <kerning first="118" second="39" amount="1" />
kerning first=69 second=119 amount=-1 <kerning first="46" second="39" amount="-10" />
kerning first=73 second=89 amount=-1 <kerning first="72" second="84" amount="-1" />
kerning first=39 second=39 amount=-1 <kerning first="86" second="46" amount="-8" />
kerning first=69 second=117 amount=-1 <kerning first="69" second="113" amount="-1" />
kerning first=111 second=39 amount=-3 <kerning first="69" second="119" amount="-1" />
kerning first=90 second=81 amount=-1 <kerning first="73" second="89" amount="-1" />
<kerning first="39" second="39" amount="-1" />
<kerning first="69" second="117" amount="-1" />
<kerning first="111" second="39" amount="-3" />
<kerning first="90" second="81" amount="-1" />
</kernings>
</font>

View File

@ -1,103 +1,110 @@
info face="Roboto Mono" size=72 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2 <?xml version="1.0"?>
common lineHeight=96 base=76 scaleW=512 scaleH=512 pages=1 packed=0 <font>
page id=0 file="RobotoMono72White.png" <info face="Roboto Mono" size="72" bold="0" italic="0" charset="" unicode="0" stretchH="100" smooth="1" aa="1" padding="1,1,1,1" spacing="-2,-2" outline="0" />
chars count=98 <common lineHeight="96" base="76" scaleW="512" scaleH="512" pages="1" packed="0" alphaChnl="0" redChnl="0" greenChnl="0" blueChnl="0" />
char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=0 page=0 chnl=0 <pages>
char id=10 x=0 y=0 width=45 height=99 xoffset=-1 yoffset=-2 xadvance=43 page=0 chnl=0 <page id="0" file="RobotoMono72White.png" /> </pages>
char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=43 page=0 chnl=0 <chars count="98">
char id=33 x=498 y=99 width=10 height=55 xoffset=16 yoffset=23 xadvance=43 page=0 chnl=0 <char id="0" x="0" y="0" width="0" height="0" xoffset="-1" yoffset="75" xadvance="0" page="0" chnl="0" />
char id=34 x=434 y=319 width=20 height=19 xoffset=11 yoffset=21 xadvance=43 page=0 chnl=0 <char id="10" x="0" y="0" width="45" height="99" xoffset="-1" yoffset="-2" xadvance="43" page="0" chnl="0" />
char id=35 x=175 y=265 width=41 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0 <char id="32" x="0" y="0" width="0" height="0" xoffset="-1" yoffset="75" xadvance="43" page="0" chnl="0" />
char id=36 x=200 y=0 width=35 height=69 xoffset=5 yoffset=15 xadvance=43 page=0 chnl=0 <char id="33" x="498" y="99" width="10" height="55" xoffset="16" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=37 x=0 y=155 width=42 height=56 xoffset=1 yoffset=22 xadvance=44 page=0 chnl=0 <char id="34" x="434" y="319" width="20" height="19" xoffset="11" yoffset="21" xadvance="43" page="0" chnl="0" />
char id=38 x=42 y=155 width=41 height=56 xoffset=3 yoffset=22 xadvance=44 page=0 chnl=0 <char id="35" x="175" y="265" width="41" height="54" xoffset="1" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=39 x=502 y=211 width=7 height=19 xoffset=16 yoffset=21 xadvance=43 page=0 chnl=0 <char id="36" x="200" y="0" width="35" height="69" xoffset="5" yoffset="15" xadvance="43" page="0" chnl="0" />
char id=40 x=45 y=0 width=21 height=78 xoffset=12 yoffset=16 xadvance=44 page=0 chnl=0 <char id="37" x="0" y="155" width="42" height="56" xoffset="1" yoffset="22" xadvance="44" page="0" chnl="0" />
char id=41 x=66 y=0 width=22 height=78 xoffset=9 yoffset=16 xadvance=43 page=0 chnl=0 <char id="38" x="42" y="155" width="41" height="56" xoffset="3" yoffset="22" xadvance="44" page="0" chnl="0" />
char id=42 x=256 y=319 width=37 height=37 xoffset=4 yoffset=32 xadvance=43 page=0 chnl=0 <char id="39" x="502" y="211" width="7" height="19" xoffset="16" yoffset="21" xadvance="43" page="0" chnl="0" />
char id=43 x=219 y=319 width=37 height=40 xoffset=3 yoffset=32 xadvance=43 page=0 chnl=0 <char id="40" x="45" y="0" width="21" height="78" xoffset="12" yoffset="16" xadvance="44" page="0" chnl="0" />
char id=44 x=421 y=319 width=13 height=22 xoffset=11 yoffset=67 xadvance=43 page=0 chnl=0 <char id="41" x="66" y="0" width="22" height="78" xoffset="9" yoffset="16" xadvance="43" page="0" chnl="0" />
char id=45 x=17 y=360 width=29 height=8 xoffset=7 yoffset=49 xadvance=44 page=0 chnl=0 <char id="42" x="256" y="319" width="37" height="37" xoffset="4" yoffset="32" xadvance="43" page="0" chnl="0" />
char id=46 x=496 y=319 width=12 height=13 xoffset=16 yoffset=65 xadvance=43 page=0 chnl=0 <char id="43" x="219" y="319" width="37" height="40" xoffset="3" yoffset="32" xadvance="43" page="0" chnl="0" />
char id=47 x=319 y=0 width=31 height=58 xoffset=7 yoffset=23 xadvance=43 page=0 chnl=0 <char id="44" x="421" y="319" width="13" height="22" xoffset="11" yoffset="67" xadvance="43" page="0" chnl="0" />
char id=48 x=431 y=99 width=35 height=56 xoffset=4 yoffset=22 xadvance=43 page=0 chnl=0 <char id="45" x="17" y="360" width="29" height="8" xoffset="7" yoffset="49" xadvance="44" page="0" chnl="0" />
char id=49 x=36 y=265 width=23 height=54 xoffset=6 yoffset=23 xadvance=44 page=0 chnl=0 <char id="46" x="496" y="319" width="12" height="13" xoffset="16" yoffset="65" xadvance="43" page="0" chnl="0" />
char id=50 x=189 y=155 width=37 height=55 xoffset=2 yoffset=22 xadvance=44 page=0 chnl=0 <char id="47" x="319" y="0" width="31" height="58" xoffset="7" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=51 x=361 y=99 width=35 height=56 xoffset=2 yoffset=22 xadvance=43 page=0 chnl=0 <char id="48" x="431" y="99" width="35" height="56" xoffset="4" yoffset="22" xadvance="43" page="0" chnl="0" />
char id=52 x=59 y=265 width=39 height=54 xoffset=2 yoffset=23 xadvance=44 page=0 chnl=0 <char id="49" x="36" y="265" width="23" height="54" xoffset="6" yoffset="23" xadvance="44" page="0" chnl="0" />
char id=53 x=226 y=155 width=35 height=55 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0 <char id="50" x="189" y="155" width="37" height="55" xoffset="2" yoffset="22" xadvance="44" page="0" chnl="0" />
char id=54 x=261 y=155 width=35 height=55 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0 <char id="51" x="361" y="99" width="35" height="56" xoffset="2" yoffset="22" xadvance="43" page="0" chnl="0" />
char id=55 x=98 y=265 width=37 height=54 xoffset=3 yoffset=23 xadvance=44 page=0 chnl=0 <char id="52" x="59" y="265" width="39" height="54" xoffset="2" yoffset="23" xadvance="44" page="0" chnl="0" />
char id=56 x=396 y=99 width=35 height=56 xoffset=5 yoffset=22 xadvance=43 page=0 chnl=0 <char id="53" x="226" y="155" width="35" height="55" xoffset="5" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=57 x=296 y=155 width=34 height=55 xoffset=4 yoffset=22 xadvance=43 page=0 chnl=0 <char id="54" x="261" y="155" width="35" height="55" xoffset="4" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=58 x=490 y=211 width=12 height=43 xoffset=18 yoffset=35 xadvance=43 page=0 chnl=0 <char id="55" x="98" y="265" width="37" height="54" xoffset="3" yoffset="23" xadvance="44" page="0" chnl="0" />
char id=59 x=486 y=0 width=14 height=55 xoffset=16 yoffset=35 xadvance=43 page=0 chnl=0 <char id="56" x="396" y="99" width="35" height="56" xoffset="5" yoffset="22" xadvance="43" page="0" chnl="0" />
char id=60 x=293 y=319 width=32 height=35 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0 <char id="57" x="296" y="155" width="34" height="55" xoffset="4" yoffset="22" xadvance="43" page="0" chnl="0" />
char id=61 x=388 y=319 width=33 height=23 xoffset=5 yoffset=41 xadvance=43 page=0 chnl=0 <char id="58" x="490" y="211" width="12" height="43" xoffset="18" yoffset="35" xadvance="43" page="0" chnl="0" />
char id=62 x=325 y=319 width=33 height=35 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0 <char id="59" x="486" y="0" width="14" height="55" xoffset="16" yoffset="35" xadvance="43" page="0" chnl="0" />
char id=63 x=466 y=99 width=32 height=56 xoffset=6 yoffset=22 xadvance=43 page=0 chnl=0 <char id="60" x="293" y="319" width="32" height="35" xoffset="5" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=64 x=135 y=265 width=40 height=54 xoffset=1 yoffset=23 xadvance=42 page=0 chnl=0 <char id="61" x="388" y="319" width="33" height="23" xoffset="5" yoffset="41" xadvance="43" page="0" chnl="0" />
char id=65 x=330 y=155 width=42 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0 <char id="62" x="325" y="319" width="33" height="35" xoffset="5" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=66 x=372 y=155 width=35 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0 <char id="63" x="466" y="99" width="32" height="56" xoffset="6" yoffset="22" xadvance="43" page="0" chnl="0" />
char id=67 x=448 y=0 width=38 height=56 xoffset=3 yoffset=22 xadvance=43 page=0 chnl=0 <char id="64" x="135" y="265" width="40" height="54" xoffset="1" yoffset="23" xadvance="42" page="0" chnl="0" />
char id=68 x=407 y=155 width=37 height=54 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0 <char id="65" x="330" y="155" width="42" height="54" xoffset="1" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=69 x=444 y=155 width=34 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0 <char id="66" x="372" y="155" width="35" height="54" xoffset="5" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=70 x=0 y=211 width=34 height=54 xoffset=6 yoffset=23 xadvance=44 page=0 chnl=0 <char id="67" x="448" y="0" width="38" height="56" xoffset="3" yoffset="22" xadvance="43" page="0" chnl="0" />
char id=71 x=0 y=99 width=38 height=56 xoffset=3 yoffset=22 xadvance=44 page=0 chnl=0 <char id="68" x="407" y="155" width="37" height="54" xoffset="4" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=72 x=34 y=211 width=36 height=54 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0 <char id="69" x="444" y="155" width="34" height="54" xoffset="5" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=73 x=478 y=155 width=33 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0 <char id="70" x="0" y="211" width="34" height="54" xoffset="6" yoffset="23" xadvance="44" page="0" chnl="0" />
char id=74 x=83 y=155 width=36 height=55 xoffset=2 yoffset=23 xadvance=43 page=0 chnl=0 <char id="71" x="0" y="99" width="38" height="56" xoffset="3" yoffset="22" xadvance="44" page="0" chnl="0" />
char id=75 x=70 y=211 width=38 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0 <char id="72" x="34" y="211" width="36" height="54" xoffset="4" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=76 x=108 y=211 width=34 height=54 xoffset=6 yoffset=23 xadvance=43 page=0 chnl=0 <char id="73" x="478" y="155" width="33" height="54" xoffset="5" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=77 x=142 y=211 width=36 height=54 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0 <char id="74" x="83" y="155" width="36" height="55" xoffset="2" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=78 x=178 y=211 width=35 height=54 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0 <char id="75" x="70" y="211" width="38" height="54" xoffset="5" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=79 x=38 y=99 width=38 height=56 xoffset=3 yoffset=22 xadvance=43 page=0 chnl=0 <char id="76" x="108" y="211" width="34" height="54" xoffset="6" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=80 x=213 y=211 width=36 height=54 xoffset=6 yoffset=23 xadvance=43 page=0 chnl=0 <char id="77" x="142" y="211" width="36" height="54" xoffset="4" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=81 x=242 y=0 width=40 height=64 xoffset=2 yoffset=22 xadvance=43 page=0 chnl=0 <char id="78" x="178" y="211" width="35" height="54" xoffset="4" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=82 x=249 y=211 width=36 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0 <char id="79" x="38" y="99" width="38" height="56" xoffset="3" yoffset="22" xadvance="43" page="0" chnl="0" />
char id=83 x=76 y=99 width=38 height=56 xoffset=3 yoffset=22 xadvance=44 page=0 chnl=0 <char id="80" x="213" y="211" width="36" height="54" xoffset="6" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=84 x=285 y=211 width=40 height=54 xoffset=2 yoffset=23 xadvance=44 page=0 chnl=0 <char id="81" x="242" y="0" width="40" height="64" xoffset="2" yoffset="22" xadvance="43" page="0" chnl="0" />
char id=85 x=119 y=155 width=36 height=55 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0 <char id="82" x="249" y="211" width="36" height="54" xoffset="5" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=86 x=325 y=211 width=41 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0 <char id="83" x="76" y="99" width="38" height="56" xoffset="3" yoffset="22" xadvance="44" page="0" chnl="0" />
char id=87 x=366 y=211 width=42 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0 <char id="84" x="285" y="211" width="40" height="54" xoffset="2" yoffset="23" xadvance="44" page="0" chnl="0" />
char id=88 x=408 y=211 width=41 height=54 xoffset=2 yoffset=23 xadvance=43 page=0 chnl=0 <char id="85" x="119" y="155" width="36" height="55" xoffset="4" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=89 x=449 y=211 width=41 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0 <char id="86" x="325" y="211" width="41" height="54" xoffset="1" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=90 x=0 y=265 width=36 height=54 xoffset=3 yoffset=23 xadvance=43 page=0 chnl=0 <char id="87" x="366" y="211" width="42" height="54" xoffset="1" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=91 x=88 y=0 width=16 height=72 xoffset=14 yoffset=16 xadvance=43 page=0 chnl=0 <char id="88" x="408" y="211" width="41" height="54" xoffset="2" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=92 x=350 y=0 width=30 height=58 xoffset=7 yoffset=23 xadvance=43 page=0 chnl=0 <char id="89" x="449" y="211" width="41" height="54" xoffset="1" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=93 x=104 y=0 width=17 height=72 xoffset=13 yoffset=16 xadvance=44 page=0 chnl=0 <char id="90" x="0" y="265" width="36" height="54" xoffset="3" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=94 x=358 y=319 width=30 height=30 xoffset=7 yoffset=23 xadvance=43 page=0 chnl=0 <char id="91" x="88" y="0" width="16" height="72" xoffset="14" yoffset="16" xadvance="43" page="0" chnl="0" />
char id=95 x=46 y=360 width=34 height=8 xoffset=4 yoffset=74 xadvance=43 page=0 chnl=0 <char id="92" x="350" y="0" width="30" height="58" xoffset="7" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=96 x=0 y=360 width=17 height=12 xoffset=13 yoffset=22 xadvance=43 page=0 chnl=0 <char id="93" x="104" y="0" width="17" height="72" xoffset="13" yoffset="16" xadvance="44" page="0" chnl="0" />
char id=97 x=251 y=265 width=35 height=42 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0 <char id="94" x="358" y="319" width="30" height="30" xoffset="7" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=98 x=380 y=0 width=34 height=57 xoffset=5 yoffset=21 xadvance=43 page=0 chnl=0 <char id="95" x="46" y="360" width="34" height="8" xoffset="4" yoffset="74" xadvance="43" page="0" chnl="0" />
char id=99 x=286 y=265 width=35 height=42 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0 <char id="96" x="0" y="360" width="17" height="12" xoffset="13" yoffset="22" xadvance="43" page="0" chnl="0" />
char id=100 x=414 y=0 width=34 height=57 xoffset=4 yoffset=21 xadvance=43 page=0 chnl=0 <char id="97" x="251" y="265" width="35" height="42" xoffset="4" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=101 x=321 y=265 width=36 height=42 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0 <char id="98" x="380" y="0" width="34" height="57" xoffset="5" yoffset="21" xadvance="43" page="0" chnl="0" />
char id=102 x=282 y=0 width=37 height=58 xoffset=4 yoffset=19 xadvance=43 page=0 chnl=0 <char id="99" x="286" y="265" width="35" height="42" xoffset="4" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=103 x=114 y=99 width=34 height=56 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0 <char id="100" x="414" y="0" width="34" height="57" xoffset="4" yoffset="21" xadvance="43" page="0" chnl="0" />
char id=104 x=148 y=99 width=34 height=56 xoffset=5 yoffset=21 xadvance=43 page=0 chnl=0 <char id="101" x="321" y="265" width="36" height="42" xoffset="4" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=105 x=155 y=155 width=34 height=55 xoffset=6 yoffset=22 xadvance=43 page=0 chnl=0 <char id="102" x="282" y="0" width="37" height="58" xoffset="4" yoffset="19" xadvance="43" page="0" chnl="0" />
char id=106 x=121 y=0 width=26 height=71 xoffset=6 yoffset=22 xadvance=44 page=0 chnl=0 <char id="103" x="114" y="99" width="34" height="56" xoffset="4" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=107 x=182 y=99 width=36 height=56 xoffset=5 yoffset=21 xadvance=43 page=0 chnl=0 <char id="104" x="148" y="99" width="34" height="56" xoffset="5" yoffset="21" xadvance="43" page="0" chnl="0" />
char id=108 x=218 y=99 width=34 height=56 xoffset=6 yoffset=21 xadvance=43 page=0 chnl=0 <char id="105" x="155" y="155" width="34" height="55" xoffset="6" yoffset="22" xadvance="43" page="0" chnl="0" />
char id=109 x=428 y=265 width=39 height=41 xoffset=2 yoffset=36 xadvance=43 page=0 chnl=0 <char id="106" x="121" y="0" width="26" height="71" xoffset="6" yoffset="22" xadvance="44" page="0" chnl="0" />
char id=110 x=467 y=265 width=34 height=41 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0 <char id="107" x="182" y="99" width="36" height="56" xoffset="5" yoffset="21" xadvance="43" page="0" chnl="0" />
char id=111 x=357 y=265 width=37 height=42 xoffset=3 yoffset=36 xadvance=43 page=0 chnl=0 <char id="108" x="218" y="99" width="34" height="56" xoffset="6" yoffset="21" xadvance="43" page="0" chnl="0" />
char id=112 x=252 y=99 width=34 height=56 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0 <char id="109" x="428" y="265" width="39" height="41" xoffset="2" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=113 x=286 y=99 width=34 height=56 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0 <char id="110" x="467" y="265" width="34" height="41" xoffset="5" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=114 x=0 y=319 width=29 height=41 xoffset=11 yoffset=36 xadvance=44 page=0 chnl=0 <char id="111" x="357" y="265" width="37" height="42" xoffset="3" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=115 x=394 y=265 width=34 height=42 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0 <char id="112" x="252" y="99" width="34" height="56" xoffset="5" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=116 x=216 y=265 width=35 height=51 xoffset=4 yoffset=27 xadvance=43 page=0 chnl=0 <char id="113" x="286" y="99" width="34" height="56" xoffset="4" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=117 x=29 y=319 width=33 height=41 xoffset=5 yoffset=37 xadvance=43 page=0 chnl=0 <char id="114" x="0" y="319" width="29" height="41" xoffset="11" yoffset="36" xadvance="44" page="0" chnl="0" />
char id=118 x=62 y=319 width=39 height=40 xoffset=2 yoffset=37 xadvance=43 page=0 chnl=0 <char id="115" x="394" y="265" width="34" height="42" xoffset="5" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=119 x=101 y=319 width=43 height=40 xoffset=0 yoffset=37 xadvance=43 page=0 chnl=0 <char id="116" x="216" y="265" width="35" height="51" xoffset="4" yoffset="27" xadvance="43" page="0" chnl="0" />
char id=120 x=144 y=319 width=40 height=40 xoffset=2 yoffset=37 xadvance=43 page=0 chnl=0 <char id="117" x="29" y="319" width="33" height="41" xoffset="5" yoffset="37" xadvance="43" page="0" chnl="0" />
char id=121 x=320 y=99 width=41 height=56 xoffset=1 yoffset=37 xadvance=43 page=0 chnl=0 <char id="118" x="62" y="319" width="39" height="40" xoffset="2" yoffset="37" xadvance="43" page="0" chnl="0" />
char id=122 x=184 y=319 width=35 height=40 xoffset=5 yoffset=37 xadvance=44 page=0 chnl=0 <char id="119" x="101" y="319" width="43" height="40" xoffset="0" yoffset="37" xadvance="43" page="0" chnl="0" />
char id=123 x=147 y=0 width=26 height=71 xoffset=10 yoffset=19 xadvance=43 page=0 chnl=0 <char id="120" x="144" y="319" width="40" height="40" xoffset="2" yoffset="37" xadvance="43" page="0" chnl="0" />
char id=124 x=235 y=0 width=7 height=68 xoffset=18 yoffset=23 xadvance=43 page=0 chnl=0 <char id="121" x="320" y="99" width="41" height="56" xoffset="1" yoffset="37" xadvance="43" page="0" chnl="0" />
char id=125 x=173 y=0 width=27 height=71 xoffset=10 yoffset=19 xadvance=44 page=0 chnl=0 <char id="122" x="184" y="319" width="35" height="40" xoffset="5" yoffset="37" xadvance="44" page="0" chnl="0" />
char id=126 x=454 y=319 width=42 height=16 xoffset=1 yoffset=47 xadvance=44 page=0 chnl=0 <char id="123" x="147" y="0" width="26" height="71" xoffset="10" yoffset="19" xadvance="43" page="0" chnl="0" />
char id=127 x=0 y=0 width=45 height=99 xoffset=-1 yoffset=-2 xadvance=43 page=0 chnl=0 <char id="124" x="235" y="0" width="7" height="68" xoffset="18" yoffset="23" xadvance="43" page="0" chnl="0" />
kernings count=0 <char id="125" x="173" y="0" width="27" height="71" xoffset="10" yoffset="19" xadvance="44" page="0" chnl="0" />
<char id="126" x="454" y="319" width="42" height="16" xoffset="1" yoffset="47" xadvance="44" page="0" chnl="0" />
<char id="127" x="0" y="0" width="45" height="99" xoffset="-1" yoffset="-2" xadvance="43" page="0" chnl="0" />
</chars>
<kernings count="1">
<kerning first="0" second="0" amount="0" />
</kernings>
</font>

View File

@ -1,492 +1,498 @@
info face="Roboto Slab Regular" size=72 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2 <?xml version="1.0"?>
common lineHeight=96 base=76 scaleW=512 scaleH=512 pages=1 packed=0 <font>
page id=0 file="RobotoSlab72White.png" <info face="Roboto Slab Regular" size="72" bold="0" italic="0" charset="" unicode="0" stretchH="100" smooth="1" aa="1" padding="1,1,1,1" spacing="-2,-2" outline="0" />
chars count=98 <common lineHeight="96" base="76" scaleW="512" scaleH="512" pages="1" packed="0" alphaChnl="0" redChnl="0" greenChnl="0" blueChnl="0" />
char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=0 page=0 chnl=0 <pages>
char id=10 x=0 y=0 width=70 height=98 xoffset=0 yoffset=-1 xadvance=70 page=0 chnl=0 <page id="0" file="RobotoSlab72White.png" /> </pages>
char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=18 page=0 chnl=0 <chars count="98">
char id=33 x=497 y=156 width=9 height=54 xoffset=4 yoffset=23 xadvance=17 page=0 chnl=0 <char id="0" x="0" y="0" width="0" height="0" xoffset="-1" yoffset="75" xadvance="0" page="0" chnl="0" />
char id=34 x=191 y=362 width=19 height=20 xoffset=5 yoffset=20 xadvance=28 page=0 chnl=0 <char id="10" x="0" y="0" width="70" height="98" xoffset="0" yoffset="-1" xadvance="70" page="0" chnl="0" />
char id=35 x=406 y=266 width=41 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0 <char id="32" x="0" y="0" width="0" height="0" xoffset="-1" yoffset="75" xadvance="18" page="0" chnl="0" />
char id=36 x=212 y=0 width=35 height=69 xoffset=2 yoffset=15 xadvance=39 page=0 chnl=0 <char id="33" x="497" y="156" width="9" height="54" xoffset="4" yoffset="23" xadvance="17" page="0" chnl="0" />
char id=37 x=174 y=156 width=48 height=56 xoffset=2 yoffset=22 xadvance=52 page=0 chnl=0 <char id="34" x="191" y="362" width="19" height="20" xoffset="5" yoffset="20" xadvance="28" page="0" chnl="0" />
char id=38 x=222 y=156 width=44 height=56 xoffset=2 yoffset=22 xadvance=46 page=0 chnl=0 <char id="35" x="406" y="266" width="41" height="54" xoffset="1" yoffset="23" xadvance="43" page="0" chnl="0" />
char id=39 x=210 y=362 width=8 height=20 xoffset=5 yoffset=20 xadvance=17 page=0 chnl=0 <char id="36" x="212" y="0" width="35" height="69" xoffset="2" yoffset="15" xadvance="39" page="0" chnl="0" />
char id=40 x=70 y=0 width=21 height=77 xoffset=3 yoffset=17 xadvance=23 page=0 chnl=0 <char id="37" x="174" y="156" width="48" height="56" xoffset="2" yoffset="22" xadvance="52" page="0" chnl="0" />
char id=41 x=91 y=0 width=21 height=77 xoffset=-1 yoffset=17 xadvance=23 page=0 chnl=0 <char id="38" x="222" y="156" width="44" height="56" xoffset="2" yoffset="22" xadvance="46" page="0" chnl="0" />
char id=42 x=100 y=362 width=31 height=33 xoffset=1 yoffset=23 xadvance=33 page=0 chnl=0 <char id="39" x="210" y="362" width="8" height="20" xoffset="5" yoffset="20" xadvance="17" page="0" chnl="0" />
char id=43 x=0 y=362 width=37 height=40 xoffset=2 yoffset=32 xadvance=41 page=0 chnl=0 <char id="40" x="70" y="0" width="21" height="77" xoffset="3" yoffset="17" xadvance="23" page="0" chnl="0" />
char id=44 x=492 y=320 width=13 height=21 xoffset=-1 yoffset=67 xadvance=14 page=0 chnl=0 <char id="41" x="91" y="0" width="21" height="77" xoffset="-1" yoffset="17" xadvance="23" page="0" chnl="0" />
char id=45 x=287 y=362 width=19 height=8 xoffset=4 yoffset=50 xadvance=27 page=0 chnl=0 <char id="42" x="100" y="362" width="31" height="33" xoffset="1" yoffset="23" xadvance="33" page="0" chnl="0" />
char id=46 x=278 y=362 width=9 height=9 xoffset=4 yoffset=68 xadvance=17 page=0 chnl=0 <char id="43" x="0" y="362" width="37" height="40" xoffset="2" yoffset="32" xadvance="41" page="0" chnl="0" />
char id=47 x=470 y=0 width=30 height=58 xoffset=-1 yoffset=23 xadvance=29 page=0 chnl=0 <char id="44" x="492" y="320" width="13" height="21" xoffset="-1" yoffset="67" xadvance="14" page="0" chnl="0" />
char id=48 x=139 y=156 width=35 height=56 xoffset=3 yoffset=22 xadvance=41 page=0 chnl=0 <char id="45" x="287" y="362" width="19" height="8" xoffset="4" yoffset="50" xadvance="27" page="0" chnl="0" />
char id=49 x=305 y=266 width=25 height=54 xoffset=3 yoffset=23 xadvance=30 page=0 chnl=0 <char id="46" x="278" y="362" width="9" height="9" xoffset="4" yoffset="68" xadvance="17" page="0" chnl="0" />
char id=50 x=357 y=156 width=36 height=55 xoffset=2 yoffset=22 xadvance=40 page=0 chnl=0 <char id="47" x="470" y="0" width="30" height="58" xoffset="-1" yoffset="23" xadvance="29" page="0" chnl="0" />
char id=51 x=0 y=156 width=34 height=56 xoffset=2 yoffset=22 xadvance=39 page=0 chnl=0 <char id="48" x="139" y="156" width="35" height="56" xoffset="3" yoffset="22" xadvance="41" page="0" chnl="0" />
char id=52 x=330 y=266 width=39 height=54 xoffset=1 yoffset=23 xadvance=42 page=0 chnl=0 <char id="49" x="305" y="266" width="25" height="54" xoffset="3" yoffset="23" xadvance="30" page="0" chnl="0" />
char id=53 x=393 y=156 width=33 height=55 xoffset=2 yoffset=23 xadvance=37 page=0 chnl=0 <char id="50" x="357" y="156" width="36" height="55" xoffset="2" yoffset="22" xadvance="40" page="0" chnl="0" />
char id=54 x=34 y=156 width=35 height=56 xoffset=3 yoffset=22 xadvance=40 page=0 chnl=0 <char id="51" x="0" y="156" width="34" height="56" xoffset="2" yoffset="22" xadvance="39" page="0" chnl="0" />
char id=55 x=369 y=266 width=37 height=54 xoffset=2 yoffset=23 xadvance=40 page=0 chnl=0 <char id="52" x="330" y="266" width="39" height="54" xoffset="1" yoffset="23" xadvance="42" page="0" chnl="0" />
char id=56 x=69 y=156 width=35 height=56 xoffset=2 yoffset=22 xadvance=39 page=0 chnl=0 <char id="53" x="393" y="156" width="33" height="55" xoffset="2" yoffset="23" xadvance="37" page="0" chnl="0" />
char id=57 x=104 y=156 width=35 height=56 xoffset=2 yoffset=22 xadvance=41 page=0 chnl=0 <char id="54" x="34" y="156" width="35" height="56" xoffset="3" yoffset="22" xadvance="40" page="0" chnl="0" />
char id=58 x=500 y=0 width=9 height=40 xoffset=4 yoffset=37 xadvance=15 page=0 chnl=0 <char id="55" x="369" y="266" width="37" height="54" xoffset="2" yoffset="23" xadvance="40" page="0" chnl="0" />
char id=59 x=447 y=266 width=13 height=52 xoffset=0 yoffset=37 xadvance=15 page=0 chnl=0 <char id="56" x="69" y="156" width="35" height="56" xoffset="2" yoffset="22" xadvance="39" page="0" chnl="0" />
char id=60 x=37 y=362 width=31 height=35 xoffset=2 yoffset=39 xadvance=36 page=0 chnl=0 <char id="57" x="104" y="156" width="35" height="56" xoffset="2" yoffset="22" xadvance="41" page="0" chnl="0" />
char id=61 x=160 y=362 width=31 height=23 xoffset=4 yoffset=40 xadvance=39 page=0 chnl=0 <char id="58" x="500" y="0" width="9" height="40" xoffset="4" yoffset="37" xadvance="15" page="0" chnl="0" />
char id=62 x=68 y=362 width=32 height=35 xoffset=3 yoffset=39 xadvance=37 page=0 chnl=0 <char id="59" x="447" y="266" width="13" height="52" xoffset="0" yoffset="37" xadvance="15" page="0" chnl="0" />
char id=63 x=480 y=98 width=31 height=55 xoffset=1 yoffset=22 xadvance=33 page=0 chnl=0 <char id="60" x="37" y="362" width="31" height="35" xoffset="2" yoffset="39" xadvance="36" page="0" chnl="0" />
char id=64 x=247 y=0 width=60 height=68 xoffset=1 yoffset=25 xadvance=64 page=0 chnl=0 <char id="61" x="160" y="362" width="31" height="23" xoffset="4" yoffset="40" xadvance="39" page="0" chnl="0" />
char id=65 x=426 y=156 width=51 height=54 xoffset=1 yoffset=23 xadvance=53 page=0 chnl=0 <char id="62" x="68" y="362" width="32" height="35" xoffset="3" yoffset="39" xadvance="37" page="0" chnl="0" />
char id=66 x=0 y=212 width=44 height=54 xoffset=1 yoffset=23 xadvance=47 page=0 chnl=0 <char id="63" x="480" y="98" width="31" height="55" xoffset="1" yoffset="22" xadvance="33" page="0" chnl="0" />
char id=67 x=191 y=98 width=42 height=56 xoffset=1 yoffset=22 xadvance=46 page=0 chnl=0 <char id="64" x="247" y="0" width="60" height="68" xoffset="1" yoffset="25" xadvance="64" page="0" chnl="0" />
char id=68 x=44 y=212 width=46 height=54 xoffset=1 yoffset=23 xadvance=50 page=0 chnl=0 <char id="65" x="426" y="156" width="51" height="54" xoffset="1" yoffset="23" xadvance="53" page="0" chnl="0" />
char id=69 x=90 y=212 width=42 height=54 xoffset=1 yoffset=23 xadvance=46 page=0 chnl=0 <char id="66" x="0" y="212" width="44" height="54" xoffset="1" yoffset="23" xadvance="47" page="0" chnl="0" />
char id=70 x=132 y=212 width=42 height=54 xoffset=1 yoffset=23 xadvance=44 page=0 chnl=0 <char id="67" x="191" y="98" width="42" height="56" xoffset="1" yoffset="22" xadvance="46" page="0" chnl="0" />
char id=71 x=233 y=98 width=43 height=56 xoffset=1 yoffset=22 xadvance=49 page=0 chnl=0 <char id="68" x="44" y="212" width="46" height="54" xoffset="1" yoffset="23" xadvance="50" page="0" chnl="0" />
char id=72 x=174 y=212 width=52 height=54 xoffset=1 yoffset=23 xadvance=55 page=0 chnl=0 <char id="69" x="90" y="212" width="42" height="54" xoffset="1" yoffset="23" xadvance="46" page="0" chnl="0" />
char id=73 x=477 y=156 width=20 height=54 xoffset=1 yoffset=23 xadvance=22 page=0 chnl=0 <char id="70" x="132" y="212" width="42" height="54" xoffset="1" yoffset="23" xadvance="44" page="0" chnl="0" />
char id=74 x=266 y=156 width=39 height=55 xoffset=1 yoffset=23 xadvance=41 page=0 chnl=0 <char id="71" x="233" y="98" width="43" height="56" xoffset="1" yoffset="22" xadvance="49" page="0" chnl="0" />
char id=75 x=226 y=212 width=48 height=54 xoffset=1 yoffset=23 xadvance=50 page=0 chnl=0 <char id="72" x="174" y="212" width="52" height="54" xoffset="1" yoffset="23" xadvance="55" page="0" chnl="0" />
char id=76 x=274 y=212 width=39 height=54 xoffset=1 yoffset=23 xadvance=42 page=0 chnl=0 <char id="73" x="477" y="156" width="20" height="54" xoffset="1" yoffset="23" xadvance="22" page="0" chnl="0" />
char id=77 x=313 y=212 width=64 height=54 xoffset=1 yoffset=23 xadvance=66 page=0 chnl=0 <char id="74" x="266" y="156" width="39" height="55" xoffset="1" yoffset="23" xadvance="41" page="0" chnl="0" />
char id=78 x=377 y=212 width=52 height=54 xoffset=1 yoffset=23 xadvance=54 page=0 chnl=0 <char id="75" x="226" y="212" width="48" height="54" xoffset="1" yoffset="23" xadvance="50" page="0" chnl="0" />
char id=79 x=276 y=98 width=47 height=56 xoffset=2 yoffset=22 xadvance=51 page=0 chnl=0 <char id="76" x="274" y="212" width="39" height="54" xoffset="1" yoffset="23" xadvance="42" page="0" chnl="0" />
char id=80 x=429 y=212 width=43 height=54 xoffset=1 yoffset=23 xadvance=45 page=0 chnl=0 <char id="77" x="313" y="212" width="64" height="54" xoffset="1" yoffset="23" xadvance="66" page="0" chnl="0" />
char id=81 x=307 y=0 width=48 height=64 xoffset=2 yoffset=22 xadvance=51 page=0 chnl=0 <char id="78" x="377" y="212" width="52" height="54" xoffset="1" yoffset="23" xadvance="54" page="0" chnl="0" />
char id=82 x=0 y=266 width=46 height=54 xoffset=1 yoffset=23 xadvance=48 page=0 chnl=0 <char id="79" x="276" y="98" width="47" height="56" xoffset="2" yoffset="22" xadvance="51" page="0" chnl="0" />
char id=83 x=323 y=98 width=38 height=56 xoffset=3 yoffset=22 xadvance=43 page=0 chnl=0 <char id="80" x="429" y="212" width="43" height="54" xoffset="1" yoffset="23" xadvance="45" page="0" chnl="0" />
char id=84 x=46 y=266 width=45 height=54 xoffset=0 yoffset=23 xadvance=45 page=0 chnl=0 <char id="81" x="307" y="0" width="48" height="64" xoffset="2" yoffset="22" xadvance="51" page="0" chnl="0" />
char id=85 x=305 y=156 width=52 height=55 xoffset=1 yoffset=23 xadvance=54 page=0 chnl=0 <char id="82" x="0" y="266" width="46" height="54" xoffset="1" yoffset="23" xadvance="48" page="0" chnl="0" />
char id=86 x=91 y=266 width=50 height=54 xoffset=1 yoffset=23 xadvance=52 page=0 chnl=0 <char id="83" x="323" y="98" width="38" height="56" xoffset="3" yoffset="22" xadvance="43" page="0" chnl="0" />
char id=87 x=141 y=266 width=67 height=54 xoffset=0 yoffset=23 xadvance=67 page=0 chnl=0 <char id="84" x="46" y="266" width="45" height="54" xoffset="0" yoffset="23" xadvance="45" page="0" chnl="0" />
char id=88 x=208 y=266 width=49 height=54 xoffset=1 yoffset=23 xadvance=51 page=0 chnl=0 <char id="85" x="305" y="156" width="52" height="55" xoffset="1" yoffset="23" xadvance="54" page="0" chnl="0" />
char id=89 x=257 y=266 width=48 height=54 xoffset=1 yoffset=23 xadvance=50 page=0 chnl=0 <char id="86" x="91" y="266" width="50" height="54" xoffset="1" yoffset="23" xadvance="52" page="0" chnl="0" />
char id=90 x=472 y=212 width=38 height=54 xoffset=2 yoffset=23 xadvance=42 page=0 chnl=0 <char id="87" x="141" y="266" width="67" height="54" xoffset="0" yoffset="23" xadvance="67" page="0" chnl="0" />
char id=91 x=180 y=0 width=16 height=72 xoffset=5 yoffset=16 xadvance=21 page=0 chnl=0 <char id="88" x="208" y="266" width="49" height="54" xoffset="1" yoffset="23" xadvance="51" page="0" chnl="0" />
char id=92 x=0 y=98 width=31 height=58 xoffset=0 yoffset=23 xadvance=30 page=0 chnl=0 <char id="89" x="257" y="266" width="48" height="54" xoffset="1" yoffset="23" xadvance="50" page="0" chnl="0" />
char id=93 x=196 y=0 width=16 height=72 xoffset=-1 yoffset=16 xadvance=19 page=0 chnl=0 <char id="90" x="472" y="212" width="38" height="54" xoffset="2" yoffset="23" xadvance="42" page="0" chnl="0" />
char id=94 x=131 y=362 width=29 height=28 xoffset=1 yoffset=23 xadvance=30 page=0 chnl=0 <char id="91" x="180" y="0" width="16" height="72" xoffset="5" yoffset="16" xadvance="21" page="0" chnl="0" />
char id=95 x=306 y=362 width=34 height=8 xoffset=3 yoffset=74 xadvance=40 page=0 chnl=0 <char id="92" x="0" y="98" width="31" height="58" xoffset="0" yoffset="23" xadvance="30" page="0" chnl="0" />
char id=96 x=260 y=362 width=18 height=12 xoffset=1 yoffset=22 xadvance=20 page=0 chnl=0 <char id="93" x="196" y="0" width="16" height="72" xoffset="-1" yoffset="16" xadvance="19" page="0" chnl="0" />
char id=97 x=0 y=320 width=36 height=42 xoffset=3 yoffset=36 xadvance=41 page=0 chnl=0 <char id="94" x="131" y="362" width="29" height="28" xoffset="1" yoffset="23" xadvance="30" page="0" chnl="0" />
char id=98 x=363 y=0 width=41 height=58 xoffset=-2 yoffset=20 xadvance=42 page=0 chnl=0 <char id="95" x="306" y="362" width="34" height="8" xoffset="3" yoffset="74" xadvance="40" page="0" chnl="0" />
char id=99 x=36 y=320 width=34 height=42 xoffset=2 yoffset=36 xadvance=39 page=0 chnl=0 <char id="96" x="260" y="362" width="18" height="12" xoffset="1" yoffset="22" xadvance="20" page="0" chnl="0" />
char id=100 x=404 y=0 width=40 height=58 xoffset=2 yoffset=20 xadvance=43 page=0 chnl=0 <char id="97" x="0" y="320" width="36" height="42" xoffset="3" yoffset="36" xadvance="41" page="0" chnl="0" />
char id=101 x=70 y=320 width=34 height=42 xoffset=2 yoffset=36 xadvance=39 page=0 chnl=0 <char id="98" x="363" y="0" width="41" height="58" xoffset="-2" yoffset="20" xadvance="42" page="0" chnl="0" />
char id=102 x=444 y=0 width=26 height=58 xoffset=1 yoffset=19 xadvance=25 page=0 chnl=0 <char id="99" x="36" y="320" width="34" height="42" xoffset="2" yoffset="36" xadvance="39" page="0" chnl="0" />
char id=103 x=31 y=98 width=34 height=57 xoffset=2 yoffset=36 xadvance=40 page=0 chnl=0 <char id="100" x="404" y="0" width="40" height="58" xoffset="2" yoffset="20" xadvance="43" page="0" chnl="0" />
char id=104 x=65 y=98 width=44 height=57 xoffset=1 yoffset=20 xadvance=46 page=0 chnl=0 <char id="101" x="70" y="320" width="34" height="42" xoffset="2" yoffset="36" xadvance="39" page="0" chnl="0" />
char id=105 x=109 y=98 width=20 height=57 xoffset=2 yoffset=20 xadvance=23 page=0 chnl=0 <char id="102" x="444" y="0" width="26" height="58" xoffset="1" yoffset="19" xadvance="25" page="0" chnl="0" />
char id=106 x=112 y=0 width=18 height=73 xoffset=-2 yoffset=20 xadvance=20 page=0 chnl=0 <char id="103" x="31" y="98" width="34" height="57" xoffset="2" yoffset="36" xadvance="40" page="0" chnl="0" />
char id=107 x=129 y=98 width=42 height=57 xoffset=1 yoffset=20 xadvance=44 page=0 chnl=0 <char id="104" x="65" y="98" width="44" height="57" xoffset="1" yoffset="20" xadvance="46" page="0" chnl="0" />
char id=108 x=171 y=98 width=20 height=57 xoffset=1 yoffset=20 xadvance=22 page=0 chnl=0 <char id="105" x="109" y="98" width="20" height="57" xoffset="2" yoffset="20" xadvance="23" page="0" chnl="0" />
char id=109 x=171 y=320 width=66 height=41 xoffset=1 yoffset=36 xadvance=68 page=0 chnl=0 <char id="106" x="112" y="0" width="18" height="73" xoffset="-2" yoffset="20" xadvance="20" page="0" chnl="0" />
char id=110 x=237 y=320 width=44 height=41 xoffset=1 yoffset=36 xadvance=46 page=0 chnl=0 <char id="107" x="129" y="98" width="42" height="57" xoffset="1" yoffset="20" xadvance="44" page="0" chnl="0" />
char id=111 x=104 y=320 width=36 height=42 xoffset=2 yoffset=36 xadvance=40 page=0 chnl=0 <char id="108" x="171" y="98" width="20" height="57" xoffset="1" yoffset="20" xadvance="22" page="0" chnl="0" />
char id=112 x=361 y=98 width=40 height=56 xoffset=1 yoffset=36 xadvance=43 page=0 chnl=0 <char id="109" x="171" y="320" width="66" height="41" xoffset="1" yoffset="36" xadvance="68" page="0" chnl="0" />
char id=113 x=401 y=98 width=39 height=56 xoffset=2 yoffset=36 xadvance=40 page=0 chnl=0 <char id="110" x="237" y="320" width="44" height="41" xoffset="1" yoffset="36" xadvance="46" page="0" chnl="0" />
char id=114 x=484 y=266 width=27 height=41 xoffset=2 yoffset=36 xadvance=30 page=0 chnl=0 <char id="111" x="104" y="320" width="36" height="42" xoffset="2" yoffset="36" xadvance="40" page="0" chnl="0" />
char id=115 x=140 y=320 width=31 height=42 xoffset=3 yoffset=36 xadvance=36 page=0 chnl=0 <char id="112" x="361" y="98" width="40" height="56" xoffset="1" yoffset="36" xadvance="43" page="0" chnl="0" />
char id=116 x=460 y=266 width=24 height=51 xoffset=1 yoffset=27 xadvance=26 page=0 chnl=0 <char id="113" x="401" y="98" width="39" height="56" xoffset="2" yoffset="36" xadvance="40" page="0" chnl="0" />
char id=117 x=281 y=320 width=43 height=41 xoffset=0 yoffset=37 xadvance=44 page=0 chnl=0 <char id="114" x="484" y="266" width="27" height="41" xoffset="2" yoffset="36" xadvance="30" page="0" chnl="0" />
char id=118 x=324 y=320 width=39 height=40 xoffset=0 yoffset=37 xadvance=40 page=0 chnl=0 <char id="115" x="140" y="320" width="31" height="42" xoffset="3" yoffset="36" xadvance="36" page="0" chnl="0" />
char id=119 x=363 y=320 width=57 height=40 xoffset=1 yoffset=37 xadvance=59 page=0 chnl=0 <char id="116" x="460" y="266" width="24" height="51" xoffset="1" yoffset="27" xadvance="26" page="0" chnl="0" />
char id=120 x=420 y=320 width=40 height=40 xoffset=1 yoffset=37 xadvance=42 page=0 chnl=0 <char id="117" x="281" y="320" width="43" height="41" xoffset="0" yoffset="37" xadvance="44" page="0" chnl="0" />
char id=121 x=440 y=98 width=40 height=56 xoffset=0 yoffset=37 xadvance=41 page=0 chnl=0 <char id="118" x="324" y="320" width="39" height="40" xoffset="0" yoffset="37" xadvance="40" page="0" chnl="0" />
char id=122 x=460 y=320 width=32 height=40 xoffset=3 yoffset=37 xadvance=38 page=0 chnl=0 <char id="119" x="363" y="320" width="57" height="40" xoffset="1" yoffset="37" xadvance="59" page="0" chnl="0" />
char id=123 x=130 y=0 width=25 height=73 xoffset=1 yoffset=18 xadvance=25 page=0 chnl=0 <char id="120" x="420" y="320" width="40" height="40" xoffset="1" yoffset="37" xadvance="42" page="0" chnl="0" />
char id=124 x=355 y=0 width=8 height=63 xoffset=4 yoffset=23 xadvance=16 page=0 chnl=0 <char id="121" x="440" y="98" width="40" height="56" xoffset="0" yoffset="37" xadvance="41" page="0" chnl="0" />
char id=125 x=155 y=0 width=25 height=73 xoffset=-1 yoffset=18 xadvance=25 page=0 chnl=0 <char id="122" x="460" y="320" width="32" height="40" xoffset="3" yoffset="37" xadvance="38" page="0" chnl="0" />
char id=126 x=218 y=362 width=42 height=16 xoffset=3 yoffset=47 xadvance=49 page=0 chnl=0 <char id="123" x="130" y="0" width="25" height="73" xoffset="1" yoffset="18" xadvance="25" page="0" chnl="0" />
char id=127 x=0 y=0 width=70 height=98 xoffset=0 yoffset=-1 xadvance=70 page=0 chnl=0 <char id="124" x="355" y="0" width="8" height="63" xoffset="4" yoffset="23" xadvance="16" page="0" chnl="0" />
kernings count=389 <char id="125" x="155" y="0" width="25" height="73" xoffset="-1" yoffset="18" xadvance="25" page="0" chnl="0" />
kerning first=86 second=45 amount=-1 <char id="126" x="218" y="362" width="42" height="16" xoffset="3" yoffset="47" xadvance="49" page="0" chnl="0" />
kerning first=114 second=46 amount=-4 <char id="127" x="0" y="0" width="70" height="98" xoffset="0" yoffset="-1" xadvance="70" page="0" chnl="0" />
kerning first=40 second=87 amount=1 </chars>
kerning first=70 second=99 amount=-1 <kernings count="389">
kerning first=84 second=110 amount=-3 <kerning first="86" second="45" amount="-1" />
kerning first=114 second=116 amount=1 <kerning first="114" second="46" amount="-4" />
kerning first=39 second=65 amount=-4 <kerning first="40" second="87" amount="1" />
kerning first=104 second=34 amount=-1 <kerning first="70" second="99" amount="-1" />
kerning first=89 second=71 amount=-1 <kerning first="84" second="110" amount="-3" />
kerning first=107 second=113 amount=-1 <kerning first="114" second="116" amount="1" />
kerning first=78 second=88 amount=1 <kerning first="39" second="65" amount="-4" />
kerning first=109 second=39 amount=-1 <kerning first="104" second="34" amount="-1" />
kerning first=120 second=100 amount=-1 <kerning first="89" second="71" amount="-1" />
kerning first=84 second=100 amount=-3 <kerning first="107" second="113" amount="-1" />
kerning first=68 second=90 amount=-1 <kerning first="78" second="88" amount="1" />
kerning first=68 second=44 amount=-4 <kerning first="109" second="39" amount="-1" />
kerning first=84 second=103 amount=-3 <kerning first="120" second="100" amount="-1" />
kerning first=34 second=97 amount=-2 <kerning first="84" second="100" amount="-3" />
kerning first=70 second=97 amount=-1 <kerning first="68" second="90" amount="-1" />
kerning first=76 second=81 amount=-2 <kerning first="68" second="44" amount="-4" />
kerning first=73 second=89 amount=-1 <kerning first="84" second="103" amount="-3" />
kerning first=84 second=44 amount=-8 <kerning first="34" second="97" amount="-2" />
kerning first=68 second=65 amount=-3 <kerning first="70" second="97" amount="-1" />
kerning first=97 second=34 amount=-2 <kerning first="76" second="81" amount="-2" />
kerning first=111 second=121 amount=-1 <kerning first="73" second="89" amount="-1" />
kerning first=79 second=90 amount=-1 <kerning first="84" second="44" amount="-8" />
kerning first=75 second=121 amount=-1 <kerning first="68" second="65" amount="-3" />
kerning first=75 second=118 amount=-1 <kerning first="97" second="34" amount="-2" />
kerning first=111 second=118 amount=-1 <kerning first="111" second="121" amount="-1" />
kerning first=89 second=65 amount=-9 <kerning first="79" second="90" amount="-1" />
kerning first=75 second=71 amount=-4 <kerning first="75" second="121" amount="-1" />
kerning first=39 second=99 amount=-2 <kerning first="75" second="118" amount="-1" />
kerning first=75 second=99 amount=-1 <kerning first="111" second="118" amount="-1" />
kerning first=90 second=121 amount=-1 <kerning first="89" second="65" amount="-9" />
kerning first=44 second=39 amount=-6 <kerning first="75" second="71" amount="-4" />
kerning first=89 second=46 amount=-7 <kerning first="39" second="99" amount="-2" />
kerning first=89 second=74 amount=-7 <kerning first="75" second="99" amount="-1" />
kerning first=34 second=103 amount=-2 <kerning first="90" second="121" amount="-1" />
kerning first=70 second=103 amount=-1 <kerning first="44" second="39" amount="-6" />
kerning first=112 second=39 amount=-1 <kerning first="89" second="46" amount="-7" />
kerning first=122 second=113 amount=-1 <kerning first="89" second="74" amount="-7" />
kerning first=86 second=113 amount=-2 <kerning first="34" second="103" amount="-2" />
kerning first=68 second=84 amount=-1 <kerning first="70" second="103" amount="-1" />
kerning first=89 second=110 amount=-1 <kerning first="112" second="39" amount="-1" />
kerning first=34 second=100 amount=-2 <kerning first="122" second="113" amount="-1" />
kerning first=68 second=86 amount=-1 <kerning first="86" second="113" amount="-2" />
kerning first=87 second=45 amount=-2 <kerning first="68" second="84" amount="-1" />
kerning first=39 second=34 amount=-4 <kerning first="89" second="110" amount="-1" />
kerning first=114 second=100 amount=-1 <kerning first="34" second="100" amount="-2" />
kerning first=84 second=81 amount=-1 <kerning first="68" second="86" amount="-1" />
kerning first=70 second=101 amount=-1 <kerning first="87" second="45" amount="-2" />
kerning first=68 second=89 amount=-2 <kerning first="39" second="34" amount="-4" />
kerning first=88 second=117 amount=-1 <kerning first="114" second="100" amount="-1" />
kerning first=112 second=34 amount=-1 <kerning first="84" second="81" amount="-1" />
kerning first=76 second=67 amount=-2 <kerning first="70" second="101" amount="-1" />
kerning first=76 second=34 amount=-5 <kerning first="68" second="89" amount="-2" />
kerning first=88 second=111 amount=-1 <kerning first="88" second="117" amount="-1" />
kerning first=66 second=86 amount=-1 <kerning first="112" second="34" amount="-1" />
kerning first=66 second=89 amount=-2 <kerning first="76" second="67" amount="-2" />
kerning first=122 second=101 amount=-1 <kerning first="76" second="34" amount="-5" />
kerning first=86 second=101 amount=-2 <kerning first="88" second="111" amount="-1" />
kerning first=76 second=121 amount=-5 <kerning first="66" second="86" amount="-1" />
kerning first=84 second=119 amount=-2 <kerning first="66" second="89" amount="-2" />
kerning first=84 second=112 amount=-3 <kerning first="122" second="101" amount="-1" />
kerning first=87 second=111 amount=-1 <kerning first="86" second="101" amount="-2" />
kerning first=69 second=118 amount=-1 <kerning first="76" second="121" amount="-5" />
kerning first=65 second=117 amount=-2 <kerning first="84" second="119" amount="-2" />
kerning first=65 second=89 amount=-9 <kerning first="84" second="112" amount="-3" />
kerning first=72 second=89 amount=-1 <kerning first="87" second="111" amount="-1" />
kerning first=119 second=44 amount=-4 <kerning first="69" second="118" amount="-1" />
kerning first=69 second=121 amount=-1 <kerning first="65" second="117" amount="-2" />
kerning first=84 second=109 amount=-3 <kerning first="65" second="89" amount="-9" />
kerning first=84 second=122 amount=-2 <kerning first="72" second="89" amount="-1" />
kerning first=89 second=99 amount=-2 <kerning first="119" second="44" amount="-4" />
kerning first=76 second=118 amount=-5 <kerning first="69" second="121" amount="-1" />
kerning first=90 second=99 amount=-1 <kerning first="84" second="109" amount="-3" />
kerning first=90 second=103 amount=-1 <kerning first="84" second="122" amount="-2" />
kerning first=79 second=89 amount=-2 <kerning first="89" second="99" amount="-2" />
kerning first=90 second=79 amount=-1 <kerning first="76" second="118" amount="-5" />
kerning first=84 second=115 amount=-4 <kerning first="90" second="99" amount="-1" />
kerning first=76 second=65 amount=1 <kerning first="90" second="103" amount="-1" />
kerning first=90 second=100 amount=-1 <kerning first="79" second="89" amount="-2" />
kerning first=118 second=46 amount=-4 <kerning first="90" second="79" amount="-1" />
kerning first=87 second=117 amount=-1 <kerning first="84" second="115" amount="-4" />
kerning first=118 second=34 amount=1 <kerning first="76" second="65" amount="1" />
kerning first=69 second=103 amount=-1 <kerning first="90" second="100" amount="-1" />
kerning first=97 second=121 amount=-1 <kerning first="118" second="46" amount="-4" />
kerning first=39 second=111 amount=-2 <kerning first="87" second="117" amount="-1" />
kerning first=72 second=88 amount=1 <kerning first="118" second="34" amount="1" />
kerning first=76 second=87 amount=-5 <kerning first="69" second="103" amount="-1" />
kerning first=69 second=119 amount=-1 <kerning first="97" second="121" amount="-1" />
kerning first=121 second=97 amount=-1 <kerning first="39" second="111" amount="-2" />
kerning first=75 second=45 amount=-8 <kerning first="72" second="88" amount="1" />
kerning first=65 second=86 amount=-9 <kerning first="76" second="87" amount="-5" />
kerning first=46 second=34 amount=-6 <kerning first="69" second="119" amount="-1" />
kerning first=76 second=84 amount=-10 <kerning first="121" second="97" amount="-1" />
kerning first=116 second=111 amount=-1 <kerning first="75" second="45" amount="-8" />
kerning first=87 second=113 amount=-1 <kerning first="65" second="86" amount="-9" />
kerning first=69 second=100 amount=-1 <kerning first="46" second="34" amount="-6" />
kerning first=97 second=118 amount=-1 <kerning first="76" second="84" amount="-10" />
kerning first=65 second=85 amount=-2 <kerning first="116" second="111" amount="-1" />
kerning first=90 second=71 amount=-1 <kerning first="87" second="113" amount="-1" />
kerning first=68 second=46 amount=-4 <kerning first="69" second="100" amount="-1" />
kerning first=65 second=79 amount=-3 <kerning first="97" second="118" amount="-1" />
kerning first=98 second=122 amount=-1 <kerning first="65" second="85" amount="-2" />
kerning first=86 second=41 amount=1 <kerning first="90" second="71" amount="-1" />
kerning first=84 second=118 amount=-3 <kerning first="68" second="46" amount="-4" />
kerning first=70 second=118 amount=-1 <kerning first="65" second="79" amount="-3" />
kerning first=121 second=111 amount=-1 <kerning first="98" second="122" amount="-1" />
kerning first=81 second=87 amount=-1 <kerning first="86" second="41" amount="1" />
kerning first=70 second=100 amount=-1 <kerning first="84" second="118" amount="-3" />
kerning first=102 second=93 amount=1 <kerning first="70" second="118" amount="-1" />
kerning first=114 second=101 amount=-1 <kerning first="121" second="111" amount="-1" />
kerning first=88 second=45 amount=-2 <kerning first="81" second="87" amount="-1" />
kerning first=39 second=103 amount=-2 <kerning first="70" second="100" amount="-1" />
kerning first=75 second=103 amount=-1 <kerning first="102" second="93" amount="1" />
kerning first=88 second=101 amount=-1 <kerning first="114" second="101" amount="-1" />
kerning first=89 second=103 amount=-2 <kerning first="88" second="45" amount="-2" />
kerning first=110 second=39 amount=-1 <kerning first="39" second="103" amount="-2" />
kerning first=89 second=89 amount=1 <kerning first="75" second="103" amount="-1" />
kerning first=87 second=65 amount=-2 <kerning first="88" second="101" amount="-1" />
kerning first=119 second=46 amount=-4 <kerning first="89" second="103" amount="-2" />
kerning first=34 second=34 amount=-4 <kerning first="110" second="39" amount="-1" />
kerning first=88 second=79 amount=-2 <kerning first="89" second="89" amount="1" />
kerning first=79 second=86 amount=-1 <kerning first="87" second="65" amount="-2" />
kerning first=76 second=119 amount=-3 <kerning first="119" second="46" amount="-4" />
kerning first=75 second=111 amount=-1 <kerning first="34" second="34" amount="-4" />
kerning first=65 second=116 amount=-4 <kerning first="88" second="79" amount="-2" />
kerning first=86 second=65 amount=-9 <kerning first="79" second="86" amount="-1" />
kerning first=70 second=84 amount=1 <kerning first="76" second="119" amount="-3" />
kerning first=75 second=117 amount=-1 <kerning first="75" second="111" amount="-1" />
kerning first=80 second=65 amount=-9 <kerning first="65" second="116" amount="-4" />
kerning first=34 second=112 amount=-1 <kerning first="86" second="65" amount="-9" />
kerning first=102 second=99 amount=-1 <kerning first="70" second="84" amount="1" />
kerning first=118 second=97 amount=-1 <kerning first="75" second="117" amount="-1" />
kerning first=89 second=81 amount=-1 <kerning first="80" second="65" amount="-9" />
kerning first=118 second=111 amount=-1 <kerning first="34" second="112" amount="-1" />
kerning first=102 second=101 amount=-1 <kerning first="102" second="99" amount="-1" />
kerning first=114 second=44 amount=-4 <kerning first="118" second="97" amount="-1" />
kerning first=90 second=119 amount=-1 <kerning first="89" second="81" amount="-1" />
kerning first=75 second=81 amount=-4 <kerning first="118" second="111" amount="-1" />
kerning first=88 second=121 amount=-1 <kerning first="102" second="101" amount="-1" />
kerning first=34 second=110 amount=-1 <kerning first="114" second="44" amount="-4" />
kerning first=86 second=100 amount=-2 <kerning first="90" second="119" amount="-1" />
kerning first=122 second=100 amount=-1 <kerning first="75" second="81" amount="-4" />
kerning first=89 second=67 amount=-1 <kerning first="88" second="121" amount="-1" />
kerning first=90 second=118 amount=-1 <kerning first="34" second="110" amount="-1" />
kerning first=84 second=84 amount=1 <kerning first="86" second="100" amount="-2" />
kerning first=121 second=34 amount=1 <kerning first="122" second="100" amount="-1" />
kerning first=91 second=74 amount=-1 <kerning first="89" second="67" amount="-1" />
kerning first=88 second=113 amount=-1 <kerning first="90" second="118" amount="-1" />
kerning first=77 second=88 amount=1 <kerning first="84" second="84" amount="1" />
kerning first=75 second=119 amount=-2 <kerning first="121" second="34" amount="1" />
kerning first=114 second=104 amount=-1 <kerning first="91" second="74" amount="-1" />
kerning first=68 second=88 amount=-2 <kerning first="88" second="113" amount="-1" />
kerning first=121 second=44 amount=-4 <kerning first="77" second="88" amount="1" />
kerning first=81 second=89 amount=-1 <kerning first="75" second="119" amount="-2" />
kerning first=102 second=39 amount=1 <kerning first="114" second="104" amount="-1" />
kerning first=74 second=65 amount=-2 <kerning first="68" second="88" amount="-2" />
kerning first=114 second=118 amount=1 <kerning first="121" second="44" amount="-4" />
kerning first=84 second=46 amount=-8 <kerning first="81" second="89" amount="-1" />
kerning first=111 second=34 amount=-1 <kerning first="102" second="39" amount="1" />
kerning first=88 second=71 amount=-2 <kerning first="74" second="65" amount="-2" />
kerning first=88 second=99 amount=-1 <kerning first="114" second="118" amount="1" />
kerning first=84 second=74 amount=-8 <kerning first="84" second="46" amount="-8" />
kerning first=39 second=109 amount=-1 <kerning first="111" second="34" amount="-1" />
kerning first=98 second=34 amount=-1 <kerning first="88" second="71" amount="-2" />
kerning first=86 second=114 amount=-1 <kerning first="88" second="99" amount="-1" />
kerning first=88 second=81 amount=-2 <kerning first="84" second="74" amount="-8" />
kerning first=70 second=74 amount=-11 <kerning first="39" second="109" amount="-1" />
kerning first=89 second=83 amount=-1 <kerning first="98" second="34" amount="-1" />
kerning first=87 second=41 amount=1 <kerning first="86" second="114" amount="-1" />
kerning first=89 second=97 amount=-3 <kerning first="88" second="81" amount="-2" />
kerning first=89 second=87 amount=1 <kerning first="70" second="74" amount="-11" />
kerning first=67 second=125 amount=-1 <kerning first="89" second="83" amount="-1" />
kerning first=89 second=93 amount=1 <kerning first="87" second="41" amount="1" />
kerning first=80 second=118 amount=1 <kerning first="89" second="97" amount="-3" />
kerning first=107 second=100 amount=-1 <kerning first="89" second="87" amount="1" />
kerning first=114 second=34 amount=1 <kerning first="67" second="125" amount="-1" />
kerning first=89 second=109 amount=-1 <kerning first="89" second="93" amount="1" />
kerning first=89 second=45 amount=-2 <kerning first="80" second="118" amount="1" />
kerning first=70 second=44 amount=-8 <kerning first="107" second="100" amount="-1" />
kerning first=34 second=39 amount=-4 <kerning first="114" second="34" amount="1" />
kerning first=88 second=67 amount=-2 <kerning first="89" second="109" amount="-1" />
kerning first=70 second=46 amount=-8 <kerning first="89" second="45" amount="-2" />
kerning first=102 second=41 amount=1 <kerning first="70" second="44" amount="-8" />
kerning first=89 second=117 amount=-1 <kerning first="34" second="39" amount="-4" />
kerning first=89 second=111 amount=-4 <kerning first="88" second="67" amount="-2" />
kerning first=89 second=115 amount=-4 <kerning first="70" second="46" amount="-8" />
kerning first=114 second=102 amount=1 <kerning first="102" second="41" amount="1" />
kerning first=89 second=125 amount=1 <kerning first="89" second="117" amount="-1" />
kerning first=89 second=121 amount=-1 <kerning first="89" second="111" amount="-4" />
kerning first=114 second=108 amount=-1 <kerning first="89" second="115" amount="-4" />
kerning first=47 second=47 amount=-8 <kerning first="114" second="102" amount="1" />
kerning first=65 second=63 amount=-2 <kerning first="89" second="125" amount="1" />
kerning first=75 second=67 amount=-4 <kerning first="89" second="121" amount="-1" />
kerning first=87 second=100 amount=-1 <kerning first="114" second="108" amount="-1" />
kerning first=111 second=104 amount=-1 <kerning first="47" second="47" amount="-8" />
kerning first=111 second=107 amount=-1 <kerning first="65" second="63" amount="-2" />
kerning first=75 second=109 amount=-1 <kerning first="75" second="67" amount="-4" />
kerning first=87 second=114 amount=-1 <kerning first="87" second="100" amount="-1" />
kerning first=111 second=120 amount=-1 <kerning first="111" second="104" amount="-1" />
kerning first=69 second=99 amount=-1 <kerning first="111" second="107" amount="-1" />
kerning first=65 second=84 amount=-6 <kerning first="75" second="109" amount="-1" />
kerning first=39 second=97 amount=-2 <kerning first="87" second="114" amount="-1" />
kerning first=121 second=46 amount=-4 <kerning first="111" second="120" amount="-1" />
kerning first=89 second=85 amount=-3 <kerning first="69" second="99" amount="-1" />
kerning first=75 second=79 amount=-4 <kerning first="65" second="84" amount="-6" />
kerning first=107 second=99 amount=-1 <kerning first="39" second="97" amount="-2" />
kerning first=102 second=100 amount=-1 <kerning first="121" second="46" amount="-4" />
kerning first=102 second=103 amount=-1 <kerning first="89" second="85" amount="-3" />
kerning first=75 second=110 amount=-1 <kerning first="75" second="79" amount="-4" />
kerning first=39 second=110 amount=-1 <kerning first="107" second="99" amount="-1" />
kerning first=69 second=84 amount=1 <kerning first="102" second="100" amount="-1" />
kerning first=84 second=111 amount=-3 <kerning first="102" second="103" amount="-1" />
kerning first=120 second=111 amount=-1 <kerning first="75" second="110" amount="-1" />
kerning first=84 second=114 amount=-3 <kerning first="39" second="110" amount="-1" />
kerning first=112 second=120 amount=-1 <kerning first="69" second="84" amount="1" />
kerning first=79 second=84 amount=-1 <kerning first="84" second="111" amount="-3" />
kerning first=84 second=117 amount=-3 <kerning first="120" second="111" amount="-1" />
kerning first=89 second=79 amount=-1 <kerning first="84" second="114" amount="-3" />
kerning first=75 second=113 amount=-1 <kerning first="112" second="120" amount="-1" />
kerning first=39 second=113 amount=-2 <kerning first="79" second="84" amount="-1" />
kerning first=80 second=44 amount=-11 <kerning first="84" second="117" amount="-3" />
kerning first=79 second=88 amount=-2 <kerning first="89" second="79" amount="-1" />
kerning first=98 second=39 amount=-1 <kerning first="75" second="113" amount="-1" />
kerning first=65 second=118 amount=-4 <kerning first="39" second="113" amount="-2" />
kerning first=65 second=34 amount=-4 <kerning first="80" second="44" amount="-11" />
kerning first=88 second=103 amount=-1 <kerning first="79" second="88" amount="-2" />
kerning first=77 second=89 amount=-1 <kerning first="98" second="39" amount="-1" />
kerning first=39 second=101 amount=-2 <kerning first="65" second="118" amount="-4" />
kerning first=75 second=101 amount=-1 <kerning first="65" second="34" amount="-4" />
kerning first=88 second=100 amount=-1 <kerning first="88" second="103" amount="-1" />
kerning first=78 second=65 amount=-3 <kerning first="77" second="89" amount="-1" />
kerning first=87 second=44 amount=-4 <kerning first="39" second="101" amount="-2" />
kerning first=67 second=41 amount=-1 <kerning first="75" second="101" amount="-1" />
kerning first=86 second=93 amount=1 <kerning first="88" second="100" amount="-1" />
kerning first=84 second=83 amount=-1 <kerning first="78" second="65" amount="-3" />
kerning first=102 second=113 amount=-1 <kerning first="87" second="44" amount="-4" />
kerning first=34 second=111 amount=-2 <kerning first="67" second="41" amount="-1" />
kerning first=70 second=111 amount=-1 <kerning first="86" second="93" amount="1" />
kerning first=86 second=99 amount=-2 <kerning first="84" second="83" amount="-1" />
kerning first=84 second=86 amount=1 <kerning first="102" second="113" amount="-1" />
kerning first=122 second=99 amount=-1 <kerning first="34" second="111" amount="-2" />
kerning first=84 second=89 amount=1 <kerning first="70" second="111" amount="-1" />
kerning first=70 second=114 amount=-1 <kerning first="86" second="99" amount="-2" />
kerning first=86 second=74 amount=-8 <kerning first="84" second="86" amount="1" />
kerning first=89 second=38 amount=-1 <kerning first="122" second="99" amount="-1" />
kerning first=87 second=97 amount=-1 <kerning first="84" second="89" amount="1" />
kerning first=76 second=86 amount=-9 <kerning first="70" second="114" amount="-1" />
kerning first=40 second=86 amount=1 <kerning first="86" second="74" amount="-8" />
kerning first=90 second=113 amount=-1 <kerning first="89" second="38" amount="-1" />
kerning first=39 second=39 amount=-4 <kerning first="87" second="97" amount="-1" />
kerning first=111 second=39 amount=-1 <kerning first="76" second="86" amount="-9" />
kerning first=90 second=117 amount=-1 <kerning first="40" second="86" amount="1" />
kerning first=89 second=41 amount=1 <kerning first="90" second="113" amount="-1" />
kerning first=65 second=121 amount=-4 <kerning first="39" second="39" amount="-4" />
kerning first=89 second=100 amount=-2 <kerning first="111" second="39" amount="-1" />
kerning first=89 second=42 amount=-2 <kerning first="90" second="117" amount="-1" />
kerning first=76 second=117 amount=-2 <kerning first="89" second="41" amount="1" />
kerning first=69 second=111 amount=-1 <kerning first="65" second="121" amount="-4" />
kerning first=46 second=39 amount=-6 <kerning first="89" second="100" amount="-2" />
kerning first=118 second=39 amount=1 <kerning first="89" second="42" amount="-2" />
kerning first=91 second=85 amount=-1 <kerning first="76" second="117" amount="-2" />
kerning first=80 second=90 amount=-1 <kerning first="69" second="111" amount="-1" />
kerning first=90 second=81 amount=-1 <kerning first="46" second="39" amount="-6" />
kerning first=69 second=117 amount=-1 <kerning first="118" second="39" amount="1" />
kerning first=76 second=39 amount=-5 <kerning first="91" second="85" amount="-1" />
kerning first=90 second=67 amount=-1 <kerning first="80" second="90" amount="-1" />
kerning first=87 second=103 amount=-1 <kerning first="90" second="81" amount="-1" />
kerning first=84 second=120 amount=-3 <kerning first="69" second="117" amount="-1" />
kerning first=89 second=101 amount=-2 <kerning first="76" second="39" amount="-5" />
kerning first=102 second=125 amount=1 <kerning first="90" second="67" amount="-1" />
kerning first=76 second=85 amount=-2 <kerning first="87" second="103" amount="-1" />
kerning first=79 second=65 amount=-3 <kerning first="84" second="120" amount="-3" />
kerning first=65 second=71 amount=-3 <kerning first="89" second="101" amount="-2" />
kerning first=79 second=44 amount=-4 <kerning first="102" second="125" amount="1" />
kerning first=97 second=39 amount=-2 <kerning first="76" second="85" amount="-2" />
kerning first=90 second=101 amount=-1 <kerning first="79" second="65" amount="-3" />
kerning first=65 second=87 amount=-5 <kerning first="65" second="71" amount="-3" />
kerning first=79 second=46 amount=-4 <kerning first="79" second="44" amount="-4" />
kerning first=87 second=99 amount=-1 <kerning first="97" second="39" amount="-2" />
kerning first=34 second=101 amount=-2 <kerning first="90" second="101" amount="-1" />
kerning first=40 second=89 amount=1 <kerning first="65" second="87" amount="-5" />
kerning first=76 second=89 amount=-8 <kerning first="79" second="46" amount="-4" />
kerning first=69 second=113 amount=-1 <kerning first="87" second="99" amount="-1" />
kerning first=120 second=103 amount=-1 <kerning first="34" second="101" amount="-2" />
kerning first=69 second=101 amount=-1 <kerning first="40" second="89" amount="1" />
kerning first=69 second=102 amount=-1 <kerning first="76" second="89" amount="-8" />
kerning first=104 second=39 amount=-1 <kerning first="69" second="113" amount="-1" />
kerning first=80 second=121 amount=1 <kerning first="120" second="103" amount="-1" />
kerning first=86 second=46 amount=-8 <kerning first="69" second="101" amount="-1" />
kerning first=65 second=81 amount=-3 <kerning first="69" second="102" amount="-1" />
kerning first=86 second=44 amount=-8 <kerning first="104" second="39" amount="-1" />
kerning first=120 second=99 amount=-1 <kerning first="80" second="121" amount="1" />
kerning first=98 second=120 amount=-1 <kerning first="86" second="46" amount="-8" />
kerning first=39 second=115 amount=-3 <kerning first="65" second="81" amount="-3" />
kerning first=121 second=39 amount=1 <kerning first="86" second="44" amount="-8" />
kerning first=88 second=118 amount=-1 <kerning first="120" second="99" amount="-1" />
kerning first=84 second=65 amount=-6 <kerning first="98" second="120" amount="-1" />
kerning first=65 second=39 amount=-4 <kerning first="39" second="115" amount="-3" />
kerning first=84 second=79 amount=-1 <kerning first="121" second="39" amount="1" />
kerning first=65 second=119 amount=-4 <kerning first="88" second="118" amount="-1" />
kerning first=70 second=117 amount=-1 <kerning first="84" second="65" amount="-6" />
kerning first=75 second=100 amount=-1 <kerning first="65" second="39" amount="-4" />
kerning first=86 second=111 amount=-2 <kerning first="84" second="79" amount="-1" />
kerning first=122 second=111 amount=-1 <kerning first="65" second="119" amount="-4" />
kerning first=81 second=84 amount=-2 <kerning first="70" second="117" amount="-1" />
kerning first=107 second=103 amount=-1 <kerning first="75" second="100" amount="-1" />
kerning first=118 second=44 amount=-4 <kerning first="86" second="111" amount="-2" />
kerning first=87 second=46 amount=-4 <kerning first="122" second="111" amount="-1" />
kerning first=87 second=101 amount=-1 <kerning first="81" second="84" amount="-2" />
kerning first=70 second=79 amount=-2 <kerning first="107" second="103" amount="-1" />
kerning first=87 second=74 amount=-2 <kerning first="118" second="44" amount="-4" />
kerning first=123 second=74 amount=-1 <kerning first="87" second="46" amount="-4" />
kerning first=76 second=71 amount=-2 <kerning first="87" second="101" amount="-1" />
kerning first=39 second=100 amount=-2 <kerning first="70" second="79" amount="-2" />
kerning first=80 second=88 amount=-1 <kerning first="87" second="74" amount="-2" />
kerning first=84 second=121 amount=-3 <kerning first="123" second="74" amount="-1" />
kerning first=112 second=122 amount=-1 <kerning first="76" second="71" amount="-2" />
kerning first=84 second=71 amount=-1 <kerning first="39" second="100" amount="-2" />
kerning first=89 second=86 amount=1 <kerning first="80" second="88" amount="-1" />
kerning first=84 second=113 amount=-3 <kerning first="84" second="121" amount="-3" />
kerning first=120 second=113 amount=-1 <kerning first="112" second="122" amount="-1" />
kerning first=89 second=44 amount=-7 <kerning first="84" second="71" amount="-1" />
kerning first=84 second=99 amount=-3 <kerning first="89" second="86" amount="1" />
kerning first=34 second=113 amount=-2 <kerning first="84" second="113" amount="-3" />
kerning first=80 second=46 amount=-11 <kerning first="120" second="113" amount="-1" />
kerning first=86 second=117 amount=-1 <kerning first="89" second="44" amount="-7" />
kerning first=110 second=34 amount=-1 <kerning first="84" second="99" amount="-3" />
kerning first=80 second=74 amount=-7 <kerning first="34" second="113" amount="-2" />
kerning first=120 second=101 amount=-1 <kerning first="80" second="46" amount="-11" />
kerning first=73 second=88 amount=1 <kerning first="86" second="117" amount="-1" />
kerning first=108 second=111 amount=-1 <kerning first="110" second="34" amount="-1" />
kerning first=34 second=115 amount=-3 <kerning first="80" second="74" amount="-7" />
kerning first=89 second=113 amount=-2 <kerning first="120" second="101" amount="-1" />
kerning first=82 second=86 amount=-3 <kerning first="73" second="88" amount="1" />
kerning first=114 second=39 amount=1 <kerning first="108" second="111" amount="-1" />
kerning first=34 second=109 amount=-1 <kerning first="34" second="115" amount="-3" />
kerning first=84 second=101 amount=-3 <kerning first="89" second="113" amount="-2" />
kerning first=70 second=121 amount=-1 <kerning first="82" second="86" amount="-3" />
kerning first=123 second=85 amount=-1 <kerning first="114" second="39" amount="1" />
kerning first=122 second=103 amount=-1 <kerning first="34" second="109" amount="-1" />
kerning first=86 second=97 amount=-2 <kerning first="84" second="101" amount="-3" />
kerning first=82 second=89 amount=-4 <kerning first="70" second="121" amount="-1" />
kerning first=66 second=84 amount=-1 <kerning first="123" second="85" amount="-1" />
kerning first=84 second=97 amount=-4 <kerning first="122" second="103" amount="-1" />
kerning first=86 second=103 amount=-2 <kerning first="86" second="97" amount="-2" />
kerning first=70 second=113 amount=-1 <kerning first="82" second="89" amount="-4" />
kerning first=84 second=87 amount=1 <kerning first="66" second="84" amount="-1" />
kerning first=75 second=112 amount=-1 <kerning first="84" second="97" amount="-4" />
kerning first=114 second=111 amount=-1 <kerning first="86" second="103" amount="-2" />
kerning first=39 second=112 amount=-1 <kerning first="70" second="113" amount="-1" />
kerning first=107 second=101 amount=-1 <kerning first="84" second="87" amount="1" />
kerning first=82 second=84 amount=-3 <kerning first="75" second="112" amount="-1" />
kerning first=114 second=121 amount=1 <kerning first="114" second="111" amount="-1" />
kerning first=34 second=99 amount=-2 <kerning first="39" second="112" amount="-1" />
kerning first=70 second=81 amount=-2 <kerning first="107" second="101" amount="-1" />
kerning first=111 second=122 amount=-1 <kerning first="82" second="84" amount="-3" />
kerning first=84 second=67 amount=-1 <kerning first="114" second="121" amount="1" />
kerning first=111 second=108 amount=-1 <kerning first="34" second="99" amount="-2" />
kerning first=89 second=84 amount=1 <kerning first="70" second="81" amount="-2" />
kerning first=76 second=79 amount=-2 <kerning first="111" second="122" amount="-1" />
kerning first=85 second=65 amount=-2 <kerning first="84" second="67" amount="-1" />
kerning first=44 second=34 amount=-6 <kerning first="111" second="108" amount="-1" />
kerning first=65 second=67 amount=-3 <kerning first="89" second="84" amount="1" />
kerning first=109 second=34 amount=-1 <kerning first="76" second="79" amount="-2" />
kerning first=114 second=103 amount=-1 <kerning first="85" second="65" amount="-2" />
kerning first=78 second=89 amount=-1 <kerning first="44" second="34" amount="-6" />
kerning first=89 second=114 amount=-1 <kerning first="65" second="67" amount="-3" />
kerning first=89 second=112 amount=-1 <kerning first="109" second="34" amount="-1" />
kerning first=34 second=65 amount=-4 <kerning first="114" second="103" amount="-1" />
kerning first=70 second=65 amount=-11 <kerning first="78" second="89" amount="-1" />
kerning first=81 second=86 amount=-1 <kerning first="89" second="114" amount="-1" />
kerning first=114 second=119 amount=1 <kerning first="89" second="112" amount="-1" />
kerning first=89 second=102 amount=-1 <kerning first="34" second="65" amount="-4" />
kerning first=84 second=45 amount=-8 <kerning first="70" second="65" amount="-11" />
kerning first=86 second=125 amount=1 <kerning first="81" second="86" amount="-1" />
kerning first=70 second=67 amount=-2 <kerning first="114" second="119" amount="1" />
kerning first=89 second=116 amount=-1 <kerning first="89" second="102" amount="-1" />
kerning first=102 second=34 amount=1 <kerning first="84" second="45" amount="-8" />
kerning first=114 second=99 amount=-1 <kerning first="86" second="125" amount="1" />
kerning first=67 second=84 amount=-1 <kerning first="70" second="67" amount="-2" />
kerning first=114 second=113 amount=-1 <kerning first="89" second="116" amount="-1" />
kerning first=89 second=122 amount=-1 <kerning first="102" second="34" amount="1" />
kerning first=89 second=118 amount=-1 <kerning first="114" second="99" amount="-1" />
kerning first=70 second=71 amount=-2 <kerning first="67" second="84" amount="-1" />
kerning first=114 second=107 amount=-1 <kerning first="114" second="113" amount="-1" />
kerning first=89 second=120 amount=-1 <kerning first="89" second="122" amount="-1" />
<kerning first="89" second="118" amount="-1" />
<kerning first="70" second="71" amount="-2" />
<kerning first="114" second="107" amount="-1" />
<kerning first="89" second="120" amount="-1" />
</kernings>
</font>

View File

@ -151,8 +151,26 @@ class InputWaiter {
// Event handlers // Event handlers
EditorView.domEventHandlers({ EditorView.domEventHandlers({
paste(event, view) { paste(event, view) {
const clipboardData = event.clipboardData;
const items = clipboardData.items;
let files = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.kind === "string") {
// If there are any string items they should be preferred over
// files.
files = [];
break;
} else if (item.kind === "file") {
files.push(item.getAsFile());
}
}
if (files.length > 0) {
// Prevent the default paste behavior, afterPaste will load the files instead
event.preventDefault();
}
setTimeout(() => { setTimeout(() => {
self.afterPaste(event); self.afterPaste(files);
}); });
} }
}) })
@ -914,9 +932,12 @@ class InputWaiter {
* Handler that fires just after input paste events. * Handler that fires just after input paste events.
* Checks whether the EOL separator or character encoding should be updated. * Checks whether the EOL separator or character encoding should be updated.
* *
* @param {event} e * @param {File[]} files - An array of any files that were included in the paste event
*/ */
afterPaste(e) { afterPaste(files) {
if (files.length > 0) {
this.loadUIFiles(files);
}
// If EOL has been fixed, skip this. // If EOL has been fixed, skip this.
if (this.eolState > 1) return; if (this.eolState > 1) return;

View File

@ -417,6 +417,8 @@ class RecipeWaiter {
el.classList.add("flow-control-op"); el.classList.add("flow-control-op");
} }
$(el).find("[data-toggle='tooltip']").tooltip();
// Disable auto-bake if this is a manual op // Disable auto-bake if this is a manual op
if (op.manualBake && this.app.autoBake_) { if (op.manualBake && this.app.autoBake_) {
this.manager.controls.setAutoBake(false); this.manager.controls.setAutoBake(false);
@ -442,8 +444,6 @@ class RecipeWaiter {
this.buildRecipeOperation(item); this.buildRecipeOperation(item);
document.getElementById("rec-list").appendChild(item); document.getElementById("rec-list").appendChild(item);
$(item).find("[data-toggle='tooltip']").tooltip();
item.dispatchEvent(this.manager.operationadd); item.dispatchEvent(this.manager.operationadd);
return item; return item;
} }

View File

@ -7,6 +7,8 @@
*/ */
import Dish from "../../core/Dish.mjs"; import Dish from "../../core/Dish.mjs";
import DishError from "../../core/errors/DishError.mjs";
import { CHR_ENC_SIMPLE_REVERSE_LOOKUP } from "../../core/lib/ChrEnc.mjs";
import Utils from "../../core/Utils.mjs"; import Utils from "../../core/Utils.mjs";
import cptable from "codepage"; import cptable from "codepage";
import loglevelMessagePrefix from "loglevel-message-prefix"; import loglevelMessagePrefix from "loglevel-message-prefix";
@ -98,7 +100,7 @@ async function bufferToStr(data) {
try { try {
str = cptable.utils.decode(data.encoding, new Uint8Array(data.buffer)); str = cptable.utils.decode(data.encoding, new Uint8Array(data.buffer));
} catch (err) { } catch (err) {
str = err; str = new DishError(`Error decoding buffer with encoding ${CHR_ENC_SIMPLE_REVERSE_LOOKUP[data.encoding]}: ${err.message}`).toString();
} }
} }

View File

@ -34,7 +34,14 @@ module.exports = {
testOp(browser, "AES Encrypt", "test input", "e42eb8fbfb7a98fff061cd2c1a794d92", [{"option": "Hex", "string": "00112233445566778899aabbccddeeff"}, {"option": "Hex", "string": "00000000000000000000000000000000"}, "CBC", "Raw", "Hex"]); testOp(browser, "AES Encrypt", "test input", "e42eb8fbfb7a98fff061cd2c1a794d92", [{"option": "Hex", "string": "00112233445566778899aabbccddeeff"}, {"option": "Hex", "string": "00000000000000000000000000000000"}, "CBC", "Raw", "Hex"]);
testOp(browser, "AND", "test input", "4$04 $044", [{ "option": "Hex", "string": "34" }]); testOp(browser, "AND", "test input", "4$04 $044", [{ "option": "Hex", "string": "34" }]);
testOp(browser, "Add line numbers", "test input", "1 test input"); testOp(browser, "Add line numbers", "test input", "1 test input");
testOp(browser, ["From Hex", "Add Text To Image", "To Base64"], Images.PNG_HEX, Images.PNG_CHEF_B64, [[], ["Chef", "Center", "Middle", 0, 0, 16], []]); testOp(browser, ["From Hex", "Add Text To Image", "SHA2"], Images.PNG_HEX, "50cdf8ea483c55564a091650c2bccb4586f919b721e5fe9d6a61660505b4346d6ebdb2ef0cf075a7728cd84cb26ea3e477b5bd86a94a49a27d79423994afb60a", [[], ["Chef", "Center", "Middle", 0, 0, 16, "Roboto"], []]);
testOp(browser, ["From Hex", "Add Text To Image", "SHA2"], Images.PNG_HEX, "78b3055463d9167dd039e47f451acaf06c593d209f8e405b4e18011cdcf190dc0af5952be887d93c0ebd38738e978120c1294c71104e6b00d3f9de8d6320ec1c", [[], ["Chef", "Center", "Middle", 0, 0, 16, "Roboto Black"], []]);
testOp(browser, ["From Hex", "Add Text To Image", "SHA2"], Images.PNG_HEX, "4ab4d4b6cb22ad700f6cd144c2c8ecad2a094f21a1d1d5d48eb6c8f97417192f89b4512f6a78276d49668ebef5e89c3a4d14860cb79399a0dafce98c92209e07", [[], ["Chef", "Center", "Middle", 0, 0, 16, "Roboto Mono"], []]);
testOp(browser, ["From Hex", "Add Text To Image", "SHA2"], Images.PNG_HEX, "11490db4907516b4d9e256da1ac0b02b561fa7547971e6316a8a0b90c9c66585a11f3145672c6d972b1a221d3bfad9c8a97de7ff77fd9442ebc40f39c1ef9ef7", [[], ["Chef", "Center", "Middle", 0, 0, 16, "Roboto Slab"], []]);
testOp(browser, ["From Hex", "Dither Image", "SHA2"], Images.PNG_HEX, "cbf587a78915cfb14546ba83080b13e5054800802488dd0cb786b8951e7dc0b48f055260917bd0ccfc075e422b9d6aff112948562653995d74e70f0b66367ac3", [[], [], []]);
testOp(browser, ["From Hex", "Generate Image", "SHA2"], Images.PNG_HEX, "2c451762a6c9192fd31dc80765eab3f447be70ea51f6fdb6911ade4d89d4a98bd0a1ff00b08d76aac472faeceb54b66092e3f3be7bbf899bf3e55ca9c96a56aa", [[], [], []]);
testOp(browser, ["From Hex", "Image Hue/Saturation/Lightness", "SHA2"], Images.PNG_HEX, "522dfc0bbef00e05c5d6861a002039fa2952e4bbb7fe8d21d0d538ef6f9d65da82065929b4150dc5b8b49460ee6c9bef7f660b86f8d4e7442a07c61c0a152a4b", [[], [50, 50, 50], []]);
testOp(browser, ["From Hex", "Resize Image", "SHA2"], Images.PNG_HEX, "654bfbf0a0537c901459c4bc22c5fb0bacbf01af775a0733e3a1c46cda5b699bcc4ed85322d813c7bb9b245d62d64425c0766fe03d3d20bc63634e2a4df17626", [[], [64, 64], []]);
testOp(browser, "Adler-32 Checksum", "test input", "16160411"); testOp(browser, "Adler-32 Checksum", "test input", "16160411");
testOp(browser, "Affine Cipher Decode", "test input", "rcqr glnsr", [1, 2]); testOp(browser, "Affine Cipher Decode", "test input", "rcqr glnsr", [1, 2]);
testOp(browser, "Affine Cipher Encode", "test input", "gndg zoujg", [3, 1]); testOp(browser, "Affine Cipher Encode", "test input", "gndg zoujg", [3, 1]);
@ -43,6 +50,14 @@ module.exports = {
testOp(browser, "Analyse hash", "0123456789abcdef", /CRC-64/); testOp(browser, "Analyse hash", "0123456789abcdef", /CRC-64/);
testOp(browser, "Atbash Cipher", "test input", "gvhg rmkfg"); testOp(browser, "Atbash Cipher", "test input", "gvhg rmkfg");
// testOp(browser, "Avro to JSON", "test input", "test_output"); // testOp(browser, "Avro to JSON", "test input", "test_output");
testOp(browser,
[
"From Hex", "Avro to JSON"
],
"4f626a0104166176726f2e736368656d6196017b2274797065223a227265636f7264222c226e616d65223a22736d616c6c222c226669656c6473223a5b7b226e616d65223a226e616d65222c2274797065223a22737472696e67227d5d7d146176726f2e636f646563086e756c6c004e0247632e3702e5b75cdab9a62f1541020e0c6d796e616d654e0247632e3702e5b75cdab9a62f1541",
'{"name":"myname"}\n',
[[], [false]]
);
testOp(browser, "BLAKE2b", "test input", "33ebdc8f38177f3f3f334eeb117a84e11f061bbca4db6b8923e5cec85103f59f415551a5d5a933fdb6305dc7bf84671c2540b463dbfa08ee1895cfaa5bd780b5", ["512", "Hex", { "option": "UTF8", "string": "pass" }]); testOp(browser, "BLAKE2b", "test input", "33ebdc8f38177f3f3f334eeb117a84e11f061bbca4db6b8923e5cec85103f59f415551a5d5a933fdb6305dc7bf84671c2540b463dbfa08ee1895cfaa5bd780b5", ["512", "Hex", { "option": "UTF8", "string": "pass" }]);
testOp(browser, "BLAKE2s", "test input", "defe73d61dfa6e5807e4f9643e159a09ccda6be3c26dcd65f8a9bb38bfc973a7", ["256", "Hex", { "option": "UTF8", "string": "pass" }]); testOp(browser, "BLAKE2s", "test input", "defe73d61dfa6e5807e4f9643e159a09ccda6be3c26dcd65f8a9bb38bfc973a7", ["256", "Hex", { "option": "UTF8", "string": "pass" }]);
testOp(browser, "BSON deserialise", "\u0011\u0000\u0000\u0000\u0002a\u0000\u0005\u0000\u0000\u0000test\u0000\u0000", '{\u000A "a": "test"\u000A}'); testOp(browser, "BSON deserialise", "\u0011\u0000\u0000\u0000\u0002a\u0000\u0005\u0000\u0000\u0000test\u0000\u0000", '{\u000A "a": "test"\u000A}');
@ -58,7 +73,10 @@ module.exports = {
testOp(browser, "Bit shift right", "test input", ":29:\u0010478::"); testOp(browser, "Bit shift right", "test input", ":29:\u0010478::");
testOp(browser, "Blowfish Decrypt", "10884e15427dd84ec35204e9c8e921ae", "test_output", [{"option": "Hex", "string": "1234567801234567"}, {"option": "Hex", "string": "0011223344556677"}, "CBC", "Hex", "Raw"]); testOp(browser, "Blowfish Decrypt", "10884e15427dd84ec35204e9c8e921ae", "test_output", [{"option": "Hex", "string": "1234567801234567"}, {"option": "Hex", "string": "0011223344556677"}, "CBC", "Hex", "Raw"]);
testOp(browser, "Blowfish Encrypt", "test input", "f0fadbd1d90d774f714248cf26b96410", [{"option": "Hex", "string": "1234567801234567"}, {"option": "Hex", "string": "0011223344556677"}, "CBC", "Raw", "Hex"]); testOp(browser, "Blowfish Encrypt", "test input", "f0fadbd1d90d774f714248cf26b96410", [{"option": "Hex", "string": "1234567801234567"}, {"option": "Hex", "string": "0011223344556677"}, "CBC", "Raw", "Hex"]);
testOp(browser, ["From Hex", "Blur Image", "To Base64"], Images.PNG_HEX, Images.PNG_BLUR_B64); testOp(browser, ["From Hex", "Blur Image", "SHA2"], Images.PNG_HEX, "24f2e89f3e00cc35f551bbc48ea82e76474946ce0282183494d1ca3d3b0012c27b6102c4368ae056dc7fecb6df7886d86ff3d29b7e5965493f30c371eee9a24e");
testOp(browser, ["From Hex", "Blur Image", "SHA2"], Images.PNG_HEX, "2c49d89fc10c94352c9a19f82de353c37928831d6f976a6b36eb918825a0ba027980801838228a4a0da63f1886e4fa59b6666f992ad2d2b7d4622253dc034052", [[], [5, "Gaussian"], []]);
testOp(browser, ["From Hex", "Sharpen Image", "SHA2"], Images.PNG_HEX, "acc7027642c2eeb67d7356a80ed8a1bdce9adabf656ea1294e47723f506626a7aa41f1660fa844a1e1e83b17180017ab0d5bccd7f6a341692832020dc887eaa5");
testOp(browser, ["From Hex", "Contain Image", "SHA2"], Images.PNG_HEX, "cb871ad0722d487d56a2b18247b1aa30ecc244eb717e08e23a55cae78759553312dc1717196d7cb9daa04743e57c56fc3901ba92be5a68fb03c377f718e8efe7");
testOpHtml(browser, "Bombe", "XTSYN WAEUG EZALY NRQIM AMLZX MFUOD AWXLY LZCUZ QOQBQ JLCPK NDDRW F", "table tr:last-child td:first-child", "ECG", ["3-rotor", "LEYJVCNIXWPBQMDRTAKZGFUHOS", "BDFHJLCPRTXVZNYEIWGAKMUSQO<W", "AJDKSIRUXBLHWTMCQGZNPYFVOE<F", "ESOVPZJAYQUIRHXLNFTGKDCMWB<K", "AY BR CU DH EQ FS GL IP JX KN MO TZ VW", "HELLO CYBER CHEFU SER", 0, true]); testOpHtml(browser, "Bombe", "XTSYN WAEUG EZALY NRQIM AMLZX MFUOD AWXLY LZCUZ QOQBQ JLCPK NDDRW F", "table tr:last-child td:first-child", "ECG", ["3-rotor", "LEYJVCNIXWPBQMDRTAKZGFUHOS", "BDFHJLCPRTXVZNYEIWGAKMUSQO<W", "AJDKSIRUXBLHWTMCQGZNPYFVOE<F", "ESOVPZJAYQUIRHXLNFTGKDCMWB<K", "AY BR CU DH EQ FS GL IP JX KN MO TZ VW", "HELLO CYBER CHEFU SER", 0, true]);
testOp(browser, ["Bzip2 Compress", "To Hex"], "test input", "42 5a 68 39 31 41 59 26 53 59 cf 96 82 1d 00 00 03 91 80 40 00 02 21 4e 00 20 00 21 90 c2 10 c0 88 33 92 8e df 17 72 45 38 50 90 cf 96 82 1d"); testOp(browser, ["Bzip2 Compress", "To Hex"], "test input", "42 5a 68 39 31 41 59 26 53 59 cf 96 82 1d 00 00 03 91 80 40 00 02 21 4e 00 20 00 21 90 c2 10 c0 88 33 92 8e df 17 72 45 38 50 90 cf 96 82 1d");
testOp(browser, ["From Hex", "Bzip2 Decompress"], "425a68393141592653597b0884b7000003038000008200ce00200021a647a4218013709517c5dc914e14241ec2212dc0", "test_output", [[], [true]]); testOp(browser, ["From Hex", "Bzip2 Decompress"], "425a68393141592653597b0884b7000003038000008200ce00200021a647a4218013709517c5dc914e14241ec2212dc0", "test_output", [[], [true]]);
@ -196,6 +214,7 @@ module.exports = {
testOpHtml(browser, "Index of Coincidence", "test input", "", /Index of Coincidence: 0.08333333333333333/); testOpHtml(browser, "Index of Coincidence", "test input", "", /Index of Coincidence: 0.08333333333333333/);
testOpImage(browser, "Invert Image", "files/Hitchhikers_Guide.jpeg"); testOpImage(browser, "Invert Image", "files/Hitchhikers_Guide.jpeg");
// testOp(browser, "JPath expression", "test input", "test_output"); // testOp(browser, "JPath expression", "test input", "test_output");
testOp(browser, "Jq", '{"a":{"b":1}}', '{"b":1}', [".a"]);
testOpHtml(browser, "JSON Beautify", "{a:1}", ".json-dict .json-literal", "1"); testOpHtml(browser, "JSON Beautify", "{a:1}", ".json-dict .json-literal", "1");
// testOp(browser, "JSON Minify", "test input", "test_output"); // testOp(browser, "JSON Minify", "test input", "test_output");
// testOp(browser, "JSON to CSV", "test input", "test_output"); // testOp(browser, "JSON to CSV", "test input", "test_output");

View File

@ -23,6 +23,7 @@ import "./tests/Dish.mjs";
import "./tests/NodeDish.mjs"; import "./tests/NodeDish.mjs";
import "./tests/Utils.mjs"; import "./tests/Utils.mjs";
import "./tests/Categories.mjs"; import "./tests/Categories.mjs";
import "./tests/lib/BigIntUtils.mjs";
const testStatus = { const testStatus = {
allTestsPassing: true, allTestsPassing: true,

View File

@ -0,0 +1,150 @@
import TestRegister from "../../../lib/TestRegister.mjs";
import { parseBigInt, egcd, modPow } from "../../../../src/core/lib/BigIntUtils.mjs";
import it from "../../assertionHandler.mjs";
import assert from "assert";
TestRegister.addApiTests([
// ===== parseBigInt tests =====
it("BigIntUtils: parseBigInt - decimal number", () => {
const value = parseBigInt("1", "test value");
assert.deepStrictEqual(value, BigInt("1"));
}),
it("BigIntUtils: parseBigInt - large decimal", () => {
const value = parseBigInt("123456789012345678901234567890", "test value");
assert.deepStrictEqual(value, BigInt("123456789012345678901234567890"));
}),
it("BigIntUtils: parseBigInt - hexadecimal lowercase", () => {
const value = parseBigInt("0xff", "test value");
assert.deepStrictEqual(value, BigInt("255"));
}),
it("BigIntUtils: parseBigInt - hexadecimal uppercase", () => {
const value = parseBigInt("0xFF", "test value");
assert.deepStrictEqual(value, BigInt("255"));
}),
it("BigIntUtils: parseBigInt - large hexadecimal", () => {
const value = parseBigInt("0x123456789ABCDEF", "test value");
assert.deepStrictEqual(value, BigInt("0x123456789ABCDEF"));
}),
it("BigIntUtils: parseBigInt - whitespace trimming", () => {
const value = parseBigInt(" 42 ", "test value");
assert.deepStrictEqual(value, BigInt("42"));
}),
it("BigIntUtils: parseBigInt - invalid input (text)", () => {
assert.throws(() => parseBigInt("test", "test value"), {
name: "Error",
message: "test value must be decimal or hex (0x...)"
});
}),
it("BigIntUtils: parseBigInt - invalid input (hex without prefix)", () => {
assert.throws(() => parseBigInt("FF", "test value"), {
name: "Error",
message: "test value must be decimal or hex (0x...)"
});
}),
it("BigIntUtils: parseBigInt - invalid input (mixed)", () => {
assert.throws(() => parseBigInt("12abc", "test value"), {
name: "Error",
message: "test value must be decimal or hex (0x...)"
});
}),
// ===== egcd tests =====
it("BigIntUtils: egcd - basic coprime", () => {
const a = BigInt("36");
const b = BigInt("48");
const gcd = BigInt("12");
const bezout1 = BigInt("-1");
const bezout2 = BigInt("1");
assert.deepStrictEqual(egcd(a, b), [gcd, bezout1, bezout2]);
}),
it("BigIntUtils: egcd - coprime numbers", () => {
const [g, x, y] = egcd(BigInt("3"), BigInt("11"));
assert.strictEqual(g, BigInt("1"));
// Verify Bézout identity: a*x + b*y = gcd
assert.strictEqual(BigInt("3") * x + BigInt("11") * y, g);
}),
it("BigIntUtils: egcd - non-coprime numbers", () => {
const [g, x, y] = egcd(BigInt("240"), BigInt("46"));
assert.strictEqual(g, BigInt("2"));
// Verify Bézout identity
assert.strictEqual(BigInt("240") * x + BigInt("46") * y, g);
}),
it("BigIntUtils: egcd - with zero", () => {
const [g, x, y] = egcd(BigInt("17"), BigInt("0"));
assert.strictEqual(g, BigInt("17"));
assert.strictEqual(x, BigInt("1"));
assert.strictEqual(y, BigInt("0"));
}),
it("BigIntUtils: egcd - identical numbers", () => {
const [g, x, y] = egcd(BigInt("42"), BigInt("42"));
assert.strictEqual(g, BigInt("42"));
// Verify Bézout identity
assert.strictEqual(BigInt("42") * x + BigInt("42") * y, g);
}),
it("BigIntUtils: egcd - large numbers", () => {
const a = BigInt("123456789012345678901234567890");
const b = BigInt("987654321098765432109876543210");
const [g, x, y] = egcd(a, b);
// Verify Bézout identity
assert.strictEqual(a * x + b * y, g);
}),
// ===== modPow tests =====
it("BigIntUtils: modPow - basic", () => {
// 2^10 mod 1000 = 1024 mod 1000 = 24
const result = modPow(BigInt("2"), BigInt("10"), BigInt("1000"));
assert.strictEqual(result, BigInt("24"));
}),
it("BigIntUtils: modPow - RSA-like example", () => {
// Common RSA public exponent
const base = BigInt("123456789");
const exp = BigInt("65537");
const mod = BigInt("999999999999");
const result = modPow(base, exp, mod);
// Result should be less than modulus
assert(result < mod);
assert(result >= BigInt("0"));
}),
it("BigIntUtils: modPow - exponent zero", () => {
// Any number^0 = 1
const result = modPow(BigInt("999"), BigInt("0"), BigInt("100"));
assert.strictEqual(result, BigInt("1"));
}),
it("BigIntUtils: modPow - base zero", () => {
// 0^n = 0
const result = modPow(BigInt("0"), BigInt("5"), BigInt("100"));
assert.strictEqual(result, BigInt("0"));
}),
it("BigIntUtils: modPow - large exponent", () => {
// Test with very large exponent (efficient algorithm should handle this)
const result = modPow(BigInt("3"), BigInt("1000000"), BigInt("1000000007"));
assert(result >= BigInt("0"));
assert(result < BigInt("1000000007"));
}),
it("BigIntUtils: modPow - modular inverse verification", () => {
// If a*x . 1 (mod m), then modPow(a, 1, m) * x . 1 (mod m)
const a = BigInt("3");
const m = BigInt("11");
const x = BigInt("4"); // inverse of 3 mod 11
const result = modPow(a, BigInt("1"), m) * x % m;
assert.strictEqual(result, BigInt("1"));
}),
]);

View File

@ -867,13 +867,15 @@ pCGTErs=
}), }),
it("SQL Beautify", () => { it("SQL Beautify", () => {
const result = chef.SQLBeautify(`SELECT MONTH, ID, RAIN_I, TEMP_F const result = chef.SQLBeautify(`SELECT MONTH, ID, RAIN_I, TEMP_F FROM STATS;`);
FROM STATS;`); const expected =
const expected = `SELECT MONTH, `SELECT
MONTH,
ID, ID,
RAIN_I, RAIN_I,
TEMP_F TEMP_F
FROM STATS;`; FROM
STATS;`;
assert.strictEqual(result.toString(), expected); assert.strictEqual(result.toString(), expected);
}), }),

View File

@ -76,6 +76,7 @@ import "./tests/FromDecimal.mjs";
import "./tests/GenerateAllChecksums.mjs"; import "./tests/GenerateAllChecksums.mjs";
import "./tests/GenerateAllHashes.mjs"; import "./tests/GenerateAllHashes.mjs";
import "./tests/GenerateDeBruijnSequence.mjs"; import "./tests/GenerateDeBruijnSequence.mjs";
import "./tests/GenerateQRCode.mjs";
import "./tests/GetAllCasings.mjs"; import "./tests/GetAllCasings.mjs";
import "./tests/GOST.mjs"; import "./tests/GOST.mjs";
import "./tests/Gunzip.mjs"; import "./tests/Gunzip.mjs";
@ -152,6 +153,7 @@ import "./tests/SIGABA.mjs";
import "./tests/SM2.mjs"; import "./tests/SM2.mjs";
import "./tests/SM4.mjs"; import "./tests/SM4.mjs";
// import "./tests/SplitColourChannels.mjs"; // Cannot test operations that use the File type yet // import "./tests/SplitColourChannels.mjs"; // Cannot test operations that use the File type yet
import "./tests/SQLBeautify.mjs";
import "./tests/StrUtils.mjs"; import "./tests/StrUtils.mjs";
import "./tests/StripIPv4Header.mjs"; import "./tests/StripIPv4Header.mjs";
import "./tests/StripTCPHeader.mjs"; import "./tests/StripTCPHeader.mjs";

View File

@ -0,0 +1,246 @@
/**
* Flask Session tests
*
* @author ThePlayer372-FR []
*
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
const validTokenSha1 = "eyJyb2xlIjoic3VwZXJ1c2VyIiwidXNlciI6ImFkbWluIn0.aZ-KEw.E_x6bOhA4GU9t72pMinJUjN-O3I";
const validTokenSha256 = "eyJyb2xlIjoic3VwZXJ1c2VyIiwidXNlciI6ImFkbWluIn0.aab3Ew.Jsx2DOx_H9anZg0YcvhsASxQ11897EFHeQfS2oja4y8";
const validKey = "mysecretkey";
const wrongKey = "notTheKey";
const outputObject = {
user: "admin",
role: "superuser",
};
const outputVerify = {
valid: true,
payload: outputObject,
};
TestRegister.addTests([
{
name: "Flask Session: Decode",
input: validTokenSha1,
expectedOutput: outputObject,
recipeConfig: [
{
op: "Flask Session Decode",
args: [
false
],
}
]
},
{
name: "Flask Session: Verify Sha1",
input: validTokenSha1,
expectedOutput: outputVerify,
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha1",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha256",
input: validTokenSha256,
expectedOutput: outputVerify,
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha256",
false,
],
}
]
},
{
name: "Flask Session: Sign Sha1",
input: outputObject,
expectedOutput: outputVerify,
recipeConfig: [
{
op: "Flask Session Sign",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha1"
]
},
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha1",
false,
],
}
]
},
{
name: "Flask Session: Sign Sha256",
input: outputObject,
expectedOutput: outputVerify,
recipeConfig: [
{
op: "Flask Session Sign",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha256"
]
},
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha256",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha1 Wrong Key",
input: validTokenSha1,
expectedOutput: "Invalid signature!",
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: wrongKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha1",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha256 Wrong Key",
input: validTokenSha256,
expectedOutput: "Invalid signature!",
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: wrongKey,
option: "UTF8"
},
{
string: "cookie-session",
option: "UTF8"
},
"sha256",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha1 Wrong Salt",
input: validTokenSha1,
expectedOutput: "Invalid signature!",
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "notTheSalt",
option: "UTF8"
},
"sha1",
false,
],
}
]
},
{
name: "Flask Session: Verify Sha256 Wrong Salt",
input: validTokenSha256,
expectedOutput: "Invalid signature!",
recipeConfig: [
{
op: "Flask Session Verify",
args: [
{
string: validKey,
option: "UTF8"
},
{
string: "notTheSalt",
option: "UTF8"
},
"sha256",
false,
],
}
]
},
]);

View File

@ -0,0 +1,67 @@
/**
* Generate QR Code tests
*
* @author GCHQDeveloper581
* @copyright Crown Copyright 2025
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Generate QR Code : PNG",
input: "Hello world!",
expectedOutput: "89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 91 00 00 00 91 08 00 00 00 00 e6 b3 05 ff 00 00 01 1a 49 44 41 54 78 da ed da 41 12 83 20 0c 05 50 ef 7f e9 76 dd 05 f4 47 6c c4 ce 63 e5 8c 0c be 4d 24 24 1c af dd c6 41 44 44 44 44 44 44 44 44 44 f4 9f a2 e3 fb 98 cf 2b ad 42 44 d4 2a 1a 07 c3 e7 37 83 a7 d2 37 88 88 1a 44 c3 18 1a 46 e7 ca 2a 44 44 7b 88 4a f3 88 88 1e 23 9a ef 09 44 44 fb 8a 82 b7 c3 3c fe 8e 8c 8d 88 e8 b2 33 6d ba b3 f4 9d b2 89 88 16 eb 90 f3 a5 ef a8 8c 12 11 55 f3 a3 61 a2 93 e6 4c c3 45 89 88 ba 44 a5 e4 e7 64 5d 9d 88 a8 5f 14 74 82 d2 8a 64 5a b4 24 22 6a 10 a5 2d cf 79 3f e9 6c 53 89 88 e8 a7 a2 79 4f a8 b4 b3 ac 57 6b 88 88 ae 15 a5 de b9 bc 14 70 44 44 3f 13 ad d4 21 03 ea d9 6a 0d 11 d1 15 a2 e0 ff 5f 47 07 36 22 a2 06 d1 4a d2 1f 1c 94 89 88 b6 14 95 ee d5 12 11 3d 50 14 74 8c 82 70 24 22 ea 12 05 6f d3 4b 2c 4b d5 1a 22 a2 cb 44 2b 69 7d e9 5e 00 11 51 97 e8 d6 41 44 44 44 44 44 44 44 44 44 f4 7c d1 1b 1c 52 72 cb 26 c8 c7 0b 00 00 00 00 49 45 4e 44 ae 42 60 82",
recipeConfig: [
{
"op": "Generate QR Code",
"args": ["PNG", 5, 4, "Medium"]
},
{
"op": "To Hex",
"args": ["Space", 0]
}
],
},
{
name: "Generate QR Code : SVG",
input: "Hello world!",
expectedOutput: '<svg xmlns="http://www.w3.org/2000/svg" width="145" height="145" viewBox="0 0 29 29"><path d="M4 4h7v7h-7zM12 4h5v2h-1v-1h-1v1h1v1h1v1h-2v3h-1v-1h-1v-1h1v-4h-1v1h-1zM18 4h7v7h-7zM5 5v5h5v-5zM19 5v5h5v-5zM6 6h3v3h-3zM20 6h3v3h-3zM12 7h1v1h-1zM12 10h1v1h1v1h-2zM16 10h1v1h-1zM4 12h1v1h-1zM6 12h2v2h-1v1h1v-1h3v1h-1v1h-2v1h-2v-1h-1v-1h-1v-1h2zM9 12h3v2h-1v-1h-2zM14 12h1v2h-2v-1h1zM18 12h1v1h2v-1h1v1h1v1h-3v2h-1v-2h-1v1h-1v1h-1v-3h2zM23 12h2v3h-1v1h-1v-2h1v-1h-1zM12 14h1v1h-1zM11 15h1v2h-2v-1h1zM14 15h1v1h-1zM21 15h1v1h-1zM15 16h1v2h-1v1h2v-1h1v-1h-1v-1h2v1h2v2h-1v1h-1v-1h-1v1h-2v1h-1v-1h-1v1h-1v-1h-1v-3h3zM24 16h1v2h-1zM22 17h1v1h-1zM4 18h7v7h-7zM13 18v1h1v-1zM5 19v5h5v-5zM21 19h2v1h1v2h-1v1h-1v-1h-1zM6 20h3v3h-3zM12 21h1v2h-1zM14 21h1v1h-1zM16 21h1v2h-1zM18 21h1v1h-1zM19 22h1v1h1v1h-1v1h-3v-2h2zM13 23h1v1h-1zM15 23h1v2h-2v-1h1zM24 23h1v1h-1zM12 24h1v1h-1zM22 24h1v1h-1z"/></svg>',
recipeConfig: [
{
"op": "Generate QR Code",
"args": ["SVG", 5, 4, "Medium"]
},
],
},
{
name: "Generate QR Code : EPS",
input: "Hello world!",
expectedOutput: "%!PS-Adobe-3.0 EPSF-3.0%%BoundingBox: 0 0 315 315/h { 0 rlineto } bind def/v { 0 exch neg rlineto } bind def/M { neg 30 add moveto } bind def/z { closepath } bind def9 9 scale5 0 M 7 h 7 v -7 h z13 0 M 1 h 1 v -1 h z16 0 M 2 h 1 v -1 h 1 v 1 h 1 v -2 h -1 v -1 h 2 v -1 h -3 v 2 h z20 0 M 2 h 1 v -2 h z23 0 M 7 h 7 v -7 h z6 1 M 5 v 5 h -5 v z24 1 M 5 v 5 h -5 v z7 2 M 3 h 3 v -3 h z19 2 M 1 h 1 v -1 h z21 2 M 1 h 2 v -1 h z25 2 M 3 h 3 v -3 h z13 4 M 1 h 3 v -1 h z17 4 M 4 h 1 v 1 h 2 v -1 h -1 v -1 h 1 v -1 h -1 v -1 h -1 v -1 h z15 6 M 1 h 1 v -1 h z17 6 M 1 h 1 v -1 h z16 7 M 1 h 1 v -1 h z18 7 M 1 h 1 v -1 h z20 7 M 1 h 2 v 1 h 1 v -2 h -1 v -1 h -1 v 1 h z6 8 M 7 h 1 v 2 h 1 v -2 h 1 v -2 h -1 v 1 h -1 v -4 h 1 v -1 h -1 v -1 h z17 8 M 1 h 1 v -1 h z24 8 M 2 h 1 v -1 h 1 v -1 h z29 8 M 1 h 1 v -1 h z27 9 M 1 h 1 v -1 h z6 10 M 1 h 1 v 1 h 1 v -1 h 1 v -1 h z8 10 M 2 h 5 v 1 h 1 v -3 h 1 v -1 h -4 v 1 h 1 v 1 h -1 v -1 h -1 v 1 h -1 v -1 h z16 10 M 2 h 4 v 1 h -1 v 2 h 1 v 3 h -1 v -3 h -2 v 1 h 1 v 1 h -1 v 1 h 1 v 4 h -2 v 2 h 3 v -2 h 1 v -1 h -1 v -2 h 1 v 2 h 1 v -1 h 1 v 2 h 2 v 1 h -1 v 1 h 3 v -1 h -1 v -2 h -2 v -1 h 2 v 1 h 1 v 2 h 1 v -2 h 2 v -1 h -1 v -1 h -1 v -1 h 1 v -1 h -1 v -1 h 2 v -1 h -1 v -1 h -1 v 1 h -2 v -1 h -1 v -1 h 1 v 1 h 2 v -1 h -1 v -1 h 1 v -1 h -3 v 1 h -1 v -1 h 1 v -1 h -1 v -1 h -1 v 4 h 1 v 1 h -2 v -3 h -1 v -1 h 1 v -1 h -1 v -1 h 2 v -1 h 1 v -2 h -1 v 1 h -1 v -1 h -1 v 1 h -1 v 1 h -1 v 1 h 1 v 1 h -1 v 1 h 1 v 1 h -1 v -1 h z22 10 M 1 h 1 v -1 h z25 10 M 2 h 1 v -2 h z19 11 M 1 h 1 v -1 h z11 12 M 1 h 1 v -1 h z5 13 M 1 h 4 v -1 h z28 14 M 2 h 2 v -1 h -1 v -1 h z21 15 M 1 v 2 h -1 v z13 17 M 1 h 1 v 2 h 1 v -2 h 1 v 1 h 1 v 1 h 1 v -2 h 1 v 2 h 2 v -1 h -1 v -2 h z22 17 M 3 v 3 h -3 v z5 18 M 7 h 7 v -7 h z23 18 M 1 h 1 v -1 h z6 19 M 5 v 5 h -5 v z7 20 M 3 h 3 v -3 h z29 21 M 1 h 4 v -3 h -1 v 2 h z17 22 M 2 h 3 v -2 h -1 v 1 h -1 v -1 h z24 22 M 1 h 1 v 1 h 1 v -1 h 1 v -3 h -1 v 1 h -1 v 1 h z20 23 M 1 h 2 v -1 h zfill%%EOF",
recipeConfig: [
{
"op": "Generate QR Code",
"args": ["EPS", 6, 5, "Quartile"]
},
{
"op": "Remove whitespace",
"args": [false, true, true, false, false, false]
},
],
},
{
name: "Generate QR Code : PDF",
input: "Hello world!",
expectedOutput: "%PDF-1.01 0 obj << /Type /Catalog /Pages 2 0 R >> endobj2 0 obj << /Type /Pages /Count 1 /Kids [ 3 0 R ] >> endobj3 0 obj << /Type /Page /Parent 2 0 R /Resources <<>> /Contents 4 0 R /MediaBox [ 0 0 261 261 ] >> endobj4 0 obj << /Length 1837 >> stream9 0 0 9 0 0 cm4 25 m 11 25 l 11 18 l 4 18 l h12 25 m 14 25 l 14 23 l 13 23 l 13 24 l 12 24 l h16 25 m 17 25 l 17 22 l 16 22 l h18 25 m 25 25 l 25 18 l 18 18 l h5 24 m 5 19 l 10 19 l 10 24 l h19 24 m 19 19 l 24 19 l 24 24 l h6 23 m 9 23 l 9 20 l 6 20 l h12 23 m 13 23 l 13 21 l 15 21 l 15 20 l 12 20 l h14 23 m 15 23 l 15 22 l 14 22 l h20 23 m 23 23 l 23 20 l 20 20 l h15 22 m 16 22 l 16 21 l 15 21 l h12 19 m 13 19 l 13 18 l 12 18 l h14 19 m 15 19 l 15 13 l 13 13 l 13 11 l 12 11 l 12 14 l 14 14 l 14 15 l 11 15 l 11 16 l 12 16 l 12 17 l 13 17 l 13 16 l 14 16 l 14 17 l 13 17 l 13 18 l 14 18 l h16 19 m 17 19 l 17 18 l 16 18 l h4 17 m 8 17 l 8 16 l 10 16 l 10 15 l 11 15 l 11 14 l 10 14 l 10 13 l 11 13 l 11 12 l 9 12 l 9 15 l 8 15 l 8 13 l 6 13 l 6 15 l 7 15 l 7 16 l 4 16 l h10 17 m 11 17 l 11 16 l 10 16 l h17 17 m 18 17 l 18 16 l 20 16 l 20 17 l 23 17 l 23 15 l 20 15 l 20 13 l 19 13 l 19 15 l 18 15 l 18 14 l 17 14 l 17 13 l 16 13 l 16 16 l 17 16 l h24 17 m 25 17 l 25 14 l 24 14 l 24 13 l 23 13 l 23 15 l 24 15 l h21 14 m 22 14 l 22 13 l 21 13 l h15 13 m 16 13 l 16 11 l 15 11 l h17 13 m 19 13 l 19 12 l 21 12 l 21 10 l 20 10 l 20 9 l 19 9 l 19 10 l 18 10 l 18 9 l 16 9 l 16 8 l 15 8 l 15 10 l 17 10 l 17 11 l 18 11 l 18 12 l 17 12 l h24 13 m 25 13 l 25 11 l 24 11 l h22 12 m 23 12 l 23 11 l 22 11 l h4 11 m 11 11 l 11 4 l 4 4 l h14 11 m 15 11 l 15 10 l 14 10 l h5 10 m 5 5 l 10 5 l 10 10 l h13 10 m 14 10 l 14 9 l 13 9 l h21 10 m 23 10 l 23 9 l 24 9 l 24 7 l 23 7 l 23 6 l 22 6 l 22 7 l 21 7 l h6 9 m 9 9 l 9 6 l 6 6 l h12 8 m 15 8 l 15 7 l 13 7 l 13 6 l 16 6 l 16 4 l 15 4 l 15 5 l 14 5 l 14 4 l 12 4 l h16 8 m 17 8 l 17 6 l 16 6 l h18 8 m 19 8 l 19 7 l 18 7 l h19 7 m 20 7 l 20 6 l 21 6 l 21 5 l 20 5 l 20 4 l 17 4 l 17 6 l 19 6 l h24 6 m 25 6 l 25 5 l 24 5 l h22 5 m 23 5 l 23 4 l 22 4 l hfendstreamendobjxref0 50000000000 65535 f 0000000010 00000 n 0000000059 00000 n 0000000118 00000 n 0000000223 00000 n trailer << /Root 1 0 R /Size 5 >>startxref2111%%EOF",
recipeConfig: [
{
"op": "Generate QR Code",
"args": ["PDF", 5, 4, "Low"]
},
{
"op": "Remove whitespace",
"args": [false, true, true, false, false, false]
},
],
},
]);

View File

@ -43,6 +43,20 @@ TestRegister.addTests([
} }
] ]
}, },
{
name: "ASCII to Hex with percent deliminator",
input: "aberystwyth",
expectedOutput: "%61%62%65%72%79%73%74%77%79%74%68",
recipeConfig: [
{
"op": "To Hex",
"args": [
"Percent",
0
]
}
]
},
{ {
name: "ASCII to 0x Hex with comma and line breaks", name: "ASCII to 0x Hex with comma and line breaks",
input: "aberystwyth", input: "aberystwyth",

View File

@ -15,7 +15,7 @@ const inputObject = JSON.stringify({
}, null, 4); }, null, 4);
const hsKey = "secret_cat"; const hsKey = "secret_cat";
const rsKey = `-----BEGIN RSA PRIVATE KEY----- const rsKey1024 = `-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw MIICWwIBAAKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw
33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW 33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW
+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB +jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB
@ -30,11 +30,52 @@ fSSjAkLRi54PKJ8TFUeOP15h9sQzydI8zJU+upvDEKZsZc/UhT/SySDOxQ4G/523
Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aP Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aP
FaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw== FaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw==
-----END RSA PRIVATE KEY-----`; -----END RSA PRIVATE KEY-----`;
const esKey = `-----BEGIN PRIVATE KEY----- const rsKey2048 = `-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAk0VOoksAblwP82DALTG6xGC86Hfho3nChbcPGWyqn+ScfHBF
cg3SeKyy6aWCyLcKfNwE5cPYzuYvVBsZyIrdfFOuV90D/aRYbuw6UkKR3cmmy9qE
qvu05dogvc0BcmkwbC37Q8JnsZBRcosoLGgTFxcK+LXdsG7DukajpsGesxQjOLb2
1jnx+ypzx74xvj7grqlXkxeDKr22q7QkO3A1ApoOuJRAU+SjEEZmqdXzRery2RWx
hkWbCXuQw4PnW5Lh3Wwabnu7XKVIa6wJa1pqL2IAxmlZ0bvGTfjtO5ggNfgJk5V4
bGSOXnsplpG71AWMrK2q6NqHjFIE1szEycUKrwIDAQABAoIBAAivyt6Zy/G2g8kC
852hfvcRubLV92eRdAmNGFqTOqaUcS00i3QZyp4MRGqxtOV/88y/nEOtP1RHkZJw
HXTjHq4JsDvwhnQR8JbCX6z1zkLQdS01u3jrwJTaPpooxdATfPlfO6CYjqM+SapB
o7dS1ZAZb4U8vPx+MWoDEVNxvO7/xyqho1Oc4H9MwqQUiyG2WfIoqxLSrBYcambv
RmySwTIpgQZTr61EeWf/0eWpV0iEYbSnkB/VaKW+5tg4gCjPgy5v6/LQ0u/pzlYz
ayCL3xN2rp0tigXsiiWz3cM5gDsnatK4nVNRs9y3JSZpWpI236ZfZjs8Lts+WBUw
hAEoE9kCgYEAyEIGD1A7R/t5EYk5HhHDH5tGdyxejAcQL5AIz0YnTZU8Iixyc7FR
uDmAMiuKIcJY/nUlxZjSxNc3MkOfZNggQvf9ONrt+ftQ1yyTjv+019NfU4w4d0Ep
LNaiAHgaPKimBUZjYXbLgiMXj/1pBaQmgUYTK/VlO3PVdowxxzxMYlMCgYEAvEOG
GrhVaQV1nAYx86BgZ3wn90hBFXZWGaN+eXUmyrast93Ih3TCSgQDKPuN3pdv/TIe
cpQv/BxEMpW+6d5Z1NP3GbrLpaZUiUNk8fqw1S3pmD5aWZrYIUaNukAyOxnZVgjv
EWD9QTpI663gODaeZZTkDYiRNzTzGOg5HtzporUCgYBBOphEtqqImNXnq13qeHip
O+eo+8/UJpzUEUN9WGmG8NxEeVvSaWin7DrgnKQCuQ5J3Biwk0XcDgoRmks6Ctf/
WE2oDk/DxGOhowhxZMMgJd6AFUVzOstRqpvcMULCjWB+iV3nqk1Bl3KeWTmzN7O/
Gfc2s1kFE4btdV7lebObtwKBgE3rkLS8eLVYCh6Cvef9CAms7Im/wRhV+zrvXWh9
4YljZEdRpy7RV5z03i33N/faLALa3JlF1jp9pIhfTD5Vxk59ULe4hZNRLYoGd+Bj
hw8kyps1q4WMvkm/fueIrIGjqD2gwvopb4iwy/+n3rbFfHfE0UL8tEXqR3eWnhW1
D4pFAoGAccR4eMJD43hJWaUQLtsj0RoW9lFKVXj7aqkIIeupXwt7Ic2z/FhCAJi+
V0MWpd3K6+kPl+ifdt8U4kcYfubPMfJhd7IkMcgQS+yZK1+5xWdRISvI8GpNwIHE
LUkVkCCadXNNZ7b1nmUKjse95u4IaE6hwAqjSTNb05gPmCfoEjg=
-----END RSA PRIVATE KEY-----`;
const esKeyP256 = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2
OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r
1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G 1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G
-----END PRIVATE KEY-----`; -----END PRIVATE KEY-----`;
const esKeyP384 = `-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDpgCvB2frnLKd7TuWe
JM1ejXXmr9y/5gskxKuuylLvpQTiDdtLtuhJnvw1/zWKWO6hZANiAAQ5Crhsi5FD
t55i53dCtdzG9OzCnbDFf/6136ZfEiakDTDeWCdUvNnB3WQEcVBr97BfSWLI9mO+
T5yzm0RfhgvWIq/tBou+sIDeGp6NQfJwhDhf+JsdeF174gtfNMZGj/s=
-----END PRIVATE KEY-----`;
const esKeyP521 = `-----BEGIN PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIA0dBErrZ5ovKq4Xf/
iTlRkYxuOfgBZ6+tWIfG13YwthB1XrH06YmteZGNjHHLZEeycwUt0jM4kUb+tOsJ
3ckhj1ihgYkDgYYABACYgsa8JWKH46CQagwNw14v/L+DIs1WAjJdMXZySjKlRkD9
LtLMxkbX2H4H4Zl2KzCMJkwTSETzSKNlXvAUJqKbRwHezCp4y5XZN9MOBYdmyylZ
NOVxwwTouimNkJ0K6A8+/Im5S3PWB8Ra1D6t+bT1WHHhEePZcltSLLFlbIIyot5m
2w==
-----END PRIVATE KEY-----`;
TestRegister.addTests([ TestRegister.addTests([
{ {
@ -88,7 +129,24 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [esKey, "ES256", "{}"], args: [esKeyP256, "ES256", "{}"],
},
{
op: "JWT Decode",
args: []
}
],
},
{
name: "JWT Sign: ES384 - P256 key",
input: inputObject,
expectedOutput: `Error: Have you entered the key correctly? The key should be either the secret for HMAC algorithms or the PEM-encoded private key for RSA and ECDSA.
Error: "alg" parameter "ES384" requires curve "secp384r1".`,
recipeConfig: [
{
op: "JWT Sign",
args: [esKeyP256, "ES384", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -103,7 +161,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [esKey, "ES384", "{}"], args: [esKeyP384, "ES384", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -118,7 +176,24 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [esKey, "ES512", "{}"], args: [esKeyP521, "ES512", "{}"],
},
{
op: "JWT Decode",
args: []
}
],
},
{
name: "JWT Sign: RS256, weak key",
input: inputObject,
expectedOutput: `Error: Have you entered the key correctly? The key should be either the secret for HMAC algorithms or the PEM-encoded private key for RSA and ECDSA.
Error: secretOrPrivateKey has a minimum key size of 2048 bits for RS256`,
recipeConfig: [
{
op: "JWT Sign",
args: [rsKey1024, "RS256", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -133,7 +208,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [rsKey, "RS256", "{}"], args: [rsKey2048, "RS256", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -148,7 +223,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [rsKey, "RS384", "{}"], args: [rsKey2048, "RS384", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",
@ -163,7 +238,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "JWT Sign", op: "JWT Sign",
args: [esKey, "RS512", "{}"], args: [rsKey2048, "RS512", "{}"],
}, },
{ {
op: "JWT Decode", op: "JWT Decode",

View File

@ -0,0 +1,54 @@
/**
* SQLBeautify tests.
*
* @author GCHQDeveloper581
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "SQL Beautify - basic",
input: "SELECT MONTH, ID, RAIN_I, TEMP_F FROM STATS;",
expectedOutput:
`SELECT
MONTH,
ID,
RAIN_I,
TEMP_F
FROM
STATS;`,
recipeConfig: [
{
op: "SQL Beautify",
args: [" "],
},
],
},
{
name: "SQL Beautify - upsert",
input: "INSERT INTO Table1 SELECT * FROM (SELECT :Bind1 as Field1, :Bind2 as Field2, :id as id) as new_data ON DUPLICATE KEY UPDATE Field1 = new_data.Field1, Field2 = new_data.Field2;",
expectedOutput:
`INSERT INTO
Table1
SELECT
*
FROM
(
SELECT
:Bind1 as Field1,
:Bind2 as Field2,
:id as id
) as new_data
ON DUPLICATE KEY UPDATE
Field1 = new_data.Field1,
Field2 = new_data.Field2;`,
recipeConfig: [
{
op: "SQL Beautify",
args: [" "],
},
],
},
]);

View File

@ -18,18 +18,6 @@ export const GIF_ANIMATED_HEX = "4749463839610f000f00b30b00424242ffe700ffef00ffc
*/ */
export const PNG_HEX = "89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af400000006624b474400ff00ff00ffa0bda793000000097048597300000dd700000dd70142289b78000005184944415458c3c5575d6c145514feeeccecccacdbddb6e096a5dbcdb6d06d80d06090466d6953454ab52ad0a65589840ac1d02a313c989af062820fa66210130d9a68b0363c34610135690b188b7183c13f44506c8115ba535ab6ddd2617f667f66ae0fb41596ddee2eadf13c4de69e7bcf77cff9cecf25b83f613b3b3b975b2c96f25028c47a3c9e1f5a5a5a7e05a0016000d0c9ef9442d23448a60edeb973a769c78e1d077272721a65594620106000505996bf1a1f1f3f67369bebc2e1f0ef6bd7aedd0a409d2d00e2743a1f2929296915046199a66901007aa3d1580600131313da24000000a594124288aaaab72a2b2bed1d1d1d8f8ba2386fc3860d9f25f3c84c0088cbe56a2d2c2cdc4708d12552880770a7288a3228088215003c1ecfd68d1b377e9e488f4b66dde974aeb2dbed498da71251146d538ed1b4e4746092dddee170b4300ca3c32c251c0edfd8bc79f3d164de4e0680110461794a02119292c482202c387efcf86f3d3d3d7b13814816024a2955e62a8b4451b4abaafad8e485d5743ca005028153699c4dd30c83140a857e4c9409c900a0bbbbfbc368343a34a3754a693a1c58b76eddf2dadada5d89002705b07bf7eee13367ce3cab284aff6c482808425e6767e70bc9ea0033d3e6c6c6c65fd6ac5953a1695a3453c3a150c84d295529a59aa669914cd3705adc6eb7926eaca74455d5605555d5c3030303f59224bd525f5f7f30992e87ff40344d5328a5caa64d9bbe4ca5cbe07f1666ae522dae40a5dd8ed30941c8e5727d63341a9f8a5f181a1ac2f0f07022029e02109d2b00bae2e26207cbb2f72cf03c8f9c9c9c441c580c804dc70b330258b6c020beb87ac9abecb59f8b087377b4f4f30a68b6de482549a29224ddb5168bc51cd5d5d54ff6f5f575cfa69633edeb971c78e2d195db055e77cfb6a2eaadb816e5b59ffafb19a7d3095555e3ab64341a8d96f6f6f6fe755f247c69d542abd9c0bd3c70f90a628c30fd5f56542c5c550fc3837600406e6e2eca9e2e433837fcefc0c8b2e079fe7b9fcfe7aba9a9296613c52f55084acc864a027013b28c828a2d30e805bcbe670fac4b5740f5a9285b18c6a0db4da8c180fdc6fdb035d850c555a174a4148410b85cae7293c97442a7d395363434347775757d91b6075a2a6c45d66ce18369258685de644659d96af45ff80345f9f908c932821313c4eff7639b6d1b06838358242c82d96c86288abe582ce6e6797e052184701c9797910796e61976b10c991fff7f7b5313b6373541d5340426d36f747414e5c67294679503a1e90634e6f57adbac56ebb14020f0e9a14387decf84038c8e232b53b45888dc6dec63636389d290c9caca5a3d09a6a2a6a6a628130054d33092a2c52272bbe4515996113f16288ab2c86432bd01001cc72db5582caf651202eaf5473e7e80d7af270409d9cb320c0c66331ca5a5602c1624180d492412392bcbf2db46a3f1394992f665c481b77a2f9f78e719476b5e16ff2e00d31dae8524cb30e8f560390ee72e5e243d7d7d34168bc16030a87575752ccbb20400a2d1e8b7478e1c390ce0f0fd5442fae6d7039f343d643956345f5fcbf1fafd00b219868145afc78d4b97101a1b833a32426d361bcdcfcf87cd6663a7a6649ee70725497a6faede86e4c2c993cf171716eee5753aeb9d0b7f5ebfae5df67a99b86164e8e6cd9badcdcdcdc7d27ae5a6a3f45147c7794dd30e2e59bcf896c0f3851ccbe602c0a8df4fc783413269d8130c06f79d3e7d7a4b5b5bdbd9b45b77c60304c3f0df75752db31714acf8dbe7cbbee2f5fafd7efff9f6f6f6b357af5e8d647ade3fa1780bad734c65970000000049454e44ae426082"; export const PNG_HEX = "89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af400000006624b474400ff00ff00ffa0bda793000000097048597300000dd700000dd70142289b78000005184944415458c3c5575d6c145514feeeccecccacdbddb6e096a5dbcdb6d06d80d06090466d6953454ab52ad0a65589840ac1d02a313c989af062820fa66210130d9a68b0363c34610135690b188b7183c13f44506c8115ba535ab6ddd2617f667f66ae0fb41596ddee2eadf13c4de69e7bcf77cff9cecf25b83f613b3b3b975b2c96f25028c47a3c9e1f5a5a5a7e05a0016000d0c9ef9442d23448a60edeb973a769c78e1d077272721a65594620106000505996bf1a1f1f3f67369bebc2e1f0ef6bd7aedd0a409d2d00e2743a1f2929296915046199a66901007aa3d1580600131313da24000000a594124288aaaab72a2b2bed1d1d1d8f8ba2386fc3860d9f25f3c84c0088cbe56a2d2c2cdc4708d12552880770a7288a3228088215003c1ecfd68d1b377e9e488f4b66dde974aeb2dbed498da71251146d538ed1b4e4746092dddee170b4300ca3c32c251c0edfd8bc79f3d164de4e0680110461794a02119292c482202c387efcf86f3d3d3d7b13814816024a2955e62a8b4451b4abaafad8e485d5743ca005028153699c4dd30c83140a857e4c9409c900a0bbbbfbc368343a34a3754a693a1c58b76eddf2dadada5d89002705b07bf7eee13367ce3cab284aff6c482808425e6767e70bc9ea0033d3e6c6c6c65fd6ac5953a1695a3453c3a150c84d295529a59aa669914cd3705adc6eb7926eaca74455d5605555d5c3030303f59224bd525f5f7f30992e87ff40344d5328a5caa64d9bbe4ca5cbe07f1666ae522dae40a5dd8ed30941c8e5727d63341a9f8a5f181a1ac2f0f07022029e02109d2b00bae2e26207cbb2f72cf03c8f9c9c9c441c580c804dc70b330258b6c020beb87ac9abecb59f8b087377b4f4f30a68b6de482549a29224ddb5168bc51cd5d5d54ff6f5f575cfa69633edeb971c78e2d195db055e77cfb6a2eaadb816e5b59ffafb19a7d3095555e3ab64341a8d96f6f6f6fe755f247c69d542abd9c0bd3c70f90a628c30fd5f56542c5c550fc3837600406e6e2eca9e2e433837fcefc0c8b2e079fe7b9fcfe7aba9a9296613c52f55084acc864a027013b28c828a2d30e805bcbe670fac4b5740f5a9285b18c6a0db4da8c180fdc6fdb035d850c555a174a4148410b85cae7293c97442a7d395363434347775757d91b6075a2a6c45d66ce18369258685de644659d96af45ff80345f9f908c932821313c4eff7639b6d1b06838358242c82d96c86288abe582ce6e6797e052184701c9797910796e61976b10c991fff7f7b5313b6373541d5340426d36f747414e5c67294679503a1e90634e6f57adbac56ebb14020f0e9a14387decf84038c8e232b53b45888dc6dec63636389d290c9caca5a3d09a6a2a6a6a628130054d33092a2c52272bbe4515996113f16288ab2c86432bd01001cc72db5582caf651202eaf5473e7e80d7af270409d9cb320c0c66331ca5a5602c1624180d492412392bcbf2db46a3f1394992f665c481b77a2f9f78e719476b5e16ff2e00d31dae8524cb30e8f560390ee72e5e243d7d7d34168bc16030a87575752ccbb20400a2d1e8b7478e1c390ce0f0fd5442fae6d7039f343d643956345f5fcbf1fafd00b219868145afc78d4b97101a1b833a32426d361bcdcfcf87cd6663a7a6649ee70725497a6faede86e4c2c993cf171716eee5753aeb9d0b7f5ebfae5df67a99b86164e8e6cd9badcdcdcdc7d27ae5a6a3f45147c7794dd30e2e59bcf896c0f3851ccbe602c0a8df4fc783413269d8130c06f79d3e7d7a4b5b5bdbd9b45b77c60304c3f0df75752db31714acf8dbe7cbbee2f5fafd7efff9f6f6f6b357af5e8d647ade3fa1780bad734c65970000000049454e44ae426082";
/**
* The CyberChef logo with 'chef'
* 32x32
*/
export const PNG_CHEF_B64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAHqElEQVR4AcXBfUyU9wEH8O/v9zz3PHfPvXLewXHnxYKgYCFW6ktHmXUySIdKcVlFs2ZkrRB1m6kx00XTbGJjFrvVTSexZusGviQqwsxImM5laOZclG3EisEXWK2cvBwcuYN7456XPawQb+fx4l/7fAji5Obmvrlx48ZKjUZjlyTpaXNz89nOzs42TDp37lzLwMCAcefOnWtPnz6d53A4CsPhMNPb2/v37du3/wuADIACUADImIZer3e3tLS0rVy50k2gIqo9e/b8et++fd8+ceLEqXA47Jk3b97SmpqaDTU1NT+ur68/BICeOXPmEsuyzry8vAcWi+XtQCCAYDBIASiBQOAPIyMjd+x2+7poNPpZSUnJuwAkJLFr165j1dXVlQUFBa+yUBUXF79TW1tbtWTJkrXd3d3XMam9vb325MmTH27durXc4XAwBoPh1Z6eHqSkpCzDl2R8iZhMpnKj0biBqHiez+I47v2GhoavabVaa0VFxacAZEwqLy/Pa2xsbI9EIk8YqA4ePPhJa2tre2Nj40d4hnz88cerMzMz1/T29rrS0tKcLMvC7/eDUooJg4ODBCpCCAghICqoxsfH+aqqqr2CIFTabLbykpIS74ULF24zDKPbvXv3nuLi4rUmk0mfmppqoQzDGMvKyl55+PDhdcRpampavnjx4v3d3d1wOByY4nQ6IQgCIpEIrFYrVq1aBVEUMcXv9yMjIwNOpxPp6emw2WzIz8//lUajMQNg9Xp9oSiKxvT0dLsgCF+hPM/rLRaL9tGjRwN4hmRnZ2+nlGoMBgMEQcAUk8mEkZERmM1mqORbt24hJSUFExRFgcVigc/ng9frxejoKHp6eiRRFCPl5eU7JEkaPXDgwPq+vr67p06d+lttbe0GCoBAJUmSjGcoz/N5SKKrqws2mw0TiMrv98PlcmFCIBBAQUEBBgYGEI1GEQqFwLIsA0C7Y8eOQwAIJlFKCVSsLMsSVHa73YRnFFUEsyOKooAQggmiKGJCcXExEoVCIagoAAlx2Gg0OtzR0dHndruz8YwcDAavGQyGr46NjSEQCMDlciEJBQBBgpaWFpjNZkyJRqOxYDB4DoCMBFRRFOnixYstmzdvrmQYRodJra2tx30+HxYtWgRZlpGMokIcnuchiiIcDgcEQYAgCGAYZrCysvKlioqKKgAKElCo6urqDmZnZxuOHDlyRhCEBZRSXV1dXaYgCLh//z7S09MxFwaDAbdv30ZWVhZ8Ph+i0SiCwWBqZ2enp6ys7H0kwULl8/me5OXlrTl+/PinwWDwc6gikYh49OjR39fX139w5cqVfwLQMAwDQgjiEUJAKcUUhmHQ1dWF1atXg+d5yLKMtrY2XL169beIwzAMhYogDiGE8jyfrtVqrcFgsDcWi40AMPT29g5TSrlwOAxFUSAIAib4/X55ZGSEiqIIQRAwRZKkUFFRUf6xY8c2m81mecuWLYcByJjEsqxJUUmSNMoijqIociQS8UQiEQ+S0Ol0SMRxHDiOQzxZliOxWKxv27Zth5CEKIoBTKL4P6OYnYIXRAhRAMiYAxazC9+4cePPRqPxG0jw9OlT9Pf3I1E4HL4GIIY5YDE7TVZWVjbDMEjEcRwsFgsSybK8EAADQMYsWMxgSZpeu6Uo53vMF//IIJQins46XzHrjIrH41E8Hg/iiaKYvWbNmq+3tbW1YhYE06OH38o5sfa1gmqe0+B/EWSseRdfxDi5/cED2tTUBEmSEE9RlJgq//Lly/cxA4ppvLM83WXXs9992N0DkfKYEohISF/+TehtCzAhJSUFK8pWIJoSxRSGYcBx3F99qtLS0ixGhWkwmMa3ljrWW3Ts28FwBK9t/gBpOYU4cPYKvNZcGMxpSLNa8dm9exA1GrKf7IdmqQbLVi7DG+43kJOTg6GhIafNZiuzWq0HMzMzuzs7O+8gCYoktr/uznCZ+aOYRCkDncmOFSuK8KDzHjKcToQDAYT8fjI2Nob33O/hSegJMvlM2O12aLVanyiK/+Y4bilRsSybimmwSCI3Vb+LoWQeElRv2oTqTZsgyTKC/f2YMDQ0hEJjIQoNhUAY/8Xz/LDX693rcrkuBYPB35w9e/YXmAbF86iGJQWYQTQahZZlMWF4eBiJZFmmBoOhCCqe518vLS3NwDQonqfIMgYwA57nMQ4VIUogEICiKIgXiUQyTSbTD6FiWTbX4XB8H9Ng8TzFOzb+icDp3iIEDJJgKIXebkd2fj6owwFCCBKQ8fHxjkAg8KHRaCz3eDxHMA0WSfzkcveffro+e0eqgfsIgAmTZFmGJxCAXqcDw7K409VF/tjWpoiiCL1eL61bt45REahisdhfmpubLwK4iBmwSE75UcvDk5tecVzKmKd7k+V0vwRgppTCodNh8NEjhIeHIQ0MKG63W3E6nXC73QxRQcVx3BOPx/NzzAGLGZzv6B8A0PCdQW80S9D/jNNoXBqeh+vllzEh1tenWLxeijiSJD0NhUI/uHbtmgdzwGAO6hoa7sqy/LuchQtHeY57iWWYFKiGxsaUkVCIQCVJUm8oFDpy8+bNqr1793ZgjgheEKWUu37+/JIF8+cv/dznM/d4vWOqu4cPH+54/PjxOF7QfwCiFwbr9BCaBwAAAABJRU5ErkJggg==";
/**
* The CyberChef logo with blur
* 32x32
*/
export const PNG_BLUR_B64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAANT0lEQVR4AS3BC5CV1X0A8P8553s/7mMvyz5gWUFApEKpEtloSQdLDDDoREXMDK1Zx8dg4xjHqdWpQwgI29QoFrE6gyFJ6YhmoDNMapIBYhzHqgg1ZAvRdIVld5Vl9+597N17v+8779NNJ78fklL6GzdutJ955hnnrbfect98803vxIkTwccffxwdOnQoGhoaihuNRiilDKvValCr1XzGmNtqtdz29naru7sburq6dG9vr1i6dClbtWoV7evryzZv3pw8/PDDSX9/f+vIkSPN/fv3t86cOZMYY7KBgQFarVY5AAjr4MGD6KOPPsK1Wg0bY4ht25Y9q1gs2h0dHW6apm5PT4/HGPMrlYpv23aQJIk3y8nn85bneYgQopVSglJq1et1Mjk5iS5fvqzPnz+vzp49K44fP85uueUW++jRozalVDz77LOkUCjg/v5+hI8dO4YajQZSSuE0TYnW2rJneZ7nlEolt7293SuVSn5nZ6ff1tYWtrW1hcViMczn81EYhpHjOBFCKNJah5TSsNVqBZVKJRgdHQ0uXLjgnzt3znv77bfdo0ePOkeOHLEPHTpkHThwgKxbt46sWLECk4sXL3ozMzPWmTNnnEaj4QwPD3vbt2/3a7VaeOXKlVBKGRFCIsdxIilluGPHjnDXrl0BQsh3Xdf3PM8dGBiwXNe1fN/Hvu8jz/MgjmP98ssvq3379qkoiuTrr78ub731VvHhhx+qbdu2qf7+frV69WqNq9UqmpycxK7rYtu2ied5VhAEdj6ftwuFglssFr1SqeTncrkgn88HuVwuCMMwCsMw8jwvtCwrJISExpiQcx6maRq0Wi2/Uqn4X375pTcyMuIODg46586ds9977z373LlzZHx8HJ8+fRoXCgVEDh486D755JN2uVx2xsfH3eHhYf+JJ57wa7VaVC6XIyFERAiJACBijEWMscgYE+zcuTPYs2ePZ9u2Y9u25TgOdl0XeZ4HnufpKIpUHMfqtddeEwMDA2LZsmX85MmTYnx8XGit5djYmHznnXcUNsagKIqQbdvYsixSKBRIGIZWGIZ2oVBwCoWCm8vlvCiK/GBWGIbBrDAIgtBxnJAQEgJAqJQKpZQB59xPksRvNptuvV53JiYmnMuXL9vDw8PW+Pg4GRoaIrVaDc+dOxeVSiWE169fD3PnzkVdXV2oVCqhfD6P4zgm+Xye5PN5K45jO5fLObNcjLFHCPEAwEMIeUopXwjhc869LMu8VqvlzszMOI1Gw6nX6061WrUbjYY1PT1Nrl69SkZGRvDZs2fxBx98gF599VX0/vvvg3XvvffCyMgILFiwADzPQ/l8HsVBgKMowgBAQGsiGLNZltmcMZtS6lBKXZplLmXMUlIiTDC2bQdmScdx7DiOSZqmmDGG0zTFjDEkhEBSSlQqlaC3txd6e3vhtttuA7xo/U3w1W+uN/kFeQgLIfixBcR3QAFHQDQSWmHGKU7ThCRJy8rS1Mqy1KKUWlma2hmlVpZRizFGKKWEMYYZpVgIgYQQIIQwWmswsxhjptFomMnJSTMxMWFGR0cN/un3vmX65gLc3NtrlnTnTGQBBJYCDzT4WANIhjRniGUJZlmGGU0Jp5QwmhEhOBaMYc4Z5oxhKTkSgiMuBCiljBDCKKU0xlgbY7SUUgshtBBC53I5PTg4aPC2mwEe2txjruuYhqXzjJmf12ZOwE3B48ZDythGGC0YSEpB8AxxRpHgDEkhkOQcCcmRFAIJKUBKZaSURv8JIUTZtq3iOFBxHKv29nbl+77u7OzU9XrdvPTSSxofe+4ec20nwI2LPFOEFnQEyrR5yoRYGFdnAJKB4RQES0EyCkowkIKDksJIJY1S0mgltdZSKyW11koBAomQkRiDJDaRYZyTxWJRBkGg2tvblW3buqurS1+6dMngB5ZWIPnlHLN9QwPuukmba4t1mAMzUCDceIoZWzEDghkpqJGcghLCKMGNUsJoKYxWSisttVZaaa2UBiOVlkoDSESIAAAZBZG0bVs5gaOKxaJatmyZWrBggR4YGND4ujv/F368+HO4zxuBlfAFdMsyBGwSSFIFyJogswaIrAkqS0FxaiSnoJUwWnKjlDBKCaOU0lJJrZTUUkqt1Syp/h8QkEEcSMdxlOu6KteeU8YY3dHRoc+cOWPw/i9+jrak/42+VZiG7mQUdcsy5NIKqOkJBHQadNYEmTaBpy0ksxRpRkEwiiQXoIQwUgijpDBKcCOl1FJyLaVUUnOllFCUUi200MY1iraoHjo/ZJIk0fPnz9eHDx/W+HH/12gd/gA5w+/iztZFBONDmE2N4GxyDNPaBGb1Ck4bVcyTJhI0QYJlSHEKklOkBAfJOXDOQChhhOBGcGY4Zyaj1LSyluFZpgFAN5tNM1GdMIwxff78efPuu++aU6dOgfVt979QbvBXeKU8iMbGz2OYvIhVdQyLX43jrDpJ0ukqyWamMU9nMM+qmNMUS8aQ5BQJqZAEZLAhIAgBJhhwLkxKU6BZZrI0MzNJYrjgZmZmxkxVpsyJ/zwBr7zyCnz66adw3333GXz29ZfwzewjhC++jxuffkySkQskHf2cVL8Ytmh9ktDpCskadUJbM0RkCRY0xZJnWAqOpOBIco6E5CAEA8E4MMaAMgpZlkCz2YR6ow6N6QZUK1UYGx2Der1uyq2y4ZybO+64A/BN079GldO/wJfOvEPY+CCuj1wg5dE/WDMTo2RmatLKGjXCZupEZgmWNMWKUywZw5JzrKRASnKkhEBCCCQlBy4yxBmFJElQo9lA1WoVVWtlNDE5AZ999hkc/tlh9MjfPIKGhobQ7t27AV/+xXEMnw3i4MolTEcvEqt2FaNqGbNKhdBalfCZBmFZiwhKsRQcGyGRURIZJUEribRWoLQGrRVorUAqDZxzxIWAjGbQSmZQlmZQmaqgNE2h2WxCpVKBsbExGB8fB3wDrqP6l5/hrDqC82wa6Zk6pq0Gps0ZTFtNLFmGNOfIaIlAKQBjAAEYBAaQAYMAGYSMQQjMHwFoAwBGSA5SStAKIOMcwaxas4bK5TKCP7nzzjuR1YMFKhhAJZCo2qKYMIkMk0hQgUBrwNoAwdhYCBvHsnSVSG0RrW3LKI0MQoCUsSxlWZayLEtb2NKEEI3nYYPAzJJGKWVaSQsajQZUKhX4zUe/QfPa5qGTJ08CXpJJdG9vAW4qRtDrW9DmAoQA4CNsbGwZxyLGd2zl25byHVsFriN9x5G+6wrf9YTneSJwPRF4vgh8XwS+LwPPU0HgKdfxtesF2nd843u+ieLItOfazZL5S8ymTZsMzMLJvwHA+wA9dd8sJLF2pWdsJ9DY97Tju8oLA+n5vgziiHthyP0wYn4UMtcPeRCGLA5DFoUxi4KQ5+KYx1Ek4lxO5OKcbCsU5JxiUXV1zVVd87r00oVLdU9PjwmCAP7o2LFjhry3d2N4+O57rHXXbrceXFN12r7ZdNY8IJwNZeVeyoxzRex2ptCAXdu5x2ruskn6nIWetmwQjqvBcRXxfGEHAQtzuSzOFdK2A6WkY25H0tXdnSy8ZnFy3fLl6e8G/yfp+0pftmHDBlq+WmYHDhzg99xzj5yamlLk0Z9sCcJ3P7YW5bZbz94urTm/Z/aND0rnzjo4f5va9rhy7Wnjkgb+J9zAe3HzuQFI93haOp4CJxA4iJgd5TK/WMj2zfnXpK2jM5nX29vqXXRta8Wfr2itX7eudfHzS+mWLVvSx7/7OH3hn19gfX19or+/X65evVpZA3/do89vvVHNt1dKtuoTma4SYskKwntO+WxOo0Bj08hcXHeQk1nIaWLldxk6l0tFhUW0QTYQ5bou87u6szn5Qlrq6Uk7FvRmi66/Plu+chW7Zc0tfNfePeL2zbfLPd/fo3bs2KFaqKURQnrFihXGemP9Tfr03YPq9xNfqN1fvUGOfh+LNZditvgvYtZZLdO8PZ15uGwTr0mM30Ki0TKMSoEYt4hQiBBL2a7L/TkhzRXb0tKSpen8xQuzJStW0r6vraUbH3iQbd26lX9y7hOx43s7JG1QFcexPn78uFm7dq0h8Vs/cb+9Zht56FSAH97yPH7hoX3kuvJu646nI3L3EzEZsfJ4nNl4yg3RlBPCd93I1N1QGz9SVhALLy6yuNROCws6s+6enuSalSuT6//shuTmvjXJ3Z/8Lnnq0UfS3c/tyfYO7KXLFy9nzWaT7927V7744oty06ZNmtzK1zul+/4R/3uvwVu3tuOd93vkmseeJxv4D8iG7+wn255rw2MU4yt2iCZIgL7j5syUH2vsx8rLFbmfL/GX2jtp+7Xzsnm916SLb1ydrl79leT2D7+e3L/l3nTgqX/Ifvyjn2Zv/OwN+tsLv+Wff/q5LJfLIgxDjTHWZN8f9ts3tp5HT/3gGXz6/g789GNz8DWPReSvdkTk1OUfki279uPhp3bii8jDV6wAHvYiMxHntRXmlBvlZVTsYP/S3sU6li3IehYuypavXZv+5dfeSU5//RvZ3//do+nCMMiOHv0PeuKXJ9iVkSvirv67xOGfH5aPP/i4unr1qv4/bGwpHb1ZNmYAAAAASUVORK5CYII=";
/** /**
* Sunglasses smiley * Sunglasses smiley
* 32x32 * 32x32

View File

@ -120,17 +120,20 @@ module.exports = {
jquery: "jquery/src/jquery", jquery: "jquery/src/jquery",
}, },
fallback: { fallback: {
"fs": false, "assert": require.resolve("assert/"),
"child_process": false,
"net": false,
"tls": false,
"path": require.resolve("path/"),
"buffer": require.resolve("buffer/"), "buffer": require.resolve("buffer/"),
"child_process": false,
"crypto": require.resolve("crypto-browserify"), "crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"), "events": require.resolve("events/"),
"zlib": require.resolve("browserify-zlib"), "fs": false,
"net": false,
"path": require.resolve("path/"),
"process": false, "process": false,
"vm": false "stream": require.resolve("stream-browserify"),
"tls": false,
"url": require.resolve("url/"),
"vm": false,
"zlib": require.resolve("browserify-zlib")
} }
}, },
module: { module: {