2
0
mirror of https://github.com/gchq/CyberChef synced 2025-12-05 23:53:27 +00:00

Compare commits

..

66 Commits

Author SHA1 Message Date
n1474335
bb0eaf4597 8.35.0 2019-07-03 12:34:17 +01:00
n1474335
8ada3bae0c Updated CHANGELOG 2019-07-03 12:34:13 +01:00
n1474335
db72cad610 Merge branch 'more-image-ops' of https://github.com/j433866/CyberChef into j433866-more-image-ops 2019-07-03 12:24:03 +01:00
j433866
bed66298d1 Change jpeg test data to be a full image 2019-07-03 08:53:44 +01:00
n1474335
6726994ad5 8.34.3 2019-07-02 18:40:03 +01:00
n1474335
5abc5279f5 Fixed spread operator support in older browsers 2019-07-02 18:39:21 +01:00
j433866
1135ca5fb4 Remove duplicate function.
Fix reading QR codes with transparent backgrounds
2019-07-02 16:31:26 +01:00
j433866
be08a62f52 Add webpack config for font files 2019-07-02 15:31:29 +01:00
j433866
f473807459 Bring up to date with master 2019-07-02 14:23:17 +01:00
n1474335
d148cae814 8.34.2 2019-06-28 17:13:58 +01:00
n1474335
f22211ce8c Backslashes are now escaped correctly by 'Unescape string'. CLoses #582 2019-06-28 17:13:54 +01:00
n1474335
f9c24f2528 8.34.1 2019-06-28 17:09:04 +01:00
n1474335
6f6786d79e Baking controls now scale to fit the pane correctly 2019-06-28 17:09:00 +01:00
n1474335
a5ea7f7d58 8.34.0 2019-06-28 15:03:55 +01:00
n1474335
d637ac7633 Updated CHANGELOG 2019-06-28 15:03:49 +01:00
n1474335
c1ad2386ef Improvements to Entropy operation. Converted to ArrayBuffers, improved efficiency with large files, added present method back in. 2019-06-28 15:00:19 +01:00
mshwed
b0b6de116d Fixed linting issue 2019-06-27 14:11:26 -04:00
mshwed
1b161f997b Refactored advanced entropy operation into entropy operation 2019-06-27 14:09:41 -04:00
mshwed
b99af58636 Merge branch 'master' of https://github.com/gchq/CyberChef into feature/advanced-entropy 2019-06-27 13:15:19 -04:00
n1474335
d4edbb3c3e 8.33.1 2019-06-27 17:30:43 +01:00
n1474335
1b765605ca Updated dependencies 2019-06-27 17:29:27 +01:00
n1474335
8286dc26ad Updated patch dependencies 2019-06-27 17:06:07 +01:00
n1474335
19a438c15b Updated Bootstrap dependecy 2019-06-27 16:58:26 +01:00
n1474335
12898a1a8e Merge branch 'artemisbot-features/bz2-comp' 2019-06-27 16:48:15 +01:00
n1474335
541f2a2988 8.33.0 2019-06-27 16:47:32 +01:00
n1474335
d0277dde3f Updated CHANGELOG 2019-06-27 16:47:26 +01:00
n1474335
d184e40116 Added min and max for Bzip2 block size arg 2019-06-27 16:45:16 +01:00
n1474335
596db07647 Merge branch 'features/bz2-comp' of https://github.com/artemisbot/CyberChef into artemisbot-features/bz2-comp 2019-06-27 16:44:42 +01:00
n1474335
c233c5c67e Modified operation request template description 2019-06-27 16:01:44 +01:00
n1474335
db788b57e7 Merge branch 'update-issue-templates' of https://github.com/h345983745/CyberChef into h345983745-update-issue-templates 2019-06-27 15:58:38 +01:00
mshwed
daee7ac761 Merge branch 'master' of https://github.com/gchq/CyberChef into feature/advanced-entropy 2019-06-20 14:11:51 -04:00
j433866
0bcf57e89c Improve printing text to improve output quality.
Now resizes the text instead of the image
2019-06-20 12:58:02 +01:00
h345983745
1d130c88a8 Inital commit 2019-05-19 15:40:17 +01:00
mshwed
5225874498 Fixed handling of large files and fixed issue with line histogram colour fill 2019-04-28 14:38:03 -04:00
mshwed
802493fec4 Merge branch 'master' of https://github.com/gchq/CyberChef into feature/advanced-entropy 2019-04-24 19:48:03 -04:00
Matt
18408901be removed old bzip2 dependency 2019-04-07 19:11:46 +01:00
Matt
982c915931 Change author 2019-04-07 19:02:27 +01:00
Matt
a339eacd45 Bzip2 compression support changed to use wasm backend
x4 speed.
2019-04-07 18:59:03 +01:00
mshwed
8fc0e012e3 Fixed formatting issues 2019-04-06 23:09:46 -04:00
mshwed
b7fb9635e5 Added operation for entropy visualization as an image 2019-04-06 19:40:07 -04:00
mshwed
f988a958bb Added support for generating an entropy curve based on the input data 2019-04-06 15:59:36 -04:00
mshwed
c80cb57b07 Added histogram line, refactored axes generation 2019-04-05 14:30:24 -04:00
mshwed
dec28e16d4 Added histogram visualization for text entropy 2019-04-05 11:12:44 -04:00
Matt
7796c473ae Fix lint issue 2019-04-02 17:01:47 +01:00
Matt
8445165491 Use all the arraybuffers
cuts a solid 1/3 off the compression time
2019-04-02 16:47:38 +01:00
Matt
c5698fcd65 Merge remote-tracking branch 'upstream/master' into features/bz2-comp 2019-04-02 16:26:29 +01:00
Matt
8b12caad78 Merge gchq/master into bz2-comp 2019-04-02 12:08:30 +01:00
Matt
e1492c3bb1 Added (non-garbage) description and fixed wikipedia link. 2019-04-02 12:05:17 +01:00
Matt
3cc66e9db9 Added Bzip2 compression support 2019-04-02 11:55:59 +01:00
j433866
99bef09e0e Fix invalid file type error 2019-04-01 14:13:30 +01:00
j433866
c97e77c765 Merge with qr-improvements.
Change QR code ops to use ArrayBuffer.
Add new function to Utils to convert a string to arraybuffer.
2019-04-01 14:11:37 +01:00
j433866
e44a22e143 Change ops to use ArrayBuffer instead of byteArray 2019-04-01 10:54:46 +01:00
j433866
c2496fe63e Change to use Promise.all 2019-04-01 09:00:41 +01:00
j433866
6a01e40394 Fix bug where GIF input would error on output.
Also minor corrections to jsdocs in ImageManipulation
2019-03-20 11:20:34 +00:00
j433866
b312e17904 Change title to title case 2019-03-19 13:54:39 +00:00
j433866
2cd3e9cacd Add new implementation of gaussian blur.
Changed SharpenImage to use the new algorithm.
2019-03-19 13:54:26 +00:00
j433866
ce72acdd61 Add 'add text to image' operation.
Included variants of the Roboto fonts as bitmap fonts for jimp.
Changed webpack config to import the font files.
2019-03-19 13:53:09 +00:00
j433866
8e74acbf3e Add opaque background option 2019-03-18 09:43:37 +00:00
j433866
4fafa39e54 Fix magic library to better handle operation error 2019-03-13 13:09:02 +00:00
j433866
bb7487c476 Change to use new FileType library 2019-03-13 09:20:13 +00:00
j433866
308195279c Bring up to date with master 2019-03-13 09:17:43 +00:00
j433866
e95b7075b9 Add convert image format operation 2019-03-12 16:29:20 +00:00
j433866
dfbc1beccd Add sharpen image operation 2019-03-12 16:24:43 +00:00
j433866
3e428c044a Add min values to operation args 2019-03-08 13:38:59 +00:00
j433866
11451ac6b9 Add image format pattern.
("borrowed" from RenderImage)
2019-03-07 13:35:37 +00:00
j433866
21a8d03201 Move parsing and generation of QR codes to lib folder.
Also rewrote QR code parsing to be more readable and actually error out properly.
2019-03-07 13:21:26 +00:00
53 changed files with 4831 additions and 2463 deletions

View File

@@ -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
View 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.

View 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.

View 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

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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",

View File

@@ -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
}),

View File

@@ -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":

View File

@@ -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",

View 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;
}

View File

@@ -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
View 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.");
}
}

View 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;

View File

@@ -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)}">`;
}
}

View 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;

View File

@@ -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));
}
});
});
}
}

View File

@@ -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)}">`;
}
}

View 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;

View File

@@ -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)}">`;
}
}

View File

@@ -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)}">`;
}
}

View File

@@ -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)}">`;
}
}

View File

@@ -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;

View File

@@ -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)}">`;
}
}

View File

@@ -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);
}
}

View File

@@ -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)}">`;
}
}

View File

@@ -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)}">`;
}
}

View File

@@ -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)}">`;
}
}

View File

@@ -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)}">`;
}
}

View File

@@ -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)}">`;
}
}

View File

@@ -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)}">`;
}
}

View File

@@ -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);
}
}

View File

@@ -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)}">`;
}
}

View File

@@ -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)}">`;
}
}

View 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;

View File

@@ -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;

View File

@@ -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)

View File

@@ -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})`;
}
}

View File

@@ -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>

View File

@@ -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";

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -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;

View File

@@ -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

View File

@@ -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]"