mirror of
https://github.com/gchq/CyberChef
synced 2025-12-05 23:53:27 +00:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb0eaf4597 | ||
|
|
8ada3bae0c | ||
|
|
db72cad610 | ||
|
|
bed66298d1 | ||
|
|
6726994ad5 | ||
|
|
5abc5279f5 | ||
|
|
1135ca5fb4 | ||
|
|
be08a62f52 | ||
|
|
f473807459 | ||
|
|
d148cae814 | ||
|
|
f22211ce8c | ||
|
|
f9c24f2528 | ||
|
|
6f6786d79e | ||
|
|
a5ea7f7d58 | ||
|
|
d637ac7633 | ||
|
|
c1ad2386ef | ||
|
|
b0b6de116d | ||
|
|
1b161f997b | ||
|
|
b99af58636 | ||
|
|
d4edbb3c3e | ||
|
|
1b765605ca | ||
|
|
8286dc26ad | ||
|
|
19a438c15b | ||
|
|
12898a1a8e | ||
|
|
541f2a2988 | ||
|
|
d0277dde3f | ||
|
|
d184e40116 | ||
|
|
596db07647 | ||
|
|
c233c5c67e | ||
|
|
db788b57e7 | ||
|
|
daee7ac761 | ||
|
|
0bcf57e89c | ||
|
|
1d130c88a8 | ||
|
|
5225874498 | ||
|
|
802493fec4 | ||
|
|
18408901be | ||
|
|
982c915931 | ||
|
|
a339eacd45 | ||
|
|
8fc0e012e3 | ||
|
|
b7fb9635e5 | ||
|
|
f988a958bb | ||
|
|
c80cb57b07 | ||
|
|
dec28e16d4 | ||
|
|
7796c473ae | ||
|
|
8445165491 | ||
|
|
c5698fcd65 | ||
|
|
8b12caad78 | ||
|
|
e1492c3bb1 | ||
|
|
3cc66e9db9 | ||
|
|
99bef09e0e | ||
|
|
c97e77c765 | ||
|
|
e44a22e143 | ||
|
|
c2496fe63e | ||
|
|
6a01e40394 | ||
|
|
b312e17904 | ||
|
|
2cd3e9cacd | ||
|
|
ce72acdd61 | ||
|
|
8e74acbf3e | ||
|
|
4fafa39e54 | ||
|
|
bb7487c476 | ||
|
|
308195279c | ||
|
|
e95b7075b9 | ||
|
|
dfbc1beccd | ||
|
|
3e428c044a | ||
|
|
11451ac6b9 | ||
|
|
21a8d03201 |
15
.github/ISSUE_TEMPLATE.md
vendored
15
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,14 +1 @@
|
||||
<!-- Prefix the title above with one of the following: -->
|
||||
<!-- Bug report: -->
|
||||
<!-- Operation request: -->
|
||||
<!-- Feature request: -->
|
||||
<!-- Misc: -->
|
||||
|
||||
### Summary
|
||||
|
||||
|
||||
### Example
|
||||
<!-- If describing a bug, tell us what happens instead of the expected behavior -->
|
||||
<!-- Include a link that triggers the bug if possible -->
|
||||
<!-- If you are requesting a new operation, include example input and output -->
|
||||
|
||||
<!-- Prefix the title above with 'Misc:' -->
|
||||
|
||||
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: 'Bug report: <Insert title here>'
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Prefix the title above with 'Bug report:' -->
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior or a link to the recipe / input used to cause the bug:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (if relevant, please complete the following information):**
|
||||
- OS: [e.g. Windows]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for the project
|
||||
title: 'Feature request: <Insert title here>'
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
<!-- Prefix the title above with 'Feature request:' -->
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
16
.github/ISSUE_TEMPLATE/operation-request.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/operation-request.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Operation request
|
||||
about: Suggest a new operation
|
||||
title: 'Operation request: <Insert title here>'
|
||||
labels: operation
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Prefix the title above with 'Operation request:' -->
|
||||
|
||||
## Summary
|
||||
|
||||
### Example Input
|
||||
|
||||
### Example Output
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -2,6 +2,16 @@
|
||||
All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master).
|
||||
|
||||
|
||||
### [8.35.0] - 2019-07-03
|
||||
- 'Sharpen Image', 'Convert Image Format' and 'Add Text To Image' operations added [@j433866] | [#515]
|
||||
|
||||
### [8.34.0] - 2019-06-28
|
||||
- Various new visualisations added to the 'Entropy' operation [@MShwed] | [#535]
|
||||
- Efficiency improvements made to the 'Entropy' operation for large file support [@n1474335]
|
||||
|
||||
### [8.33.0] - 2019-06-27
|
||||
- 'Bzip2 Compress' operation added and 'Bzip2 Decompress' operation greatly improved [@artemisbot] | [#531]
|
||||
|
||||
### [8.32.0] - 2019-06-27
|
||||
- 'Indec of Coincidence' operation added [@Ge0rg3] | [#571]
|
||||
|
||||
@@ -133,6 +143,9 @@ All major and minor version changes will be documented in this file. Details of
|
||||
|
||||
|
||||
|
||||
[8.35.0]: https://github.com/gchq/CyberChef/releases/tag/v8.35.0
|
||||
[8.34.0]: https://github.com/gchq/CyberChef/releases/tag/v8.34.0
|
||||
[8.33.0]: https://github.com/gchq/CyberChef/releases/tag/v8.33.0
|
||||
[8.32.0]: https://github.com/gchq/CyberChef/releases/tag/v8.32.0
|
||||
[8.31.0]: https://github.com/gchq/CyberChef/releases/tag/v8.31.0
|
||||
[8.30.0]: https://github.com/gchq/CyberChef/releases/tag/v8.30.0
|
||||
@@ -194,6 +207,7 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[@anthony-arnold]: https://github.com/anthony-arnold
|
||||
[@masq]: https://github.com/masq
|
||||
[@Ge0rg3]: https://github.com/Ge0rg3
|
||||
[@MShwed]: https://github.com/MShwed
|
||||
|
||||
[#95]: https://github.com/gchq/CyberChef/pull/299
|
||||
[#173]: https://github.com/gchq/CyberChef/pull/173
|
||||
@@ -234,7 +248,10 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[#489]: https://github.com/gchq/CyberChef/pull/489
|
||||
[#496]: https://github.com/gchq/CyberChef/pull/496
|
||||
[#506]: https://github.com/gchq/CyberChef/pull/506
|
||||
[#515]: https://github.com/gchq/CyberChef/pull/515
|
||||
[#516]: https://github.com/gchq/CyberChef/pull/516
|
||||
[#525]: https://github.com/gchq/CyberChef/pull/525
|
||||
[#531]: https://github.com/gchq/CyberChef/pull/531
|
||||
[#533]: https://github.com/gchq/CyberChef/pull/533
|
||||
[#535]: https://github.com/gchq/CyberChef/pull/535
|
||||
[#571]: https://github.com/gchq/CyberChef/pull/571
|
||||
|
||||
@@ -4,12 +4,6 @@ module.exports = function(api) {
|
||||
return {
|
||||
"presets": [
|
||||
["@babel/preset-env", {
|
||||
"targets": {
|
||||
"chrome": 40,
|
||||
"firefox": 35,
|
||||
"edge": 14,
|
||||
"node": "6.5"
|
||||
},
|
||||
"modules": false,
|
||||
"useBuiltIns": "entry",
|
||||
"corejs": 3
|
||||
|
||||
3194
package-lock.json
generated
3194
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
75
package.json
75
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "8.32.1",
|
||||
"version": "8.35.0",
|
||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||
"author": "n1474335 <n1474335@gmail.com>",
|
||||
"homepage": "https://gchq.github.io/CyberChef",
|
||||
@@ -29,29 +29,35 @@
|
||||
},
|
||||
"main": "build/node/CyberChef.js",
|
||||
"bugs": "https://github.com/gchq/CyberChef/issues",
|
||||
"browserslist": [
|
||||
"Chrome >= 40",
|
||||
"Firefox >= 35",
|
||||
"Edge >= 14",
|
||||
"node >= 6.5"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.4.4",
|
||||
"@babel/core": "^7.4.5",
|
||||
"@babel/plugin-transform-runtime": "^7.4.4",
|
||||
"@babel/preset-env": "^7.4.4",
|
||||
"autoprefixer": "^9.5.1",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-loader": "^8.0.5",
|
||||
"@babel/preset-env": "^7.4.5",
|
||||
"autoprefixer": "^9.6.0",
|
||||
"babel-eslint": "^10.0.2",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||
"chromedriver": "^74.0.0",
|
||||
"chromedriver": "^75.0.0",
|
||||
"colors": "^1.3.3",
|
||||
"css-loader": "^2.1.1",
|
||||
"eslint": "^5.16.0",
|
||||
"css-loader": "^3.0.0",
|
||||
"eslint": "^6.0.1",
|
||||
"exports-loader": "^0.7.0",
|
||||
"file-loader": "^3.0.1",
|
||||
"file-loader": "^4.0.0",
|
||||
"grunt": "^1.0.4",
|
||||
"grunt-accessibility": "~6.0.0",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
"grunt-concurrent": "^2.3.1",
|
||||
"grunt-concurrent": "^3.0.0",
|
||||
"grunt-contrib-clean": "~2.0.0",
|
||||
"grunt-contrib-connect": "^2.0.0",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-eslint": "^21.0.0",
|
||||
"grunt-eslint": "^21.1.0",
|
||||
"grunt-exec": "~3.0.0",
|
||||
"grunt-jsdoc": "^2.4.0",
|
||||
"grunt-webpack": "^3.1.3",
|
||||
@@ -60,43 +66,43 @@
|
||||
"imports-loader": "^0.8.0",
|
||||
"ink-docstrap": "^1.3.2",
|
||||
"jsdoc-babel": "^0.5.0",
|
||||
"mini-css-extract-plugin": "^0.6.0",
|
||||
"nightwatch": "^1.0.19",
|
||||
"mini-css-extract-plugin": "^0.7.0",
|
||||
"nightwatch": "^1.1.12",
|
||||
"node-sass": "^4.12.0",
|
||||
"postcss-css-variables": "^0.12.0",
|
||||
"postcss-css-variables": "^0.13.0",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"prompt": "^1.0.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"sitemap": "^2.2.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"svg-url-loader": "^2.3.2",
|
||||
"url-loader": "^1.1.2",
|
||||
"webpack": "^4.31.0",
|
||||
"svg-url-loader": "^2.3.3",
|
||||
"url-loader": "^2.0.1",
|
||||
"webpack": "^4.35.0",
|
||||
"webpack-bundle-analyzer": "^3.3.2",
|
||||
"webpack-dev-server": "^3.3.1",
|
||||
"webpack-dev-server": "^3.7.2",
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"worker-loader": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.4.4",
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@babel/runtime": "^7.4.5",
|
||||
"arrive": "^2.4.1",
|
||||
"babel-plugin-transform-builtin-extend": "1.1.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bignumber.js": "^8.1.1",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"blakejs": "^1.1.0",
|
||||
"bootstrap": "4.2.1",
|
||||
"bootstrap": "4.3.1",
|
||||
"bootstrap-colorpicker": "^2.5.3",
|
||||
"bootstrap-material-design": "^4.1.1",
|
||||
"bootstrap-material-design": "^4.1.2",
|
||||
"bson": "^4.0.2",
|
||||
"chi-squared": "^1.1.0",
|
||||
"clippyjs": "0.0.3",
|
||||
"core-js": "^3.0.1",
|
||||
"core-js": "^3.1.4",
|
||||
"crypto-api": "^0.8.3",
|
||||
"crypto-js": "^3.1.9-1",
|
||||
"ctph.js": "0.0.5",
|
||||
"d3": "^5.9.2",
|
||||
"d3": "^5.9.4",
|
||||
"d3-hexbin": "^0.2.2",
|
||||
"diff": "^4.0.1",
|
||||
"es6-promisify": "^6.0.1",
|
||||
@@ -104,27 +110,28 @@
|
||||
"esmangle": "^1.0.1",
|
||||
"esprima": "^4.0.1",
|
||||
"exif-parser": "^0.1.12",
|
||||
"file-saver": "^2.0.1",
|
||||
"file-saver": "^2.0.2",
|
||||
"geodesy": "^1.1.3",
|
||||
"highlight.js": "^9.15.6",
|
||||
"highlight.js": "^9.15.8",
|
||||
"jimp": "^0.6.4",
|
||||
"jquery": "3.4.1",
|
||||
"js-crc": "^0.2.0",
|
||||
"js-sha3": "^0.8.0",
|
||||
"jsesc": "^2.5.2",
|
||||
"jsonpath": "^1.0.1",
|
||||
"jsonpath": "^1.0.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jsqr": "^1.2.0",
|
||||
"jsrsasign": "8.0.12",
|
||||
"kbpgp": "2.1.0",
|
||||
"kbpgp": "2.1.2",
|
||||
"libbzip2-wasm": "0.0.4",
|
||||
"libyara-wasm": "0.0.12",
|
||||
"lodash": "^4.17.11",
|
||||
"loglevel": "^1.6.1",
|
||||
"loglevel": "^1.6.3",
|
||||
"loglevel-message-prefix": "^3.0.0",
|
||||
"moment": "^2.24.0",
|
||||
"moment-timezone": "^0.5.25",
|
||||
"ngeohash": "^0.6.3",
|
||||
"node-forge": "^0.8.2",
|
||||
"node-forge": "^0.8.5",
|
||||
"node-md6": "^0.1.0",
|
||||
"nodom": "^2.2.0",
|
||||
"notepack.io": "^2.2.0",
|
||||
@@ -132,12 +139,12 @@
|
||||
"otp": "^0.1.3",
|
||||
"popper.js": "^1.15.0",
|
||||
"qr-image": "^3.2.0",
|
||||
"scryptsy": "^2.0.0",
|
||||
"scryptsy": "^2.1.0",
|
||||
"snackbarjs": "^1.1.0",
|
||||
"sortablejs": "^1.9.0",
|
||||
"split.js": "^1.5.10",
|
||||
"split.js": "^1.5.11",
|
||||
"ssdeep.js": "0.0.2",
|
||||
"ua-parser-js": "^0.7.19",
|
||||
"ua-parser-js": "^0.7.20",
|
||||
"utf8": "^3.0.0",
|
||||
"vkbeautify": "^0.99.3",
|
||||
"xmldom": "^0.1.27",
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require("postcss-import"),
|
||||
require("autoprefixer")({
|
||||
browsers: [
|
||||
"Chrome >= 40",
|
||||
"Firefox >= 35",
|
||||
"Edge >= 14"
|
||||
]
|
||||
}),
|
||||
require("autoprefixer"),
|
||||
require("postcss-css-variables")({
|
||||
preserve: true
|
||||
}),
|
||||
|
||||
@@ -201,9 +201,11 @@ class Utils {
|
||||
* Utils.parseEscapedChars("\\n");
|
||||
*/
|
||||
static parseEscapedChars(str) {
|
||||
return str.replace(/(\\)?\\([bfnrtv'"]|[0-3][0-7]{2}|[0-7]{1,2}|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\})/g, function(m, a, b) {
|
||||
return str.replace(/(\\)?\\([bfnrtv'"]|[0-3][0-7]{2}|[0-7]{1,2}|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\}|\\)/g, function(m, a, b) {
|
||||
if (a === "\\") return "\\"+b;
|
||||
switch (b[0]) {
|
||||
case "\\":
|
||||
return "\\";
|
||||
case "0":
|
||||
case "1":
|
||||
case "2":
|
||||
|
||||
@@ -384,6 +384,9 @@
|
||||
"Contain Image",
|
||||
"Cover Image",
|
||||
"Image Hue/Saturation/Lightness",
|
||||
"Sharpen Image",
|
||||
"Convert Image Format",
|
||||
"Add Text To Image",
|
||||
"Hex Density chart",
|
||||
"Scatter chart",
|
||||
"Series chart",
|
||||
|
||||
251
src/core/lib/ImageManipulation.mjs
Normal file
251
src/core/lib/ImageManipulation.mjs
Normal file
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
* Image manipulation resources
|
||||
*
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -312,6 +312,11 @@ class Magic {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the recipe returned an empty buffer, do not continue
|
||||
if (_buffersEqual(output, new ArrayBuffer())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const magic = new Magic(output, this.opPatterns),
|
||||
speculativeResults = await magic.speculativeExecution(
|
||||
depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful, crib);
|
||||
@@ -395,7 +400,12 @@ class Magic {
|
||||
const recipe = new Recipe(recipeConfig);
|
||||
try {
|
||||
await recipe.execute(dish);
|
||||
return dish.get(Dish.ARRAY_BUFFER);
|
||||
// Return an empty buffer if the recipe did not run to completion
|
||||
if (recipe.lastRunOp === recipe.opList[recipe.opList.length - 1]) {
|
||||
return dish.get(Dish.ARRAY_BUFFER);
|
||||
} else {
|
||||
return new ArrayBuffer();
|
||||
}
|
||||
} catch (err) {
|
||||
// If there are errors, return an empty buffer
|
||||
return new ArrayBuffer();
|
||||
|
||||
93
src/core/lib/QRCode.mjs
Normal file
93
src/core/lib/QRCode.mjs
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* QR code resources
|
||||
*
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError";
|
||||
import jsQR from "jsqr";
|
||||
import qr from "qr-image";
|
||||
import jimp from "jimp";
|
||||
import Utils from "../Utils";
|
||||
|
||||
/**
|
||||
* Parses a QR code image from an image
|
||||
*
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {boolean} normalise
|
||||
* @returns {string}
|
||||
*/
|
||||
export async function parseQrCode(input, normalise) {
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error opening image. (${err})`);
|
||||
}
|
||||
|
||||
try {
|
||||
if (normalise) {
|
||||
image.rgba(false);
|
||||
image.background(0xFFFFFFFF);
|
||||
image.normalize();
|
||||
image.greyscale();
|
||||
image = await image.getBufferAsync(jimp.MIME_JPEG);
|
||||
image = await jimp.read(image);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error normalising iamge. (${err})`);
|
||||
}
|
||||
|
||||
const qrData = jsQR(image.bitmap.data, image.getWidth(), image.getHeight());
|
||||
if (qrData) {
|
||||
return qrData.data;
|
||||
} else {
|
||||
throw new OperationError("Could not read a QR code from the image.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a QR code from the input string
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {string} format
|
||||
* @param {number} moduleSize
|
||||
* @param {number} margin
|
||||
* @param {string} errorCorrection
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
export function generateQrCode(input, format, moduleSize, margin, errorCorrection) {
|
||||
const formats = ["SVG", "EPS", "PDF", "PNG"];
|
||||
if (!formats.includes(format.toUpperCase())) {
|
||||
throw new OperationError("Unsupported QR code format.");
|
||||
}
|
||||
|
||||
let qrImage;
|
||||
try {
|
||||
qrImage = qr.imageSync(input, {
|
||||
type: format,
|
||||
size: moduleSize,
|
||||
margin: margin,
|
||||
"ec_level": errorCorrection.charAt(0).toUpperCase()
|
||||
});
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error generating QR code. (${err})`);
|
||||
}
|
||||
|
||||
if (!qrImage) {
|
||||
throw new OperationError("Error generating QR code.");
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case "SVG":
|
||||
case "EPS":
|
||||
case "PDF":
|
||||
return Utils.strToArrayBuffer(qrImage);
|
||||
case "PNG":
|
||||
return qrImage.buffer;
|
||||
default:
|
||||
throw new OperationError("Unsupported QR code format.");
|
||||
}
|
||||
}
|
||||
266
src/core/operations/AddTextToImage.mjs
Normal file
266
src/core/operations/AddTextToImage.mjs
Normal file
@@ -0,0 +1,266 @@
|
||||
/**
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import { isImage } from "../lib/FileType";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import jimp from "jimp";
|
||||
|
||||
/**
|
||||
* Add Text To Image operation
|
||||
*/
|
||||
class AddTextToImage extends Operation {
|
||||
|
||||
/**
|
||||
* AddTextToImage constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Add Text To 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.infoURL = "";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
name: "Text",
|
||||
type: "string",
|
||||
value: ""
|
||||
},
|
||||
{
|
||||
name: "Horizontal align",
|
||||
type: "option",
|
||||
value: ["None", "Left", "Center", "Right"]
|
||||
},
|
||||
{
|
||||
name: "Vertical align",
|
||||
type: "option",
|
||||
value: ["None", "Top", "Middle", "Bottom"]
|
||||
},
|
||||
{
|
||||
name: "X position",
|
||||
type: "number",
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: "Y position",
|
||||
type: "number",
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: "Size",
|
||||
type: "number",
|
||||
value: 32,
|
||||
min: 8
|
||||
},
|
||||
{
|
||||
name: "Font face",
|
||||
type: "option",
|
||||
value: [
|
||||
"Roboto",
|
||||
"Roboto Black",
|
||||
"Roboto Mono",
|
||||
"Roboto Slab"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Red",
|
||||
type: "number",
|
||||
value: 255,
|
||||
min: 0,
|
||||
max: 255
|
||||
},
|
||||
{
|
||||
name: "Green",
|
||||
type: "number",
|
||||
value: 255,
|
||||
min: 0,
|
||||
max: 255
|
||||
},
|
||||
{
|
||||
name: "Blue",
|
||||
type: "number",
|
||||
value: 255,
|
||||
min: 0,
|
||||
max: 255
|
||||
},
|
||||
{
|
||||
name: "Alpha",
|
||||
type: "number",
|
||||
value: 255,
|
||||
min: 0,
|
||||
max: 255
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const text = args[0],
|
||||
hAlign = args[1],
|
||||
vAlign = args[2],
|
||||
size = args[5],
|
||||
fontFace = args[6],
|
||||
red = args[7],
|
||||
green = args[8],
|
||||
blue = args[9],
|
||||
alpha = args[10];
|
||||
|
||||
let xPos = args[3],
|
||||
yPos = args[4];
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
try {
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("Adding text to image...");
|
||||
|
||||
const fontsMap = {};
|
||||
const fonts = [
|
||||
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.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)
|
||||
.then(fonts => {
|
||||
fontsMap.Roboto = fonts[0];
|
||||
fontsMap["Roboto Black"] = fonts[1];
|
||||
fontsMap["Roboto Mono"] = fonts[2];
|
||||
fontsMap["Roboto Slab"] = fonts[3];
|
||||
});
|
||||
|
||||
|
||||
// Make Webpack load the png font images
|
||||
await Promise.all([
|
||||
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.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")
|
||||
]);
|
||||
|
||||
const font = fontsMap[fontFace];
|
||||
|
||||
// LoadFont needs an absolute url, so append the font name to self.docURL
|
||||
const jimpFont = await jimp.loadFont(self.docURL + "/" + font.default);
|
||||
|
||||
jimpFont.pages.forEach(function(page) {
|
||||
if (page.bitmap) {
|
||||
// Adjust the RGB values of the image pages to change the font colour.
|
||||
const pageWidth = page.bitmap.width;
|
||||
const pageHeight = page.bitmap.height;
|
||||
for (let ix = 0; ix < pageWidth; ix++) {
|
||||
for (let iy = 0; iy < pageHeight; iy++) {
|
||||
const idx = (iy * pageWidth + ix) << 2;
|
||||
|
||||
const newRed = page.bitmap.data[idx] - (255 - red);
|
||||
const newGreen = page.bitmap.data[idx + 1] - (255 - green);
|
||||
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
|
||||
page.bitmap.data[idx] = (newRed > 0) ? newRed : 0;
|
||||
page.bitmap.data[idx + 1] = (newGreen > 0) ? newGreen : 0;
|
||||
page.bitmap.data[idx + 2] = (newBlue > 0) ? newBlue : 0;
|
||||
page.bitmap.data[idx + 3] = (newAlpha > 0) ? newAlpha : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Create a temporary image to hold the rendered text
|
||||
const textImage = new jimp(jimp.measureText(jimpFont, text), jimp.measureTextHeight(jimpFont, text));
|
||||
textImage.print(jimpFont, 0, 0, text);
|
||||
|
||||
// Scale the rendered text image to the correct size
|
||||
const scaleFactor = size / 72;
|
||||
if (size !== 1) {
|
||||
// Use bicubic for decreasing size
|
||||
if (size > 1) {
|
||||
textImage.scale(scaleFactor, jimp.RESIZE_BICUBIC);
|
||||
} else {
|
||||
textImage.scale(scaleFactor, jimp.RESIZE_BILINEAR);
|
||||
}
|
||||
}
|
||||
|
||||
// If using the alignment options, calculate the pixel values AFTER the image has been scaled
|
||||
switch (hAlign) {
|
||||
case "Left":
|
||||
xPos = 0;
|
||||
break;
|
||||
case "Center":
|
||||
xPos = (image.getWidth() / 2) - (textImage.getWidth() / 2);
|
||||
break;
|
||||
case "Right":
|
||||
xPos = image.getWidth() - textImage.getWidth();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (vAlign) {
|
||||
case "Top":
|
||||
yPos = 0;
|
||||
break;
|
||||
case "Middle":
|
||||
yPos = (image.getHeight() / 2) - (textImage.getHeight() / 2);
|
||||
break;
|
||||
case "Bottom":
|
||||
yPos = image.getHeight() - textImage.getHeight();
|
||||
break;
|
||||
}
|
||||
|
||||
// Blit the rendered text image onto the original source image
|
||||
image.blit(textImage, xPos, yPos);
|
||||
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error adding text to image. (${err})`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the blurred image using HTML for web apps
|
||||
*
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default AddTextToImage;
|
||||
@@ -9,6 +9,7 @@ import OperationError from "../errors/OperationError";
|
||||
import { isImage } from "../lib/FileType";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import jimp from "jimp";
|
||||
import { gaussianBlur } from "../lib/ImageManipulation";
|
||||
|
||||
/**
|
||||
* Blur Image operation
|
||||
@@ -25,8 +26,8 @@ class BlurImage extends Operation {
|
||||
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.infoURL = "https://wikipedia.org/wiki/Gaussian_blur";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
@@ -44,37 +45,44 @@ class BlurImage extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [blurAmount, blurType] = args;
|
||||
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
try {
|
||||
switch (blurType){
|
||||
case "Fast":
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("Fast blurring image...");
|
||||
image.blur(blurAmount);
|
||||
break;
|
||||
case "Gaussian":
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("Gaussian blurring image. This may take a while...");
|
||||
image.gaussian(blurAmount);
|
||||
self.sendStatusMessage("Gaussian blurring image...");
|
||||
image = gaussianBlur(image, blurAmount);
|
||||
break;
|
||||
}
|
||||
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error blurring image. (${err})`);
|
||||
}
|
||||
@@ -83,18 +91,19 @@ class BlurImage extends Operation {
|
||||
/**
|
||||
* Displays the blurred image using HTML for web apps
|
||||
*
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
72
src/core/operations/Bzip2Compress.mjs
Normal file
72
src/core/operations/Bzip2Compress.mjs
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @author Matt C [me@mitt.dev]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Bzip2 from "libbzip2-wasm";
|
||||
|
||||
/**
|
||||
* Bzip2 Compress operation
|
||||
*/
|
||||
class Bzip2Compress extends Operation {
|
||||
|
||||
/**
|
||||
* Bzip2Compress constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Bzip2 Compress";
|
||||
this.module = "Compression";
|
||||
this.description = "Bzip2 is a compression library developed by Julian Seward (of GHC fame) that uses the Burrows-Wheeler algorithm. It only supports compressing single files and its compression is slow, however is more effective than Deflate (.gz & .zip).";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bzip2";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
name: "Block size (100s of kb)",
|
||||
type: "number",
|
||||
value: 9,
|
||||
min: 1,
|
||||
max: 9
|
||||
},
|
||||
{
|
||||
name: "Work factor",
|
||||
type: "number",
|
||||
value: 30
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {File}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [blockSize, workFactor] = args;
|
||||
if (input.byteLength <= 0) {
|
||||
throw new OperationError("Please provide an input.");
|
||||
}
|
||||
if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Loading Bzip2...");
|
||||
return new Promise((resolve, reject) => {
|
||||
Bzip2().then(bzip2 => {
|
||||
if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Compressing data...");
|
||||
const inpArray = new Uint8Array(input);
|
||||
const bzip2cc = bzip2.compressBZ2(inpArray, blockSize, workFactor);
|
||||
if (bzip2cc.error !== 0) {
|
||||
reject(new OperationError(bzip2cc.error_msg));
|
||||
} else {
|
||||
const output = bzip2cc.output;
|
||||
resolve(output.buffer.slice(output.byteOffset, output.byteLength + output.byteOffset));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Bzip2Compress;
|
||||
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @author Matt C [me@mitt.dev]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import bzip2 from "../vendor/bzip2";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Bzip2 from "libbzip2-wasm";
|
||||
|
||||
/**
|
||||
* Bzip2 Decompress operation
|
||||
@@ -23,9 +23,15 @@ class Bzip2Decompress extends Operation {
|
||||
this.module = "Compression";
|
||||
this.description = "Decompresses data using the Bzip2 algorithm.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bzip2";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
name: "Use low-memory, slower decompression algorithm",
|
||||
type: "boolean",
|
||||
value: false
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
{
|
||||
"match": "^\\x42\\x5a\\x68",
|
||||
@@ -41,14 +47,24 @@ class Bzip2Decompress extends Operation {
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const compressed = new Uint8Array(input);
|
||||
|
||||
try {
|
||||
const bzip2Reader = bzip2.array(compressed);
|
||||
return bzip2.simple(bzip2Reader);
|
||||
} catch (err) {
|
||||
throw new OperationError(err);
|
||||
const [small] = args;
|
||||
if (input.byteLength <= 0) {
|
||||
throw new OperationError("Please provide an input.");
|
||||
}
|
||||
if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Loading Bzip2...");
|
||||
return new Promise((resolve, reject) => {
|
||||
Bzip2().then(bzip2 => {
|
||||
if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Decompressing data...");
|
||||
const inpArray = new Uint8Array(input);
|
||||
const bzip2cc = bzip2.decompressBZ2(inpArray, small ? 1 : 0);
|
||||
if (bzip2cc.error !== 0) {
|
||||
reject(new OperationError(bzip2cc.error_msg));
|
||||
} else {
|
||||
const output = bzip2cc.output;
|
||||
resolve(output.buffer.slice(output.byteOffset, output.byteLength + output.byteOffset));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ class ContainImage extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Scales an image to the specified width and height, maintaining the aspect ratio. The image may be letterboxed.";
|
||||
this.infoURL = "";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
@@ -72,17 +72,22 @@ class ContainImage extends Operation {
|
||||
"Bezier"
|
||||
],
|
||||
defaultIndex: 1
|
||||
},
|
||||
{
|
||||
name: "Opaque background",
|
||||
type: "boolean",
|
||||
value: true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [width, height, hAlign, vAlign, alg] = args;
|
||||
const [width, height, hAlign, vAlign, alg, opaqueBg] = args;
|
||||
|
||||
const resizeMap = {
|
||||
"Nearest Neighbour": jimp.RESIZE_NEAREST_NEIGHBOR,
|
||||
@@ -101,13 +106,13 @@ class ContainImage extends Operation {
|
||||
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
|
||||
};
|
||||
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
@@ -115,8 +120,20 @@ class ContainImage extends Operation {
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("Containing image...");
|
||||
image.contain(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]);
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
|
||||
if (opaqueBg) {
|
||||
const newImage = await jimp.read(width, height, 0x000000FF);
|
||||
newImage.blit(image, 0, 0);
|
||||
image = newImage;
|
||||
}
|
||||
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error containing image. (${err})`);
|
||||
}
|
||||
@@ -124,18 +141,19 @@ class ContainImage extends Operation {
|
||||
|
||||
/**
|
||||
* Displays the contained image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
143
src/core/operations/ConvertImageFormat.mjs
Normal file
143
src/core/operations/ConvertImageFormat.mjs
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import { isImage } from "../lib/FileType";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import jimp from "jimp";
|
||||
|
||||
/**
|
||||
* Convert Image Format operation
|
||||
*/
|
||||
class ConvertImageFormat extends Operation {
|
||||
|
||||
/**
|
||||
* ConvertImageFormat constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Convert Image Format";
|
||||
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.infoURL = "https://wikipedia.org/wiki/Image_file_formats";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
name: "Output Format",
|
||||
type: "option",
|
||||
value: [
|
||||
"JPEG",
|
||||
"PNG",
|
||||
"BMP",
|
||||
"TIFF"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "JPEG Quality",
|
||||
type: "number",
|
||||
value: 80,
|
||||
min: 1,
|
||||
max: 100
|
||||
},
|
||||
{
|
||||
name: "PNG Filter Type",
|
||||
type: "option",
|
||||
value: [
|
||||
"Auto",
|
||||
"None",
|
||||
"Sub",
|
||||
"Up",
|
||||
"Average",
|
||||
"Paeth"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "PNG Deflate Level",
|
||||
type: "number",
|
||||
value: 9,
|
||||
min: 0,
|
||||
max: 9
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [format, jpegQuality, pngFilterType, pngDeflateLevel] = args;
|
||||
const formatMap = {
|
||||
"JPEG": jimp.MIME_JPEG,
|
||||
"PNG": jimp.MIME_PNG,
|
||||
"BMP": jimp.MIME_BMP,
|
||||
"TIFF": jimp.MIME_TIFF
|
||||
};
|
||||
|
||||
const pngFilterMap = {
|
||||
"Auto": jimp.PNG_FILTER_AUTO,
|
||||
"None": jimp.PNG_FILTER_NONE,
|
||||
"Sub": jimp.PNG_FILTER_SUB,
|
||||
"Up": jimp.PNG_FILTER_UP,
|
||||
"Average": jimp.PNG_FILTER_AVERAGE,
|
||||
"Paeth": jimp.PNG_FILTER_PATH // Incorrect spelling in Jimp library
|
||||
};
|
||||
|
||||
const mime = formatMap[format];
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file format.");
|
||||
}
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error opening image file. (${err})`);
|
||||
}
|
||||
try {
|
||||
switch (format) {
|
||||
case "JPEG":
|
||||
image.quality(jpegQuality);
|
||||
break;
|
||||
case "PNG":
|
||||
image.filterType(pngFilterMap[pngFilterType]);
|
||||
image.deflateLevel(pngDeflateLevel);
|
||||
break;
|
||||
}
|
||||
|
||||
const imageBuffer = await image.getBufferAsync(mime);
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error converting image format. (${err})`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the converted image using HTML for web apps
|
||||
*
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ConvertImageFormat;
|
||||
@@ -25,8 +25,8 @@ class CoverImage extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Scales the image to the given width and height, keeping the aspect ratio. The image may be clipped.";
|
||||
this.infoURL = "";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
@@ -77,7 +77,7 @@ class CoverImage extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
@@ -101,13 +101,13 @@ class CoverImage extends Operation {
|
||||
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
|
||||
};
|
||||
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
@@ -115,8 +115,13 @@ class CoverImage extends Operation {
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("Covering image...");
|
||||
image.cover(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]);
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error covering image. (${err})`);
|
||||
}
|
||||
@@ -124,18 +129,19 @@ class CoverImage extends Operation {
|
||||
|
||||
/**
|
||||
* Displays the covered image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ class CropImage extends Operation {
|
||||
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.infoURL = "https://wikipedia.org/wiki/Cropping_(image)";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
@@ -86,19 +86,19 @@ class CropImage extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [xPos, yPos, width, height, autocrop, autoTolerance, autoFrames, autoSymmetric, autoBorder] = args;
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
@@ -116,8 +116,13 @@ class CropImage extends Operation {
|
||||
image.crop(xPos, yPos, width, height);
|
||||
}
|
||||
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error cropping image. (${err})`);
|
||||
}
|
||||
@@ -125,18 +130,19 @@ class CropImage extends Operation {
|
||||
|
||||
/**
|
||||
* Displays the cropped image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,25 +25,25 @@ class DitherImage extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Apply a dither effect to an image.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Dither";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
@@ -51,8 +51,14 @@ class DitherImage extends Operation {
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("Applying dither to image...");
|
||||
image.dither565();
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error applying dither to image. (${err})`);
|
||||
}
|
||||
@@ -60,18 +66,19 @@ class DitherImage extends Operation {
|
||||
|
||||
/**
|
||||
* Displays the dithered image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,8 +4,13 @@
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import * as d3temp from "d3";
|
||||
import * as nodomtemp from "nodom";
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
|
||||
const d3 = d3temp.default ? d3temp.default : d3temp;
|
||||
const nodom = nodomtemp.default ? nodomtemp.default: nodomtemp;
|
||||
|
||||
/**
|
||||
* Entropy operation
|
||||
@@ -19,30 +24,45 @@ class Entropy extends Operation {
|
||||
super();
|
||||
|
||||
this.name = "Entropy";
|
||||
this.module = "Default";
|
||||
this.module = "Charts";
|
||||
this.description = "Shannon Entropy, in the context of information theory, is a measure of the rate at which information is produced by a source of data. It can be used, in a broad sense, to detect whether data is likely to be structured or unstructured. 8 is the maximum, representing highly unstructured, 'random' data. English language text usually falls somewhere between 3.5 and 5. Properly encrypted or compressed data should have an entropy of over 7.5.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Entropy_(information_theory)";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "number";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "json";
|
||||
this.presentType = "html";
|
||||
this.args = [];
|
||||
this.args = [
|
||||
{
|
||||
"name": "Visualisation",
|
||||
"type": "option",
|
||||
"value": ["Shannon scale", "Histogram (Bar)", "Histogram (Line)", "Curve", "Image"]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* Calculates the frequency of bytes in the input.
|
||||
*
|
||||
* @param {Uint8Array} input
|
||||
* @returns {number}
|
||||
*/
|
||||
run(input, args) {
|
||||
calculateShannonEntropy(input) {
|
||||
const prob = [],
|
||||
uniques = input.unique(),
|
||||
str = Utils.byteArrayToChars(input);
|
||||
let i;
|
||||
occurrences = new Array(256).fill(0);
|
||||
|
||||
for (i = 0; i < uniques.length; i++) {
|
||||
prob.push(str.count(Utils.chr(uniques[i])) / input.length);
|
||||
// Count occurrences of each byte in the input
|
||||
let i;
|
||||
for (i = 0; i < input.length; i++) {
|
||||
occurrences[input[i]]++;
|
||||
}
|
||||
|
||||
// Store probability list
|
||||
for (i = 0; i < occurrences.length; i++) {
|
||||
if (occurrences[i] > 0) {
|
||||
prob.push(occurrences[i] / input.length);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate Shannon entropy
|
||||
let entropy = 0,
|
||||
p;
|
||||
|
||||
@@ -54,44 +74,357 @@ class Entropy extends Operation {
|
||||
return -entropy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the scanning entropy of the input
|
||||
*
|
||||
* @param {Uint8Array} inputBytes
|
||||
* @returns {Object}
|
||||
*/
|
||||
calculateScanningEntropy(inputBytes) {
|
||||
const entropyData = [];
|
||||
const binWidth = inputBytes.length < 256 ? 8 : 256;
|
||||
|
||||
for (let bytePos = 0; bytePos < inputBytes.length; bytePos += binWidth) {
|
||||
const block = inputBytes.slice(bytePos, bytePos+binWidth);
|
||||
entropyData.push(this.calculateShannonEntropy(block));
|
||||
}
|
||||
|
||||
return { entropyData, binWidth };
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the frequency of bytes in the input.
|
||||
*
|
||||
* @param {object} svg
|
||||
* @param {function} xScale
|
||||
* @param {function} yScale
|
||||
* @param {integer} svgHeight
|
||||
* @param {integer} svgWidth
|
||||
* @param {object} margins
|
||||
* @param {string} xTitle
|
||||
* @param {string} yTitle
|
||||
*/
|
||||
createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, title, xTitle, yTitle) {
|
||||
// Axes
|
||||
const yAxis = d3.axisLeft()
|
||||
.scale(yScale);
|
||||
|
||||
const xAxis = d3.axisBottom()
|
||||
.scale(xScale);
|
||||
|
||||
svg.append("g")
|
||||
.attr("transform", `translate(0, ${svgHeight - margins.bottom})`)
|
||||
.call(xAxis);
|
||||
|
||||
svg.append("g")
|
||||
.attr("transform", `translate(${margins.left},0)`)
|
||||
.call(yAxis);
|
||||
|
||||
// Axes labels
|
||||
svg.append("text")
|
||||
.attr("transform", "rotate(-90)")
|
||||
.attr("y", 0 - margins.left)
|
||||
.attr("x", 0 - (svgHeight / 2))
|
||||
.attr("dy", "1em")
|
||||
.style("text-anchor", "middle")
|
||||
.text(yTitle);
|
||||
|
||||
svg.append("text")
|
||||
.attr("transform", `translate(${svgWidth / 2}, ${svgHeight - margins.bottom + 40})`)
|
||||
.style("text-anchor", "middle")
|
||||
.text(xTitle);
|
||||
|
||||
// Add title
|
||||
svg.append("text")
|
||||
.attr("transform", `translate(${svgWidth / 2}, ${margins.top - 10})`)
|
||||
.style("text-anchor", "middle")
|
||||
.text(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the frequency of bytes in the input.
|
||||
*
|
||||
* @param {Uint8Array} inputBytes
|
||||
* @returns {number[]}
|
||||
*/
|
||||
calculateByteFrequency(inputBytes) {
|
||||
const freq = new Array(256).fill(0);
|
||||
if (inputBytes.length === 0) return freq;
|
||||
|
||||
// Count occurrences of each byte in the input
|
||||
let i;
|
||||
for (i = 0; i < inputBytes.length; i++) {
|
||||
freq[inputBytes[i]]++;
|
||||
}
|
||||
|
||||
for (i = 0; i < freq.length; i++) {
|
||||
freq[i] = freq[i] / inputBytes.length;
|
||||
}
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the frequency of bytes in the input.
|
||||
*
|
||||
* @param {number[]} byteFrequency
|
||||
* @returns {HTML}
|
||||
*/
|
||||
createByteFrequencyLineHistogram(byteFrequency) {
|
||||
const margins = { top: 30, right: 20, bottom: 50, left: 30 };
|
||||
|
||||
const svgWidth = 500,
|
||||
svgHeight = 500;
|
||||
|
||||
const document = new nodom.Document();
|
||||
let svg = document.createElement("svg");
|
||||
|
||||
svg = d3.select(svg)
|
||||
.attr("width", "100%")
|
||||
.attr("height", "100%")
|
||||
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
|
||||
|
||||
const yScale = d3.scaleLinear()
|
||||
.domain([0, d3.max(byteFrequency, d => d)])
|
||||
.range([svgHeight - margins.bottom, margins.top]);
|
||||
|
||||
const xScale = d3.scaleLinear()
|
||||
.domain([0, byteFrequency.length - 1])
|
||||
.range([margins.left, svgWidth - margins.right]);
|
||||
|
||||
const line = d3.line()
|
||||
.x((_, i) => xScale(i))
|
||||
.y(d => yScale(d))
|
||||
.curve(d3.curveMonotoneX);
|
||||
|
||||
svg.append("path")
|
||||
.datum(byteFrequency)
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", "steelblue")
|
||||
.attr("d", line);
|
||||
|
||||
this.createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, "", "Byte", "Byte Frequency");
|
||||
|
||||
return svg._groups[0][0].outerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a byte frequency histogram
|
||||
*
|
||||
* @param {number[]} byteFrequency
|
||||
* @returns {HTML}
|
||||
*/
|
||||
createByteFrequencyBarHistogram(byteFrequency) {
|
||||
const margins = { top: 30, right: 20, bottom: 50, left: 30 };
|
||||
|
||||
const svgWidth = 500,
|
||||
svgHeight = 500,
|
||||
binWidth = 1;
|
||||
|
||||
const document = new nodom.Document();
|
||||
let svg = document.createElement("svg");
|
||||
svg = d3.select(svg)
|
||||
.attr("width", "100%")
|
||||
.attr("height", "100%")
|
||||
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
|
||||
|
||||
const yExtent = d3.extent(byteFrequency, d => d);
|
||||
const yScale = d3.scaleLinear()
|
||||
.domain(yExtent)
|
||||
.range([svgHeight - margins.bottom, margins.top]);
|
||||
|
||||
const xScale = d3.scaleLinear()
|
||||
.domain([0, byteFrequency.length - 1])
|
||||
.range([margins.left - binWidth, svgWidth - margins.right]);
|
||||
|
||||
svg.selectAll("rect")
|
||||
.data(byteFrequency)
|
||||
.enter().append("rect")
|
||||
.attr("x", (_, i) => xScale(i) + binWidth)
|
||||
.attr("y", dataPoint => yScale(dataPoint))
|
||||
.attr("width", binWidth)
|
||||
.attr("height", dataPoint => yScale(yExtent[0]) - yScale(dataPoint))
|
||||
.attr("fill", "blue");
|
||||
|
||||
this.createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, "", "Byte", "Byte Frequency");
|
||||
|
||||
return svg._groups[0][0].outerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a byte frequency histogram
|
||||
*
|
||||
* @param {number[]} entropyData
|
||||
* @returns {HTML}
|
||||
*/
|
||||
createEntropyCurve(entropyData) {
|
||||
const margins = { top: 30, right: 20, bottom: 50, left: 30 };
|
||||
|
||||
const svgWidth = 500,
|
||||
svgHeight = 500;
|
||||
|
||||
const document = new nodom.Document();
|
||||
let svg = document.createElement("svg");
|
||||
svg = d3.select(svg)
|
||||
.attr("width", "100%")
|
||||
.attr("height", "100%")
|
||||
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
|
||||
|
||||
const yScale = d3.scaleLinear()
|
||||
.domain([0, d3.max(entropyData, d => d)])
|
||||
.range([svgHeight - margins.bottom, margins.top]);
|
||||
|
||||
const xScale = d3.scaleLinear()
|
||||
.domain([0, entropyData.length])
|
||||
.range([margins.left, svgWidth - margins.right]);
|
||||
|
||||
const line = d3.line()
|
||||
.x((_, i) => xScale(i))
|
||||
.y(d => yScale(d))
|
||||
.curve(d3.curveMonotoneX);
|
||||
|
||||
if (entropyData.length > 0) {
|
||||
svg.append("path")
|
||||
.datum(entropyData)
|
||||
.attr("d", line);
|
||||
|
||||
svg.selectAll("path").attr("fill", "none").attr("stroke", "steelblue");
|
||||
}
|
||||
|
||||
this.createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, "Scanning Entropy", "Block", "Entropy");
|
||||
|
||||
return svg._groups[0][0].outerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an image representation of the entropy
|
||||
*
|
||||
* @param {number[]} entropyData
|
||||
* @returns {HTML}
|
||||
*/
|
||||
createEntropyImage(entropyData) {
|
||||
const svgHeight = 100,
|
||||
svgWidth = 100,
|
||||
cellSize = 1,
|
||||
nodes = [];
|
||||
|
||||
for (let i = 0; i < entropyData.length; i++) {
|
||||
nodes.push({
|
||||
x: i % svgWidth,
|
||||
y: Math.floor(i / svgWidth),
|
||||
entropy: entropyData[i]
|
||||
});
|
||||
}
|
||||
|
||||
const document = new nodom.Document();
|
||||
let svg = document.createElement("svg");
|
||||
svg = d3.select(svg)
|
||||
.attr("width", "100%")
|
||||
.attr("height", "100%")
|
||||
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
|
||||
|
||||
const greyScale = d3.scaleLinear()
|
||||
.domain([0, d3.max(entropyData, d => d)])
|
||||
.range(["#000000", "#FFFFFF"])
|
||||
.interpolate(d3.interpolateRgb);
|
||||
|
||||
svg
|
||||
.selectAll("rect")
|
||||
.data(nodes)
|
||||
.enter().append("rect")
|
||||
.attr("x", d => d.x * cellSize)
|
||||
.attr("y", d => d.y * cellSize)
|
||||
.attr("width", cellSize)
|
||||
.attr("height", cellSize)
|
||||
.style("fill", d => greyScale(d.entropy));
|
||||
|
||||
return svg._groups[0][0].outerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the entropy as a scale bar for web apps.
|
||||
*
|
||||
* @param {number} entropy
|
||||
* @returns {html}
|
||||
* @returns {HTML}
|
||||
*/
|
||||
present(entropy) {
|
||||
createShannonEntropyVisualization(entropy) {
|
||||
return `Shannon entropy: ${entropy}
|
||||
<br><canvas id='chart-area'></canvas><br>
|
||||
- 0 represents no randomness (i.e. all the bytes in the data have the same value) whereas 8, the maximum, represents a completely random string.
|
||||
- Standard English text usually falls somewhere between 3.5 and 5.
|
||||
- Properly encrypted or compressed data of a reasonable length should have an entropy of over 7.5.
|
||||
<br><canvas id='chart-area'></canvas><br>
|
||||
- 0 represents no randomness (i.e. all the bytes in the data have the same value) whereas 8, the maximum, represents a completely random string.
|
||||
- Standard English text usually falls somewhere between 3.5 and 5.
|
||||
- Properly encrypted or compressed data of a reasonable length should have an entropy of over 7.5.
|
||||
|
||||
The following results show the entropy of chunks of the input data. Chunks with particularly high entropy could suggest encrypted or compressed sections.
|
||||
The following results show the entropy of chunks of the input data. Chunks with particularly high entropy could suggest encrypted or compressed sections.
|
||||
|
||||
<br><script>
|
||||
var canvas = document.getElementById("chart-area"),
|
||||
parentRect = canvas.parentNode.getBoundingClientRect(),
|
||||
entropy = ${entropy},
|
||||
height = parentRect.height * 0.25;
|
||||
<br><script>
|
||||
var canvas = document.getElementById("chart-area"),
|
||||
parentRect = canvas.parentNode.getBoundingClientRect(),
|
||||
entropy = ${entropy},
|
||||
height = parentRect.height * 0.25;
|
||||
|
||||
canvas.width = parentRect.width * 0.95;
|
||||
canvas.height = height > 150 ? 150 : height;
|
||||
canvas.width = parentRect.width * 0.95;
|
||||
canvas.height = height > 150 ? 150 : height;
|
||||
|
||||
CanvasComponents.drawScaleBar(canvas, entropy, 8, [
|
||||
{
|
||||
label: "English text",
|
||||
min: 3.5,
|
||||
max: 5
|
||||
},{
|
||||
label: "Encrypted/compressed",
|
||||
min: 7.5,
|
||||
max: 8
|
||||
}
|
||||
]);
|
||||
</script>`;
|
||||
CanvasComponents.drawScaleBar(canvas, entropy, 8, [
|
||||
{
|
||||
label: "English text",
|
||||
min: 3.5,
|
||||
max: 5
|
||||
},{
|
||||
label: "Encrypted/compressed",
|
||||
min: 7.5,
|
||||
max: 8
|
||||
}
|
||||
]);
|
||||
</script>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {json}
|
||||
*/
|
||||
run(input, args) {
|
||||
const visualizationType = args[0];
|
||||
input = new Uint8Array(input);
|
||||
|
||||
switch (visualizationType) {
|
||||
case "Histogram (Bar)":
|
||||
case "Histogram (Line)":
|
||||
return this.calculateByteFrequency(input);
|
||||
case "Curve":
|
||||
case "Image":
|
||||
return this.calculateScanningEntropy(input).entropyData;
|
||||
case "Shannon scale":
|
||||
default:
|
||||
return this.calculateShannonEntropy(input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the entropy in a visualisation for web apps.
|
||||
*
|
||||
* @param {json} entropyData
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
present(entropyData, args) {
|
||||
const visualizationType = args[0];
|
||||
|
||||
switch (visualizationType) {
|
||||
case "Histogram (Bar)":
|
||||
return this.createByteFrequencyBarHistogram(entropyData);
|
||||
case "Histogram (Line)":
|
||||
return this.createByteFrequencyLineHistogram(entropyData);
|
||||
case "Curve":
|
||||
return this.createEntropyCurve(entropyData);
|
||||
case "Image":
|
||||
return this.createEntropyImage(entropyData);
|
||||
case "Shannon scale":
|
||||
default:
|
||||
return this.createShannonEntropyVisualization(entropyData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Entropy;
|
||||
|
||||
@@ -25,8 +25,8 @@ class FlipImage extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Flips an image along its X or Y axis.";
|
||||
this.infoURL = "";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
@@ -38,19 +38,19 @@ class FlipImage extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [flipAxis] = args;
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid input file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
@@ -66,8 +66,13 @@ class FlipImage extends Operation {
|
||||
break;
|
||||
}
|
||||
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error flipping image. (${err})`);
|
||||
}
|
||||
@@ -75,18 +80,19 @@ class FlipImage extends Operation {
|
||||
|
||||
/**
|
||||
* Displays the flipped image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import qr from "qr-image";
|
||||
import { generateQrCode } from "../lib/QRCode";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import { isImage } from "../lib/FileType";
|
||||
import Utils from "../Utils";
|
||||
@@ -27,7 +27,7 @@ class GenerateQRCode extends Operation {
|
||||
this.description = "Generates a Quick Response (QR) code from the input text.<br><br>A QR code is a type of matrix barcode (or two-dimensional barcode) first designed in 1994 for the automotive industry in Japan. A barcode is a machine-readable optical label that contains information about the item to which it is attached.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/QR_code";
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
@@ -38,12 +38,14 @@ class GenerateQRCode extends Operation {
|
||||
{
|
||||
"name": "Module size (px)",
|
||||
"type": "number",
|
||||
"value": 5
|
||||
"value": 5,
|
||||
"min": 1
|
||||
},
|
||||
{
|
||||
"name": "Margin (num modules)",
|
||||
"type": "number",
|
||||
"value": 2
|
||||
"value": 2,
|
||||
"min": 0
|
||||
},
|
||||
{
|
||||
"name": "Error correction",
|
||||
@@ -57,61 +59,34 @@ class GenerateQRCode extends Operation {
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [format, size, margin, errorCorrection] = args;
|
||||
|
||||
// Create new QR image from the input data, and convert it to a buffer
|
||||
const qrImage = qr.imageSync(input, {
|
||||
type: format,
|
||||
size: size,
|
||||
margin: margin,
|
||||
"ec_level": errorCorrection.charAt(0).toUpperCase()
|
||||
});
|
||||
|
||||
if (qrImage == null) {
|
||||
throw new OperationError("Error generating QR code.");
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case "SVG":
|
||||
case "EPS":
|
||||
case "PDF":
|
||||
return [...Buffer.from(qrImage)];
|
||||
case "PNG":
|
||||
// Return the QR image buffer as a byte array
|
||||
return [...qrImage];
|
||||
default:
|
||||
throw new OperationError("Unsupported QR code format.");
|
||||
}
|
||||
return generateQrCode(input, format, size, margin, errorCorrection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the QR image using HTML for web apps
|
||||
*
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data, args) {
|
||||
if (!data.length) return "";
|
||||
|
||||
const [format] = args;
|
||||
|
||||
if (!data.byteLength && !data.length) return "";
|
||||
const dataArray = new Uint8Array(data),
|
||||
[format] = args;
|
||||
if (format === "PNG") {
|
||||
let dataURI = "data:";
|
||||
const mime = isImage(data);
|
||||
if (mime){
|
||||
dataURI += mime + ";";
|
||||
} else {
|
||||
throw new OperationError("Invalid PNG file generated by QR image");
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
dataURI += "base64," + toBase64(data);
|
||||
|
||||
return `<img src="${dataURI}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
return Utils.byteArrayToChars(data);
|
||||
return Utils.arrayBufferToStr(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ class ImageBrightnessContrast extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Adjust the brightness or contrast of an image.";
|
||||
this.infoURL = "";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
@@ -47,19 +47,19 @@ class ImageBrightnessContrast extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [brightness, contrast] = args;
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
@@ -75,8 +75,13 @@ class ImageBrightnessContrast extends Operation {
|
||||
image.contrast(contrast / 100);
|
||||
}
|
||||
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error adjusting image brightness or contrast. (${err})`);
|
||||
}
|
||||
@@ -84,18 +89,19 @@ class ImageBrightnessContrast extends Operation {
|
||||
|
||||
/**
|
||||
* Displays the image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ class ImageFilter extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Applies a greyscale or sepia filter to an image.";
|
||||
this.infoURL = "";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
@@ -41,19 +41,19 @@ class ImageFilter extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [filterType] = args;
|
||||
if (!isImage(input)){
|
||||
if (!isImage(new Uint8Array(input))){
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
@@ -66,8 +66,13 @@ class ImageFilter extends Operation {
|
||||
image.sepia();
|
||||
}
|
||||
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error applying filter to image. (${err})`);
|
||||
}
|
||||
@@ -75,18 +80,19 @@ class ImageFilter extends Operation {
|
||||
|
||||
/**
|
||||
* Displays the blurred image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ class ImageHueSaturationLightness extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Adjusts the hue / saturation / lightness (HSL) values of an image.";
|
||||
this.infoURL = "";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
@@ -54,20 +54,20 @@ class ImageHueSaturationLightness extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [hue, saturation, lightness] = args;
|
||||
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
@@ -102,8 +102,14 @@ class ImageHueSaturationLightness extends Operation {
|
||||
}
|
||||
]);
|
||||
}
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error adjusting image hue / saturation / lightness. (${err})`);
|
||||
}
|
||||
@@ -111,18 +117,19 @@ class ImageHueSaturationLightness extends Operation {
|
||||
|
||||
/**
|
||||
* Displays the image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ class ImageOpacity extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Adjust the opacity of an image.";
|
||||
this.infoURL = "";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
@@ -40,19 +40,19 @@ class ImageOpacity extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [opacity] = args;
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
@@ -61,8 +61,13 @@ class ImageOpacity extends Operation {
|
||||
self.sendStatusMessage("Changing image opacity...");
|
||||
image.opacity(opacity / 100);
|
||||
|
||||
const imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
return [...imageBuffer];
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error changing image opacity. (${err})`);
|
||||
}
|
||||
@@ -70,18 +75,19 @@ class ImageOpacity extends Operation {
|
||||
|
||||
/**
|
||||
* Displays the image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,25 +25,25 @@ class InvertImage extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Invert the colours of an image.";
|
||||
this.infoURL = "";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid input file format.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
@@ -51,8 +51,14 @@ class InvertImage extends Operation {
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("Inverting image...");
|
||||
image.invert();
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error inverting image. (${err})`);
|
||||
}
|
||||
@@ -60,18 +66,19 @@ class InvertImage extends Operation {
|
||||
|
||||
/**
|
||||
* Displays the inverted image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,44 +25,59 @@ class NormaliseImage extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Normalise the image colours.";
|
||||
this.infoURL = "";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType= "html";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
const image = await jimp.read(Buffer.from(input));
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error opening image file. (${err})`);
|
||||
}
|
||||
|
||||
image.normalize();
|
||||
try {
|
||||
image.normalize();
|
||||
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error normalising image. (${err})`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the normalised image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import { isImage } from "../lib/FileType";
|
||||
import jsqr from "jsqr";
|
||||
import jimp from "jimp";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { parseQrCode } from "../lib/QRCode";
|
||||
|
||||
/**
|
||||
* Parse QR Code operation
|
||||
@@ -25,7 +24,7 @@ class ParseQRCode extends Operation {
|
||||
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.infoURL = "https://wikipedia.org/wiki/QR_code";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
@@ -34,69 +33,28 @@ class ParseQRCode extends Operation {
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
{
|
||||
"match": "^(?:\\xff\\xd8\\xff|\\x89\\x50\\x4e\\x47|\\x47\\x49\\x46|.{8}\\x57\\x45\\x42\\x50|\\x42\\x4d)",
|
||||
"flags": "",
|
||||
"args": [false],
|
||||
"useful": true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [normalise] = args;
|
||||
|
||||
// Make sure that the input is an image
|
||||
if (!isImage(input)) throw new OperationError("Invalid file type.");
|
||||
|
||||
let image = input;
|
||||
|
||||
if (normalise) {
|
||||
// Process the image to be easier to read by jsqr
|
||||
// Disables the alpha channel
|
||||
// Sets the image default background to white
|
||||
// Normalises the image colours
|
||||
// Makes the image greyscale
|
||||
// Converts image to a JPEG
|
||||
image = await new Promise((resolve, reject) => {
|
||||
jimp.read(Buffer.from(input))
|
||||
.then(image => {
|
||||
image
|
||||
.rgba(false)
|
||||
.background(0xFFFFFFFF)
|
||||
.normalize()
|
||||
.greyscale()
|
||||
.getBuffer(jimp.MIME_JPEG, (error, result) => {
|
||||
resolve(result);
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
reject(new OperationError("Error reading the image file."));
|
||||
});
|
||||
});
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
if (image instanceof OperationError) {
|
||||
throw image;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
jimp.read(Buffer.from(image))
|
||||
.then(image => {
|
||||
if (image.bitmap != null) {
|
||||
const qrData = jsqr(image.bitmap.data, image.getWidth(), image.getHeight());
|
||||
if (qrData != null) {
|
||||
resolve(qrData.data);
|
||||
} else {
|
||||
reject(new OperationError("Couldn't read a QR code from the image."));
|
||||
}
|
||||
} else {
|
||||
reject(new OperationError("Error reading the image file."));
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
reject(new OperationError("Error reading the image file."));
|
||||
});
|
||||
});
|
||||
|
||||
return await parseQrCode(input, normalise);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ class ResizeImage extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Resizes an image to the specified width and height values.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Image_scaling";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
@@ -67,7 +67,7 @@ class ResizeImage extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
@@ -86,13 +86,13 @@ class ResizeImage extends Operation {
|
||||
"Bezier": jimp.RESIZE_BEZIER
|
||||
};
|
||||
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
@@ -110,8 +110,13 @@ class ResizeImage extends Operation {
|
||||
image.resize(width, height, resizeMap[resizeAlg]);
|
||||
}
|
||||
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error resizing image. (${err})`);
|
||||
}
|
||||
@@ -119,18 +124,19 @@ class ResizeImage extends Operation {
|
||||
|
||||
/**
|
||||
* Displays the resized image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ class RotateImage extends Operation {
|
||||
this.module = "Image";
|
||||
this.description = "Rotates an image by the specified number of degrees.";
|
||||
this.infoURL = "";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
@@ -38,20 +38,20 @@ class RotateImage extends Operation {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [degrees] = args;
|
||||
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
@@ -59,8 +59,14 @@ class RotateImage extends Operation {
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("Rotating image...");
|
||||
image.rotate(degrees);
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error rotating image. (${err})`);
|
||||
}
|
||||
@@ -68,18 +74,19 @@ class RotateImage extends Operation {
|
||||
|
||||
/**
|
||||
* Displays the rotated image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
168
src/core/operations/SharpenImage.mjs
Normal file
168
src/core/operations/SharpenImage.mjs
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import { isImage } from "../lib/FileType";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import { gaussianBlur } from "../lib/ImageManipulation";
|
||||
import jimp from "jimp";
|
||||
|
||||
/**
|
||||
* Sharpen Image operation
|
||||
*/
|
||||
class SharpenImage extends Operation {
|
||||
|
||||
/**
|
||||
* SharpenImage constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Sharpen Image";
|
||||
this.module = "Image";
|
||||
this.description = "Sharpens an image (Unsharp mask)";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Unsharp_masking";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
name: "Radius",
|
||||
type: "number",
|
||||
value: 2,
|
||||
min: 1
|
||||
},
|
||||
{
|
||||
name: "Amount",
|
||||
type: "number",
|
||||
value: 1,
|
||||
min: 0,
|
||||
step: 0.1
|
||||
},
|
||||
{
|
||||
name: "Threshold",
|
||||
type: "number",
|
||||
value: 10,
|
||||
min: 0,
|
||||
max: 100
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [radius, amount, threshold] = args;
|
||||
|
||||
if (!isImage(new Uint8Array(input))){
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
|
||||
try {
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("Sharpening image... (Cloning image)");
|
||||
const blurMask = image.clone();
|
||||
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("Sharpening image... (Blurring cloned image)");
|
||||
const blurImage = gaussianBlur(image.clone(), radius, 3);
|
||||
|
||||
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("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 blurGreen = blurImage.bitmap.data[idx + 1];
|
||||
const blurBlue = blurImage.bitmap.data[idx + 2];
|
||||
|
||||
const normalRed = this.bitmap.data[idx];
|
||||
const normalGreen = this.bitmap.data[idx + 1];
|
||||
const normalBlue = this.bitmap.data[idx + 2];
|
||||
|
||||
// Subtract blurred pixel value from normal image
|
||||
this.bitmap.data[idx] = (normalRed > blurRed) ? normalRed - blurRed : 0;
|
||||
this.bitmap.data[idx + 1] = (normalGreen > blurGreen) ? normalGreen - blurGreen : 0;
|
||||
this.bitmap.data[idx + 2] = (normalBlue > blurBlue) ? normalBlue - blurBlue : 0;
|
||||
});
|
||||
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("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 maskGreen = blurMask.bitmap.data[idx + 1];
|
||||
let maskBlue = blurMask.bitmap.data[idx + 2];
|
||||
|
||||
const normalRed = this.bitmap.data[idx];
|
||||
const normalGreen = this.bitmap.data[idx + 1];
|
||||
const normalBlue = this.bitmap.data[idx + 2];
|
||||
|
||||
// Calculate luminance
|
||||
const maskLuminance = (0.2126 * maskRed + 0.7152 * maskGreen + 0.0722 * maskBlue);
|
||||
const normalLuminance = (0.2126 * normalRed + 0.7152 * normalGreen + 0.0722 * normalBlue);
|
||||
|
||||
let luminanceDiff;
|
||||
if (maskLuminance > normalLuminance) {
|
||||
luminanceDiff = maskLuminance - normalLuminance;
|
||||
} else {
|
||||
luminanceDiff = normalLuminance - maskLuminance;
|
||||
}
|
||||
|
||||
// Scale mask colours by amount
|
||||
maskRed = maskRed * amount;
|
||||
maskGreen = maskGreen * amount;
|
||||
maskBlue = maskBlue * amount;
|
||||
|
||||
// Only change pixel value if the difference is higher than threshold
|
||||
if ((luminanceDiff / 255) * 100 >= threshold) {
|
||||
this.bitmap.data[idx] = (normalRed + maskRed) <= 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;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error sharpening image. (${err})`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the sharpened image using HTML for web apps
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default SharpenImage;
|
||||
265
src/core/vendor/bzip2.mjs
vendored
265
src/core/vendor/bzip2.mjs
vendored
@@ -1,265 +0,0 @@
|
||||
/** @license
|
||||
========================================================================
|
||||
bzip2.js - a small bzip2 decompression implementation
|
||||
|
||||
Copyright 2011 by antimatter15 (antimatter15@gmail.com)
|
||||
|
||||
Based on micro-bunzip by Rob Landley (rob@landley.net).
|
||||
|
||||
Copyright (c) 2011 by antimatter15 (antimatter15@gmail.com).
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
var bzip2 = {};
|
||||
|
||||
bzip2.array = function(bytes){
|
||||
var bit = 0, byte = 0;
|
||||
var BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ];
|
||||
return function(n){
|
||||
var result = 0;
|
||||
while(n > 0){
|
||||
var left = 8 - bit;
|
||||
if(n >= left){
|
||||
result <<= left;
|
||||
result |= (BITMASK[left] & bytes[byte++]);
|
||||
bit = 0;
|
||||
n -= left;
|
||||
}else{
|
||||
result <<= n;
|
||||
result |= ((bytes[byte] & (BITMASK[n] << (8 - n - bit))) >> (8 - n - bit));
|
||||
bit += n;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
bzip2.simple = function(bits){
|
||||
var size = bzip2.header(bits);
|
||||
var all = '', chunk = '';
|
||||
do{
|
||||
all += chunk;
|
||||
chunk = bzip2.decompress(bits, size);
|
||||
}while(chunk != -1);
|
||||
return all;
|
||||
}
|
||||
|
||||
bzip2.header = function(bits){
|
||||
if(bits(8*3) != 4348520) throw "No magic number found";
|
||||
var i = bits(8) - 48;
|
||||
if(i < 1 || i > 9) throw "Not a BZIP archive";
|
||||
return i;
|
||||
};
|
||||
|
||||
|
||||
//takes a function for reading the block data (starting with 0x314159265359)
|
||||
//a block size (0-9) (optional, defaults to 9)
|
||||
//a length at which to stop decompressing and return the output
|
||||
bzip2.decompress = function(bits, size, len){
|
||||
var MAX_HUFCODE_BITS = 20;
|
||||
var MAX_SYMBOLS = 258;
|
||||
var SYMBOL_RUNA = 0;
|
||||
var SYMBOL_RUNB = 1;
|
||||
var GROUP_SIZE = 50;
|
||||
|
||||
var bufsize = 100000 * size;
|
||||
for(var h = '', i = 0; i < 6; i++) h += bits(8).toString(16);
|
||||
if(h == "177245385090") return -1; //last block
|
||||
if(h != "314159265359") throw "Not valid bzip data";
|
||||
bits(32); //ignore CRC codes
|
||||
if(bits(1)) throw "Unsupported obsolete version";
|
||||
var origPtr = bits(24);
|
||||
if(origPtr > bufsize) throw "Initial position larger than buffer size";
|
||||
var t = bits(16);
|
||||
var symToByte = new Uint8Array(256),
|
||||
symTotal = 0;
|
||||
for (i = 0; i < 16; i++) {
|
||||
if(t & (1 << (15 - i))) {
|
||||
var k = bits(16);
|
||||
for(j = 0; j < 16; j++){
|
||||
if(k & (1 << (15 - j))){
|
||||
symToByte[symTotal++] = (16 * i) + j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var groupCount = bits(3);
|
||||
if(groupCount < 2 || groupCount > 6) throw "Error 1";
|
||||
var nSelectors = bits(15);
|
||||
if(nSelectors == 0) throw "Error";
|
||||
var mtfSymbol = []; //TODO: possibly replace JS array with typed arrays
|
||||
for(var i = 0; i < groupCount; i++) mtfSymbol[i] = i;
|
||||
var selectors = new Uint8Array(32768);
|
||||
|
||||
for(var i = 0; i < nSelectors; i++){
|
||||
for(var j = 0; bits(1); j++) if(j >= groupCount) throw "Error 2";
|
||||
var uc = mtfSymbol[j];
|
||||
mtfSymbol.splice(j, 1); //this is a probably inefficient MTF transform
|
||||
mtfSymbol.splice(0, 0, uc);
|
||||
selectors[i] = uc;
|
||||
}
|
||||
|
||||
var symCount = symTotal + 2;
|
||||
var groups = [];
|
||||
for(var j = 0; j < groupCount; j++){
|
||||
var length = new Uint8Array(MAX_SYMBOLS),
|
||||
temp = new Uint8Array(MAX_HUFCODE_BITS+1);
|
||||
t = bits(5); //lengths
|
||||
for(var i = 0; i < symCount; i++){
|
||||
while(true){
|
||||
if (t < 1 || t > MAX_HUFCODE_BITS) throw "Error 3";
|
||||
if(!bits(1)) break;
|
||||
if(!bits(1)) t++;
|
||||
else t--;
|
||||
}
|
||||
length[i] = t;
|
||||
}
|
||||
var minLen, maxLen;
|
||||
minLen = maxLen = length[0];
|
||||
for(var i = 1; i < symCount; i++){
|
||||
if(length[i] > maxLen) maxLen = length[i];
|
||||
else if(length[i] < minLen) minLen = length[i];
|
||||
}
|
||||
var hufGroup;
|
||||
hufGroup = groups[j] = {};
|
||||
hufGroup.permute = new Uint32Array(MAX_SYMBOLS);
|
||||
hufGroup.limit = new Uint32Array(MAX_HUFCODE_BITS + 1);
|
||||
hufGroup.base = new Uint32Array(MAX_HUFCODE_BITS + 1);
|
||||
hufGroup.minLen = minLen;
|
||||
hufGroup.maxLen = maxLen;
|
||||
var base = hufGroup.base.subarray(1);
|
||||
var limit = hufGroup.limit.subarray(1);
|
||||
var pp = 0;
|
||||
for(var i = minLen; i <= maxLen; i++)
|
||||
for(var t = 0; t < symCount; t++)
|
||||
if(length[t] == i) hufGroup.permute[pp++] = t;
|
||||
for(i = minLen; i <= maxLen; i++) temp[i] = limit[i] = 0;
|
||||
for(i = 0; i < symCount; i++) temp[length[i]]++;
|
||||
pp = t = 0;
|
||||
for(i = minLen; i < maxLen; i++) {
|
||||
pp += temp[i];
|
||||
limit[i] = pp - 1;
|
||||
pp <<= 1;
|
||||
base[i+1] = pp - (t += temp[i]);
|
||||
}
|
||||
limit[maxLen]=pp+temp[maxLen]-1;
|
||||
base[minLen]=0;
|
||||
}
|
||||
var byteCount = new Uint32Array(256);
|
||||
for(var i = 0; i < 256; i++) mtfSymbol[i] = i;
|
||||
var runPos, count, symCount, selector;
|
||||
runPos = count = symCount = selector = 0;
|
||||
var buf = new Uint32Array(bufsize);
|
||||
while(true){
|
||||
if(!(symCount--)){
|
||||
symCount = GROUP_SIZE - 1;
|
||||
if(selector >= nSelectors) throw "Error 4";
|
||||
hufGroup = groups[selectors[selector++]];
|
||||
base = hufGroup.base.subarray(1);
|
||||
limit = hufGroup.limit.subarray(1);
|
||||
}
|
||||
i = hufGroup.minLen;
|
||||
j = bits(i);
|
||||
while(true){
|
||||
if(i > hufGroup.maxLen) throw "Error 5";
|
||||
if(j <= limit[i]) break;
|
||||
i++;
|
||||
j = (j << 1) | bits(1);
|
||||
}
|
||||
j -= base[i];
|
||||
if(j < 0 || j >= MAX_SYMBOLS) throw "Error 6";
|
||||
var nextSym = hufGroup.permute[j];
|
||||
if (nextSym == SYMBOL_RUNA || nextSym == SYMBOL_RUNB) {
|
||||
if(!runPos){
|
||||
runPos = 1;
|
||||
t = 0;
|
||||
}
|
||||
if(nextSym == SYMBOL_RUNA) t += runPos;
|
||||
else t += 2 * runPos;
|
||||
runPos <<= 1;
|
||||
continue;
|
||||
}
|
||||
if(runPos){
|
||||
runPos = 0;
|
||||
if(count + t >= bufsize) throw "Error 7";
|
||||
uc = symToByte[mtfSymbol[0]];
|
||||
byteCount[uc] += t;
|
||||
while(t--) buf[count++] = uc;
|
||||
}
|
||||
if(nextSym > symTotal) break;
|
||||
if(count >= bufsize) throw "Error 8";
|
||||
i = nextSym -1;
|
||||
uc = mtfSymbol[i];
|
||||
mtfSymbol.splice(i, 1);
|
||||
mtfSymbol.splice(0, 0, uc);
|
||||
uc = symToByte[uc];
|
||||
byteCount[uc]++;
|
||||
buf[count++] = uc;
|
||||
}
|
||||
if(origPtr < 0 || origPtr >= count) throw "Error 9";
|
||||
var j = 0;
|
||||
for(var i = 0; i < 256; i++){
|
||||
k = j + byteCount[i];
|
||||
byteCount[i] = j;
|
||||
j = k;
|
||||
}
|
||||
for(var i = 0; i < count; i++){
|
||||
uc = buf[i] & 0xff;
|
||||
buf[byteCount[uc]] |= (i << 8);
|
||||
byteCount[uc]++;
|
||||
}
|
||||
var pos = 0, current = 0, run = 0;
|
||||
if(count) {
|
||||
pos = buf[origPtr];
|
||||
current = (pos & 0xff);
|
||||
pos >>= 8;
|
||||
run = -1;
|
||||
}
|
||||
count = count;
|
||||
var output = '';
|
||||
var copies, previous, outbyte;
|
||||
if(!len) len = Infinity;
|
||||
while(count){
|
||||
count--;
|
||||
previous = current;
|
||||
pos = buf[pos];
|
||||
current = pos & 0xff;
|
||||
pos >>= 8;
|
||||
if(run++ == 3){
|
||||
copies = current;
|
||||
outbyte = previous;
|
||||
current = -1;
|
||||
}else{
|
||||
copies = 1;
|
||||
outbyte = current;
|
||||
}
|
||||
while(copies--){
|
||||
output += (String.fromCharCode(outbyte));
|
||||
if(!--len) return output;
|
||||
}
|
||||
if(current != previous) run = 0;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
export default bzip2;
|
||||
@@ -244,7 +244,7 @@ class App {
|
||||
/**
|
||||
* Sets up the adjustable splitter to allow the user to resize areas of the page.
|
||||
*
|
||||
* @param {boolean} [minimise=false] - Set this flag if attempting to minimuse frames to 0 width
|
||||
* @param {boolean} [minimise=false] - Set this flag if attempting to minimise frames to 0 width
|
||||
*/
|
||||
initialiseSplitter(minimise=false) {
|
||||
if (this.columnSplitter) this.columnSplitter.destroy();
|
||||
@@ -252,9 +252,9 @@ class App {
|
||||
|
||||
this.columnSplitter = Split(["#operations", "#recipe", "#IO"], {
|
||||
sizes: [20, 30, 50],
|
||||
minSize: minimise ? [0, 0, 0] : [240, 370, 450],
|
||||
minSize: minimise ? [0, 0, 0] : [240, 310, 450],
|
||||
gutterSize: 4,
|
||||
expandToMin: false,
|
||||
expandToMin: true,
|
||||
onDrag: function() {
|
||||
this.manager.recipe.adjustWidth();
|
||||
}.bind(this)
|
||||
|
||||
@@ -617,6 +617,23 @@ class RecipeWaiter {
|
||||
ingredientRule.style.gridTemplateColumns = "auto auto auto auto";
|
||||
ingredientChildRule.style.gridColumn = "1 / span 4";
|
||||
}
|
||||
|
||||
// Hide Chef icon on Bake button if the page is compressed
|
||||
const bakeIcon = document.querySelector("#bake img");
|
||||
|
||||
if (recList.clientWidth < 370) {
|
||||
// Hide Chef icon on Bake button
|
||||
bakeIcon.style.display = "none";
|
||||
} else {
|
||||
bakeIcon.style.display = "inline-block";
|
||||
}
|
||||
|
||||
// Scale controls to fit pane width
|
||||
const controls = document.getElementById("controls");
|
||||
const controlsContent = document.getElementById("controls-content");
|
||||
const scale = (controls.clientWidth - 1) / controlsContent.scrollWidth;
|
||||
|
||||
controlsContent.style.transform = `translate(-50%, -50%) scale(${scale})`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
<ul id="rec-list" class="list-area no-select"></ul>
|
||||
|
||||
<div id="controls" class="no-select">
|
||||
<div class="d-flex align-items-center">
|
||||
<div id="controls-content" class="d-flex align-items-center">
|
||||
<button type="button" class="mx-2 btn btn-lg btn-secondary" id="step" data-toggle="tooltip" title="Step through the recipe">
|
||||
Step
|
||||
</button>
|
||||
|
||||
@@ -10,7 +10,7 @@ import "./stylesheets/index.js";
|
||||
// Libs
|
||||
import "arrive";
|
||||
import "snackbarjs";
|
||||
import "bootstrap-material-design";
|
||||
import "bootstrap-material-design/js/index";
|
||||
import "bootstrap-colorpicker";
|
||||
import moment from "moment-timezone";
|
||||
import * as CanvasComponents from "../core/lib/CanvasComponents";
|
||||
|
||||
485
src/web/static/fonts/bmfonts/Roboto72White.fnt
Normal file
485
src/web/static/fonts/bmfonts/Roboto72White.fnt
Normal file
@@ -0,0 +1,485 @@
|
||||
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
|
||||
common lineHeight=85 base=67 scaleW=512 scaleH=512 pages=1 packed=0
|
||||
page id=0 file="Roboto72White.png"
|
||||
chars count=98
|
||||
char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=0 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=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=18 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=34 x=446 y=319 width=16 height=19 xoffset=4 yoffset=12 xadvance=23 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=36 x=269 y=0 width=35 height=69 xoffset=3 yoffset=6 xadvance=40 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=38 x=79 y=155 width=43 height=56 xoffset=3 yoffset=13 xadvance=45 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=40 x=70 y=0 width=21 height=78 xoffset=4 yoffset=7 xadvance=25 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=42 x=342 y=319 width=32 height=32 xoffset=-1 yoffset=14 xadvance=31 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=44 x=433 y=319 width=13 height=21 xoffset=-1 yoffset=58 xadvance=14 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=46 x=17 y=360 width=10 height=11 xoffset=4 yoffset=58 xadvance=19 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=48 x=449 y=99 width=34 height=56 xoffset=3 yoffset=13 xadvance=40 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=50 x=195 y=155 width=37 height=55 xoffset=2 yoffset=13 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=52 x=128 y=265 width=39 height=54 xoffset=1 yoffset=14 xadvance=41 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=54 x=267 y=155 width=35 height=55 xoffset=4 yoffset=14 xadvance=41 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=56 x=414 y=99 width=35 height=56 xoffset=3 yoffset=13 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=58 x=495 y=265 width=10 height=41 xoffset=4 yoffset=28 xadvance=18 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=60 x=279 y=319 width=31 height=35 xoffset=2 yoffset=27 xadvance=37 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=62 x=310 y=319 width=32 height=35 xoffset=4 yoffset=27 xadvance=38 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=64 x=210 y=0 width=59 height=69 xoffset=3 yoffset=15 xadvance=65 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=66 x=385 y=155 width=37 height=54 xoffset=5 yoffset=14 xadvance=45 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=68 x=422 y=155 width=39 height=54 xoffset=5 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=70 x=0 y=211 width=34 height=54 xoffset=5 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=72 x=34 y=211 width=41 height=54 xoffset=5 yoffset=14 xadvance=51 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=74 x=122 y=155 width=34 height=55 xoffset=1 yoffset=14 xadvance=40 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=76 x=116 y=211 width=33 height=54 xoffset=5 yoffset=14 xadvance=39 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=78 x=202 y=211 width=41 height=54 xoffset=5 yoffset=14 xadvance=51 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=80 x=243 y=211 width=39 height=54 xoffset=5 yoffset=14 xadvance=45 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=82 x=282 y=211 width=40 height=54 xoffset=5 yoffset=14 xadvance=45 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=84 x=322 y=211 width=42 height=54 xoffset=1 yoffset=14 xadvance=44 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=86 x=364 y=211 width=47 height=54 xoffset=-1 yoffset=14 xadvance=46 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=88 x=0 y=265 width=44 height=54 xoffset=1 yoffset=14 xadvance=45 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=90 x=89 y=265 width=39 height=54 xoffset=2 yoffset=14 xadvance=43 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=92 x=385 y=0 width=30 height=58 xoffset=0 yoffset=14 xadvance=30 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=94 x=374 y=319 width=28 height=28 xoffset=1 yoffset=14 xadvance=30 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=96 x=0 y=360 width=17 height=13 xoffset=1 yoffset=11 xadvance=22 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=98 x=415 y=0 width=34 height=57 xoffset=4 yoffset=12 xadvance=40 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=100 x=449 y=0 width=34 height=57 xoffset=2 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=102 x=483 y=0 width=25 height=57 xoffset=1 yoffset=11 xadvance=26 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=104 x=200 y=99 width=32 height=56 xoffset=4 yoffset=12 xadvance=40 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=106 x=193 y=0 width=17 height=71 xoffset=-4 yoffset=13 xadvance=17 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=108 x=266 y=99 width=9 height=56 xoffset=4 yoffset=12 xadvance=17 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=110 x=0 y=319 width=32 height=41 xoffset=4 yoffset=27 xadvance=40 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=112 x=275 y=99 width=34 height=56 xoffset=4 yoffset=27 xadvance=40 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=114 x=32 y=319 width=21 height=41 xoffset=4 yoffset=27 xadvance=25 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=116 x=245 y=265 width=23 height=51 xoffset=0 yoffset=18 xadvance=25 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=118 x=85 y=319 width=35 height=40 xoffset=0 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=120 x=174 y=319 width=36 height=40 xoffset=0 yoffset=28 xadvance=36 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=122 x=210 y=319 width=32 height=40 xoffset=2 yoffset=28 xadvance=35 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=124 x=348 y=0 width=7 height=63 xoffset=5 yoffset=14 xadvance=17 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=126 x=462 y=319 width=42 height=16 xoffset=4 yoffset=38 xadvance=50 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
|
||||
kernings count=382
|
||||
kerning first=70 second=74 amount=-9
|
||||
kerning first=34 second=97 amount=-2
|
||||
kerning first=34 second=101 amount=-2
|
||||
kerning first=34 second=113 amount=-2
|
||||
kerning first=34 second=99 amount=-2
|
||||
kerning first=70 second=99 amount=-1
|
||||
kerning first=88 second=113 amount=-1
|
||||
kerning first=84 second=46 amount=-8
|
||||
kerning first=84 second=119 amount=-2
|
||||
kerning first=87 second=97 amount=-1
|
||||
kerning first=90 second=117 amount=-1
|
||||
kerning first=39 second=97 amount=-2
|
||||
kerning first=69 second=111 amount=-1
|
||||
kerning first=87 second=41 amount=1
|
||||
kerning first=76 second=86 amount=-6
|
||||
kerning first=121 second=34 amount=1
|
||||
kerning first=40 second=86 amount=1
|
||||
kerning first=85 second=65 amount=-1
|
||||
kerning first=89 second=89 amount=1
|
||||
kerning first=72 second=65 amount=1
|
||||
kerning first=104 second=39 amount=-4
|
||||
kerning first=114 second=102 amount=1
|
||||
kerning first=89 second=42 amount=-2
|
||||
kerning first=114 second=34 amount=1
|
||||
kerning first=84 second=115 amount=-4
|
||||
kerning first=84 second=71 amount=-1
|
||||
kerning first=89 second=101 amount=-2
|
||||
kerning first=89 second=45 amount=-2
|
||||
kerning first=122 second=99 amount=-1
|
||||
kerning first=78 second=88 amount=1
|
||||
kerning first=68 second=89 amount=-2
|
||||
kerning first=122 second=103 amount=-1
|
||||
kerning first=78 second=84 amount=-1
|
||||
kerning first=86 second=103 amount=-2
|
||||
kerning first=89 second=67 amount=-1
|
||||
kerning first=89 second=79 amount=-1
|
||||
kerning first=75 second=111 amount=-1
|
||||
kerning first=111 second=120 amount=-1
|
||||
kerning first=87 second=44 amount=-4
|
||||
kerning first=91 second=74 amount=-1
|
||||
kerning first=120 second=111 amount=-1
|
||||
kerning first=84 second=111 amount=-3
|
||||
kerning first=102 second=113 amount=-1
|
||||
kerning first=80 second=88 amount=-1
|
||||
kerning first=66 second=84 amount=-1
|
||||
kerning first=65 second=87 amount=-2
|
||||
kerning first=86 second=100 amount=-2
|
||||
kerning first=122 second=100 amount=-1
|
||||
kerning first=75 second=118 amount=-1
|
||||
kerning first=70 second=118 amount=-1
|
||||
kerning first=73 second=88 amount=1
|
||||
kerning first=70 second=121 amount=-1
|
||||
kerning first=65 second=34 amount=-4
|
||||
kerning first=39 second=101 amount=-2
|
||||
kerning first=75 second=101 amount=-1
|
||||
kerning first=84 second=99 amount=-3
|
||||
kerning first=84 second=65 amount=-3
|
||||
kerning first=112 second=39 amount=-1
|
||||
kerning first=76 second=39 amount=-12
|
||||
kerning first=78 second=65 amount=1
|
||||
kerning first=88 second=45 amount=-2
|
||||
kerning first=65 second=121 amount=-2
|
||||
kerning first=34 second=111 amount=-2
|
||||
kerning first=89 second=85 amount=-3
|
||||
kerning first=114 second=99 amount=-1
|
||||
kerning first=86 second=125 amount=1
|
||||
kerning first=70 second=111 amount=-1
|
||||
kerning first=89 second=120 amount=-1
|
||||
kerning first=90 second=119 amount=-1
|
||||
kerning first=120 second=99 amount=-1
|
||||
kerning first=89 second=117 amount=-1
|
||||
kerning first=82 second=89 amount=-2
|
||||
kerning first=75 second=117 amount=-1
|
||||
kerning first=34 second=34 amount=-4
|
||||
kerning first=89 second=110 amount=-1
|
||||
kerning first=88 second=101 amount=-1
|
||||
kerning first=107 second=103 amount=-1
|
||||
kerning first=34 second=115 amount=-3
|
||||
kerning first=98 second=39 amount=-1
|
||||
kerning first=70 second=65 amount=-6
|
||||
kerning first=70 second=46 amount=-8
|
||||
kerning first=98 second=34 amount=-1
|
||||
kerning first=70 second=84 amount=1
|
||||
kerning first=114 second=100 amount=-1
|
||||
kerning first=88 second=79 amount=-1
|
||||
kerning first=39 second=113 amount=-2
|
||||
kerning first=114 second=103 amount=-1
|
||||
kerning first=77 second=65 amount=1
|
||||
kerning first=120 second=103 amount=-1
|
||||
kerning first=114 second=121 amount=1
|
||||
kerning first=89 second=100 amount=-2
|
||||
kerning first=80 second=65 amount=-5
|
||||
kerning first=121 second=111 amount=-1
|
||||
kerning first=84 second=74 amount=-8
|
||||
kerning first=122 second=111 amount=-1
|
||||
kerning first=114 second=118 amount=1
|
||||
kerning first=102 second=41 amount=1
|
||||
kerning first=122 second=113 amount=-1
|
||||
kerning first=89 second=122 amount=-1
|
||||
kerning first=89 second=38 amount=-1
|
||||
kerning first=81 second=89 amount=-1
|
||||
kerning first=114 second=111 amount=-1
|
||||
kerning first=46 second=34 amount=-6
|
||||
kerning first=84 second=112 amount=-4
|
||||
kerning first=112 second=34 amount=-1
|
||||
kerning first=76 second=34 amount=-12
|
||||
kerning first=102 second=125 amount=1
|
||||
kerning first=39 second=115 amount=-3
|
||||
kerning first=76 second=118 amount=-5
|
||||
kerning first=86 second=99 amount=-2
|
||||
kerning first=84 second=84 amount=1
|
||||
kerning first=86 second=65 amount=-3
|
||||
kerning first=87 second=101 amount=-1
|
||||
kerning first=67 second=125 amount=-1
|
||||
kerning first=120 second=113 amount=-1
|
||||
kerning first=118 second=46 amount=-4
|
||||
kerning first=88 second=103 amount=-1
|
||||
kerning first=111 second=122 amount=-1
|
||||
kerning first=77 second=84 amount=-1
|
||||
kerning first=114 second=46 amount=-4
|
||||
kerning first=34 second=39 amount=-4
|
||||
kerning first=114 second=44 amount=-4
|
||||
kerning first=69 second=84 amount=1
|
||||
kerning first=89 second=46 amount=-7
|
||||
kerning first=97 second=39 amount=-2
|
||||
kerning first=34 second=100 amount=-2
|
||||
kerning first=70 second=100 amount=-1
|
||||
kerning first=84 second=120 amount=-3
|
||||
kerning first=90 second=118 amount=-1
|
||||
kerning first=70 second=114 amount=-1
|
||||
kerning first=34 second=112 amount=-1
|
||||
kerning first=109 second=34 amount=-4
|
||||
kerning first=86 second=113 amount=-2
|
||||
kerning first=88 second=71 amount=-1
|
||||
kerning first=66 second=89 amount=-2
|
||||
kerning first=102 second=103 amount=-1
|
||||
kerning first=88 second=67 amount=-1
|
||||
kerning first=39 second=110 amount=-1
|
||||
kerning first=75 second=110 amount=-1
|
||||
kerning first=88 second=117 amount=-1
|
||||
kerning first=89 second=118 amount=-1
|
||||
kerning first=97 second=118 amount=-1
|
||||
kerning first=87 second=65 amount=-2
|
||||
kerning first=73 second=89 amount=-1
|
||||
kerning first=89 second=74 amount=-3
|
||||
kerning first=102 second=101 amount=-1
|
||||
kerning first=86 second=111 amount=-2
|
||||
kerning first=65 second=119 amount=-1
|
||||
kerning first=84 second=100 amount=-3
|
||||
kerning first=104 second=34 amount=-4
|
||||
kerning first=86 second=41 amount=1
|
||||
kerning first=111 second=34 amount=-5
|
||||
kerning first=40 second=89 amount=1
|
||||
kerning first=121 second=39 amount=1
|
||||
kerning first=68 second=90 amount=-1
|
||||
kerning first=114 second=113 amount=-1
|
||||
kerning first=68 second=88 amount=-1
|
||||
kerning first=98 second=120 amount=-1
|
||||
kerning first=110 second=34 amount=-4
|
||||
kerning first=119 second=44 amount=-4
|
||||
kerning first=119 second=46 amount=-4
|
||||
kerning first=118 second=44 amount=-4
|
||||
kerning first=84 second=114 amount=-3
|
||||
kerning first=86 second=97 amount=-2
|
||||
kerning first=68 second=86 amount=-1
|
||||
kerning first=86 second=93 amount=1
|
||||
kerning first=97 second=34 amount=-2
|
||||
kerning first=34 second=65 amount=-4
|
||||
kerning first=84 second=118 amount=-3
|
||||
kerning first=76 second=84 amount=-10
|
||||
kerning first=107 second=99 amount=-1
|
||||
kerning first=121 second=46 amount=-4
|
||||
kerning first=123 second=85 amount=-1
|
||||
kerning first=65 second=63 amount=-2
|
||||
kerning first=89 second=44 amount=-7
|
||||
kerning first=80 second=118 amount=1
|
||||
kerning first=112 second=122 amount=-1
|
||||
kerning first=79 second=65 amount=-1
|
||||
kerning first=80 second=121 amount=1
|
||||
kerning first=118 second=34 amount=1
|
||||
kerning first=87 second=45 amount=-2
|
||||
kerning first=69 second=100 amount=-1
|
||||
kerning first=87 second=103 amount=-1
|
||||
kerning first=112 second=120 amount=-1
|
||||
kerning first=68 second=44 amount=-4
|
||||
kerning first=86 second=45 amount=-1
|
||||
kerning first=39 second=34 amount=-4
|
||||
kerning first=68 second=46 amount=-4
|
||||
kerning first=65 second=89 amount=-3
|
||||
kerning first=69 second=118 amount=-1
|
||||
kerning first=88 second=99 amount=-1
|
||||
kerning first=87 second=46 amount=-4
|
||||
kerning first=47 second=47 amount=-8
|
||||
kerning first=73 second=65 amount=1
|
||||
kerning first=123 second=74 amount=-1
|
||||
kerning first=69 second=102 amount=-1
|
||||
kerning first=87 second=111 amount=-1
|
||||
kerning first=39 second=112 amount=-1
|
||||
kerning first=89 second=116 amount=-1
|
||||
kerning first=70 second=113 amount=-1
|
||||
kerning first=77 second=88 amount=1
|
||||
kerning first=84 second=32 amount=-1
|
||||
kerning first=90 second=103 amount=-1
|
||||
kerning first=65 second=86 amount=-3
|
||||
kerning first=75 second=112 amount=-1
|
||||
kerning first=39 second=109 amount=-1
|
||||
kerning first=75 second=81 amount=-1
|
||||
kerning first=89 second=115 amount=-2
|
||||
kerning first=84 second=83 amount=-1
|
||||
kerning first=89 second=87 amount=1
|
||||
kerning first=114 second=101 amount=-1
|
||||
kerning first=116 second=111 amount=-1
|
||||
kerning first=90 second=100 amount=-1
|
||||
kerning first=84 second=122 amount=-2
|
||||
kerning first=68 second=84 amount=-1
|
||||
kerning first=32 second=84 amount=-1
|
||||
kerning first=84 second=117 amount=-3
|
||||
kerning first=74 second=65 amount=-1
|
||||
kerning first=107 second=101 amount=-1
|
||||
kerning first=75 second=109 amount=-1
|
||||
kerning first=80 second=46 amount=-11
|
||||
kerning first=89 second=93 amount=1
|
||||
kerning first=89 second=65 amount=-3
|
||||
kerning first=87 second=117 amount=-1
|
||||
kerning first=89 second=81 amount=-1
|
||||
kerning first=39 second=103 amount=-2
|
||||
kerning first=86 second=101 amount=-2
|
||||
kerning first=86 second=117 amount=-1
|
||||
kerning first=84 second=113 amount=-3
|
||||
kerning first=34 second=110 amount=-1
|
||||
kerning first=89 second=84 amount=1
|
||||
kerning first=84 second=110 amount=-4
|
||||
kerning first=39 second=99 amount=-2
|
||||
kerning first=88 second=121 amount=-1
|
||||
kerning first=65 second=39 amount=-4
|
||||
kerning first=110 second=39 amount=-4
|
||||
kerning first=75 second=67 amount=-1
|
||||
kerning first=88 second=118 amount=-1
|
||||
kerning first=86 second=114 amount=-1
|
||||
kerning first=80 second=74 amount=-7
|
||||
kerning first=84 second=97 amount=-4
|
||||
kerning first=82 second=84 amount=-3
|
||||
kerning first=91 second=85 amount=-1
|
||||
kerning first=102 second=99 amount=-1
|
||||
kerning first=66 second=86 amount=-1
|
||||
kerning first=120 second=101 amount=-1
|
||||
kerning first=102 second=93 amount=1
|
||||
kerning first=75 second=100 amount=-1
|
||||
kerning first=84 second=79 amount=-1
|
||||
kerning first=111 second=121 amount=-1
|
||||
kerning first=75 second=121 amount=-1
|
||||
kerning first=81 second=87 amount=-1
|
||||
kerning first=107 second=113 amount=-1
|
||||
kerning first=120 second=100 amount=-1
|
||||
kerning first=90 second=79 amount=-1
|
||||
kerning first=89 second=114 amount=-1
|
||||
kerning first=122 second=101 amount=-1
|
||||
kerning first=111 second=118 amount=-1
|
||||
kerning first=82 second=86 amount=-1
|
||||
kerning first=67 second=84 amount=-1
|
||||
kerning first=70 second=101 amount=-1
|
||||
kerning first=89 second=83 amount=-1
|
||||
kerning first=114 second=97 amount=-1
|
||||
kerning first=70 second=97 amount=-1
|
||||
kerning first=89 second=102 amount=-1
|
||||
kerning first=78 second=89 amount=-1
|
||||
kerning first=70 second=44 amount=-8
|
||||
kerning first=44 second=39 amount=-6
|
||||
kerning first=84 second=45 amount=-8
|
||||
kerning first=89 second=121 amount=-1
|
||||
kerning first=84 second=86 amount=1
|
||||
kerning first=87 second=99 amount=-1
|
||||
kerning first=98 second=122 amount=-1
|
||||
kerning first=89 second=112 amount=-1
|
||||
kerning first=89 second=103 amount=-2
|
||||
kerning first=88 second=81 amount=-1
|
||||
kerning first=102 second=34 amount=1
|
||||
kerning first=109 second=39 amount=-4
|
||||
kerning first=81 second=84 amount=-2
|
||||
kerning first=121 second=97 amount=-1
|
||||
kerning first=89 second=99 amount=-2
|
||||
kerning first=89 second=125 amount=1
|
||||
kerning first=81 second=86 amount=-1
|
||||
kerning first=114 second=116 amount=2
|
||||
kerning first=114 second=119 amount=1
|
||||
kerning first=84 second=44 amount=-8
|
||||
kerning first=102 second=39 amount=1
|
||||
kerning first=44 second=34 amount=-6
|
||||
kerning first=34 second=109 amount=-1
|
||||
kerning first=75 second=119 amount=-2
|
||||
kerning first=76 second=65 amount=1
|
||||
kerning first=84 second=81 amount=-1
|
||||
kerning first=76 second=121 amount=-5
|
||||
kerning first=69 second=101 amount=-1
|
||||
kerning first=89 second=111 amount=-2
|
||||
kerning first=80 second=90 amount=-1
|
||||
kerning first=89 second=97 amount=-3
|
||||
kerning first=89 second=109 amount=-1
|
||||
kerning first=90 second=99 amount=-1
|
||||
kerning first=89 second=86 amount=1
|
||||
kerning first=79 second=88 amount=-1
|
||||
kerning first=70 second=103 amount=-1
|
||||
kerning first=34 second=103 amount=-2
|
||||
kerning first=84 second=67 amount=-1
|
||||
kerning first=76 second=79 amount=-2
|
||||
kerning first=89 second=41 amount=1
|
||||
kerning first=65 second=118 amount=-2
|
||||
kerning first=75 second=71 amount=-1
|
||||
kerning first=76 second=87 amount=-5
|
||||
kerning first=77 second=89 amount=-1
|
||||
kerning first=90 second=113 amount=-1
|
||||
kerning first=79 second=89 amount=-2
|
||||
kerning first=118 second=111 amount=-1
|
||||
kerning first=118 second=97 amount=-1
|
||||
kerning first=88 second=100 amount=-1
|
||||
kerning first=90 second=121 amount=-1
|
||||
kerning first=89 second=113 amount=-2
|
||||
kerning first=84 second=87 amount=1
|
||||
kerning first=39 second=111 amount=-2
|
||||
kerning first=80 second=44 amount=-11
|
||||
kerning first=39 second=100 amount=-2
|
||||
kerning first=75 second=113 amount=-1
|
||||
kerning first=88 second=111 amount=-1
|
||||
kerning first=84 second=89 amount=1
|
||||
kerning first=84 second=103 amount=-3
|
||||
kerning first=70 second=117 amount=-1
|
||||
kerning first=67 second=41 amount=-1
|
||||
kerning first=89 second=71 amount=-1
|
||||
kerning first=121 second=44 amount=-4
|
||||
kerning first=97 second=121 amount=-1
|
||||
kerning first=87 second=113 amount=-1
|
||||
kerning first=73 second=84 amount=-1
|
||||
kerning first=84 second=101 amount=-3
|
||||
kerning first=75 second=99 amount=-1
|
||||
kerning first=65 second=85 amount=-1
|
||||
kerning first=76 second=67 amount=-2
|
||||
kerning first=76 second=81 amount=-2
|
||||
kerning first=75 second=79 amount=-1
|
||||
kerning first=39 second=65 amount=-4
|
||||
kerning first=76 second=117 amount=-2
|
||||
kerning first=65 second=84 amount=-5
|
||||
kerning first=90 second=101 amount=-1
|
||||
kerning first=84 second=121 amount=-3
|
||||
kerning first=69 second=99 amount=-1
|
||||
kerning first=114 second=39 amount=1
|
||||
kerning first=84 second=109 amount=-4
|
||||
kerning first=76 second=119 amount=-3
|
||||
kerning first=76 second=85 amount=-2
|
||||
kerning first=65 second=116 amount=-1
|
||||
kerning first=76 second=71 amount=-2
|
||||
kerning first=79 second=90 amount=-1
|
||||
kerning first=107 second=100 amount=-1
|
||||
kerning first=90 second=111 amount=-1
|
||||
kerning first=79 second=44 amount=-4
|
||||
kerning first=75 second=45 amount=-2
|
||||
kerning first=40 second=87 amount=1
|
||||
kerning first=79 second=86 amount=-1
|
||||
kerning first=102 second=100 amount=-1
|
||||
kerning first=72 second=89 amount=-1
|
||||
kerning first=72 second=88 amount=1
|
||||
kerning first=79 second=46 amount=-4
|
||||
kerning first=76 second=89 amount=-8
|
||||
kerning first=68 second=65 amount=-1
|
||||
kerning first=79 second=84 amount=-1
|
||||
kerning first=87 second=100 amount=-1
|
||||
kerning first=75 second=103 amount=-1
|
||||
kerning first=90 second=67 amount=-1
|
||||
kerning first=69 second=103 amount=-1
|
||||
kerning first=90 second=71 amount=-1
|
||||
kerning first=86 second=44 amount=-8
|
||||
kerning first=69 second=121 amount=-1
|
||||
kerning first=87 second=114 amount=-1
|
||||
kerning first=118 second=39 amount=1
|
||||
kerning first=46 second=39 amount=-6
|
||||
kerning first=72 second=84 amount=-1
|
||||
kerning first=86 second=46 amount=-8
|
||||
kerning first=69 second=113 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
|
||||
BIN
src/web/static/fonts/bmfonts/Roboto72White.png
Normal file
BIN
src/web/static/fonts/bmfonts/Roboto72White.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
488
src/web/static/fonts/bmfonts/RobotoBlack72White.fnt
Normal file
488
src/web/static/fonts/bmfonts/RobotoBlack72White.fnt
Normal file
@@ -0,0 +1,488 @@
|
||||
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
|
||||
common lineHeight=85 base=67 scaleW=512 scaleH=512 pages=1 packed=0
|
||||
page id=0 file="RobotoBlack72White.png"
|
||||
chars count=98
|
||||
char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=0 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=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=18 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=34 x=207 y=362 width=22 height=22 xoffset=0 yoffset=12 xadvance=23 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=36 x=220 y=0 width=38 height=69 xoffset=2 yoffset=7 xadvance=42 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=38 x=216 y=156 width=48 height=56 xoffset=1 yoffset=13 xadvance=48 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=40 x=70 y=0 width=22 height=75 xoffset=3 yoffset=9 xadvance=25 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=42 x=103 y=362 width=36 height=34 xoffset=-1 yoffset=14 xadvance=33 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=44 x=483 y=320 width=16 height=25 xoffset=0 yoffset=57 xadvance=20 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=46 x=270 y=362 width=15 height=15 xoffset=3 yoffset=54 xadvance=22 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=48 x=77 y=156 width=38 height=56 xoffset=2 yoffset=13 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=50 x=383 y=156 width=39 height=55 xoffset=1 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=52 x=325 y=266 width=40 height=54 xoffset=1 yoffset=14 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=54 x=0 y=156 width=39 height=56 xoffset=2 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=56 x=473 y=99 width=38 height=56 xoffset=2 yoffset=13 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=58 x=471 y=266 width=15 height=43 xoffset=3 yoffset=26 xadvance=21 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=60 x=37 y=362 width=33 height=38 xoffset=1 yoffset=26 xadvance=37 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=62 x=70 y=362 width=33 height=38 xoffset=3 yoffset=26 xadvance=37 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=64 x=258 y=0 width=61 height=68 xoffset=1 yoffset=16 xadvance=64 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=66 x=53 y=212 width=42 height=54 xoffset=3 yoffset=14 xadvance=47 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=68 x=95 y=212 width=42 height=54 xoffset=3 yoffset=14 xadvance=47 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=70 x=475 y=156 width=36 height=54 xoffset=3 yoffset=14 xadvance=39 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=72 x=175 y=212 width=45 height=54 xoffset=3 yoffset=14 xadvance=51 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=74 x=264 y=156 width=37 height=55 xoffset=0 yoffset=14 xadvance=40 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=76 x=279 y=212 width=36 height=54 xoffset=3 yoffset=14 xadvance=39 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=78 x=373 y=212 width=45 height=54 xoffset=3 yoffset=14 xadvance=51 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=80 x=418 y=212 width=43 height=54 xoffset=3 yoffset=14 xadvance=48 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=82 x=461 y=212 width=43 height=54 xoffset=3 yoffset=14 xadvance=46 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=84 x=0 y=266 width=45 height=54 xoffset=0 yoffset=14 xadvance=45 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=86 x=45 y=266 width=51 height=54 xoffset=-2 yoffset=14 xadvance=48 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=88 x=160 y=266 width=48 height=54 xoffset=-1 yoffset=14 xadvance=46 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=90 x=257 y=266 width=42 height=54 xoffset=1 yoffset=14 xadvance=44 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=92 x=403 y=0 width=37 height=58 xoffset=-2 yoffset=14 xadvance=31 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=94 x=139 y=362 width=33 height=28 xoffset=0 yoffset=14 xadvance=32 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=96 x=285 y=362 width=23 height=13 xoffset=0 yoffset=12 xadvance=24 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=98 x=440 y=0 width=37 height=57 xoffset=2 yoffset=12 xadvance=40 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=100 x=0 y=99 width=37 height=57 xoffset=1 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=102 x=477 y=0 width=28 height=57 xoffset=0 yoffset=11 xadvance=27 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=104 x=255 y=99 width=36 height=56 xoffset=2 yoffset=12 xadvance=40 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=106 x=197 y=0 width=23 height=71 xoffset=-5 yoffset=12 xadvance=20 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=108 x=346 y=99 width=14 height=56 xoffset=3 yoffset=12 xadvance=20 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=110 x=244 y=320 width=36 height=41 xoffset=2 yoffset=27 xadvance=40 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=112 x=360 y=99 width=37 height=56 xoffset=2 yoffset=27 xadvance=40 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=114 x=486 y=266 width=25 height=41 xoffset=2 yoffset=27 xadvance=27 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=116 x=445 y=266 width=26 height=51 xoffset=0 yoffset=18 xadvance=25 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=118 x=316 y=320 width=39 height=40 xoffset=-1 yoffset=28 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=120 x=409 y=320 width=40 height=40 xoffset=-1 yoffset=28 xadvance=37 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=122 x=449 y=320 width=34 height=40 xoffset=1 yoffset=28 xadvance=36 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=124 x=366 y=0 width=8 height=63 xoffset=5 yoffset=14 xadvance=18 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=126 x=229 y=362 width=41 height=19 xoffset=2 yoffset=36 xadvance=45 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
|
||||
kernings count=385
|
||||
kerning first=84 second=74 amount=-8
|
||||
kerning first=86 second=100 amount=-2
|
||||
kerning first=114 second=113 amount=-1
|
||||
kerning first=70 second=121 amount=-1
|
||||
kerning first=34 second=99 amount=-2
|
||||
kerning first=70 second=99 amount=-1
|
||||
kerning first=69 second=99 amount=-1
|
||||
kerning first=88 second=113 amount=-1
|
||||
kerning first=84 second=46 amount=-9
|
||||
kerning first=87 second=97 amount=-1
|
||||
kerning first=90 second=117 amount=-1
|
||||
kerning first=39 second=97 amount=-2
|
||||
kerning first=69 second=111 amount=-1
|
||||
kerning first=87 second=41 amount=1
|
||||
kerning first=121 second=34 amount=1
|
||||
kerning first=40 second=86 amount=1
|
||||
kerning first=85 second=65 amount=-1
|
||||
kerning first=72 second=65 amount=1
|
||||
kerning first=114 second=102 amount=1
|
||||
kerning first=89 second=42 amount=-2
|
||||
kerning first=114 second=34 amount=1
|
||||
kerning first=75 second=67 amount=-1
|
||||
kerning first=89 second=85 amount=-3
|
||||
kerning first=77 second=88 amount=1
|
||||
kerning first=84 second=115 amount=-3
|
||||
kerning first=84 second=71 amount=-1
|
||||
kerning first=89 second=101 amount=-2
|
||||
kerning first=89 second=45 amount=-5
|
||||
kerning first=78 second=88 amount=1
|
||||
kerning first=68 second=89 amount=-2
|
||||
kerning first=122 second=103 amount=-1
|
||||
kerning first=78 second=84 amount=-1
|
||||
kerning first=86 second=103 amount=-2
|
||||
kerning first=89 second=79 amount=-1
|
||||
kerning first=75 second=111 amount=-1
|
||||
kerning first=111 second=120 amount=-1
|
||||
kerning first=87 second=44 amount=-5
|
||||
kerning first=67 second=84 amount=-1
|
||||
kerning first=84 second=111 amount=-7
|
||||
kerning first=84 second=83 amount=-1
|
||||
kerning first=102 second=113 amount=-1
|
||||
kerning first=39 second=101 amount=-2
|
||||
kerning first=80 second=88 amount=-2
|
||||
kerning first=66 second=84 amount=-1
|
||||
kerning first=65 second=87 amount=-1
|
||||
kerning first=122 second=100 amount=-1
|
||||
kerning first=75 second=118 amount=-1
|
||||
kerning first=73 second=65 amount=1
|
||||
kerning first=70 second=118 amount=-1
|
||||
kerning first=73 second=88 amount=1
|
||||
kerning first=82 second=89 amount=-2
|
||||
kerning first=65 second=34 amount=-4
|
||||
kerning first=120 second=99 amount=-1
|
||||
kerning first=84 second=99 amount=-3
|
||||
kerning first=84 second=65 amount=-4
|
||||
kerning first=112 second=39 amount=-1
|
||||
kerning first=76 second=39 amount=-10
|
||||
kerning first=78 second=65 amount=1
|
||||
kerning first=88 second=45 amount=-5
|
||||
kerning first=34 second=111 amount=-3
|
||||
kerning first=114 second=99 amount=-1
|
||||
kerning first=86 second=125 amount=1
|
||||
kerning first=70 second=111 amount=-1
|
||||
kerning first=89 second=120 amount=-1
|
||||
kerning first=90 second=119 amount=-1
|
||||
kerning first=89 second=89 amount=1
|
||||
kerning first=89 second=117 amount=-1
|
||||
kerning first=75 second=117 amount=-1
|
||||
kerning first=76 second=65 amount=1
|
||||
kerning first=34 second=34 amount=-1
|
||||
kerning first=89 second=110 amount=-1
|
||||
kerning first=88 second=101 amount=-1
|
||||
kerning first=107 second=103 amount=-1
|
||||
kerning first=34 second=115 amount=-3
|
||||
kerning first=80 second=44 amount=-14
|
||||
kerning first=98 second=39 amount=-1
|
||||
kerning first=70 second=65 amount=-7
|
||||
kerning first=89 second=116 amount=-1
|
||||
kerning first=70 second=46 amount=-10
|
||||
kerning first=98 second=34 amount=-1
|
||||
kerning first=70 second=84 amount=1
|
||||
kerning first=114 second=100 amount=-1
|
||||
kerning first=88 second=79 amount=-1
|
||||
kerning first=39 second=113 amount=-2
|
||||
kerning first=65 second=118 amount=-2
|
||||
kerning first=114 second=103 amount=-1
|
||||
kerning first=77 second=65 amount=1
|
||||
kerning first=120 second=103 amount=-1
|
||||
kerning first=65 second=110 amount=-2
|
||||
kerning first=114 second=121 amount=1
|
||||
kerning first=89 second=100 amount=-2
|
||||
kerning first=80 second=65 amount=-6
|
||||
kerning first=121 second=111 amount=-1
|
||||
kerning first=34 second=101 amount=-2
|
||||
kerning first=122 second=111 amount=-1
|
||||
kerning first=114 second=118 amount=1
|
||||
kerning first=102 second=41 amount=1
|
||||
kerning first=122 second=113 amount=-1
|
||||
kerning first=89 second=122 amount=-1
|
||||
kerning first=68 second=88 amount=-1
|
||||
kerning first=81 second=89 amount=-1
|
||||
kerning first=114 second=111 amount=-1
|
||||
kerning first=46 second=34 amount=-10
|
||||
kerning first=84 second=112 amount=-3
|
||||
kerning first=76 second=34 amount=-10
|
||||
kerning first=39 second=115 amount=-3
|
||||
kerning first=76 second=118 amount=-4
|
||||
kerning first=86 second=99 amount=-2
|
||||
kerning first=84 second=84 amount=1
|
||||
kerning first=120 second=111 amount=-1
|
||||
kerning first=65 second=79 amount=-1
|
||||
kerning first=87 second=101 amount=-1
|
||||
kerning first=67 second=125 amount=-1
|
||||
kerning first=120 second=113 amount=-1
|
||||
kerning first=118 second=46 amount=-6
|
||||
kerning first=88 second=103 amount=-1
|
||||
kerning first=111 second=122 amount=-1
|
||||
kerning first=77 second=84 amount=-1
|
||||
kerning first=114 second=46 amount=-6
|
||||
kerning first=34 second=39 amount=-1
|
||||
kerning first=65 second=121 amount=-2
|
||||
kerning first=114 second=44 amount=-6
|
||||
kerning first=69 second=84 amount=1
|
||||
kerning first=89 second=46 amount=-8
|
||||
kerning first=97 second=39 amount=-1
|
||||
kerning first=34 second=100 amount=-2
|
||||
kerning first=70 second=100 amount=-1
|
||||
kerning first=84 second=120 amount=-3
|
||||
kerning first=90 second=118 amount=-1
|
||||
kerning first=70 second=114 amount=-1
|
||||
kerning first=34 second=112 amount=-1
|
||||
kerning first=89 second=86 amount=1
|
||||
kerning first=86 second=113 amount=-2
|
||||
kerning first=88 second=71 amount=-1
|
||||
kerning first=122 second=99 amount=-1
|
||||
kerning first=66 second=89 amount=-2
|
||||
kerning first=102 second=103 amount=-1
|
||||
kerning first=88 second=67 amount=-1
|
||||
kerning first=39 second=110 amount=-1
|
||||
kerning first=88 second=117 amount=-1
|
||||
kerning first=89 second=118 amount=-1
|
||||
kerning first=97 second=118 amount=-1
|
||||
kerning first=87 second=65 amount=-2
|
||||
kerning first=89 second=67 amount=-1
|
||||
kerning first=89 second=74 amount=-3
|
||||
kerning first=102 second=101 amount=-1
|
||||
kerning first=86 second=111 amount=-2
|
||||
kerning first=65 second=119 amount=-1
|
||||
kerning first=84 second=100 amount=-3
|
||||
kerning first=120 second=100 amount=-1
|
||||
kerning first=104 second=34 amount=-3
|
||||
kerning first=86 second=41 amount=1
|
||||
kerning first=111 second=34 amount=-3
|
||||
kerning first=40 second=89 amount=1
|
||||
kerning first=121 second=39 amount=1
|
||||
kerning first=70 second=74 amount=-7
|
||||
kerning first=68 second=90 amount=-1
|
||||
kerning first=98 second=120 amount=-1
|
||||
kerning first=110 second=34 amount=-3
|
||||
kerning first=119 second=46 amount=-4
|
||||
kerning first=69 second=102 amount=-1
|
||||
kerning first=118 second=44 amount=-6
|
||||
kerning first=84 second=114 amount=-2
|
||||
kerning first=86 second=97 amount=-2
|
||||
kerning first=40 second=87 amount=1
|
||||
kerning first=65 second=109 amount=-2
|
||||
kerning first=68 second=86 amount=-1
|
||||
kerning first=86 second=93 amount=1
|
||||
kerning first=65 second=67 amount=-1
|
||||
kerning first=97 second=34 amount=-1
|
||||
kerning first=34 second=65 amount=-4
|
||||
kerning first=84 second=118 amount=-3
|
||||
kerning first=112 second=34 amount=-1
|
||||
kerning first=76 second=84 amount=-7
|
||||
kerning first=107 second=99 amount=-1
|
||||
kerning first=123 second=85 amount=-1
|
||||
kerning first=102 second=125 amount=1
|
||||
kerning first=65 second=63 amount=-3
|
||||
kerning first=89 second=44 amount=-8
|
||||
kerning first=80 second=118 amount=1
|
||||
kerning first=112 second=122 amount=-1
|
||||
kerning first=79 second=65 amount=-1
|
||||
kerning first=80 second=121 amount=1
|
||||
kerning first=118 second=34 amount=1
|
||||
kerning first=87 second=45 amount=-2
|
||||
kerning first=69 second=100 amount=-1
|
||||
kerning first=87 second=103 amount=-1
|
||||
kerning first=112 second=120 amount=-1
|
||||
kerning first=86 second=65 amount=-3
|
||||
kerning first=65 second=81 amount=-1
|
||||
kerning first=68 second=44 amount=-4
|
||||
kerning first=86 second=45 amount=-6
|
||||
kerning first=39 second=34 amount=-1
|
||||
kerning first=72 second=88 amount=1
|
||||
kerning first=68 second=46 amount=-4
|
||||
kerning first=65 second=89 amount=-5
|
||||
kerning first=69 second=118 amount=-1
|
||||
kerning first=89 second=38 amount=-1
|
||||
kerning first=88 second=99 amount=-1
|
||||
kerning first=65 second=71 amount=-1
|
||||
kerning first=91 second=74 amount=-1
|
||||
kerning first=75 second=101 amount=-1
|
||||
kerning first=39 second=112 amount=-1
|
||||
kerning first=70 second=113 amount=-1
|
||||
kerning first=119 second=44 amount=-4
|
||||
kerning first=72 second=89 amount=-1
|
||||
kerning first=90 second=103 amount=-1
|
||||
kerning first=65 second=86 amount=-3
|
||||
kerning first=84 second=119 amount=-2
|
||||
kerning first=34 second=110 amount=-1
|
||||
kerning first=39 second=109 amount=-1
|
||||
kerning first=75 second=81 amount=-1
|
||||
kerning first=89 second=115 amount=-2
|
||||
kerning first=89 second=87 amount=1
|
||||
kerning first=114 second=101 amount=-1
|
||||
kerning first=116 second=111 amount=-1
|
||||
kerning first=90 second=100 amount=-1
|
||||
kerning first=79 second=89 amount=-2
|
||||
kerning first=84 second=122 amount=-2
|
||||
kerning first=68 second=84 amount=-3
|
||||
kerning first=76 second=86 amount=-7
|
||||
kerning first=74 second=65 amount=-1
|
||||
kerning first=107 second=101 amount=-1
|
||||
kerning first=80 second=46 amount=-14
|
||||
kerning first=89 second=93 amount=1
|
||||
kerning first=89 second=65 amount=-5
|
||||
kerning first=87 second=117 amount=-1
|
||||
kerning first=89 second=81 amount=-1
|
||||
kerning first=39 second=103 amount=-2
|
||||
kerning first=86 second=101 amount=-2
|
||||
kerning first=86 second=117 amount=-1
|
||||
kerning first=84 second=113 amount=-3
|
||||
kerning first=87 second=46 amount=-5
|
||||
kerning first=47 second=47 amount=-9
|
||||
kerning first=75 second=103 amount=-1
|
||||
kerning first=89 second=84 amount=1
|
||||
kerning first=84 second=110 amount=-3
|
||||
kerning first=39 second=99 amount=-2
|
||||
kerning first=88 second=121 amount=-1
|
||||
kerning first=65 second=39 amount=-4
|
||||
kerning first=110 second=39 amount=-3
|
||||
kerning first=88 second=118 amount=-1
|
||||
kerning first=86 second=114 amount=-1
|
||||
kerning first=80 second=74 amount=-6
|
||||
kerning first=84 second=97 amount=-6
|
||||
kerning first=82 second=84 amount=-2
|
||||
kerning first=91 second=85 amount=-1
|
||||
kerning first=102 second=99 amount=-1
|
||||
kerning first=66 second=86 amount=-1
|
||||
kerning first=120 second=101 amount=-1
|
||||
kerning first=102 second=93 amount=1
|
||||
kerning first=75 second=100 amount=-1
|
||||
kerning first=84 second=79 amount=-1
|
||||
kerning first=44 second=39 amount=-10
|
||||
kerning first=111 second=121 amount=-1
|
||||
kerning first=75 second=121 amount=-1
|
||||
kerning first=81 second=87 amount=-1
|
||||
kerning first=107 second=113 amount=-1
|
||||
kerning first=90 second=79 amount=-1
|
||||
kerning first=89 second=114 amount=-1
|
||||
kerning first=122 second=101 amount=-1
|
||||
kerning first=111 second=118 amount=-1
|
||||
kerning first=82 second=86 amount=-1
|
||||
kerning first=70 second=101 amount=-1
|
||||
kerning first=114 second=97 amount=-1
|
||||
kerning first=70 second=97 amount=-1
|
||||
kerning first=34 second=97 amount=-2
|
||||
kerning first=89 second=102 amount=-1
|
||||
kerning first=78 second=89 amount=-1
|
||||
kerning first=70 second=44 amount=-10
|
||||
kerning first=104 second=39 amount=-3
|
||||
kerning first=84 second=45 amount=-10
|
||||
kerning first=89 second=121 amount=-1
|
||||
kerning first=109 second=34 amount=-3
|
||||
kerning first=84 second=86 amount=1
|
||||
kerning first=87 second=99 amount=-1
|
||||
kerning first=32 second=84 amount=-2
|
||||
kerning first=98 second=122 amount=-1
|
||||
kerning first=89 second=112 amount=-1
|
||||
kerning first=89 second=103 amount=-2
|
||||
kerning first=65 second=116 amount=-1
|
||||
kerning first=88 second=81 amount=-1
|
||||
kerning first=102 second=34 amount=1
|
||||
kerning first=109 second=39 amount=-3
|
||||
kerning first=81 second=84 amount=-1
|
||||
kerning first=121 second=97 amount=-1
|
||||
kerning first=89 second=99 amount=-2
|
||||
kerning first=89 second=125 amount=1
|
||||
kerning first=81 second=86 amount=-1
|
||||
kerning first=114 second=116 amount=2
|
||||
kerning first=114 second=119 amount=1
|
||||
kerning first=84 second=44 amount=-9
|
||||
kerning first=102 second=39 amount=1
|
||||
kerning first=44 second=34 amount=-10
|
||||
kerning first=34 second=109 amount=-1
|
||||
kerning first=84 second=101 amount=-3
|
||||
kerning first=75 second=119 amount=-2
|
||||
kerning first=84 second=81 amount=-1
|
||||
kerning first=76 second=121 amount=-4
|
||||
kerning first=69 second=101 amount=-1
|
||||
kerning first=80 second=90 amount=-1
|
||||
kerning first=89 second=97 amount=-2
|
||||
kerning first=89 second=109 amount=-1
|
||||
kerning first=90 second=99 amount=-1
|
||||
kerning first=79 second=88 amount=-1
|
||||
kerning first=70 second=103 amount=-1
|
||||
kerning first=34 second=103 amount=-2
|
||||
kerning first=84 second=67 amount=-1
|
||||
kerning first=76 second=79 amount=-2
|
||||
kerning first=34 second=113 amount=-2
|
||||
kerning first=89 second=41 amount=1
|
||||
kerning first=75 second=71 amount=-1
|
||||
kerning first=76 second=87 amount=-3
|
||||
kerning first=77 second=89 amount=-1
|
||||
kerning first=90 second=113 amount=-1
|
||||
kerning first=118 second=111 amount=-1
|
||||
kerning first=118 second=97 amount=-1
|
||||
kerning first=88 second=100 amount=-1
|
||||
kerning first=89 second=111 amount=-2
|
||||
kerning first=90 second=121 amount=-1
|
||||
kerning first=89 second=113 amount=-2
|
||||
kerning first=84 second=87 amount=1
|
||||
kerning first=39 second=111 amount=-3
|
||||
kerning first=39 second=100 amount=-2
|
||||
kerning first=75 second=113 amount=-1
|
||||
kerning first=88 second=111 amount=-1
|
||||
kerning first=87 second=111 amount=-1
|
||||
kerning first=89 second=83 amount=-1
|
||||
kerning first=84 second=89 amount=1
|
||||
kerning first=84 second=103 amount=-3
|
||||
kerning first=70 second=117 amount=-1
|
||||
kerning first=67 second=41 amount=-1
|
||||
kerning first=89 second=71 amount=-1
|
||||
kerning first=121 second=44 amount=-6
|
||||
kerning first=97 second=121 amount=-1
|
||||
kerning first=87 second=113 amount=-1
|
||||
kerning first=73 second=84 amount=-1
|
||||
kerning first=121 second=46 amount=-6
|
||||
kerning first=75 second=99 amount=-1
|
||||
kerning first=65 second=112 amount=-2
|
||||
kerning first=65 second=85 amount=-1
|
||||
kerning first=76 second=67 amount=-2
|
||||
kerning first=76 second=81 amount=-2
|
||||
kerning first=102 second=100 amount=-1
|
||||
kerning first=75 second=79 amount=-1
|
||||
kerning first=39 second=65 amount=-4
|
||||
kerning first=65 second=84 amount=-4
|
||||
kerning first=90 second=101 amount=-1
|
||||
kerning first=84 second=121 amount=-3
|
||||
kerning first=114 second=39 amount=1
|
||||
kerning first=84 second=109 amount=-3
|
||||
kerning first=123 second=74 amount=-1
|
||||
kerning first=76 second=119 amount=-2
|
||||
kerning first=84 second=117 amount=-2
|
||||
kerning first=76 second=85 amount=-1
|
||||
kerning first=76 second=71 amount=-2
|
||||
kerning first=79 second=90 amount=-1
|
||||
kerning first=107 second=100 amount=-1
|
||||
kerning first=90 second=111 amount=-1
|
||||
kerning first=79 second=44 amount=-4
|
||||
kerning first=75 second=45 amount=-6
|
||||
kerning first=79 second=86 amount=-1
|
||||
kerning first=79 second=46 amount=-4
|
||||
kerning first=76 second=89 amount=-10
|
||||
kerning first=68 second=65 amount=-1
|
||||
kerning first=79 second=84 amount=-3
|
||||
kerning first=87 second=100 amount=-1
|
||||
kerning first=84 second=32 amount=-2
|
||||
kerning first=90 second=67 amount=-1
|
||||
kerning first=69 second=103 amount=-1
|
||||
kerning first=90 second=71 amount=-1
|
||||
kerning first=86 second=44 amount=-8
|
||||
kerning first=69 second=121 amount=-1
|
||||
kerning first=87 second=114 amount=-1
|
||||
kerning first=118 second=39 amount=1
|
||||
kerning first=46 second=39 amount=-10
|
||||
kerning first=72 second=84 amount=-1
|
||||
kerning first=86 second=46 amount=-8
|
||||
kerning first=69 second=113 amount=-1
|
||||
kerning first=69 second=119 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
|
||||
BIN
src/web/static/fonts/bmfonts/RobotoBlack72White.png
Normal file
BIN
src/web/static/fonts/bmfonts/RobotoBlack72White.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
103
src/web/static/fonts/bmfonts/RobotoMono72White.fnt
Normal file
103
src/web/static/fonts/bmfonts/RobotoMono72White.fnt
Normal file
@@ -0,0 +1,103 @@
|
||||
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
|
||||
common lineHeight=96 base=76 scaleW=512 scaleH=512 pages=1 packed=0
|
||||
page id=0 file="RobotoMono72White.png"
|
||||
chars count=98
|
||||
char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=0 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=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 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=34 x=434 y=319 width=20 height=19 xoffset=11 yoffset=21 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=36 x=200 y=0 width=35 height=69 xoffset=5 yoffset=15 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=38 x=42 y=155 width=41 height=56 xoffset=3 yoffset=22 xadvance=44 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=40 x=45 y=0 width=21 height=78 xoffset=12 yoffset=16 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=42 x=256 y=319 width=37 height=37 xoffset=4 yoffset=32 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=44 x=421 y=319 width=13 height=22 xoffset=11 yoffset=67 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=46 x=496 y=319 width=12 height=13 xoffset=16 yoffset=65 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=48 x=431 y=99 width=35 height=56 xoffset=4 yoffset=22 xadvance=43 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=50 x=189 y=155 width=37 height=55 xoffset=2 yoffset=22 xadvance=44 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=52 x=59 y=265 width=39 height=54 xoffset=2 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=54 x=261 y=155 width=35 height=55 xoffset=4 yoffset=23 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=56 x=396 y=99 width=35 height=56 xoffset=5 yoffset=22 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=58 x=490 y=211 width=12 height=43 xoffset=18 yoffset=35 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=60 x=293 y=319 width=32 height=35 xoffset=5 yoffset=36 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=62 x=325 y=319 width=33 height=35 xoffset=5 yoffset=36 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=64 x=135 y=265 width=40 height=54 xoffset=1 yoffset=23 xadvance=42 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=66 x=372 y=155 width=35 height=54 xoffset=5 yoffset=23 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=68 x=407 y=155 width=37 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=70 x=0 y=211 width=34 height=54 xoffset=6 yoffset=23 xadvance=44 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=72 x=34 y=211 width=36 height=54 xoffset=4 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=74 x=83 y=155 width=36 height=55 xoffset=2 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=76 x=108 y=211 width=34 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=78 x=178 y=211 width=35 height=54 xoffset=4 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=80 x=213 y=211 width=36 height=54 xoffset=6 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=82 x=249 y=211 width=36 height=54 xoffset=5 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=84 x=285 y=211 width=40 height=54 xoffset=2 yoffset=23 xadvance=44 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=86 x=325 y=211 width=41 height=54 xoffset=1 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=88 x=408 y=211 width=41 height=54 xoffset=2 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=90 x=0 y=265 width=36 height=54 xoffset=3 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=92 x=350 y=0 width=30 height=58 xoffset=7 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=94 x=358 y=319 width=30 height=30 xoffset=7 yoffset=23 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=96 x=0 y=360 width=17 height=12 xoffset=13 yoffset=22 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=98 x=380 y=0 width=34 height=57 xoffset=5 yoffset=21 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=100 x=414 y=0 width=34 height=57 xoffset=4 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=102 x=282 y=0 width=37 height=58 xoffset=4 yoffset=19 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=104 x=148 y=99 width=34 height=56 xoffset=5 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=106 x=121 y=0 width=26 height=71 xoffset=6 yoffset=22 xadvance=44 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=108 x=218 y=99 width=34 height=56 xoffset=6 yoffset=21 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=110 x=467 y=265 width=34 height=41 xoffset=5 yoffset=36 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=112 x=252 y=99 width=34 height=56 xoffset=5 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=114 x=0 y=319 width=29 height=41 xoffset=11 yoffset=36 xadvance=44 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=116 x=216 y=265 width=35 height=51 xoffset=4 yoffset=27 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=118 x=62 y=319 width=39 height=40 xoffset=2 yoffset=37 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=120 x=144 y=319 width=40 height=40 xoffset=2 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=122 x=184 y=319 width=35 height=40 xoffset=5 yoffset=37 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=124 x=235 y=0 width=7 height=68 xoffset=18 yoffset=23 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=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
|
||||
kernings count=0
|
||||
BIN
src/web/static/fonts/bmfonts/RobotoMono72White.png
Normal file
BIN
src/web/static/fonts/bmfonts/RobotoMono72White.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
492
src/web/static/fonts/bmfonts/RobotoSlab72White.fnt
Normal file
492
src/web/static/fonts/bmfonts/RobotoSlab72White.fnt
Normal file
@@ -0,0 +1,492 @@
|
||||
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
|
||||
common lineHeight=96 base=76 scaleW=512 scaleH=512 pages=1 packed=0
|
||||
page id=0 file="RobotoSlab72White.png"
|
||||
chars count=98
|
||||
char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=0 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=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=18 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=34 x=191 y=362 width=19 height=20 xoffset=5 yoffset=20 xadvance=28 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=36 x=212 y=0 width=35 height=69 xoffset=2 yoffset=15 xadvance=39 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=38 x=222 y=156 width=44 height=56 xoffset=2 yoffset=22 xadvance=46 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=40 x=70 y=0 width=21 height=77 xoffset=3 yoffset=17 xadvance=23 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=42 x=100 y=362 width=31 height=33 xoffset=1 yoffset=23 xadvance=33 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=44 x=492 y=320 width=13 height=21 xoffset=-1 yoffset=67 xadvance=14 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=46 x=278 y=362 width=9 height=9 xoffset=4 yoffset=68 xadvance=17 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=48 x=139 y=156 width=35 height=56 xoffset=3 yoffset=22 xadvance=41 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=50 x=357 y=156 width=36 height=55 xoffset=2 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=52 x=330 y=266 width=39 height=54 xoffset=1 yoffset=23 xadvance=42 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=54 x=34 y=156 width=35 height=56 xoffset=3 yoffset=22 xadvance=40 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=56 x=69 y=156 width=35 height=56 xoffset=2 yoffset=22 xadvance=39 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=58 x=500 y=0 width=9 height=40 xoffset=4 yoffset=37 xadvance=15 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=60 x=37 y=362 width=31 height=35 xoffset=2 yoffset=39 xadvance=36 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=62 x=68 y=362 width=32 height=35 xoffset=3 yoffset=39 xadvance=37 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=64 x=247 y=0 width=60 height=68 xoffset=1 yoffset=25 xadvance=64 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=66 x=0 y=212 width=44 height=54 xoffset=1 yoffset=23 xadvance=47 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=68 x=44 y=212 width=46 height=54 xoffset=1 yoffset=23 xadvance=50 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=70 x=132 y=212 width=42 height=54 xoffset=1 yoffset=23 xadvance=44 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=72 x=174 y=212 width=52 height=54 xoffset=1 yoffset=23 xadvance=55 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=74 x=266 y=156 width=39 height=55 xoffset=1 yoffset=23 xadvance=41 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=76 x=274 y=212 width=39 height=54 xoffset=1 yoffset=23 xadvance=42 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=78 x=377 y=212 width=52 height=54 xoffset=1 yoffset=23 xadvance=54 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=80 x=429 y=212 width=43 height=54 xoffset=1 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=82 x=0 y=266 width=46 height=54 xoffset=1 yoffset=23 xadvance=48 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=84 x=46 y=266 width=45 height=54 xoffset=0 yoffset=23 xadvance=45 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=86 x=91 y=266 width=50 height=54 xoffset=1 yoffset=23 xadvance=52 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=88 x=208 y=266 width=49 height=54 xoffset=1 yoffset=23 xadvance=51 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=90 x=472 y=212 width=38 height=54 xoffset=2 yoffset=23 xadvance=42 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=92 x=0 y=98 width=31 height=58 xoffset=0 yoffset=23 xadvance=30 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=94 x=131 y=362 width=29 height=28 xoffset=1 yoffset=23 xadvance=30 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=96 x=260 y=362 width=18 height=12 xoffset=1 yoffset=22 xadvance=20 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=98 x=363 y=0 width=41 height=58 xoffset=-2 yoffset=20 xadvance=42 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=100 x=404 y=0 width=40 height=58 xoffset=2 yoffset=20 xadvance=43 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=102 x=444 y=0 width=26 height=58 xoffset=1 yoffset=19 xadvance=25 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=104 x=65 y=98 width=44 height=57 xoffset=1 yoffset=20 xadvance=46 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=106 x=112 y=0 width=18 height=73 xoffset=-2 yoffset=20 xadvance=20 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=108 x=171 y=98 width=20 height=57 xoffset=1 yoffset=20 xadvance=22 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=110 x=237 y=320 width=44 height=41 xoffset=1 yoffset=36 xadvance=46 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=112 x=361 y=98 width=40 height=56 xoffset=1 yoffset=36 xadvance=43 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=114 x=484 y=266 width=27 height=41 xoffset=2 yoffset=36 xadvance=30 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=116 x=460 y=266 width=24 height=51 xoffset=1 yoffset=27 xadvance=26 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=118 x=324 y=320 width=39 height=40 xoffset=0 yoffset=37 xadvance=40 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=120 x=420 y=320 width=40 height=40 xoffset=1 yoffset=37 xadvance=42 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=122 x=460 y=320 width=32 height=40 xoffset=3 yoffset=37 xadvance=38 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=124 x=355 y=0 width=8 height=63 xoffset=4 yoffset=23 xadvance=16 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=126 x=218 y=362 width=42 height=16 xoffset=3 yoffset=47 xadvance=49 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
|
||||
kernings count=389
|
||||
kerning first=86 second=45 amount=-1
|
||||
kerning first=114 second=46 amount=-4
|
||||
kerning first=40 second=87 amount=1
|
||||
kerning first=70 second=99 amount=-1
|
||||
kerning first=84 second=110 amount=-3
|
||||
kerning first=114 second=116 amount=1
|
||||
kerning first=39 second=65 amount=-4
|
||||
kerning first=104 second=34 amount=-1
|
||||
kerning first=89 second=71 amount=-1
|
||||
kerning first=107 second=113 amount=-1
|
||||
kerning first=78 second=88 amount=1
|
||||
kerning first=109 second=39 amount=-1
|
||||
kerning first=120 second=100 amount=-1
|
||||
kerning first=84 second=100 amount=-3
|
||||
kerning first=68 second=90 amount=-1
|
||||
kerning first=68 second=44 amount=-4
|
||||
kerning first=84 second=103 amount=-3
|
||||
kerning first=34 second=97 amount=-2
|
||||
kerning first=70 second=97 amount=-1
|
||||
kerning first=76 second=81 amount=-2
|
||||
kerning first=73 second=89 amount=-1
|
||||
kerning first=84 second=44 amount=-8
|
||||
kerning first=68 second=65 amount=-3
|
||||
kerning first=97 second=34 amount=-2
|
||||
kerning first=111 second=121 amount=-1
|
||||
kerning first=79 second=90 amount=-1
|
||||
kerning first=75 second=121 amount=-1
|
||||
kerning first=75 second=118 amount=-1
|
||||
kerning first=111 second=118 amount=-1
|
||||
kerning first=89 second=65 amount=-9
|
||||
kerning first=75 second=71 amount=-4
|
||||
kerning first=39 second=99 amount=-2
|
||||
kerning first=75 second=99 amount=-1
|
||||
kerning first=90 second=121 amount=-1
|
||||
kerning first=44 second=39 amount=-6
|
||||
kerning first=89 second=46 amount=-7
|
||||
kerning first=89 second=74 amount=-7
|
||||
kerning first=34 second=103 amount=-2
|
||||
kerning first=70 second=103 amount=-1
|
||||
kerning first=112 second=39 amount=-1
|
||||
kerning first=122 second=113 amount=-1
|
||||
kerning first=86 second=113 amount=-2
|
||||
kerning first=68 second=84 amount=-1
|
||||
kerning first=89 second=110 amount=-1
|
||||
kerning first=34 second=100 amount=-2
|
||||
kerning first=68 second=86 amount=-1
|
||||
kerning first=87 second=45 amount=-2
|
||||
kerning first=39 second=34 amount=-4
|
||||
kerning first=114 second=100 amount=-1
|
||||
kerning first=84 second=81 amount=-1
|
||||
kerning first=70 second=101 amount=-1
|
||||
kerning first=68 second=89 amount=-2
|
||||
kerning first=88 second=117 amount=-1
|
||||
kerning first=112 second=34 amount=-1
|
||||
kerning first=76 second=67 amount=-2
|
||||
kerning first=76 second=34 amount=-5
|
||||
kerning first=88 second=111 amount=-1
|
||||
kerning first=66 second=86 amount=-1
|
||||
kerning first=66 second=89 amount=-2
|
||||
kerning first=122 second=101 amount=-1
|
||||
kerning first=86 second=101 amount=-2
|
||||
kerning first=76 second=121 amount=-5
|
||||
kerning first=84 second=119 amount=-2
|
||||
kerning first=84 second=112 amount=-3
|
||||
kerning first=87 second=111 amount=-1
|
||||
kerning first=69 second=118 amount=-1
|
||||
kerning first=65 second=117 amount=-2
|
||||
kerning first=65 second=89 amount=-9
|
||||
kerning first=72 second=89 amount=-1
|
||||
kerning first=119 second=44 amount=-4
|
||||
kerning first=69 second=121 amount=-1
|
||||
kerning first=84 second=109 amount=-3
|
||||
kerning first=84 second=122 amount=-2
|
||||
kerning first=89 second=99 amount=-2
|
||||
kerning first=76 second=118 amount=-5
|
||||
kerning first=90 second=99 amount=-1
|
||||
kerning first=90 second=103 amount=-1
|
||||
kerning first=79 second=89 amount=-2
|
||||
kerning first=90 second=79 amount=-1
|
||||
kerning first=84 second=115 amount=-4
|
||||
kerning first=76 second=65 amount=1
|
||||
kerning first=90 second=100 amount=-1
|
||||
kerning first=118 second=46 amount=-4
|
||||
kerning first=87 second=117 amount=-1
|
||||
kerning first=118 second=34 amount=1
|
||||
kerning first=69 second=103 amount=-1
|
||||
kerning first=97 second=121 amount=-1
|
||||
kerning first=39 second=111 amount=-2
|
||||
kerning first=72 second=88 amount=1
|
||||
kerning first=76 second=87 amount=-5
|
||||
kerning first=69 second=119 amount=-1
|
||||
kerning first=121 second=97 amount=-1
|
||||
kerning first=75 second=45 amount=-8
|
||||
kerning first=65 second=86 amount=-9
|
||||
kerning first=46 second=34 amount=-6
|
||||
kerning first=76 second=84 amount=-10
|
||||
kerning first=116 second=111 amount=-1
|
||||
kerning first=87 second=113 amount=-1
|
||||
kerning first=69 second=100 amount=-1
|
||||
kerning first=97 second=118 amount=-1
|
||||
kerning first=65 second=85 amount=-2
|
||||
kerning first=90 second=71 amount=-1
|
||||
kerning first=68 second=46 amount=-4
|
||||
kerning first=65 second=79 amount=-3
|
||||
kerning first=98 second=122 amount=-1
|
||||
kerning first=86 second=41 amount=1
|
||||
kerning first=84 second=118 amount=-3
|
||||
kerning first=70 second=118 amount=-1
|
||||
kerning first=121 second=111 amount=-1
|
||||
kerning first=81 second=87 amount=-1
|
||||
kerning first=70 second=100 amount=-1
|
||||
kerning first=102 second=93 amount=1
|
||||
kerning first=114 second=101 amount=-1
|
||||
kerning first=88 second=45 amount=-2
|
||||
kerning first=39 second=103 amount=-2
|
||||
kerning first=75 second=103 amount=-1
|
||||
kerning first=88 second=101 amount=-1
|
||||
kerning first=89 second=103 amount=-2
|
||||
kerning first=110 second=39 amount=-1
|
||||
kerning first=89 second=89 amount=1
|
||||
kerning first=87 second=65 amount=-2
|
||||
kerning first=119 second=46 amount=-4
|
||||
kerning first=34 second=34 amount=-4
|
||||
kerning first=88 second=79 amount=-2
|
||||
kerning first=79 second=86 amount=-1
|
||||
kerning first=76 second=119 amount=-3
|
||||
kerning first=75 second=111 amount=-1
|
||||
kerning first=65 second=116 amount=-4
|
||||
kerning first=86 second=65 amount=-9
|
||||
kerning first=70 second=84 amount=1
|
||||
kerning first=75 second=117 amount=-1
|
||||
kerning first=80 second=65 amount=-9
|
||||
kerning first=34 second=112 amount=-1
|
||||
kerning first=102 second=99 amount=-1
|
||||
kerning first=118 second=97 amount=-1
|
||||
kerning first=89 second=81 amount=-1
|
||||
kerning first=118 second=111 amount=-1
|
||||
kerning first=102 second=101 amount=-1
|
||||
kerning first=114 second=44 amount=-4
|
||||
kerning first=90 second=119 amount=-1
|
||||
kerning first=75 second=81 amount=-4
|
||||
kerning first=88 second=121 amount=-1
|
||||
kerning first=34 second=110 amount=-1
|
||||
kerning first=86 second=100 amount=-2
|
||||
kerning first=122 second=100 amount=-1
|
||||
kerning first=89 second=67 amount=-1
|
||||
kerning first=90 second=118 amount=-1
|
||||
kerning first=84 second=84 amount=1
|
||||
kerning first=121 second=34 amount=1
|
||||
kerning first=91 second=74 amount=-1
|
||||
kerning first=88 second=113 amount=-1
|
||||
kerning first=77 second=88 amount=1
|
||||
kerning first=75 second=119 amount=-2
|
||||
kerning first=114 second=104 amount=-1
|
||||
kerning first=68 second=88 amount=-2
|
||||
kerning first=121 second=44 amount=-4
|
||||
kerning first=81 second=89 amount=-1
|
||||
kerning first=102 second=39 amount=1
|
||||
kerning first=74 second=65 amount=-2
|
||||
kerning first=114 second=118 amount=1
|
||||
kerning first=84 second=46 amount=-8
|
||||
kerning first=111 second=34 amount=-1
|
||||
kerning first=88 second=71 amount=-2
|
||||
kerning first=88 second=99 amount=-1
|
||||
kerning first=84 second=74 amount=-8
|
||||
kerning first=39 second=109 amount=-1
|
||||
kerning first=98 second=34 amount=-1
|
||||
kerning first=86 second=114 amount=-1
|
||||
kerning first=88 second=81 amount=-2
|
||||
kerning first=70 second=74 amount=-11
|
||||
kerning first=89 second=83 amount=-1
|
||||
kerning first=87 second=41 amount=1
|
||||
kerning first=89 second=97 amount=-3
|
||||
kerning first=89 second=87 amount=1
|
||||
kerning first=67 second=125 amount=-1
|
||||
kerning first=89 second=93 amount=1
|
||||
kerning first=80 second=118 amount=1
|
||||
kerning first=107 second=100 amount=-1
|
||||
kerning first=114 second=34 amount=1
|
||||
kerning first=89 second=109 amount=-1
|
||||
kerning first=89 second=45 amount=-2
|
||||
kerning first=70 second=44 amount=-8
|
||||
kerning first=34 second=39 amount=-4
|
||||
kerning first=88 second=67 amount=-2
|
||||
kerning first=70 second=46 amount=-8
|
||||
kerning first=102 second=41 amount=1
|
||||
kerning first=89 second=117 amount=-1
|
||||
kerning first=89 second=111 amount=-4
|
||||
kerning first=89 second=115 amount=-4
|
||||
kerning first=114 second=102 amount=1
|
||||
kerning first=89 second=125 amount=1
|
||||
kerning first=89 second=121 amount=-1
|
||||
kerning first=114 second=108 amount=-1
|
||||
kerning first=47 second=47 amount=-8
|
||||
kerning first=65 second=63 amount=-2
|
||||
kerning first=75 second=67 amount=-4
|
||||
kerning first=87 second=100 amount=-1
|
||||
kerning first=111 second=104 amount=-1
|
||||
kerning first=111 second=107 amount=-1
|
||||
kerning first=75 second=109 amount=-1
|
||||
kerning first=87 second=114 amount=-1
|
||||
kerning first=111 second=120 amount=-1
|
||||
kerning first=69 second=99 amount=-1
|
||||
kerning first=65 second=84 amount=-6
|
||||
kerning first=39 second=97 amount=-2
|
||||
kerning first=121 second=46 amount=-4
|
||||
kerning first=89 second=85 amount=-3
|
||||
kerning first=75 second=79 amount=-4
|
||||
kerning first=107 second=99 amount=-1
|
||||
kerning first=102 second=100 amount=-1
|
||||
kerning first=102 second=103 amount=-1
|
||||
kerning first=75 second=110 amount=-1
|
||||
kerning first=39 second=110 amount=-1
|
||||
kerning first=69 second=84 amount=1
|
||||
kerning first=84 second=111 amount=-3
|
||||
kerning first=120 second=111 amount=-1
|
||||
kerning first=84 second=114 amount=-3
|
||||
kerning first=112 second=120 amount=-1
|
||||
kerning first=79 second=84 amount=-1
|
||||
kerning first=84 second=117 amount=-3
|
||||
kerning first=89 second=79 amount=-1
|
||||
kerning first=75 second=113 amount=-1
|
||||
kerning first=39 second=113 amount=-2
|
||||
kerning first=80 second=44 amount=-11
|
||||
kerning first=79 second=88 amount=-2
|
||||
kerning first=98 second=39 amount=-1
|
||||
kerning first=65 second=118 amount=-4
|
||||
kerning first=65 second=34 amount=-4
|
||||
kerning first=88 second=103 amount=-1
|
||||
kerning first=77 second=89 amount=-1
|
||||
kerning first=39 second=101 amount=-2
|
||||
kerning first=75 second=101 amount=-1
|
||||
kerning first=88 second=100 amount=-1
|
||||
kerning first=78 second=65 amount=-3
|
||||
kerning first=87 second=44 amount=-4
|
||||
kerning first=67 second=41 amount=-1
|
||||
kerning first=86 second=93 amount=1
|
||||
kerning first=84 second=83 amount=-1
|
||||
kerning first=102 second=113 amount=-1
|
||||
kerning first=34 second=111 amount=-2
|
||||
kerning first=70 second=111 amount=-1
|
||||
kerning first=86 second=99 amount=-2
|
||||
kerning first=84 second=86 amount=1
|
||||
kerning first=122 second=99 amount=-1
|
||||
kerning first=84 second=89 amount=1
|
||||
kerning first=70 second=114 amount=-1
|
||||
kerning first=86 second=74 amount=-8
|
||||
kerning first=89 second=38 amount=-1
|
||||
kerning first=87 second=97 amount=-1
|
||||
kerning first=76 second=86 amount=-9
|
||||
kerning first=40 second=86 amount=1
|
||||
kerning first=90 second=113 amount=-1
|
||||
kerning first=39 second=39 amount=-4
|
||||
kerning first=111 second=39 amount=-1
|
||||
kerning first=90 second=117 amount=-1
|
||||
kerning first=89 second=41 amount=1
|
||||
kerning first=65 second=121 amount=-4
|
||||
kerning first=89 second=100 amount=-2
|
||||
kerning first=89 second=42 amount=-2
|
||||
kerning first=76 second=117 amount=-2
|
||||
kerning first=69 second=111 amount=-1
|
||||
kerning first=46 second=39 amount=-6
|
||||
kerning first=118 second=39 amount=1
|
||||
kerning first=91 second=85 amount=-1
|
||||
kerning first=80 second=90 amount=-1
|
||||
kerning first=90 second=81 amount=-1
|
||||
kerning first=69 second=117 amount=-1
|
||||
kerning first=76 second=39 amount=-5
|
||||
kerning first=90 second=67 amount=-1
|
||||
kerning first=87 second=103 amount=-1
|
||||
kerning first=84 second=120 amount=-3
|
||||
kerning first=89 second=101 amount=-2
|
||||
kerning first=102 second=125 amount=1
|
||||
kerning first=76 second=85 amount=-2
|
||||
kerning first=79 second=65 amount=-3
|
||||
kerning first=65 second=71 amount=-3
|
||||
kerning first=79 second=44 amount=-4
|
||||
kerning first=97 second=39 amount=-2
|
||||
kerning first=90 second=101 amount=-1
|
||||
kerning first=65 second=87 amount=-5
|
||||
kerning first=79 second=46 amount=-4
|
||||
kerning first=87 second=99 amount=-1
|
||||
kerning first=34 second=101 amount=-2
|
||||
kerning first=40 second=89 amount=1
|
||||
kerning first=76 second=89 amount=-8
|
||||
kerning first=69 second=113 amount=-1
|
||||
kerning first=120 second=103 amount=-1
|
||||
kerning first=69 second=101 amount=-1
|
||||
kerning first=69 second=102 amount=-1
|
||||
kerning first=104 second=39 amount=-1
|
||||
kerning first=80 second=121 amount=1
|
||||
kerning first=86 second=46 amount=-8
|
||||
kerning first=65 second=81 amount=-3
|
||||
kerning first=86 second=44 amount=-8
|
||||
kerning first=120 second=99 amount=-1
|
||||
kerning first=98 second=120 amount=-1
|
||||
kerning first=39 second=115 amount=-3
|
||||
kerning first=121 second=39 amount=1
|
||||
kerning first=88 second=118 amount=-1
|
||||
kerning first=84 second=65 amount=-6
|
||||
kerning first=65 second=39 amount=-4
|
||||
kerning first=84 second=79 amount=-1
|
||||
kerning first=65 second=119 amount=-4
|
||||
kerning first=70 second=117 amount=-1
|
||||
kerning first=75 second=100 amount=-1
|
||||
kerning first=86 second=111 amount=-2
|
||||
kerning first=122 second=111 amount=-1
|
||||
kerning first=81 second=84 amount=-2
|
||||
kerning first=107 second=103 amount=-1
|
||||
kerning first=118 second=44 amount=-4
|
||||
kerning first=87 second=46 amount=-4
|
||||
kerning first=87 second=101 amount=-1
|
||||
kerning first=70 second=79 amount=-2
|
||||
kerning first=87 second=74 amount=-2
|
||||
kerning first=123 second=74 amount=-1
|
||||
kerning first=76 second=71 amount=-2
|
||||
kerning first=39 second=100 amount=-2
|
||||
kerning first=80 second=88 amount=-1
|
||||
kerning first=84 second=121 amount=-3
|
||||
kerning first=112 second=122 amount=-1
|
||||
kerning first=84 second=71 amount=-1
|
||||
kerning first=89 second=86 amount=1
|
||||
kerning first=84 second=113 amount=-3
|
||||
kerning first=120 second=113 amount=-1
|
||||
kerning first=89 second=44 amount=-7
|
||||
kerning first=84 second=99 amount=-3
|
||||
kerning first=34 second=113 amount=-2
|
||||
kerning first=80 second=46 amount=-11
|
||||
kerning first=86 second=117 amount=-1
|
||||
kerning first=110 second=34 amount=-1
|
||||
kerning first=80 second=74 amount=-7
|
||||
kerning first=120 second=101 amount=-1
|
||||
kerning first=73 second=88 amount=1
|
||||
kerning first=108 second=111 amount=-1
|
||||
kerning first=34 second=115 amount=-3
|
||||
kerning first=89 second=113 amount=-2
|
||||
kerning first=82 second=86 amount=-3
|
||||
kerning first=114 second=39 amount=1
|
||||
kerning first=34 second=109 amount=-1
|
||||
kerning first=84 second=101 amount=-3
|
||||
kerning first=70 second=121 amount=-1
|
||||
kerning first=123 second=85 amount=-1
|
||||
kerning first=122 second=103 amount=-1
|
||||
kerning first=86 second=97 amount=-2
|
||||
kerning first=82 second=89 amount=-4
|
||||
kerning first=66 second=84 amount=-1
|
||||
kerning first=84 second=97 amount=-4
|
||||
kerning first=86 second=103 amount=-2
|
||||
kerning first=70 second=113 amount=-1
|
||||
kerning first=84 second=87 amount=1
|
||||
kerning first=75 second=112 amount=-1
|
||||
kerning first=114 second=111 amount=-1
|
||||
kerning first=39 second=112 amount=-1
|
||||
kerning first=107 second=101 amount=-1
|
||||
kerning first=82 second=84 amount=-3
|
||||
kerning first=114 second=121 amount=1
|
||||
kerning first=34 second=99 amount=-2
|
||||
kerning first=70 second=81 amount=-2
|
||||
kerning first=111 second=122 amount=-1
|
||||
kerning first=84 second=67 amount=-1
|
||||
kerning first=111 second=108 amount=-1
|
||||
kerning first=89 second=84 amount=1
|
||||
kerning first=76 second=79 amount=-2
|
||||
kerning first=85 second=65 amount=-2
|
||||
kerning first=44 second=34 amount=-6
|
||||
kerning first=65 second=67 amount=-3
|
||||
kerning first=109 second=34 amount=-1
|
||||
kerning first=114 second=103 amount=-1
|
||||
kerning first=78 second=89 amount=-1
|
||||
kerning first=89 second=114 amount=-1
|
||||
kerning first=89 second=112 amount=-1
|
||||
kerning first=34 second=65 amount=-4
|
||||
kerning first=70 second=65 amount=-11
|
||||
kerning first=81 second=86 amount=-1
|
||||
kerning first=114 second=119 amount=1
|
||||
kerning first=89 second=102 amount=-1
|
||||
kerning first=84 second=45 amount=-8
|
||||
kerning first=86 second=125 amount=1
|
||||
kerning first=70 second=67 amount=-2
|
||||
kerning first=89 second=116 amount=-1
|
||||
kerning first=102 second=34 amount=1
|
||||
kerning first=114 second=99 amount=-1
|
||||
kerning first=67 second=84 amount=-1
|
||||
kerning first=114 second=113 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
|
||||
BIN
src/web/static/fonts/bmfonts/RobotoSlab72White.png
Normal file
BIN
src/web/static/fonts/bmfonts/RobotoSlab72White.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
@@ -86,7 +86,7 @@ div.toggle-string {
|
||||
}
|
||||
|
||||
.operation .form-control {
|
||||
padding: 20px 12px 6px 12px;
|
||||
padding: 20px 12px 6px 12px !important;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
background-image: none;
|
||||
|
||||
@@ -21,6 +21,14 @@
|
||||
background-color: var(--secondary-background-colour);
|
||||
}
|
||||
|
||||
#controls-content {
|
||||
position: relative;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transform-origin: center left;
|
||||
}
|
||||
|
||||
#auto-bake-label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -60,7 +60,7 @@ module.exports = {
|
||||
rules: [
|
||||
{
|
||||
test: /\.m?js$/,
|
||||
exclude: /node_modules\/(?!jsesc|crypto-api)/,
|
||||
exclude: /node_modules\/(?!jsesc|crypto-api|bootstrap)/,
|
||||
options: {
|
||||
configFile: path.resolve(__dirname, "babel.config.js"),
|
||||
cacheDirectory: true,
|
||||
@@ -119,9 +119,17 @@ module.exports = {
|
||||
encoding: "base64"
|
||||
}
|
||||
},
|
||||
{ // Store font .fnt and .png files in a separate fonts folder
|
||||
test: /(\.fnt$|bmfonts\/.+\.png$)/,
|
||||
loader: "file-loader",
|
||||
options: {
|
||||
name: "[name].[ext]",
|
||||
outputPath: "assets/fonts"
|
||||
}
|
||||
},
|
||||
{ // First party images are saved as files to be cached
|
||||
test: /\.(png|jpg|gif)$/,
|
||||
exclude: /node_modules/,
|
||||
exclude: /(node_modules|bmfonts)/,
|
||||
loader: "file-loader",
|
||||
options: {
|
||||
name: "images/[name].[ext]"
|
||||
|
||||
Reference in New Issue
Block a user