mirror of
https://github.com/gchq/CyberChef.git
synced 2024-11-16 17:08:31 +01:00
Merge remote-tracking branch 'upstream/master' into multiple-input-files
This commit is contained in:
commit
a7eefa88fa
41 changed files with 2971 additions and 273 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -2,6 +2,12 @@
|
||||||
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).
|
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.36.0] - 2019-07-03
|
||||||
|
- 'PGP Verify' operation added [@artemisbot] | [#585]
|
||||||
|
|
||||||
|
### [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
|
### [8.34.0] - 2019-06-28
|
||||||
- Various new visualisations added to the 'Entropy' operation [@MShwed] | [#535]
|
- Various new visualisations added to the 'Entropy' operation [@MShwed] | [#535]
|
||||||
- Efficiency improvements made to the 'Entropy' operation for large file support [@n1474335]
|
- Efficiency improvements made to the 'Entropy' operation for large file support [@n1474335]
|
||||||
|
@ -140,6 +146,8 @@ All major and minor version changes will be documented in this file. Details of
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[8.36.0]: https://github.com/gchq/CyberChef/releases/tag/v8.36.0
|
||||||
|
[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.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.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.32.0]: https://github.com/gchq/CyberChef/releases/tag/v8.32.0
|
||||||
|
@ -244,9 +252,11 @@ All major and minor version changes will be documented in this file. Details of
|
||||||
[#489]: https://github.com/gchq/CyberChef/pull/489
|
[#489]: https://github.com/gchq/CyberChef/pull/489
|
||||||
[#496]: https://github.com/gchq/CyberChef/pull/496
|
[#496]: https://github.com/gchq/CyberChef/pull/496
|
||||||
[#506]: https://github.com/gchq/CyberChef/pull/506
|
[#506]: https://github.com/gchq/CyberChef/pull/506
|
||||||
|
[#515]: https://github.com/gchq/CyberChef/pull/515
|
||||||
[#516]: https://github.com/gchq/CyberChef/pull/516
|
[#516]: https://github.com/gchq/CyberChef/pull/516
|
||||||
[#525]: https://github.com/gchq/CyberChef/pull/525
|
[#525]: https://github.com/gchq/CyberChef/pull/525
|
||||||
[#531]: https://github.com/gchq/CyberChef/pull/531
|
[#531]: https://github.com/gchq/CyberChef/pull/531
|
||||||
[#533]: https://github.com/gchq/CyberChef/pull/533
|
[#533]: https://github.com/gchq/CyberChef/pull/533
|
||||||
[#535]: https://github.com/gchq/CyberChef/pull/535
|
[#535]: https://github.com/gchq/CyberChef/pull/535
|
||||||
[#571]: https://github.com/gchq/CyberChef/pull/571
|
[#571]: https://github.com/gchq/CyberChef/pull/571
|
||||||
|
[#585]: https://github.com/gchq/CyberChef/pull/585
|
||||||
|
|
|
@ -4,12 +4,6 @@ module.exports = function(api) {
|
||||||
return {
|
return {
|
||||||
"presets": [
|
"presets": [
|
||||||
["@babel/preset-env", {
|
["@babel/preset-env", {
|
||||||
"targets": {
|
|
||||||
"chrome": 40,
|
|
||||||
"firefox": 35,
|
|
||||||
"edge": 14,
|
|
||||||
"node": "6.5"
|
|
||||||
},
|
|
||||||
"modules": false,
|
"modules": false,
|
||||||
"useBuiltIns": "entry",
|
"useBuiltIns": "entry",
|
||||||
"corejs": 3
|
"corejs": 3
|
||||||
|
|
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cyberchef",
|
"name": "cyberchef",
|
||||||
"version": "8.34.2",
|
"version": "8.36.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cyberchef",
|
"name": "cyberchef",
|
||||||
"version": "8.34.2",
|
"version": "8.36.0",
|
||||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||||
"author": "n1474335 <n1474335@gmail.com>",
|
"author": "n1474335 <n1474335@gmail.com>",
|
||||||
"homepage": "https://gchq.github.io/CyberChef",
|
"homepage": "https://gchq.github.io/CyberChef",
|
||||||
|
|
|
@ -384,6 +384,9 @@
|
||||||
"Contain Image",
|
"Contain Image",
|
||||||
"Cover Image",
|
"Cover Image",
|
||||||
"Image Hue/Saturation/Lightness",
|
"Image Hue/Saturation/Lightness",
|
||||||
|
"Sharpen Image",
|
||||||
|
"Convert Image Format",
|
||||||
|
"Add Text To Image",
|
||||||
"Hex Density chart",
|
"Hex Density chart",
|
||||||
"Scatter chart",
|
"Scatter chart",
|
||||||
"Series chart",
|
"Series chart",
|
||||||
|
|
251
src/core/lib/ImageManipulation.mjs
Normal file
251
src/core/lib/ImageManipulation.mjs
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
/**
|
||||||
|
* Image manipulation resources
|
||||||
|
*
|
||||||
|
* @author j433866 [j433866@gmail.com]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gaussian blurs an image.
|
||||||
|
*
|
||||||
|
* @param {jimp} input
|
||||||
|
* @param {number} radius
|
||||||
|
* @param {boolean} fast
|
||||||
|
* @returns {jimp}
|
||||||
|
*/
|
||||||
|
export function gaussianBlur (input, radius) {
|
||||||
|
try {
|
||||||
|
// From http://blog.ivank.net/fastest-gaussian-blur.html
|
||||||
|
const boxes = boxesForGauss(radius, 3);
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
input = boxBlur(input, (boxes[i] - 1) / 2);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(`Error blurring image. (${err})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} radius
|
||||||
|
* @param {number} numBoxes
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
function boxesForGauss(radius, numBoxes) {
|
||||||
|
const idealWidth = Math.sqrt((12 * radius * radius / numBoxes) + 1);
|
||||||
|
|
||||||
|
let wl = Math.floor(idealWidth);
|
||||||
|
|
||||||
|
if (wl % 2 === 0) {
|
||||||
|
wl--;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wu = wl + 2;
|
||||||
|
|
||||||
|
const mIdeal = (12 * radius * radius - numBoxes * wl * wl - 4 * numBoxes * wl - 3 * numBoxes) / (-4 * wl - 4);
|
||||||
|
const m = Math.round(mIdeal);
|
||||||
|
|
||||||
|
const sizes = [];
|
||||||
|
for (let i = 0; i < numBoxes; i++) {
|
||||||
|
sizes.push(i < m ? wl : wu);
|
||||||
|
}
|
||||||
|
return sizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a box blur effect to the image
|
||||||
|
*
|
||||||
|
* @param {jimp} source
|
||||||
|
* @param {number} radius
|
||||||
|
* @returns {jimp}
|
||||||
|
*/
|
||||||
|
function boxBlur (source, radius) {
|
||||||
|
const width = source.bitmap.width;
|
||||||
|
const height = source.bitmap.height;
|
||||||
|
let output = source.clone();
|
||||||
|
output = boxBlurH(source, output, width, height, radius);
|
||||||
|
source = boxBlurV(output, source, width, height, radius);
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the horizontal blur
|
||||||
|
*
|
||||||
|
* @param {jimp} source
|
||||||
|
* @param {jimp} output
|
||||||
|
* @param {number} width
|
||||||
|
* @param {number} height
|
||||||
|
* @param {number} radius
|
||||||
|
* @returns {jimp}
|
||||||
|
*/
|
||||||
|
function boxBlurH (source, output, width, height, radius) {
|
||||||
|
const iarr = 1 / (radius + radius + 1);
|
||||||
|
for (let i = 0; i < height; i++) {
|
||||||
|
let ti = 0,
|
||||||
|
li = ti,
|
||||||
|
ri = ti + radius;
|
||||||
|
const idx = source.getPixelIndex(ti, i);
|
||||||
|
const firstValRed = source.bitmap.data[idx],
|
||||||
|
firstValGreen = source.bitmap.data[idx + 1],
|
||||||
|
firstValBlue = source.bitmap.data[idx + 2],
|
||||||
|
firstValAlpha = source.bitmap.data[idx + 3];
|
||||||
|
|
||||||
|
const lastIdx = source.getPixelIndex(width - 1, i),
|
||||||
|
lastValRed = source.bitmap.data[lastIdx],
|
||||||
|
lastValGreen = source.bitmap.data[lastIdx + 1],
|
||||||
|
lastValBlue = source.bitmap.data[lastIdx + 2],
|
||||||
|
lastValAlpha = source.bitmap.data[lastIdx + 3];
|
||||||
|
|
||||||
|
let red = (radius + 1) * firstValRed;
|
||||||
|
let green = (radius + 1) * firstValGreen;
|
||||||
|
let blue = (radius + 1) * firstValBlue;
|
||||||
|
let alpha = (radius + 1) * firstValAlpha;
|
||||||
|
|
||||||
|
for (let j = 0; j < radius; j++) {
|
||||||
|
const jIdx = source.getPixelIndex(ti + j, i);
|
||||||
|
red += source.bitmap.data[jIdx];
|
||||||
|
green += source.bitmap.data[jIdx + 1];
|
||||||
|
blue += source.bitmap.data[jIdx + 2];
|
||||||
|
alpha += source.bitmap.data[jIdx + 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0; j <= radius; j++) {
|
||||||
|
const jIdx = source.getPixelIndex(ri++, i);
|
||||||
|
red += source.bitmap.data[jIdx] - firstValRed;
|
||||||
|
green += source.bitmap.data[jIdx + 1] - firstValGreen;
|
||||||
|
blue += source.bitmap.data[jIdx + 2] - firstValBlue;
|
||||||
|
alpha += source.bitmap.data[jIdx + 3] - firstValAlpha;
|
||||||
|
|
||||||
|
const tiIdx = source.getPixelIndex(ti++, i);
|
||||||
|
output.bitmap.data[tiIdx] = Math.round(red * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = radius + 1; j < width - radius; j++) {
|
||||||
|
const riIdx = source.getPixelIndex(ri++, i);
|
||||||
|
const liIdx = source.getPixelIndex(li++, i);
|
||||||
|
red += source.bitmap.data[riIdx] - source.bitmap.data[liIdx];
|
||||||
|
green += source.bitmap.data[riIdx + 1] - source.bitmap.data[liIdx + 1];
|
||||||
|
blue += source.bitmap.data[riIdx + 2] - source.bitmap.data[liIdx + 2];
|
||||||
|
alpha += source.bitmap.data[riIdx + 3] - source.bitmap.data[liIdx + 3];
|
||||||
|
|
||||||
|
const tiIdx = source.getPixelIndex(ti++, i);
|
||||||
|
output.bitmap.data[tiIdx] = Math.round(red * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = width - radius; j < width; j++) {
|
||||||
|
const liIdx = source.getPixelIndex(li++, i);
|
||||||
|
red += lastValRed - source.bitmap.data[liIdx];
|
||||||
|
green += lastValGreen - source.bitmap.data[liIdx + 1];
|
||||||
|
blue += lastValBlue - source.bitmap.data[liIdx + 2];
|
||||||
|
alpha += lastValAlpha - source.bitmap.data[liIdx + 3];
|
||||||
|
|
||||||
|
const tiIdx = source.getPixelIndex(ti++, i);
|
||||||
|
output.bitmap.data[tiIdx] = Math.round(red * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the vertical blur
|
||||||
|
*
|
||||||
|
* @param {jimp} source
|
||||||
|
* @param {jimp} output
|
||||||
|
* @param {number} width
|
||||||
|
* @param {number} height
|
||||||
|
* @param {number} radius
|
||||||
|
* @returns {jimp}
|
||||||
|
*/
|
||||||
|
function boxBlurV (source, output, width, height, radius) {
|
||||||
|
const iarr = 1 / (radius + radius + 1);
|
||||||
|
for (let i = 0; i < width; i++) {
|
||||||
|
let ti = 0,
|
||||||
|
li = ti,
|
||||||
|
ri = ti + radius;
|
||||||
|
|
||||||
|
const idx = source.getPixelIndex(i, ti);
|
||||||
|
|
||||||
|
const firstValRed = source.bitmap.data[idx],
|
||||||
|
firstValGreen = source.bitmap.data[idx + 1],
|
||||||
|
firstValBlue = source.bitmap.data[idx + 2],
|
||||||
|
firstValAlpha = source.bitmap.data[idx + 3];
|
||||||
|
|
||||||
|
const lastIdx = source.getPixelIndex(i, height - 1),
|
||||||
|
lastValRed = source.bitmap.data[lastIdx],
|
||||||
|
lastValGreen = source.bitmap.data[lastIdx + 1],
|
||||||
|
lastValBlue = source.bitmap.data[lastIdx + 2],
|
||||||
|
lastValAlpha = source.bitmap.data[lastIdx + 3];
|
||||||
|
|
||||||
|
let red = (radius + 1) * firstValRed;
|
||||||
|
let green = (radius + 1) * firstValGreen;
|
||||||
|
let blue = (radius + 1) * firstValBlue;
|
||||||
|
let alpha = (radius + 1) * firstValAlpha;
|
||||||
|
|
||||||
|
for (let j = 0; j < radius; j++) {
|
||||||
|
const jIdx = source.getPixelIndex(i, ti + j);
|
||||||
|
red += source.bitmap.data[jIdx];
|
||||||
|
green += source.bitmap.data[jIdx + 1];
|
||||||
|
blue += source.bitmap.data[jIdx + 2];
|
||||||
|
alpha += source.bitmap.data[jIdx + 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0; j <= radius; j++) {
|
||||||
|
const riIdx = source.getPixelIndex(i, ri++);
|
||||||
|
red += source.bitmap.data[riIdx] - firstValRed;
|
||||||
|
green += source.bitmap.data[riIdx + 1] - firstValGreen;
|
||||||
|
blue += source.bitmap.data[riIdx + 2] - firstValBlue;
|
||||||
|
alpha += source.bitmap.data[riIdx + 3] - firstValAlpha;
|
||||||
|
|
||||||
|
const tiIdx = source.getPixelIndex(i, ti++);
|
||||||
|
output.bitmap.data[tiIdx] = Math.round(red * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = radius + 1; j < height - radius; j++) {
|
||||||
|
const riIdx = source.getPixelIndex(i, ri++);
|
||||||
|
const liIdx = source.getPixelIndex(i, li++);
|
||||||
|
red += source.bitmap.data[riIdx] - source.bitmap.data[liIdx];
|
||||||
|
green += source.bitmap.data[riIdx + 1] - source.bitmap.data[liIdx + 1];
|
||||||
|
blue += source.bitmap.data[riIdx + 2] - source.bitmap.data[liIdx + 2];
|
||||||
|
alpha += source.bitmap.data[riIdx + 3] - source.bitmap.data[liIdx + 3];
|
||||||
|
|
||||||
|
const tiIdx = source.getPixelIndex(i, ti++);
|
||||||
|
output.bitmap.data[tiIdx] = Math.round(red * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = height - radius; j < height; j++) {
|
||||||
|
const liIdx = source.getPixelIndex(i, li++);
|
||||||
|
red += lastValRed - source.bitmap.data[liIdx];
|
||||||
|
green += lastValGreen - source.bitmap.data[liIdx + 1];
|
||||||
|
blue += lastValBlue - source.bitmap.data[liIdx + 2];
|
||||||
|
alpha += lastValAlpha - source.bitmap.data[liIdx + 3];
|
||||||
|
|
||||||
|
const tiIdx = source.getPixelIndex(i, ti++);
|
||||||
|
output.bitmap.data[tiIdx] = Math.round(red * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
|
||||||
|
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
|
@ -312,6 +312,11 @@ class Magic {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the recipe returned an empty buffer, do not continue
|
||||||
|
if (_buffersEqual(output, new ArrayBuffer())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const magic = new Magic(output, this.opPatterns),
|
const magic = new Magic(output, this.opPatterns),
|
||||||
speculativeResults = await magic.speculativeExecution(
|
speculativeResults = await magic.speculativeExecution(
|
||||||
depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful, crib);
|
depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful, crib);
|
||||||
|
@ -395,7 +400,12 @@ class Magic {
|
||||||
const recipe = new Recipe(recipeConfig);
|
const recipe = new Recipe(recipeConfig);
|
||||||
try {
|
try {
|
||||||
await recipe.execute(dish);
|
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) {
|
} catch (err) {
|
||||||
// If there are errors, return an empty buffer
|
// If there are errors, return an empty buffer
|
||||||
return new ArrayBuffer();
|
return new ArrayBuffer();
|
||||||
|
|
93
src/core/lib/QRCode.mjs
Normal file
93
src/core/lib/QRCode.mjs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/**
|
||||||
|
* QR code resources
|
||||||
|
*
|
||||||
|
* @author j433866 [j433866@gmail.com]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
import jsQR from "jsqr";
|
||||||
|
import qr from "qr-image";
|
||||||
|
import jimp from "jimp";
|
||||||
|
import Utils from "../Utils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a QR code image from an image
|
||||||
|
*
|
||||||
|
* @param {ArrayBuffer} input
|
||||||
|
* @param {boolean} normalise
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export async function parseQrCode(input, normalise) {
|
||||||
|
let image;
|
||||||
|
try {
|
||||||
|
image = await jimp.read(input);
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(`Error opening image. (${err})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (normalise) {
|
||||||
|
image.rgba(false);
|
||||||
|
image.background(0xFFFFFFFF);
|
||||||
|
image.normalize();
|
||||||
|
image.greyscale();
|
||||||
|
image = await image.getBufferAsync(jimp.MIME_JPEG);
|
||||||
|
image = await jimp.read(image);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(`Error normalising iamge. (${err})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const qrData = jsQR(image.bitmap.data, image.getWidth(), image.getHeight());
|
||||||
|
if (qrData) {
|
||||||
|
return qrData.data;
|
||||||
|
} else {
|
||||||
|
throw new OperationError("Could not read a QR code from the image.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a QR code from the input string
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {string} format
|
||||||
|
* @param {number} moduleSize
|
||||||
|
* @param {number} margin
|
||||||
|
* @param {string} errorCorrection
|
||||||
|
* @returns {ArrayBuffer}
|
||||||
|
*/
|
||||||
|
export function generateQrCode(input, format, moduleSize, margin, errorCorrection) {
|
||||||
|
const formats = ["SVG", "EPS", "PDF", "PNG"];
|
||||||
|
if (!formats.includes(format.toUpperCase())) {
|
||||||
|
throw new OperationError("Unsupported QR code format.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let qrImage;
|
||||||
|
try {
|
||||||
|
qrImage = qr.imageSync(input, {
|
||||||
|
type: format,
|
||||||
|
size: moduleSize,
|
||||||
|
margin: margin,
|
||||||
|
"ec_level": errorCorrection.charAt(0).toUpperCase()
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(`Error generating QR code. (${err})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qrImage) {
|
||||||
|
throw new OperationError("Error generating QR code.");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (format) {
|
||||||
|
case "SVG":
|
||||||
|
case "EPS":
|
||||||
|
case "PDF":
|
||||||
|
return Utils.strToArrayBuffer(qrImage);
|
||||||
|
case "PNG":
|
||||||
|
return qrImage.buffer;
|
||||||
|
default:
|
||||||
|
throw new OperationError("Unsupported QR code format.");
|
||||||
|
}
|
||||||
|
}
|
266
src/core/operations/AddTextToImage.mjs
Normal file
266
src/core/operations/AddTextToImage.mjs
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
/**
|
||||||
|
* @author j433866 [j433866@gmail.com]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
import { isImage } from "../lib/FileType";
|
||||||
|
import { toBase64 } from "../lib/Base64";
|
||||||
|
import jimp from "jimp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Text To Image operation
|
||||||
|
*/
|
||||||
|
class AddTextToImage extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AddTextToImage constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Add Text To Image";
|
||||||
|
this.module = "Image";
|
||||||
|
this.description = "Adds text onto an image.<br><br>Text can be horizontally or vertically aligned, or the position can be manually specified.<br>Variants of the Roboto font face are available in any size or colour.";
|
||||||
|
this.infoURL = "";
|
||||||
|
this.inputType = "ArrayBuffer";
|
||||||
|
this.outputType = "ArrayBuffer";
|
||||||
|
this.presentType = "html";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Text",
|
||||||
|
type: "string",
|
||||||
|
value: ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Horizontal align",
|
||||||
|
type: "option",
|
||||||
|
value: ["None", "Left", "Center", "Right"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Vertical align",
|
||||||
|
type: "option",
|
||||||
|
value: ["None", "Top", "Middle", "Bottom"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "X position",
|
||||||
|
type: "number",
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Y position",
|
||||||
|
type: "number",
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Size",
|
||||||
|
type: "number",
|
||||||
|
value: 32,
|
||||||
|
min: 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Font face",
|
||||||
|
type: "option",
|
||||||
|
value: [
|
||||||
|
"Roboto",
|
||||||
|
"Roboto Black",
|
||||||
|
"Roboto Mono",
|
||||||
|
"Roboto Slab"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Red",
|
||||||
|
type: "number",
|
||||||
|
value: 255,
|
||||||
|
min: 0,
|
||||||
|
max: 255
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Green",
|
||||||
|
type: "number",
|
||||||
|
value: 255,
|
||||||
|
min: 0,
|
||||||
|
max: 255
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Blue",
|
||||||
|
type: "number",
|
||||||
|
value: 255,
|
||||||
|
min: 0,
|
||||||
|
max: 255
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Alpha",
|
||||||
|
type: "number",
|
||||||
|
value: 255,
|
||||||
|
min: 0,
|
||||||
|
max: 255
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ArrayBuffer} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {byteArray}
|
||||||
|
*/
|
||||||
|
async run(input, args) {
|
||||||
|
const text = args[0],
|
||||||
|
hAlign = args[1],
|
||||||
|
vAlign = args[2],
|
||||||
|
size = args[5],
|
||||||
|
fontFace = args[6],
|
||||||
|
red = args[7],
|
||||||
|
green = args[8],
|
||||||
|
blue = args[9],
|
||||||
|
alpha = args[10];
|
||||||
|
|
||||||
|
let xPos = args[3],
|
||||||
|
yPos = args[4];
|
||||||
|
|
||||||
|
if (!isImage(new Uint8Array(input))) {
|
||||||
|
throw new OperationError("Invalid file type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let image;
|
||||||
|
try {
|
||||||
|
image = await jimp.read(input);
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (ENVIRONMENT_IS_WORKER())
|
||||||
|
self.sendStatusMessage("Adding text to image...");
|
||||||
|
|
||||||
|
const fontsMap = {};
|
||||||
|
const fonts = [
|
||||||
|
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.fnt"),
|
||||||
|
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoBlack72White.fnt"),
|
||||||
|
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoMono72White.fnt"),
|
||||||
|
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoSlab72White.fnt")
|
||||||
|
];
|
||||||
|
|
||||||
|
await Promise.all(fonts)
|
||||||
|
.then(fonts => {
|
||||||
|
fontsMap.Roboto = fonts[0];
|
||||||
|
fontsMap["Roboto Black"] = fonts[1];
|
||||||
|
fontsMap["Roboto Mono"] = fonts[2];
|
||||||
|
fontsMap["Roboto Slab"] = fonts[3];
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Make Webpack load the png font images
|
||||||
|
await Promise.all([
|
||||||
|
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.png"),
|
||||||
|
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoSlab72White.png"),
|
||||||
|
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoMono72White.png"),
|
||||||
|
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoBlack72White.png")
|
||||||
|
]);
|
||||||
|
|
||||||
|
const font = fontsMap[fontFace];
|
||||||
|
|
||||||
|
// LoadFont needs an absolute url, so append the font name to self.docURL
|
||||||
|
const jimpFont = await jimp.loadFont(self.docURL + "/" + font.default);
|
||||||
|
|
||||||
|
jimpFont.pages.forEach(function(page) {
|
||||||
|
if (page.bitmap) {
|
||||||
|
// Adjust the RGB values of the image pages to change the font colour.
|
||||||
|
const pageWidth = page.bitmap.width;
|
||||||
|
const pageHeight = page.bitmap.height;
|
||||||
|
for (let ix = 0; ix < pageWidth; ix++) {
|
||||||
|
for (let iy = 0; iy < pageHeight; iy++) {
|
||||||
|
const idx = (iy * pageWidth + ix) << 2;
|
||||||
|
|
||||||
|
const newRed = page.bitmap.data[idx] - (255 - red);
|
||||||
|
const newGreen = page.bitmap.data[idx + 1] - (255 - green);
|
||||||
|
const newBlue = page.bitmap.data[idx + 2] - (255 - blue);
|
||||||
|
const newAlpha = page.bitmap.data[idx + 3] - (255 - alpha);
|
||||||
|
|
||||||
|
// Make sure the bitmap values don't go below 0 as that makes jimp very unhappy
|
||||||
|
page.bitmap.data[idx] = (newRed > 0) ? newRed : 0;
|
||||||
|
page.bitmap.data[idx + 1] = (newGreen > 0) ? newGreen : 0;
|
||||||
|
page.bitmap.data[idx + 2] = (newBlue > 0) ? newBlue : 0;
|
||||||
|
page.bitmap.data[idx + 3] = (newAlpha > 0) ? newAlpha : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a temporary image to hold the rendered text
|
||||||
|
const textImage = new jimp(jimp.measureText(jimpFont, text), jimp.measureTextHeight(jimpFont, text));
|
||||||
|
textImage.print(jimpFont, 0, 0, text);
|
||||||
|
|
||||||
|
// Scale the rendered text image to the correct size
|
||||||
|
const scaleFactor = size / 72;
|
||||||
|
if (size !== 1) {
|
||||||
|
// Use bicubic for decreasing size
|
||||||
|
if (size > 1) {
|
||||||
|
textImage.scale(scaleFactor, jimp.RESIZE_BICUBIC);
|
||||||
|
} else {
|
||||||
|
textImage.scale(scaleFactor, jimp.RESIZE_BILINEAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If using the alignment options, calculate the pixel values AFTER the image has been scaled
|
||||||
|
switch (hAlign) {
|
||||||
|
case "Left":
|
||||||
|
xPos = 0;
|
||||||
|
break;
|
||||||
|
case "Center":
|
||||||
|
xPos = (image.getWidth() / 2) - (textImage.getWidth() / 2);
|
||||||
|
break;
|
||||||
|
case "Right":
|
||||||
|
xPos = image.getWidth() - textImage.getWidth();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (vAlign) {
|
||||||
|
case "Top":
|
||||||
|
yPos = 0;
|
||||||
|
break;
|
||||||
|
case "Middle":
|
||||||
|
yPos = (image.getHeight() / 2) - (textImage.getHeight() / 2);
|
||||||
|
break;
|
||||||
|
case "Bottom":
|
||||||
|
yPos = image.getHeight() - textImage.getHeight();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blit the rendered text image onto the original source image
|
||||||
|
image.blit(textImage, xPos, yPos);
|
||||||
|
|
||||||
|
let imageBuffer;
|
||||||
|
if (image.getMIME() === "image/gif") {
|
||||||
|
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||||
|
} else {
|
||||||
|
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||||
|
}
|
||||||
|
return imageBuffer.buffer;
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(`Error adding text to image. (${err})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the blurred image using HTML for web apps
|
||||||
|
*
|
||||||
|
* @param {ArrayBuffer} data
|
||||||
|
* @returns {html}
|
||||||
|
*/
|
||||||
|
present(data) {
|
||||||
|
if (!data.byteLength) return "";
|
||||||
|
const dataArray = new Uint8Array(data);
|
||||||
|
|
||||||
|
const type = isImage(dataArray);
|
||||||
|
if (!type) {
|
||||||
|
throw new OperationError("Invalid file type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddTextToImage;
|
|
@ -9,6 +9,7 @@ import OperationError from "../errors/OperationError";
|
||||||
import { isImage } from "../lib/FileType";
|
import { isImage } from "../lib/FileType";
|
||||||
import { toBase64 } from "../lib/Base64";
|
import { toBase64 } from "../lib/Base64";
|
||||||
import jimp from "jimp";
|
import jimp from "jimp";
|
||||||
|
import { gaussianBlur } from "../lib/ImageManipulation";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blur Image operation
|
* Blur Image operation
|
||||||
|
@ -25,8 +26,8 @@ class BlurImage extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Applies a blur effect to the image.<br><br>Gaussian blur is much slower than fast blur, but produces better results.";
|
this.description = "Applies a blur effect to the image.<br><br>Gaussian blur is much slower than fast blur, but produces better results.";
|
||||||
this.infoURL = "https://wikipedia.org/wiki/Gaussian_blur";
|
this.infoURL = "https://wikipedia.org/wiki/Gaussian_blur";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -44,37 +45,44 @@ class BlurImage extends Operation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
const [blurAmount, blurType] = args;
|
const [blurAmount, blurType] = args;
|
||||||
|
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${err})`);
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
switch (blurType){
|
switch (blurType){
|
||||||
case "Fast":
|
case "Fast":
|
||||||
|
if (ENVIRONMENT_IS_WORKER())
|
||||||
|
self.sendStatusMessage("Fast blurring image...");
|
||||||
image.blur(blurAmount);
|
image.blur(blurAmount);
|
||||||
break;
|
break;
|
||||||
case "Gaussian":
|
case "Gaussian":
|
||||||
if (ENVIRONMENT_IS_WORKER())
|
if (ENVIRONMENT_IS_WORKER())
|
||||||
self.sendStatusMessage("Gaussian blurring image. This may take a while...");
|
self.sendStatusMessage("Gaussian blurring image...");
|
||||||
image.gaussian(blurAmount);
|
image = gaussianBlur(image, blurAmount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
let imageBuffer;
|
||||||
return [...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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error blurring image. (${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
|
* Displays the blurred image using HTML for web apps
|
||||||
*
|
*
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ class ContainImage extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Scales an image to the specified width and height, maintaining the aspect ratio. The image may be letterboxed.";
|
this.description = "Scales an image to the specified width and height, maintaining the aspect ratio. The image may be letterboxed.";
|
||||||
this.infoURL = "";
|
this.infoURL = "";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -72,17 +72,22 @@ class ContainImage extends Operation {
|
||||||
"Bezier"
|
"Bezier"
|
||||||
],
|
],
|
||||||
defaultIndex: 1
|
defaultIndex: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Opaque background",
|
||||||
|
type: "boolean",
|
||||||
|
value: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
const [width, height, hAlign, vAlign, alg] = args;
|
const [width, height, hAlign, vAlign, alg, opaqueBg] = args;
|
||||||
|
|
||||||
const resizeMap = {
|
const resizeMap = {
|
||||||
"Nearest Neighbour": jimp.RESIZE_NEAREST_NEIGHBOR,
|
"Nearest Neighbour": jimp.RESIZE_NEAREST_NEIGHBOR,
|
||||||
|
@ -101,13 +106,13 @@ class ContainImage extends Operation {
|
||||||
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
|
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${err})`);
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
}
|
}
|
||||||
|
@ -115,8 +120,20 @@ class ContainImage extends Operation {
|
||||||
if (ENVIRONMENT_IS_WORKER())
|
if (ENVIRONMENT_IS_WORKER())
|
||||||
self.sendStatusMessage("Containing image...");
|
self.sendStatusMessage("Containing image...");
|
||||||
image.contain(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]);
|
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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error containing image. (${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
|
* Displays the contained image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
143
src/core/operations/ConvertImageFormat.mjs
Normal file
143
src/core/operations/ConvertImageFormat.mjs
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
/**
|
||||||
|
* @author j433866 [j433866@gmail.com]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
import { isImage } from "../lib/FileType";
|
||||||
|
import { toBase64 } from "../lib/Base64";
|
||||||
|
import jimp from "jimp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert Image Format operation
|
||||||
|
*/
|
||||||
|
class ConvertImageFormat extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConvertImageFormat constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Convert Image Format";
|
||||||
|
this.module = "Image";
|
||||||
|
this.description = "Converts an image between different formats. Supported formats:<br><ul><li>Joint Photographic Experts Group (JPEG)</li><li>Portable Network Graphics (PNG)</li><li>Bitmap (BMP)</li><li>Tagged Image File Format (TIFF)</li></ul><br>Note: GIF files are supported for input, but cannot be outputted.";
|
||||||
|
this.infoURL = "https://wikipedia.org/wiki/Image_file_formats";
|
||||||
|
this.inputType = "ArrayBuffer";
|
||||||
|
this.outputType = "ArrayBuffer";
|
||||||
|
this.presentType = "html";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Output Format",
|
||||||
|
type: "option",
|
||||||
|
value: [
|
||||||
|
"JPEG",
|
||||||
|
"PNG",
|
||||||
|
"BMP",
|
||||||
|
"TIFF"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "JPEG Quality",
|
||||||
|
type: "number",
|
||||||
|
value: 80,
|
||||||
|
min: 1,
|
||||||
|
max: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PNG Filter Type",
|
||||||
|
type: "option",
|
||||||
|
value: [
|
||||||
|
"Auto",
|
||||||
|
"None",
|
||||||
|
"Sub",
|
||||||
|
"Up",
|
||||||
|
"Average",
|
||||||
|
"Paeth"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PNG Deflate Level",
|
||||||
|
type: "number",
|
||||||
|
value: 9,
|
||||||
|
min: 0,
|
||||||
|
max: 9
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ArrayBuffer} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {byteArray}
|
||||||
|
*/
|
||||||
|
async run(input, args) {
|
||||||
|
const [format, jpegQuality, pngFilterType, pngDeflateLevel] = args;
|
||||||
|
const formatMap = {
|
||||||
|
"JPEG": jimp.MIME_JPEG,
|
||||||
|
"PNG": jimp.MIME_PNG,
|
||||||
|
"BMP": jimp.MIME_BMP,
|
||||||
|
"TIFF": jimp.MIME_TIFF
|
||||||
|
};
|
||||||
|
|
||||||
|
const pngFilterMap = {
|
||||||
|
"Auto": jimp.PNG_FILTER_AUTO,
|
||||||
|
"None": jimp.PNG_FILTER_NONE,
|
||||||
|
"Sub": jimp.PNG_FILTER_SUB,
|
||||||
|
"Up": jimp.PNG_FILTER_UP,
|
||||||
|
"Average": jimp.PNG_FILTER_AVERAGE,
|
||||||
|
"Paeth": jimp.PNG_FILTER_PATH // Incorrect spelling in Jimp library
|
||||||
|
};
|
||||||
|
|
||||||
|
const mime = formatMap[format];
|
||||||
|
|
||||||
|
if (!isImage(new Uint8Array(input))) {
|
||||||
|
throw new OperationError("Invalid file format.");
|
||||||
|
}
|
||||||
|
let image;
|
||||||
|
try {
|
||||||
|
image = await jimp.read(input);
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(`Error opening image file. (${err})`);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
switch (format) {
|
||||||
|
case "JPEG":
|
||||||
|
image.quality(jpegQuality);
|
||||||
|
break;
|
||||||
|
case "PNG":
|
||||||
|
image.filterType(pngFilterMap[pngFilterType]);
|
||||||
|
image.deflateLevel(pngDeflateLevel);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageBuffer = await image.getBufferAsync(mime);
|
||||||
|
return imageBuffer.buffer;
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(`Error converting image format. (${err})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the converted image using HTML for web apps
|
||||||
|
*
|
||||||
|
* @param {ArrayBuffer} data
|
||||||
|
* @returns {html}
|
||||||
|
*/
|
||||||
|
present(data) {
|
||||||
|
if (!data.byteLength) return "";
|
||||||
|
const dataArray = new Uint8Array(data);
|
||||||
|
|
||||||
|
const type = isImage(dataArray);
|
||||||
|
if (!type) {
|
||||||
|
throw new OperationError("Invalid file type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConvertImageFormat;
|
|
@ -25,8 +25,8 @@ class CoverImage extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Scales the image to the given width and height, keeping the aspect ratio. The image may be clipped.";
|
this.description = "Scales the image to the given width and height, keeping the aspect ratio. The image may be clipped.";
|
||||||
this.infoURL = "";
|
this.infoURL = "";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -77,7 +77,7 @@ class CoverImage extends Operation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
|
@ -101,13 +101,13 @@ class CoverImage extends Operation {
|
||||||
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
|
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${err})`);
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
}
|
}
|
||||||
|
@ -115,8 +115,13 @@ class CoverImage extends Operation {
|
||||||
if (ENVIRONMENT_IS_WORKER())
|
if (ENVIRONMENT_IS_WORKER())
|
||||||
self.sendStatusMessage("Covering image...");
|
self.sendStatusMessage("Covering image...");
|
||||||
image.cover(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]);
|
image.cover(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]);
|
||||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
let imageBuffer;
|
||||||
return [...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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error covering image. (${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
|
* Displays the covered image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ class CropImage extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Crops an image to the specified region, or automatically crops edges.<br><br><b><u>Autocrop</u></b><br>Automatically crops same-colour borders from the image.<br><br><u>Autocrop tolerance</u><br>A percentage value for the tolerance of colour difference between pixels.<br><br><u>Only autocrop frames</u><br>Only crop real frames (all sides must have the same border)<br><br><u>Symmetric autocrop</u><br>Force autocrop to be symmetric (top/bottom and left/right are cropped by the same amount)<br><br><u>Autocrop keep border</u><br>The number of pixels of border to leave around the image.";
|
this.description = "Crops an image to the specified region, or automatically crops edges.<br><br><b><u>Autocrop</u></b><br>Automatically crops same-colour borders from the image.<br><br><u>Autocrop tolerance</u><br>A percentage value for the tolerance of colour difference between pixels.<br><br><u>Only autocrop frames</u><br>Only crop real frames (all sides must have the same border)<br><br><u>Symmetric autocrop</u><br>Force autocrop to be symmetric (top/bottom and left/right are cropped by the same amount)<br><br><u>Autocrop keep border</u><br>The number of pixels of border to leave around the image.";
|
||||||
this.infoURL = "https://wikipedia.org/wiki/Cropping_(image)";
|
this.infoURL = "https://wikipedia.org/wiki/Cropping_(image)";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -86,19 +86,19 @@ class CropImage extends Operation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
const [xPos, yPos, width, height, autocrop, autoTolerance, autoFrames, autoSymmetric, autoBorder] = args;
|
const [xPos, yPos, width, height, autocrop, autoTolerance, autoFrames, autoSymmetric, autoBorder] = args;
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${err})`);
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
}
|
}
|
||||||
|
@ -116,8 +116,13 @@ class CropImage extends Operation {
|
||||||
image.crop(xPos, yPos, width, height);
|
image.crop(xPos, yPos, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
let imageBuffer;
|
||||||
return [...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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error cropping image. (${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
|
* Displays the cropped image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,25 +25,25 @@ class DitherImage extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Apply a dither effect to an image.";
|
this.description = "Apply a dither effect to an image.";
|
||||||
this.infoURL = "https://wikipedia.org/wiki/Dither";
|
this.infoURL = "https://wikipedia.org/wiki/Dither";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [];
|
this.args = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${err})`);
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,14 @@ class DitherImage extends Operation {
|
||||||
if (ENVIRONMENT_IS_WORKER())
|
if (ENVIRONMENT_IS_WORKER())
|
||||||
self.sendStatusMessage("Applying dither to image...");
|
self.sendStatusMessage("Applying dither to image...");
|
||||||
image.dither565();
|
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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error applying dither to image. (${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
|
* Displays the dithered image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ class FlipImage extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Flips an image along its X or Y axis.";
|
this.description = "Flips an image along its X or Y axis.";
|
||||||
this.infoURL = "";
|
this.infoURL = "";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -38,19 +38,19 @@ class FlipImage extends Operation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
const [flipAxis] = args;
|
const [flipAxis] = args;
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid input file type.");
|
throw new OperationError("Invalid input file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${err})`);
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,13 @@ class FlipImage extends Operation {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
let imageBuffer;
|
||||||
return [...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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error flipping image. (${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
|
* Displays the flipped image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import Operation from "../Operation";
|
import Operation from "../Operation";
|
||||||
import OperationError from "../errors/OperationError";
|
import OperationError from "../errors/OperationError";
|
||||||
import qr from "qr-image";
|
import { generateQrCode } from "../lib/QRCode";
|
||||||
import { toBase64 } from "../lib/Base64";
|
import { toBase64 } from "../lib/Base64";
|
||||||
import { isImage } from "../lib/FileType";
|
import { isImage } from "../lib/FileType";
|
||||||
import Utils from "../Utils";
|
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.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.infoURL = "https://wikipedia.org/wiki/QR_code";
|
||||||
this.inputType = "string";
|
this.inputType = "string";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -38,12 +38,14 @@ class GenerateQRCode extends Operation {
|
||||||
{
|
{
|
||||||
"name": "Module size (px)",
|
"name": "Module size (px)",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"value": 5
|
"value": 5,
|
||||||
|
"min": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Margin (num modules)",
|
"name": "Margin (num modules)",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"value": 2
|
"value": 2,
|
||||||
|
"min": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Error correction",
|
"name": "Error correction",
|
||||||
|
@ -57,61 +59,34 @@ class GenerateQRCode extends Operation {
|
||||||
/**
|
/**
|
||||||
* @param {string} input
|
* @param {string} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {ArrayBuffer}
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const [format, size, margin, errorCorrection] = args;
|
const [format, size, margin, errorCorrection] = args;
|
||||||
|
|
||||||
// Create new QR image from the input data, and convert it to a buffer
|
return generateQrCode(input, format, size, margin, errorCorrection);
|
||||||
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.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the QR image using HTML for web apps
|
* Displays the QR image using HTML for web apps
|
||||||
*
|
*
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data, args) {
|
present(data, args) {
|
||||||
if (!data.length) return "";
|
if (!data.byteLength && !data.length) return "";
|
||||||
|
const dataArray = new Uint8Array(data),
|
||||||
const [format] = args;
|
[format] = args;
|
||||||
|
|
||||||
if (format === "PNG") {
|
if (format === "PNG") {
|
||||||
let dataURI = "data:";
|
const type = isImage(dataArray);
|
||||||
const mime = isImage(data);
|
if (!type) {
|
||||||
if (mime){
|
throw new OperationError("Invalid file type.");
|
||||||
dataURI += mime + ";";
|
|
||||||
} else {
|
|
||||||
throw new OperationError("Invalid PNG file generated by QR image");
|
|
||||||
}
|
}
|
||||||
dataURI += "base64," + toBase64(data);
|
|
||||||
|
|
||||||
return `<img src="${dataURI}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Utils.byteArrayToChars(data);
|
return Utils.arrayBufferToStr(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ class ImageBrightnessContrast extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Adjust the brightness or contrast of an image.";
|
this.description = "Adjust the brightness or contrast of an image.";
|
||||||
this.infoURL = "";
|
this.infoURL = "";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -47,19 +47,19 @@ class ImageBrightnessContrast extends Operation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
const [brightness, contrast] = args;
|
const [brightness, contrast] = args;
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${err})`);
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,13 @@ class ImageBrightnessContrast extends Operation {
|
||||||
image.contrast(contrast / 100);
|
image.contrast(contrast / 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
let imageBuffer;
|
||||||
return [...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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error adjusting image brightness or contrast. (${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
|
* Displays the image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ class ImageFilter extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Applies a greyscale or sepia filter to an image.";
|
this.description = "Applies a greyscale or sepia filter to an image.";
|
||||||
this.infoURL = "";
|
this.infoURL = "";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -41,19 +41,19 @@ class ImageFilter extends Operation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
const [filterType] = args;
|
const [filterType] = args;
|
||||||
if (!isImage(input)){
|
if (!isImage(new Uint8Array(input))){
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${err})`);
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,13 @@ class ImageFilter extends Operation {
|
||||||
image.sepia();
|
image.sepia();
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
let imageBuffer;
|
||||||
return [...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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error applying filter to image. (${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
|
* Displays the blurred image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ class ImageHueSaturationLightness extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Adjusts the hue / saturation / lightness (HSL) values of an image.";
|
this.description = "Adjusts the hue / saturation / lightness (HSL) values of an image.";
|
||||||
this.infoURL = "";
|
this.infoURL = "";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -54,20 +54,20 @@ class ImageHueSaturationLightness extends Operation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
const [hue, saturation, lightness] = args;
|
const [hue, saturation, lightness] = args;
|
||||||
|
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error adjusting image hue / saturation / lightness. (${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
|
* Displays the image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,8 @@ class ImageOpacity extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Adjust the opacity of an image.";
|
this.description = "Adjust the opacity of an image.";
|
||||||
this.infoURL = "";
|
this.infoURL = "";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -40,19 +40,19 @@ class ImageOpacity extends Operation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
const [opacity] = args;
|
const [opacity] = args;
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${err})`);
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,13 @@ class ImageOpacity extends Operation {
|
||||||
self.sendStatusMessage("Changing image opacity...");
|
self.sendStatusMessage("Changing image opacity...");
|
||||||
image.opacity(opacity / 100);
|
image.opacity(opacity / 100);
|
||||||
|
|
||||||
const imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
let imageBuffer;
|
||||||
return [...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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error changing image opacity. (${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
|
* Displays the image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,25 +25,25 @@ class InvertImage extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Invert the colours of an image.";
|
this.description = "Invert the colours of an image.";
|
||||||
this.infoURL = "";
|
this.infoURL = "";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [];
|
this.args = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid input file format.");
|
throw new OperationError("Invalid input file format.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${err})`);
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,14 @@ class InvertImage extends Operation {
|
||||||
if (ENVIRONMENT_IS_WORKER())
|
if (ENVIRONMENT_IS_WORKER())
|
||||||
self.sendStatusMessage("Inverting image...");
|
self.sendStatusMessage("Inverting image...");
|
||||||
image.invert();
|
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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error inverting image. (${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
|
* Displays the inverted image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,44 +25,59 @@ class NormaliseImage extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Normalise the image colours.";
|
this.description = "Normalise the image colours.";
|
||||||
this.infoURL = "";
|
this.infoURL = "";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType= "html";
|
this.presentType= "html";
|
||||||
this.args = [];
|
this.args = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid file type.");
|
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);
|
let imageBuffer;
|
||||||
return [...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
|
* Displays the normalised image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ class PGPDecryptAndVerify extends Operation {
|
||||||
text += `${signer.username} `;
|
text += `${signer.username} `;
|
||||||
}
|
}
|
||||||
if (signer.comment) {
|
if (signer.comment) {
|
||||||
text += `${signer.comment} `;
|
text += `(${signer.comment}) `;
|
||||||
}
|
}
|
||||||
if (signer.email) {
|
if (signer.email) {
|
||||||
text += `<${signer.email}>`;
|
text += `<${signer.email}>`;
|
||||||
|
@ -101,8 +101,9 @@ class PGPDecryptAndVerify extends Operation {
|
||||||
text += "\n";
|
text += "\n";
|
||||||
}
|
}
|
||||||
text += [
|
text += [
|
||||||
|
`PGP key ID: ${km.get_pgp_short_key_id()}`,
|
||||||
`PGP fingerprint: ${km.get_pgp_fingerprint().toString("hex")}`,
|
`PGP fingerprint: ${km.get_pgp_fingerprint().toString("hex")}`,
|
||||||
`Signed on ${new Date(ds.sig.hashed_subpackets[0].time * 1000).toUTCString()}`,
|
`Signed on ${new Date(ds.sig.when_generated() * 1000).toUTCString()}`,
|
||||||
"----------------------------------\n"
|
"----------------------------------\n"
|
||||||
].join("\n");
|
].join("\n");
|
||||||
text += unboxedLiterals.toString();
|
text += unboxedLiterals.toString();
|
||||||
|
|
111
src/core/operations/PGPVerify.mjs
Normal file
111
src/core/operations/PGPVerify.mjs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/**
|
||||||
|
* @author Matt C [me@mitt.dev]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
|
import kbpgp from "kbpgp";
|
||||||
|
import { ASP, importPublicKey } from "../lib/PGP";
|
||||||
|
import * as es6promisify from "es6-promisify";
|
||||||
|
const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PGP Verify operation
|
||||||
|
*/
|
||||||
|
class PGPVerify extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PGPVerify constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "PGP Verify";
|
||||||
|
this.module = "PGP";
|
||||||
|
this.description = [
|
||||||
|
"Input: the ASCII-armoured encrypted PGP message you want to verify.",
|
||||||
|
"<br><br>",
|
||||||
|
"Argument: the ASCII-armoured PGP public key of the signer",
|
||||||
|
"<br><br>",
|
||||||
|
"This operation uses PGP to decrypt a clearsigned message.",
|
||||||
|
"<br><br>",
|
||||||
|
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
|
||||||
|
"<br><br>",
|
||||||
|
"This function uses the Keybase implementation of PGP.",
|
||||||
|
].join("\n");
|
||||||
|
this.infoURL = "https://wikipedia.org/wiki/Pretty_Good_Privacy";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "string";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
"name": "Public key of signer",
|
||||||
|
"type": "text",
|
||||||
|
"value": ""
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
async run(input, args) {
|
||||||
|
const signedMessage = input,
|
||||||
|
[publicKey] = args,
|
||||||
|
keyring = new kbpgp.keyring.KeyRing();
|
||||||
|
let unboxedLiterals;
|
||||||
|
|
||||||
|
if (!publicKey) throw new OperationError("Enter the public key of the signer.");
|
||||||
|
const pubKey = await importPublicKey(publicKey);
|
||||||
|
keyring.add_key_manager(pubKey);
|
||||||
|
|
||||||
|
try {
|
||||||
|
unboxedLiterals = await promisify(kbpgp.unbox)({
|
||||||
|
armored: signedMessage,
|
||||||
|
keyfetch: keyring,
|
||||||
|
asp: ASP
|
||||||
|
});
|
||||||
|
const ds = unboxedLiterals[0].get_data_signer();
|
||||||
|
if (ds) {
|
||||||
|
const km = ds.get_key_manager();
|
||||||
|
if (km) {
|
||||||
|
const signer = km.get_userids_mark_primary()[0].components;
|
||||||
|
let text = "Signed by ";
|
||||||
|
if (signer.email || signer.username || signer.comment) {
|
||||||
|
if (signer.username) {
|
||||||
|
text += `${signer.username} `;
|
||||||
|
}
|
||||||
|
if (signer.comment) {
|
||||||
|
text += `(${signer.comment}) `;
|
||||||
|
}
|
||||||
|
if (signer.email) {
|
||||||
|
text += `<${signer.email}>`;
|
||||||
|
}
|
||||||
|
text += "\n";
|
||||||
|
}
|
||||||
|
text += [
|
||||||
|
`PGP key ID: ${km.get_pgp_short_key_id()}`,
|
||||||
|
`PGP fingerprint: ${km.get_pgp_fingerprint().toString("hex")}`,
|
||||||
|
`Signed on ${new Date(ds.sig.when_generated() * 1000).toUTCString()}`,
|
||||||
|
"----------------------------------\n"
|
||||||
|
].join("\n");
|
||||||
|
text += unboxedLiterals.toString();
|
||||||
|
return text.trim();
|
||||||
|
} else {
|
||||||
|
throw new OperationError("Could not identify a key manager.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new OperationError("The data does not appear to be signed.");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(`Couldn't verify message: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PGPVerify;
|
|
@ -6,9 +6,8 @@
|
||||||
|
|
||||||
import Operation from "../Operation";
|
import Operation from "../Operation";
|
||||||
import OperationError from "../errors/OperationError";
|
import OperationError from "../errors/OperationError";
|
||||||
import { isImage } from "../lib/FileType";
|
import { isImage } from "../lib/FileType.mjs";
|
||||||
import jsqr from "jsqr";
|
import { parseQrCode } from "../lib/QRCode";
|
||||||
import jimp from "jimp";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse QR Code operation
|
* Parse QR Code operation
|
||||||
|
@ -25,7 +24,7 @@ class ParseQRCode extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Reads an image file and attempts to detect and read a Quick Response (QR) code from the image.<br><br><u>Normalise Image</u><br>Attempts to normalise the image before parsing it to improve detection of a QR code.";
|
this.description = "Reads an image file and attempts to detect and read a Quick Response (QR) code from the image.<br><br><u>Normalise Image</u><br>Attempts to normalise the image before parsing it to improve detection of a QR code.";
|
||||||
this.infoURL = "https://wikipedia.org/wiki/QR_code";
|
this.infoURL = "https://wikipedia.org/wiki/QR_code";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "string";
|
this.outputType = "string";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -34,69 +33,28 @@ class ParseQRCode extends Operation {
|
||||||
"value": false
|
"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
|
* @param {Object[]} args
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
const [normalise] = args;
|
const [normalise] = args;
|
||||||
|
|
||||||
// Make sure that the input is an image
|
if (!isImage(new Uint8Array(input))) {
|
||||||
if (!isImage(input)) throw new OperationError("Invalid file type.");
|
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."));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
return await parseQrCode(input, normalise);
|
||||||
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."));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ class ResizeImage extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Resizes an image to the specified width and height values.";
|
this.description = "Resizes an image to the specified width and height values.";
|
||||||
this.infoURL = "https://wikipedia.org/wiki/Image_scaling";
|
this.infoURL = "https://wikipedia.org/wiki/Image_scaling";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -67,7 +67,7 @@ class ResizeImage extends Operation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
|
@ -86,13 +86,13 @@ class ResizeImage extends Operation {
|
||||||
"Bezier": jimp.RESIZE_BEZIER
|
"Bezier": jimp.RESIZE_BEZIER
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${err})`);
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
}
|
}
|
||||||
|
@ -110,8 +110,13 @@ class ResizeImage extends Operation {
|
||||||
image.resize(width, height, resizeMap[resizeAlg]);
|
image.resize(width, height, resizeMap[resizeAlg]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
let imageBuffer;
|
||||||
return [...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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error resizing image. (${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
|
* Displays the resized image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ class RotateImage extends Operation {
|
||||||
this.module = "Image";
|
this.module = "Image";
|
||||||
this.description = "Rotates an image by the specified number of degrees.";
|
this.description = "Rotates an image by the specified number of degrees.";
|
||||||
this.infoURL = "";
|
this.infoURL = "";
|
||||||
this.inputType = "byteArray";
|
this.inputType = "ArrayBuffer";
|
||||||
this.outputType = "byteArray";
|
this.outputType = "ArrayBuffer";
|
||||||
this.presentType = "html";
|
this.presentType = "html";
|
||||||
this.args = [
|
this.args = [
|
||||||
{
|
{
|
||||||
|
@ -38,20 +38,20 @@ class RotateImage extends Operation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {byteArray} input
|
* @param {ArrayBuffer} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {byteArray}
|
* @returns {byteArray}
|
||||||
*/
|
*/
|
||||||
async run(input, args) {
|
async run(input, args) {
|
||||||
const [degrees] = args;
|
const [degrees] = args;
|
||||||
|
|
||||||
if (!isImage(input)) {
|
if (!isImage(new Uint8Array(input))) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
try {
|
try {
|
||||||
image = await jimp.read(Buffer.from(input));
|
image = await jimp.read(input);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error loading image. (${err})`);
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,14 @@ class RotateImage extends Operation {
|
||||||
if (ENVIRONMENT_IS_WORKER())
|
if (ENVIRONMENT_IS_WORKER())
|
||||||
self.sendStatusMessage("Rotating image...");
|
self.sendStatusMessage("Rotating image...");
|
||||||
image.rotate(degrees);
|
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) {
|
} catch (err) {
|
||||||
throw new OperationError(`Error rotating image. (${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
|
* Displays the rotated image using HTML for web apps
|
||||||
* @param {byteArray} data
|
* @param {ArrayBuffer} data
|
||||||
* @returns {html}
|
* @returns {html}
|
||||||
*/
|
*/
|
||||||
present(data) {
|
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) {
|
if (!type) {
|
||||||
throw new OperationError("Invalid file type.");
|
throw new OperationError("Invalid file type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
168
src/core/operations/SharpenImage.mjs
Normal file
168
src/core/operations/SharpenImage.mjs
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
/**
|
||||||
|
* @author j433866 [j433866@gmail.com]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
import { isImage } from "../lib/FileType";
|
||||||
|
import { toBase64 } from "../lib/Base64";
|
||||||
|
import { gaussianBlur } from "../lib/ImageManipulation";
|
||||||
|
import jimp from "jimp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sharpen Image operation
|
||||||
|
*/
|
||||||
|
class SharpenImage extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SharpenImage constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Sharpen Image";
|
||||||
|
this.module = "Image";
|
||||||
|
this.description = "Sharpens an image (Unsharp mask)";
|
||||||
|
this.infoURL = "https://wikipedia.org/wiki/Unsharp_masking";
|
||||||
|
this.inputType = "ArrayBuffer";
|
||||||
|
this.outputType = "ArrayBuffer";
|
||||||
|
this.presentType = "html";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Radius",
|
||||||
|
type: "number",
|
||||||
|
value: 2,
|
||||||
|
min: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Amount",
|
||||||
|
type: "number",
|
||||||
|
value: 1,
|
||||||
|
min: 0,
|
||||||
|
step: 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Threshold",
|
||||||
|
type: "number",
|
||||||
|
value: 10,
|
||||||
|
min: 0,
|
||||||
|
max: 100
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ArrayBuffer} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {byteArray}
|
||||||
|
*/
|
||||||
|
async run(input, args) {
|
||||||
|
const [radius, amount, threshold] = args;
|
||||||
|
|
||||||
|
if (!isImage(new Uint8Array(input))){
|
||||||
|
throw new OperationError("Invalid file type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let image;
|
||||||
|
try {
|
||||||
|
image = await jimp.read(input);
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(`Error loading image. (${err})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (ENVIRONMENT_IS_WORKER())
|
||||||
|
self.sendStatusMessage("Sharpening image... (Cloning image)");
|
||||||
|
const blurMask = image.clone();
|
||||||
|
|
||||||
|
if (ENVIRONMENT_IS_WORKER())
|
||||||
|
self.sendStatusMessage("Sharpening image... (Blurring cloned image)");
|
||||||
|
const blurImage = gaussianBlur(image.clone(), radius, 3);
|
||||||
|
|
||||||
|
|
||||||
|
if (ENVIRONMENT_IS_WORKER())
|
||||||
|
self.sendStatusMessage("Sharpening image... (Creating unsharp mask)");
|
||||||
|
blurMask.scan(0, 0, blurMask.bitmap.width, blurMask.bitmap.height, function(x, y, idx) {
|
||||||
|
const blurRed = blurImage.bitmap.data[idx];
|
||||||
|
const blurGreen = blurImage.bitmap.data[idx + 1];
|
||||||
|
const blurBlue = blurImage.bitmap.data[idx + 2];
|
||||||
|
|
||||||
|
const normalRed = this.bitmap.data[idx];
|
||||||
|
const normalGreen = this.bitmap.data[idx + 1];
|
||||||
|
const normalBlue = this.bitmap.data[idx + 2];
|
||||||
|
|
||||||
|
// Subtract blurred pixel value from normal image
|
||||||
|
this.bitmap.data[idx] = (normalRed > blurRed) ? normalRed - blurRed : 0;
|
||||||
|
this.bitmap.data[idx + 1] = (normalGreen > blurGreen) ? normalGreen - blurGreen : 0;
|
||||||
|
this.bitmap.data[idx + 2] = (normalBlue > blurBlue) ? normalBlue - blurBlue : 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ENVIRONMENT_IS_WORKER())
|
||||||
|
self.sendStatusMessage("Sharpening image... (Merging with unsharp mask)");
|
||||||
|
image.scan(0, 0, image.bitmap.width, image.bitmap.height, function(x, y, idx) {
|
||||||
|
let maskRed = blurMask.bitmap.data[idx];
|
||||||
|
let maskGreen = blurMask.bitmap.data[idx + 1];
|
||||||
|
let maskBlue = blurMask.bitmap.data[idx + 2];
|
||||||
|
|
||||||
|
const normalRed = this.bitmap.data[idx];
|
||||||
|
const normalGreen = this.bitmap.data[idx + 1];
|
||||||
|
const normalBlue = this.bitmap.data[idx + 2];
|
||||||
|
|
||||||
|
// Calculate luminance
|
||||||
|
const maskLuminance = (0.2126 * maskRed + 0.7152 * maskGreen + 0.0722 * maskBlue);
|
||||||
|
const normalLuminance = (0.2126 * normalRed + 0.7152 * normalGreen + 0.0722 * normalBlue);
|
||||||
|
|
||||||
|
let luminanceDiff;
|
||||||
|
if (maskLuminance > normalLuminance) {
|
||||||
|
luminanceDiff = maskLuminance - normalLuminance;
|
||||||
|
} else {
|
||||||
|
luminanceDiff = normalLuminance - maskLuminance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale mask colours by amount
|
||||||
|
maskRed = maskRed * amount;
|
||||||
|
maskGreen = maskGreen * amount;
|
||||||
|
maskBlue = maskBlue * amount;
|
||||||
|
|
||||||
|
// Only change pixel value if the difference is higher than threshold
|
||||||
|
if ((luminanceDiff / 255) * 100 >= threshold) {
|
||||||
|
this.bitmap.data[idx] = (normalRed + maskRed) <= 255 ? normalRed + maskRed : 255;
|
||||||
|
this.bitmap.data[idx + 1] = (normalGreen + maskGreen) <= 255 ? normalGreen + maskGreen : 255;
|
||||||
|
this.bitmap.data[idx + 2] = (normalBlue + maskBlue) <= 255 ? normalBlue + maskBlue : 255;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let imageBuffer;
|
||||||
|
if (image.getMIME() === "image/gif") {
|
||||||
|
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||||
|
} else {
|
||||||
|
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||||
|
}
|
||||||
|
return imageBuffer.buffer;
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(`Error sharpening image. (${err})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the sharpened image using HTML for web apps
|
||||||
|
* @param {ArrayBuffer} data
|
||||||
|
* @returns {html}
|
||||||
|
*/
|
||||||
|
present(data) {
|
||||||
|
if (!data.byteLength) return "";
|
||||||
|
const dataArray = new Uint8Array(data);
|
||||||
|
|
||||||
|
const type = isImage(dataArray);
|
||||||
|
if (!type) {
|
||||||
|
throw new OperationError("Invalid file type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SharpenImage;
|
|
@ -10,7 +10,7 @@ import "./stylesheets/index.js";
|
||||||
// Libs
|
// Libs
|
||||||
import "arrive";
|
import "arrive";
|
||||||
import "snackbarjs";
|
import "snackbarjs";
|
||||||
import "bootstrap-material-design";
|
import "bootstrap-material-design/js/index";
|
||||||
import "bootstrap-colorpicker";
|
import "bootstrap-colorpicker";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import * as CanvasComponents from "../core/lib/CanvasComponents";
|
import * as CanvasComponents from "../core/lib/CanvasComponents";
|
||||||
|
|
485
src/web/static/fonts/bmfonts/Roboto72White.fnt
Normal file
485
src/web/static/fonts/bmfonts/Roboto72White.fnt
Normal file
|
@ -0,0 +1,485 @@
|
||||||
|
info face="Roboto" size=72 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2
|
||||||
|
common lineHeight=85 base=67 scaleW=512 scaleH=512 pages=1 packed=0
|
||||||
|
page id=0 file="Roboto72White.png"
|
||||||
|
chars count=98
|
||||||
|
char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=0 page=0 chnl=0
|
||||||
|
char id=10 x=0 y=0 width=70 height=99 xoffset=2 yoffset=-11 xadvance=74 page=0 chnl=0
|
||||||
|
char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=18 page=0 chnl=0
|
||||||
|
char id=33 x=493 y=99 width=10 height=55 xoffset=5 yoffset=14 xadvance=19 page=0 chnl=0
|
||||||
|
char id=34 x=446 y=319 width=16 height=19 xoffset=4 yoffset=12 xadvance=23 page=0 chnl=0
|
||||||
|
char id=35 x=204 y=265 width=41 height=54 xoffset=3 yoffset=14 xadvance=44 page=0 chnl=0
|
||||||
|
char id=36 x=269 y=0 width=35 height=69 xoffset=3 yoffset=6 xadvance=40 page=0 chnl=0
|
||||||
|
char id=37 x=31 y=155 width=48 height=56 xoffset=3 yoffset=13 xadvance=53 page=0 chnl=0
|
||||||
|
char id=38 x=79 y=155 width=43 height=56 xoffset=3 yoffset=13 xadvance=45 page=0 chnl=0
|
||||||
|
char id=39 x=503 y=99 width=7 height=19 xoffset=3 yoffset=12 xadvance=13 page=0 chnl=0
|
||||||
|
char id=40 x=70 y=0 width=21 height=78 xoffset=4 yoffset=7 xadvance=25 page=0 chnl=0
|
||||||
|
char id=41 x=91 y=0 width=22 height=78 xoffset=-1 yoffset=7 xadvance=25 page=0 chnl=0
|
||||||
|
char id=42 x=342 y=319 width=32 height=32 xoffset=-1 yoffset=14 xadvance=31 page=0 chnl=0
|
||||||
|
char id=43 x=242 y=319 width=37 height=40 xoffset=2 yoffset=23 xadvance=41 page=0 chnl=0
|
||||||
|
char id=44 x=433 y=319 width=13 height=21 xoffset=-1 yoffset=58 xadvance=14 page=0 chnl=0
|
||||||
|
char id=45 x=27 y=360 width=19 height=8 xoffset=0 yoffset=41 xadvance=19 page=0 chnl=0
|
||||||
|
char id=46 x=17 y=360 width=10 height=11 xoffset=4 yoffset=58 xadvance=19 page=0 chnl=0
|
||||||
|
char id=47 x=355 y=0 width=30 height=58 xoffset=-1 yoffset=14 xadvance=30 page=0 chnl=0
|
||||||
|
char id=48 x=449 y=99 width=34 height=56 xoffset=3 yoffset=13 xadvance=40 page=0 chnl=0
|
||||||
|
char id=49 x=474 y=211 width=22 height=54 xoffset=5 yoffset=14 xadvance=40 page=0 chnl=0
|
||||||
|
char id=50 x=195 y=155 width=37 height=55 xoffset=2 yoffset=13 xadvance=41 page=0 chnl=0
|
||||||
|
char id=51 x=379 y=99 width=35 height=56 xoffset=2 yoffset=13 xadvance=40 page=0 chnl=0
|
||||||
|
char id=52 x=128 y=265 width=39 height=54 xoffset=1 yoffset=14 xadvance=41 page=0 chnl=0
|
||||||
|
char id=53 x=232 y=155 width=35 height=55 xoffset=4 yoffset=14 xadvance=40 page=0 chnl=0
|
||||||
|
char id=54 x=267 y=155 width=35 height=55 xoffset=4 yoffset=14 xadvance=41 page=0 chnl=0
|
||||||
|
char id=55 x=167 y=265 width=37 height=54 xoffset=2 yoffset=14 xadvance=41 page=0 chnl=0
|
||||||
|
char id=56 x=414 y=99 width=35 height=56 xoffset=3 yoffset=13 xadvance=40 page=0 chnl=0
|
||||||
|
char id=57 x=302 y=155 width=34 height=55 xoffset=3 yoffset=13 xadvance=41 page=0 chnl=0
|
||||||
|
char id=58 x=495 y=265 width=10 height=41 xoffset=4 yoffset=28 xadvance=18 page=0 chnl=0
|
||||||
|
char id=59 x=496 y=211 width=13 height=52 xoffset=0 yoffset=28 xadvance=15 page=0 chnl=0
|
||||||
|
char id=60 x=279 y=319 width=31 height=35 xoffset=2 yoffset=27 xadvance=37 page=0 chnl=0
|
||||||
|
char id=61 x=402 y=319 width=31 height=23 xoffset=4 yoffset=31 xadvance=39 page=0 chnl=0
|
||||||
|
char id=62 x=310 y=319 width=32 height=35 xoffset=4 yoffset=27 xadvance=38 page=0 chnl=0
|
||||||
|
char id=63 x=0 y=155 width=31 height=56 xoffset=2 yoffset=13 xadvance=34 page=0 chnl=0
|
||||||
|
char id=64 x=210 y=0 width=59 height=69 xoffset=3 yoffset=15 xadvance=65 page=0 chnl=0
|
||||||
|
char id=65 x=336 y=155 width=49 height=54 xoffset=-1 yoffset=14 xadvance=47 page=0 chnl=0
|
||||||
|
char id=66 x=385 y=155 width=37 height=54 xoffset=5 yoffset=14 xadvance=45 page=0 chnl=0
|
||||||
|
char id=67 x=0 y=99 width=42 height=56 xoffset=3 yoffset=13 xadvance=46 page=0 chnl=0
|
||||||
|
char id=68 x=422 y=155 width=39 height=54 xoffset=5 yoffset=14 xadvance=47 page=0 chnl=0
|
||||||
|
char id=69 x=461 y=155 width=35 height=54 xoffset=5 yoffset=14 xadvance=41 page=0 chnl=0
|
||||||
|
char id=70 x=0 y=211 width=34 height=54 xoffset=5 yoffset=14 xadvance=40 page=0 chnl=0
|
||||||
|
char id=71 x=42 y=99 width=42 height=56 xoffset=3 yoffset=13 xadvance=49 page=0 chnl=0
|
||||||
|
char id=72 x=34 y=211 width=41 height=54 xoffset=5 yoffset=14 xadvance=51 page=0 chnl=0
|
||||||
|
char id=73 x=496 y=155 width=9 height=54 xoffset=5 yoffset=14 xadvance=19 page=0 chnl=0
|
||||||
|
char id=74 x=122 y=155 width=34 height=55 xoffset=1 yoffset=14 xadvance=40 page=0 chnl=0
|
||||||
|
char id=75 x=75 y=211 width=41 height=54 xoffset=5 yoffset=14 xadvance=45 page=0 chnl=0
|
||||||
|
char id=76 x=116 y=211 width=33 height=54 xoffset=5 yoffset=14 xadvance=39 page=0 chnl=0
|
||||||
|
char id=77 x=149 y=211 width=53 height=54 xoffset=5 yoffset=14 xadvance=63 page=0 chnl=0
|
||||||
|
char id=78 x=202 y=211 width=41 height=54 xoffset=5 yoffset=14 xadvance=51 page=0 chnl=0
|
||||||
|
char id=79 x=84 y=99 width=43 height=56 xoffset=3 yoffset=13 xadvance=49 page=0 chnl=0
|
||||||
|
char id=80 x=243 y=211 width=39 height=54 xoffset=5 yoffset=14 xadvance=45 page=0 chnl=0
|
||||||
|
char id=81 x=304 y=0 width=44 height=64 xoffset=3 yoffset=13 xadvance=49 page=0 chnl=0
|
||||||
|
char id=82 x=282 y=211 width=40 height=54 xoffset=5 yoffset=14 xadvance=45 page=0 chnl=0
|
||||||
|
char id=83 x=127 y=99 width=39 height=56 xoffset=2 yoffset=13 xadvance=43 page=0 chnl=0
|
||||||
|
char id=84 x=322 y=211 width=42 height=54 xoffset=1 yoffset=14 xadvance=44 page=0 chnl=0
|
||||||
|
char id=85 x=156 y=155 width=39 height=55 xoffset=4 yoffset=14 xadvance=47 page=0 chnl=0
|
||||||
|
char id=86 x=364 y=211 width=47 height=54 xoffset=-1 yoffset=14 xadvance=46 page=0 chnl=0
|
||||||
|
char id=87 x=411 y=211 width=63 height=54 xoffset=1 yoffset=14 xadvance=64 page=0 chnl=0
|
||||||
|
char id=88 x=0 y=265 width=44 height=54 xoffset=1 yoffset=14 xadvance=45 page=0 chnl=0
|
||||||
|
char id=89 x=44 y=265 width=45 height=54 xoffset=-1 yoffset=14 xadvance=43 page=0 chnl=0
|
||||||
|
char id=90 x=89 y=265 width=39 height=54 xoffset=2 yoffset=14 xadvance=43 page=0 chnl=0
|
||||||
|
char id=91 x=161 y=0 width=16 height=72 xoffset=4 yoffset=7 xadvance=19 page=0 chnl=0
|
||||||
|
char id=92 x=385 y=0 width=30 height=58 xoffset=0 yoffset=14 xadvance=30 page=0 chnl=0
|
||||||
|
char id=93 x=177 y=0 width=16 height=72 xoffset=0 yoffset=7 xadvance=20 page=0 chnl=0
|
||||||
|
char id=94 x=374 y=319 width=28 height=28 xoffset=1 yoffset=14 xadvance=30 page=0 chnl=0
|
||||||
|
char id=95 x=46 y=360 width=34 height=8 xoffset=0 yoffset=65 xadvance=34 page=0 chnl=0
|
||||||
|
char id=96 x=0 y=360 width=17 height=13 xoffset=1 yoffset=11 xadvance=22 page=0 chnl=0
|
||||||
|
char id=97 x=268 y=265 width=34 height=42 xoffset=3 yoffset=27 xadvance=39 page=0 chnl=0
|
||||||
|
char id=98 x=415 y=0 width=34 height=57 xoffset=4 yoffset=12 xadvance=40 page=0 chnl=0
|
||||||
|
char id=99 x=302 y=265 width=34 height=42 xoffset=2 yoffset=27 xadvance=38 page=0 chnl=0
|
||||||
|
char id=100 x=449 y=0 width=34 height=57 xoffset=2 yoffset=12 xadvance=40 page=0 chnl=0
|
||||||
|
char id=101 x=336 y=265 width=34 height=42 xoffset=2 yoffset=27 xadvance=38 page=0 chnl=0
|
||||||
|
char id=102 x=483 y=0 width=25 height=57 xoffset=1 yoffset=11 xadvance=26 page=0 chnl=0
|
||||||
|
char id=103 x=166 y=99 width=34 height=56 xoffset=2 yoffset=27 xadvance=40 page=0 chnl=0
|
||||||
|
char id=104 x=200 y=99 width=32 height=56 xoffset=4 yoffset=12 xadvance=40 page=0 chnl=0
|
||||||
|
char id=105 x=483 y=99 width=10 height=55 xoffset=4 yoffset=13 xadvance=18 page=0 chnl=0
|
||||||
|
char id=106 x=193 y=0 width=17 height=71 xoffset=-4 yoffset=13 xadvance=17 page=0 chnl=0
|
||||||
|
char id=107 x=232 y=99 width=34 height=56 xoffset=4 yoffset=12 xadvance=37 page=0 chnl=0
|
||||||
|
char id=108 x=266 y=99 width=9 height=56 xoffset=4 yoffset=12 xadvance=17 page=0 chnl=0
|
||||||
|
char id=109 x=439 y=265 width=56 height=41 xoffset=4 yoffset=27 xadvance=64 page=0 chnl=0
|
||||||
|
char id=110 x=0 y=319 width=32 height=41 xoffset=4 yoffset=27 xadvance=40 page=0 chnl=0
|
||||||
|
char id=111 x=370 y=265 width=37 height=42 xoffset=2 yoffset=27 xadvance=41 page=0 chnl=0
|
||||||
|
char id=112 x=275 y=99 width=34 height=56 xoffset=4 yoffset=27 xadvance=40 page=0 chnl=0
|
||||||
|
char id=113 x=309 y=99 width=34 height=56 xoffset=2 yoffset=27 xadvance=41 page=0 chnl=0
|
||||||
|
char id=114 x=32 y=319 width=21 height=41 xoffset=4 yoffset=27 xadvance=25 page=0 chnl=0
|
||||||
|
char id=115 x=407 y=265 width=32 height=42 xoffset=2 yoffset=27 xadvance=37 page=0 chnl=0
|
||||||
|
char id=116 x=245 y=265 width=23 height=51 xoffset=0 yoffset=18 xadvance=25 page=0 chnl=0
|
||||||
|
char id=117 x=53 y=319 width=32 height=41 xoffset=4 yoffset=28 xadvance=40 page=0 chnl=0
|
||||||
|
char id=118 x=85 y=319 width=35 height=40 xoffset=0 yoffset=28 xadvance=35 page=0 chnl=0
|
||||||
|
char id=119 x=120 y=319 width=54 height=40 xoffset=0 yoffset=28 xadvance=54 page=0 chnl=0
|
||||||
|
char id=120 x=174 y=319 width=36 height=40 xoffset=0 yoffset=28 xadvance=36 page=0 chnl=0
|
||||||
|
char id=121 x=343 y=99 width=36 height=56 xoffset=-1 yoffset=28 xadvance=34 page=0 chnl=0
|
||||||
|
char id=122 x=210 y=319 width=32 height=40 xoffset=2 yoffset=28 xadvance=35 page=0 chnl=0
|
||||||
|
char id=123 x=113 y=0 width=24 height=73 xoffset=1 yoffset=9 xadvance=25 page=0 chnl=0
|
||||||
|
char id=124 x=348 y=0 width=7 height=63 xoffset=5 yoffset=14 xadvance=17 page=0 chnl=0
|
||||||
|
char id=125 x=137 y=0 width=24 height=73 xoffset=-1 yoffset=9 xadvance=24 page=0 chnl=0
|
||||||
|
char id=126 x=462 y=319 width=42 height=16 xoffset=4 yoffset=38 xadvance=50 page=0 chnl=0
|
||||||
|
char id=127 x=0 y=0 width=70 height=99 xoffset=2 yoffset=-11 xadvance=74 page=0 chnl=0
|
||||||
|
kernings count=382
|
||||||
|
kerning first=70 second=74 amount=-9
|
||||||
|
kerning first=34 second=97 amount=-2
|
||||||
|
kerning first=34 second=101 amount=-2
|
||||||
|
kerning first=34 second=113 amount=-2
|
||||||
|
kerning first=34 second=99 amount=-2
|
||||||
|
kerning first=70 second=99 amount=-1
|
||||||
|
kerning first=88 second=113 amount=-1
|
||||||
|
kerning first=84 second=46 amount=-8
|
||||||
|
kerning first=84 second=119 amount=-2
|
||||||
|
kerning first=87 second=97 amount=-1
|
||||||
|
kerning first=90 second=117 amount=-1
|
||||||
|
kerning first=39 second=97 amount=-2
|
||||||
|
kerning first=69 second=111 amount=-1
|
||||||
|
kerning first=87 second=41 amount=1
|
||||||
|
kerning first=76 second=86 amount=-6
|
||||||
|
kerning first=121 second=34 amount=1
|
||||||
|
kerning first=40 second=86 amount=1
|
||||||
|
kerning first=85 second=65 amount=-1
|
||||||
|
kerning first=89 second=89 amount=1
|
||||||
|
kerning first=72 second=65 amount=1
|
||||||
|
kerning first=104 second=39 amount=-4
|
||||||
|
kerning first=114 second=102 amount=1
|
||||||
|
kerning first=89 second=42 amount=-2
|
||||||
|
kerning first=114 second=34 amount=1
|
||||||
|
kerning first=84 second=115 amount=-4
|
||||||
|
kerning first=84 second=71 amount=-1
|
||||||
|
kerning first=89 second=101 amount=-2
|
||||||
|
kerning first=89 second=45 amount=-2
|
||||||
|
kerning first=122 second=99 amount=-1
|
||||||
|
kerning first=78 second=88 amount=1
|
||||||
|
kerning first=68 second=89 amount=-2
|
||||||
|
kerning first=122 second=103 amount=-1
|
||||||
|
kerning first=78 second=84 amount=-1
|
||||||
|
kerning first=86 second=103 amount=-2
|
||||||
|
kerning first=89 second=67 amount=-1
|
||||||
|
kerning first=89 second=79 amount=-1
|
||||||
|
kerning first=75 second=111 amount=-1
|
||||||
|
kerning first=111 second=120 amount=-1
|
||||||
|
kerning first=87 second=44 amount=-4
|
||||||
|
kerning first=91 second=74 amount=-1
|
||||||
|
kerning first=120 second=111 amount=-1
|
||||||
|
kerning first=84 second=111 amount=-3
|
||||||
|
kerning first=102 second=113 amount=-1
|
||||||
|
kerning first=80 second=88 amount=-1
|
||||||
|
kerning first=66 second=84 amount=-1
|
||||||
|
kerning first=65 second=87 amount=-2
|
||||||
|
kerning first=86 second=100 amount=-2
|
||||||
|
kerning first=122 second=100 amount=-1
|
||||||
|
kerning first=75 second=118 amount=-1
|
||||||
|
kerning first=70 second=118 amount=-1
|
||||||
|
kerning first=73 second=88 amount=1
|
||||||
|
kerning first=70 second=121 amount=-1
|
||||||
|
kerning first=65 second=34 amount=-4
|
||||||
|
kerning first=39 second=101 amount=-2
|
||||||
|
kerning first=75 second=101 amount=-1
|
||||||
|
kerning first=84 second=99 amount=-3
|
||||||
|
kerning first=84 second=65 amount=-3
|
||||||
|
kerning first=112 second=39 amount=-1
|
||||||
|
kerning first=76 second=39 amount=-12
|
||||||
|
kerning first=78 second=65 amount=1
|
||||||
|
kerning first=88 second=45 amount=-2
|
||||||
|
kerning first=65 second=121 amount=-2
|
||||||
|
kerning first=34 second=111 amount=-2
|
||||||
|
kerning first=89 second=85 amount=-3
|
||||||
|
kerning first=114 second=99 amount=-1
|
||||||
|
kerning first=86 second=125 amount=1
|
||||||
|
kerning first=70 second=111 amount=-1
|
||||||
|
kerning first=89 second=120 amount=-1
|
||||||
|
kerning first=90 second=119 amount=-1
|
||||||
|
kerning first=120 second=99 amount=-1
|
||||||
|
kerning first=89 second=117 amount=-1
|
||||||
|
kerning first=82 second=89 amount=-2
|
||||||
|
kerning first=75 second=117 amount=-1
|
||||||
|
kerning first=34 second=34 amount=-4
|
||||||
|
kerning first=89 second=110 amount=-1
|
||||||
|
kerning first=88 second=101 amount=-1
|
||||||
|
kerning first=107 second=103 amount=-1
|
||||||
|
kerning first=34 second=115 amount=-3
|
||||||
|
kerning first=98 second=39 amount=-1
|
||||||
|
kerning first=70 second=65 amount=-6
|
||||||
|
kerning first=70 second=46 amount=-8
|
||||||
|
kerning first=98 second=34 amount=-1
|
||||||
|
kerning first=70 second=84 amount=1
|
||||||
|
kerning first=114 second=100 amount=-1
|
||||||
|
kerning first=88 second=79 amount=-1
|
||||||
|
kerning first=39 second=113 amount=-2
|
||||||
|
kerning first=114 second=103 amount=-1
|
||||||
|
kerning first=77 second=65 amount=1
|
||||||
|
kerning first=120 second=103 amount=-1
|
||||||
|
kerning first=114 second=121 amount=1
|
||||||
|
kerning first=89 second=100 amount=-2
|
||||||
|
kerning first=80 second=65 amount=-5
|
||||||
|
kerning first=121 second=111 amount=-1
|
||||||
|
kerning first=84 second=74 amount=-8
|
||||||
|
kerning first=122 second=111 amount=-1
|
||||||
|
kerning first=114 second=118 amount=1
|
||||||
|
kerning first=102 second=41 amount=1
|
||||||
|
kerning first=122 second=113 amount=-1
|
||||||
|
kerning first=89 second=122 amount=-1
|
||||||
|
kerning first=89 second=38 amount=-1
|
||||||
|
kerning first=81 second=89 amount=-1
|
||||||
|
kerning first=114 second=111 amount=-1
|
||||||
|
kerning first=46 second=34 amount=-6
|
||||||
|
kerning first=84 second=112 amount=-4
|
||||||
|
kerning first=112 second=34 amount=-1
|
||||||
|
kerning first=76 second=34 amount=-12
|
||||||
|
kerning first=102 second=125 amount=1
|
||||||
|
kerning first=39 second=115 amount=-3
|
||||||
|
kerning first=76 second=118 amount=-5
|
||||||
|
kerning first=86 second=99 amount=-2
|
||||||
|
kerning first=84 second=84 amount=1
|
||||||
|
kerning first=86 second=65 amount=-3
|
||||||
|
kerning first=87 second=101 amount=-1
|
||||||
|
kerning first=67 second=125 amount=-1
|
||||||
|
kerning first=120 second=113 amount=-1
|
||||||
|
kerning first=118 second=46 amount=-4
|
||||||
|
kerning first=88 second=103 amount=-1
|
||||||
|
kerning first=111 second=122 amount=-1
|
||||||
|
kerning first=77 second=84 amount=-1
|
||||||
|
kerning first=114 second=46 amount=-4
|
||||||
|
kerning first=34 second=39 amount=-4
|
||||||
|
kerning first=114 second=44 amount=-4
|
||||||
|
kerning first=69 second=84 amount=1
|
||||||
|
kerning first=89 second=46 amount=-7
|
||||||
|
kerning first=97 second=39 amount=-2
|
||||||
|
kerning first=34 second=100 amount=-2
|
||||||
|
kerning first=70 second=100 amount=-1
|
||||||
|
kerning first=84 second=120 amount=-3
|
||||||
|
kerning first=90 second=118 amount=-1
|
||||||
|
kerning first=70 second=114 amount=-1
|
||||||
|
kerning first=34 second=112 amount=-1
|
||||||
|
kerning first=109 second=34 amount=-4
|
||||||
|
kerning first=86 second=113 amount=-2
|
||||||
|
kerning first=88 second=71 amount=-1
|
||||||
|
kerning first=66 second=89 amount=-2
|
||||||
|
kerning first=102 second=103 amount=-1
|
||||||
|
kerning first=88 second=67 amount=-1
|
||||||
|
kerning first=39 second=110 amount=-1
|
||||||
|
kerning first=75 second=110 amount=-1
|
||||||
|
kerning first=88 second=117 amount=-1
|
||||||
|
kerning first=89 second=118 amount=-1
|
||||||
|
kerning first=97 second=118 amount=-1
|
||||||
|
kerning first=87 second=65 amount=-2
|
||||||
|
kerning first=73 second=89 amount=-1
|
||||||
|
kerning first=89 second=74 amount=-3
|
||||||
|
kerning first=102 second=101 amount=-1
|
||||||
|
kerning first=86 second=111 amount=-2
|
||||||
|
kerning first=65 second=119 amount=-1
|
||||||
|
kerning first=84 second=100 amount=-3
|
||||||
|
kerning first=104 second=34 amount=-4
|
||||||
|
kerning first=86 second=41 amount=1
|
||||||
|
kerning first=111 second=34 amount=-5
|
||||||
|
kerning first=40 second=89 amount=1
|
||||||
|
kerning first=121 second=39 amount=1
|
||||||
|
kerning first=68 second=90 amount=-1
|
||||||
|
kerning first=114 second=113 amount=-1
|
||||||
|
kerning first=68 second=88 amount=-1
|
||||||
|
kerning first=98 second=120 amount=-1
|
||||||
|
kerning first=110 second=34 amount=-4
|
||||||
|
kerning first=119 second=44 amount=-4
|
||||||
|
kerning first=119 second=46 amount=-4
|
||||||
|
kerning first=118 second=44 amount=-4
|
||||||
|
kerning first=84 second=114 amount=-3
|
||||||
|
kerning first=86 second=97 amount=-2
|
||||||
|
kerning first=68 second=86 amount=-1
|
||||||
|
kerning first=86 second=93 amount=1
|
||||||
|
kerning first=97 second=34 amount=-2
|
||||||
|
kerning first=34 second=65 amount=-4
|
||||||
|
kerning first=84 second=118 amount=-3
|
||||||
|
kerning first=76 second=84 amount=-10
|
||||||
|
kerning first=107 second=99 amount=-1
|
||||||
|
kerning first=121 second=46 amount=-4
|
||||||
|
kerning first=123 second=85 amount=-1
|
||||||
|
kerning first=65 second=63 amount=-2
|
||||||
|
kerning first=89 second=44 amount=-7
|
||||||
|
kerning first=80 second=118 amount=1
|
||||||
|
kerning first=112 second=122 amount=-1
|
||||||
|
kerning first=79 second=65 amount=-1
|
||||||
|
kerning first=80 second=121 amount=1
|
||||||
|
kerning first=118 second=34 amount=1
|
||||||
|
kerning first=87 second=45 amount=-2
|
||||||
|
kerning first=69 second=100 amount=-1
|
||||||
|
kerning first=87 second=103 amount=-1
|
||||||
|
kerning first=112 second=120 amount=-1
|
||||||
|
kerning first=68 second=44 amount=-4
|
||||||
|
kerning first=86 second=45 amount=-1
|
||||||
|
kerning first=39 second=34 amount=-4
|
||||||
|
kerning first=68 second=46 amount=-4
|
||||||
|
kerning first=65 second=89 amount=-3
|
||||||
|
kerning first=69 second=118 amount=-1
|
||||||
|
kerning first=88 second=99 amount=-1
|
||||||
|
kerning first=87 second=46 amount=-4
|
||||||
|
kerning first=47 second=47 amount=-8
|
||||||
|
kerning first=73 second=65 amount=1
|
||||||
|
kerning first=123 second=74 amount=-1
|
||||||
|
kerning first=69 second=102 amount=-1
|
||||||
|
kerning first=87 second=111 amount=-1
|
||||||
|
kerning first=39 second=112 amount=-1
|
||||||
|
kerning first=89 second=116 amount=-1
|
||||||
|
kerning first=70 second=113 amount=-1
|
||||||
|
kerning first=77 second=88 amount=1
|
||||||
|
kerning first=84 second=32 amount=-1
|
||||||
|
kerning first=90 second=103 amount=-1
|
||||||
|
kerning first=65 second=86 amount=-3
|
||||||
|
kerning first=75 second=112 amount=-1
|
||||||
|
kerning first=39 second=109 amount=-1
|
||||||
|
kerning first=75 second=81 amount=-1
|
||||||
|
kerning first=89 second=115 amount=-2
|
||||||
|
kerning first=84 second=83 amount=-1
|
||||||
|
kerning first=89 second=87 amount=1
|
||||||
|
kerning first=114 second=101 amount=-1
|
||||||
|
kerning first=116 second=111 amount=-1
|
||||||
|
kerning first=90 second=100 amount=-1
|
||||||
|
kerning first=84 second=122 amount=-2
|
||||||
|
kerning first=68 second=84 amount=-1
|
||||||
|
kerning first=32 second=84 amount=-1
|
||||||
|
kerning first=84 second=117 amount=-3
|
||||||
|
kerning first=74 second=65 amount=-1
|
||||||
|
kerning first=107 second=101 amount=-1
|
||||||
|
kerning first=75 second=109 amount=-1
|
||||||
|
kerning first=80 second=46 amount=-11
|
||||||
|
kerning first=89 second=93 amount=1
|
||||||
|
kerning first=89 second=65 amount=-3
|
||||||
|
kerning first=87 second=117 amount=-1
|
||||||
|
kerning first=89 second=81 amount=-1
|
||||||
|
kerning first=39 second=103 amount=-2
|
||||||
|
kerning first=86 second=101 amount=-2
|
||||||
|
kerning first=86 second=117 amount=-1
|
||||||
|
kerning first=84 second=113 amount=-3
|
||||||
|
kerning first=34 second=110 amount=-1
|
||||||
|
kerning first=89 second=84 amount=1
|
||||||
|
kerning first=84 second=110 amount=-4
|
||||||
|
kerning first=39 second=99 amount=-2
|
||||||
|
kerning first=88 second=121 amount=-1
|
||||||
|
kerning first=65 second=39 amount=-4
|
||||||
|
kerning first=110 second=39 amount=-4
|
||||||
|
kerning first=75 second=67 amount=-1
|
||||||
|
kerning first=88 second=118 amount=-1
|
||||||
|
kerning first=86 second=114 amount=-1
|
||||||
|
kerning first=80 second=74 amount=-7
|
||||||
|
kerning first=84 second=97 amount=-4
|
||||||
|
kerning first=82 second=84 amount=-3
|
||||||
|
kerning first=91 second=85 amount=-1
|
||||||
|
kerning first=102 second=99 amount=-1
|
||||||
|
kerning first=66 second=86 amount=-1
|
||||||
|
kerning first=120 second=101 amount=-1
|
||||||
|
kerning first=102 second=93 amount=1
|
||||||
|
kerning first=75 second=100 amount=-1
|
||||||
|
kerning first=84 second=79 amount=-1
|
||||||
|
kerning first=111 second=121 amount=-1
|
||||||
|
kerning first=75 second=121 amount=-1
|
||||||
|
kerning first=81 second=87 amount=-1
|
||||||
|
kerning first=107 second=113 amount=-1
|
||||||
|
kerning first=120 second=100 amount=-1
|
||||||
|
kerning first=90 second=79 amount=-1
|
||||||
|
kerning first=89 second=114 amount=-1
|
||||||
|
kerning first=122 second=101 amount=-1
|
||||||
|
kerning first=111 second=118 amount=-1
|
||||||
|
kerning first=82 second=86 amount=-1
|
||||||
|
kerning first=67 second=84 amount=-1
|
||||||
|
kerning first=70 second=101 amount=-1
|
||||||
|
kerning first=89 second=83 amount=-1
|
||||||
|
kerning first=114 second=97 amount=-1
|
||||||
|
kerning first=70 second=97 amount=-1
|
||||||
|
kerning first=89 second=102 amount=-1
|
||||||
|
kerning first=78 second=89 amount=-1
|
||||||
|
kerning first=70 second=44 amount=-8
|
||||||
|
kerning first=44 second=39 amount=-6
|
||||||
|
kerning first=84 second=45 amount=-8
|
||||||
|
kerning first=89 second=121 amount=-1
|
||||||
|
kerning first=84 second=86 amount=1
|
||||||
|
kerning first=87 second=99 amount=-1
|
||||||
|
kerning first=98 second=122 amount=-1
|
||||||
|
kerning first=89 second=112 amount=-1
|
||||||
|
kerning first=89 second=103 amount=-2
|
||||||
|
kerning first=88 second=81 amount=-1
|
||||||
|
kerning first=102 second=34 amount=1
|
||||||
|
kerning first=109 second=39 amount=-4
|
||||||
|
kerning first=81 second=84 amount=-2
|
||||||
|
kerning first=121 second=97 amount=-1
|
||||||
|
kerning first=89 second=99 amount=-2
|
||||||
|
kerning first=89 second=125 amount=1
|
||||||
|
kerning first=81 second=86 amount=-1
|
||||||
|
kerning first=114 second=116 amount=2
|
||||||
|
kerning first=114 second=119 amount=1
|
||||||
|
kerning first=84 second=44 amount=-8
|
||||||
|
kerning first=102 second=39 amount=1
|
||||||
|
kerning first=44 second=34 amount=-6
|
||||||
|
kerning first=34 second=109 amount=-1
|
||||||
|
kerning first=75 second=119 amount=-2
|
||||||
|
kerning first=76 second=65 amount=1
|
||||||
|
kerning first=84 second=81 amount=-1
|
||||||
|
kerning first=76 second=121 amount=-5
|
||||||
|
kerning first=69 second=101 amount=-1
|
||||||
|
kerning first=89 second=111 amount=-2
|
||||||
|
kerning first=80 second=90 amount=-1
|
||||||
|
kerning first=89 second=97 amount=-3
|
||||||
|
kerning first=89 second=109 amount=-1
|
||||||
|
kerning first=90 second=99 amount=-1
|
||||||
|
kerning first=89 second=86 amount=1
|
||||||
|
kerning first=79 second=88 amount=-1
|
||||||
|
kerning first=70 second=103 amount=-1
|
||||||
|
kerning first=34 second=103 amount=-2
|
||||||
|
kerning first=84 second=67 amount=-1
|
||||||
|
kerning first=76 second=79 amount=-2
|
||||||
|
kerning first=89 second=41 amount=1
|
||||||
|
kerning first=65 second=118 amount=-2
|
||||||
|
kerning first=75 second=71 amount=-1
|
||||||
|
kerning first=76 second=87 amount=-5
|
||||||
|
kerning first=77 second=89 amount=-1
|
||||||
|
kerning first=90 second=113 amount=-1
|
||||||
|
kerning first=79 second=89 amount=-2
|
||||||
|
kerning first=118 second=111 amount=-1
|
||||||
|
kerning first=118 second=97 amount=-1
|
||||||
|
kerning first=88 second=100 amount=-1
|
||||||
|
kerning first=90 second=121 amount=-1
|
||||||
|
kerning first=89 second=113 amount=-2
|
||||||
|
kerning first=84 second=87 amount=1
|
||||||
|
kerning first=39 second=111 amount=-2
|
||||||
|
kerning first=80 second=44 amount=-11
|
||||||
|
kerning first=39 second=100 amount=-2
|
||||||
|
kerning first=75 second=113 amount=-1
|
||||||
|
kerning first=88 second=111 amount=-1
|
||||||
|
kerning first=84 second=89 amount=1
|
||||||
|
kerning first=84 second=103 amount=-3
|
||||||
|
kerning first=70 second=117 amount=-1
|
||||||
|
kerning first=67 second=41 amount=-1
|
||||||
|
kerning first=89 second=71 amount=-1
|
||||||
|
kerning first=121 second=44 amount=-4
|
||||||
|
kerning first=97 second=121 amount=-1
|
||||||
|
kerning first=87 second=113 amount=-1
|
||||||
|
kerning first=73 second=84 amount=-1
|
||||||
|
kerning first=84 second=101 amount=-3
|
||||||
|
kerning first=75 second=99 amount=-1
|
||||||
|
kerning first=65 second=85 amount=-1
|
||||||
|
kerning first=76 second=67 amount=-2
|
||||||
|
kerning first=76 second=81 amount=-2
|
||||||
|
kerning first=75 second=79 amount=-1
|
||||||
|
kerning first=39 second=65 amount=-4
|
||||||
|
kerning first=76 second=117 amount=-2
|
||||||
|
kerning first=65 second=84 amount=-5
|
||||||
|
kerning first=90 second=101 amount=-1
|
||||||
|
kerning first=84 second=121 amount=-3
|
||||||
|
kerning first=69 second=99 amount=-1
|
||||||
|
kerning first=114 second=39 amount=1
|
||||||
|
kerning first=84 second=109 amount=-4
|
||||||
|
kerning first=76 second=119 amount=-3
|
||||||
|
kerning first=76 second=85 amount=-2
|
||||||
|
kerning first=65 second=116 amount=-1
|
||||||
|
kerning first=76 second=71 amount=-2
|
||||||
|
kerning first=79 second=90 amount=-1
|
||||||
|
kerning first=107 second=100 amount=-1
|
||||||
|
kerning first=90 second=111 amount=-1
|
||||||
|
kerning first=79 second=44 amount=-4
|
||||||
|
kerning first=75 second=45 amount=-2
|
||||||
|
kerning first=40 second=87 amount=1
|
||||||
|
kerning first=79 second=86 amount=-1
|
||||||
|
kerning first=102 second=100 amount=-1
|
||||||
|
kerning first=72 second=89 amount=-1
|
||||||
|
kerning first=72 second=88 amount=1
|
||||||
|
kerning first=79 second=46 amount=-4
|
||||||
|
kerning first=76 second=89 amount=-8
|
||||||
|
kerning first=68 second=65 amount=-1
|
||||||
|
kerning first=79 second=84 amount=-1
|
||||||
|
kerning first=87 second=100 amount=-1
|
||||||
|
kerning first=75 second=103 amount=-1
|
||||||
|
kerning first=90 second=67 amount=-1
|
||||||
|
kerning first=69 second=103 amount=-1
|
||||||
|
kerning first=90 second=71 amount=-1
|
||||||
|
kerning first=86 second=44 amount=-8
|
||||||
|
kerning first=69 second=121 amount=-1
|
||||||
|
kerning first=87 second=114 amount=-1
|
||||||
|
kerning first=118 second=39 amount=1
|
||||||
|
kerning first=46 second=39 amount=-6
|
||||||
|
kerning first=72 second=84 amount=-1
|
||||||
|
kerning first=86 second=46 amount=-8
|
||||||
|
kerning first=69 second=113 amount=-1
|
||||||
|
kerning first=69 second=119 amount=-1
|
||||||
|
kerning first=39 second=39 amount=-4
|
||||||
|
kerning first=69 second=117 amount=-1
|
||||||
|
kerning first=111 second=39 amount=-5
|
||||||
|
kerning first=90 second=81 amount=-1
|
BIN
src/web/static/fonts/bmfonts/Roboto72White.png
Normal file
BIN
src/web/static/fonts/bmfonts/Roboto72White.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
488
src/web/static/fonts/bmfonts/RobotoBlack72White.fnt
Normal file
488
src/web/static/fonts/bmfonts/RobotoBlack72White.fnt
Normal file
|
@ -0,0 +1,488 @@
|
||||||
|
info face="Roboto Black" size=72 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2
|
||||||
|
common lineHeight=85 base=67 scaleW=512 scaleH=512 pages=1 packed=0
|
||||||
|
page id=0 file="RobotoBlack72White.png"
|
||||||
|
chars count=98
|
||||||
|
char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=0 page=0 chnl=0
|
||||||
|
char id=10 x=0 y=0 width=70 height=99 xoffset=2 yoffset=-11 xadvance=74 page=0 chnl=0
|
||||||
|
char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=18 page=0 chnl=0
|
||||||
|
char id=33 x=460 y=156 width=15 height=55 xoffset=3 yoffset=14 xadvance=20 page=0 chnl=0
|
||||||
|
char id=34 x=207 y=362 width=22 height=22 xoffset=0 yoffset=12 xadvance=23 page=0 chnl=0
|
||||||
|
char id=35 x=404 y=266 width=41 height=54 xoffset=0 yoffset=14 xadvance=42 page=0 chnl=0
|
||||||
|
char id=36 x=220 y=0 width=38 height=69 xoffset=2 yoffset=7 xadvance=42 page=0 chnl=0
|
||||||
|
char id=37 x=167 y=156 width=49 height=56 xoffset=2 yoffset=13 xadvance=53 page=0 chnl=0
|
||||||
|
char id=38 x=216 y=156 width=48 height=56 xoffset=1 yoffset=13 xadvance=48 page=0 chnl=0
|
||||||
|
char id=39 x=499 y=320 width=10 height=22 xoffset=1 yoffset=12 xadvance=11 page=0 chnl=0
|
||||||
|
char id=40 x=70 y=0 width=22 height=75 xoffset=3 yoffset=9 xadvance=25 page=0 chnl=0
|
||||||
|
char id=41 x=92 y=0 width=23 height=75 xoffset=0 yoffset=9 xadvance=25 page=0 chnl=0
|
||||||
|
char id=42 x=103 y=362 width=36 height=34 xoffset=-1 yoffset=14 xadvance=33 page=0 chnl=0
|
||||||
|
char id=43 x=0 y=362 width=37 height=40 xoffset=1 yoffset=23 xadvance=39 page=0 chnl=0
|
||||||
|
char id=44 x=483 y=320 width=16 height=25 xoffset=0 yoffset=57 xadvance=20 page=0 chnl=0
|
||||||
|
char id=45 x=308 y=362 width=23 height=12 xoffset=4 yoffset=38 xadvance=32 page=0 chnl=0
|
||||||
|
char id=46 x=270 y=362 width=15 height=15 xoffset=3 yoffset=54 xadvance=22 page=0 chnl=0
|
||||||
|
char id=47 x=374 y=0 width=29 height=58 xoffset=-3 yoffset=14 xadvance=25 page=0 chnl=0
|
||||||
|
char id=48 x=77 y=156 width=38 height=56 xoffset=2 yoffset=13 xadvance=42 page=0 chnl=0
|
||||||
|
char id=49 x=299 y=266 width=26 height=54 xoffset=4 yoffset=14 xadvance=41 page=0 chnl=0
|
||||||
|
char id=50 x=383 y=156 width=39 height=55 xoffset=1 yoffset=13 xadvance=42 page=0 chnl=0
|
||||||
|
char id=51 x=434 y=99 width=39 height=56 xoffset=1 yoffset=13 xadvance=42 page=0 chnl=0
|
||||||
|
char id=52 x=325 y=266 width=40 height=54 xoffset=1 yoffset=14 xadvance=42 page=0 chnl=0
|
||||||
|
char id=53 x=422 y=156 width=38 height=55 xoffset=2 yoffset=14 xadvance=42 page=0 chnl=0
|
||||||
|
char id=54 x=0 y=156 width=39 height=56 xoffset=2 yoffset=13 xadvance=42 page=0 chnl=0
|
||||||
|
char id=55 x=365 y=266 width=39 height=54 xoffset=1 yoffset=14 xadvance=42 page=0 chnl=0
|
||||||
|
char id=56 x=473 y=99 width=38 height=56 xoffset=2 yoffset=13 xadvance=42 page=0 chnl=0
|
||||||
|
char id=57 x=39 y=156 width=38 height=56 xoffset=2 yoffset=13 xadvance=42 page=0 chnl=0
|
||||||
|
char id=58 x=471 y=266 width=15 height=43 xoffset=3 yoffset=26 xadvance=21 page=0 chnl=0
|
||||||
|
char id=59 x=150 y=156 width=17 height=56 xoffset=1 yoffset=26 xadvance=21 page=0 chnl=0
|
||||||
|
char id=60 x=37 y=362 width=33 height=38 xoffset=1 yoffset=26 xadvance=37 page=0 chnl=0
|
||||||
|
char id=61 x=172 y=362 width=35 height=27 xoffset=3 yoffset=31 xadvance=42 page=0 chnl=0
|
||||||
|
char id=62 x=70 y=362 width=33 height=38 xoffset=3 yoffset=26 xadvance=37 page=0 chnl=0
|
||||||
|
char id=63 x=115 y=156 width=35 height=56 xoffset=0 yoffset=13 xadvance=36 page=0 chnl=0
|
||||||
|
char id=64 x=258 y=0 width=61 height=68 xoffset=1 yoffset=16 xadvance=64 page=0 chnl=0
|
||||||
|
char id=65 x=0 y=212 width=53 height=54 xoffset=-2 yoffset=14 xadvance=49 page=0 chnl=0
|
||||||
|
char id=66 x=53 y=212 width=42 height=54 xoffset=3 yoffset=14 xadvance=47 page=0 chnl=0
|
||||||
|
char id=67 x=37 y=99 width=46 height=56 xoffset=1 yoffset=13 xadvance=47 page=0 chnl=0
|
||||||
|
char id=68 x=95 y=212 width=42 height=54 xoffset=3 yoffset=14 xadvance=47 page=0 chnl=0
|
||||||
|
char id=69 x=137 y=212 width=38 height=54 xoffset=3 yoffset=14 xadvance=41 page=0 chnl=0
|
||||||
|
char id=70 x=475 y=156 width=36 height=54 xoffset=3 yoffset=14 xadvance=39 page=0 chnl=0
|
||||||
|
char id=71 x=83 y=99 width=45 height=56 xoffset=2 yoffset=13 xadvance=49 page=0 chnl=0
|
||||||
|
char id=72 x=175 y=212 width=45 height=54 xoffset=3 yoffset=14 xadvance=51 page=0 chnl=0
|
||||||
|
char id=73 x=220 y=212 width=14 height=54 xoffset=4 yoffset=14 xadvance=22 page=0 chnl=0
|
||||||
|
char id=74 x=264 y=156 width=37 height=55 xoffset=0 yoffset=14 xadvance=40 page=0 chnl=0
|
||||||
|
char id=75 x=234 y=212 width=45 height=54 xoffset=3 yoffset=14 xadvance=46 page=0 chnl=0
|
||||||
|
char id=76 x=279 y=212 width=36 height=54 xoffset=3 yoffset=14 xadvance=39 page=0 chnl=0
|
||||||
|
char id=77 x=315 y=212 width=58 height=54 xoffset=3 yoffset=14 xadvance=63 page=0 chnl=0
|
||||||
|
char id=78 x=373 y=212 width=45 height=54 xoffset=3 yoffset=14 xadvance=51 page=0 chnl=0
|
||||||
|
char id=79 x=128 y=99 width=47 height=56 xoffset=1 yoffset=13 xadvance=50 page=0 chnl=0
|
||||||
|
char id=80 x=418 y=212 width=43 height=54 xoffset=3 yoffset=14 xadvance=48 page=0 chnl=0
|
||||||
|
char id=81 x=319 y=0 width=47 height=65 xoffset=2 yoffset=13 xadvance=50 page=0 chnl=0
|
||||||
|
char id=82 x=461 y=212 width=43 height=54 xoffset=3 yoffset=14 xadvance=46 page=0 chnl=0
|
||||||
|
char id=83 x=175 y=99 width=42 height=56 xoffset=1 yoffset=13 xadvance=44 page=0 chnl=0
|
||||||
|
char id=84 x=0 y=266 width=45 height=54 xoffset=0 yoffset=14 xadvance=45 page=0 chnl=0
|
||||||
|
char id=85 x=301 y=156 width=42 height=55 xoffset=3 yoffset=14 xadvance=48 page=0 chnl=0
|
||||||
|
char id=86 x=45 y=266 width=51 height=54 xoffset=-2 yoffset=14 xadvance=48 page=0 chnl=0
|
||||||
|
char id=87 x=96 y=266 width=64 height=54 xoffset=-1 yoffset=14 xadvance=63 page=0 chnl=0
|
||||||
|
char id=88 x=160 y=266 width=48 height=54 xoffset=-1 yoffset=14 xadvance=46 page=0 chnl=0
|
||||||
|
char id=89 x=208 y=266 width=49 height=54 xoffset=-2 yoffset=14 xadvance=45 page=0 chnl=0
|
||||||
|
char id=90 x=257 y=266 width=42 height=54 xoffset=1 yoffset=14 xadvance=44 page=0 chnl=0
|
||||||
|
char id=91 x=115 y=0 width=18 height=75 xoffset=3 yoffset=5 xadvance=21 page=0 chnl=0
|
||||||
|
char id=92 x=403 y=0 width=37 height=58 xoffset=-2 yoffset=14 xadvance=31 page=0 chnl=0
|
||||||
|
char id=93 x=133 y=0 width=18 height=75 xoffset=0 yoffset=5 xadvance=21 page=0 chnl=0
|
||||||
|
char id=94 x=139 y=362 width=33 height=28 xoffset=0 yoffset=14 xadvance=32 page=0 chnl=0
|
||||||
|
char id=95 x=331 y=362 width=34 height=12 xoffset=-1 yoffset=65 xadvance=33 page=0 chnl=0
|
||||||
|
char id=96 x=285 y=362 width=23 height=13 xoffset=0 yoffset=12 xadvance=24 page=0 chnl=0
|
||||||
|
char id=97 x=0 y=320 width=37 height=42 xoffset=1 yoffset=27 xadvance=38 page=0 chnl=0
|
||||||
|
char id=98 x=440 y=0 width=37 height=57 xoffset=2 yoffset=12 xadvance=40 page=0 chnl=0
|
||||||
|
char id=99 x=37 y=320 width=36 height=42 xoffset=1 yoffset=27 xadvance=38 page=0 chnl=0
|
||||||
|
char id=100 x=0 y=99 width=37 height=57 xoffset=1 yoffset=12 xadvance=40 page=0 chnl=0
|
||||||
|
char id=101 x=73 y=320 width=38 height=42 xoffset=1 yoffset=27 xadvance=39 page=0 chnl=0
|
||||||
|
char id=102 x=477 y=0 width=28 height=57 xoffset=0 yoffset=11 xadvance=27 page=0 chnl=0
|
||||||
|
char id=103 x=217 y=99 width=38 height=56 xoffset=1 yoffset=27 xadvance=41 page=0 chnl=0
|
||||||
|
char id=104 x=255 y=99 width=36 height=56 xoffset=2 yoffset=12 xadvance=40 page=0 chnl=0
|
||||||
|
char id=105 x=291 y=99 width=15 height=56 xoffset=2 yoffset=12 xadvance=19 page=0 chnl=0
|
||||||
|
char id=106 x=197 y=0 width=23 height=71 xoffset=-5 yoffset=12 xadvance=20 page=0 chnl=0
|
||||||
|
char id=107 x=306 y=99 width=40 height=56 xoffset=2 yoffset=12 xadvance=39 page=0 chnl=0
|
||||||
|
char id=108 x=346 y=99 width=14 height=56 xoffset=3 yoffset=12 xadvance=20 page=0 chnl=0
|
||||||
|
char id=109 x=186 y=320 width=58 height=41 xoffset=2 yoffset=27 xadvance=63 page=0 chnl=0
|
||||||
|
char id=110 x=244 y=320 width=36 height=41 xoffset=2 yoffset=27 xadvance=40 page=0 chnl=0
|
||||||
|
char id=111 x=111 y=320 width=39 height=42 xoffset=1 yoffset=27 xadvance=41 page=0 chnl=0
|
||||||
|
char id=112 x=360 y=99 width=37 height=56 xoffset=2 yoffset=27 xadvance=40 page=0 chnl=0
|
||||||
|
char id=113 x=397 y=99 width=37 height=56 xoffset=1 yoffset=27 xadvance=40 page=0 chnl=0
|
||||||
|
char id=114 x=486 y=266 width=25 height=41 xoffset=2 yoffset=27 xadvance=27 page=0 chnl=0
|
||||||
|
char id=115 x=150 y=320 width=36 height=42 xoffset=0 yoffset=27 xadvance=37 page=0 chnl=0
|
||||||
|
char id=116 x=445 y=266 width=26 height=51 xoffset=0 yoffset=18 xadvance=25 page=0 chnl=0
|
||||||
|
char id=117 x=280 y=320 width=36 height=41 xoffset=2 yoffset=28 xadvance=40 page=0 chnl=0
|
||||||
|
char id=118 x=316 y=320 width=39 height=40 xoffset=-1 yoffset=28 xadvance=37 page=0 chnl=0
|
||||||
|
char id=119 x=355 y=320 width=54 height=40 xoffset=-1 yoffset=28 xadvance=52 page=0 chnl=0
|
||||||
|
char id=120 x=409 y=320 width=40 height=40 xoffset=-1 yoffset=28 xadvance=37 page=0 chnl=0
|
||||||
|
char id=121 x=343 y=156 width=40 height=55 xoffset=-1 yoffset=28 xadvance=37 page=0 chnl=0
|
||||||
|
char id=122 x=449 y=320 width=34 height=40 xoffset=1 yoffset=28 xadvance=36 page=0 chnl=0
|
||||||
|
char id=123 x=151 y=0 width=23 height=72 xoffset=0 yoffset=9 xadvance=23 page=0 chnl=0
|
||||||
|
char id=124 x=366 y=0 width=8 height=63 xoffset=5 yoffset=14 xadvance=18 page=0 chnl=0
|
||||||
|
char id=125 x=174 y=0 width=23 height=72 xoffset=0 yoffset=9 xadvance=23 page=0 chnl=0
|
||||||
|
char id=126 x=229 y=362 width=41 height=19 xoffset=2 yoffset=36 xadvance=45 page=0 chnl=0
|
||||||
|
char id=127 x=0 y=0 width=70 height=99 xoffset=2 yoffset=-11 xadvance=74 page=0 chnl=0
|
||||||
|
kernings count=385
|
||||||
|
kerning first=84 second=74 amount=-8
|
||||||
|
kerning first=86 second=100 amount=-2
|
||||||
|
kerning first=114 second=113 amount=-1
|
||||||
|
kerning first=70 second=121 amount=-1
|
||||||
|
kerning first=34 second=99 amount=-2
|
||||||
|
kerning first=70 second=99 amount=-1
|
||||||
|
kerning first=69 second=99 amount=-1
|
||||||
|
kerning first=88 second=113 amount=-1
|
||||||
|
kerning first=84 second=46 amount=-9
|
||||||
|
kerning first=87 second=97 amount=-1
|
||||||
|
kerning first=90 second=117 amount=-1
|
||||||
|
kerning first=39 second=97 amount=-2
|
||||||
|
kerning first=69 second=111 amount=-1
|
||||||
|
kerning first=87 second=41 amount=1
|
||||||
|
kerning first=121 second=34 amount=1
|
||||||
|
kerning first=40 second=86 amount=1
|
||||||
|
kerning first=85 second=65 amount=-1
|
||||||
|
kerning first=72 second=65 amount=1
|
||||||
|
kerning first=114 second=102 amount=1
|
||||||
|
kerning first=89 second=42 amount=-2
|
||||||
|
kerning first=114 second=34 amount=1
|
||||||
|
kerning first=75 second=67 amount=-1
|
||||||
|
kerning first=89 second=85 amount=-3
|
||||||
|
kerning first=77 second=88 amount=1
|
||||||
|
kerning first=84 second=115 amount=-3
|
||||||
|
kerning first=84 second=71 amount=-1
|
||||||
|
kerning first=89 second=101 amount=-2
|
||||||
|
kerning first=89 second=45 amount=-5
|
||||||
|
kerning first=78 second=88 amount=1
|
||||||
|
kerning first=68 second=89 amount=-2
|
||||||
|
kerning first=122 second=103 amount=-1
|
||||||
|
kerning first=78 second=84 amount=-1
|
||||||
|
kerning first=86 second=103 amount=-2
|
||||||
|
kerning first=89 second=79 amount=-1
|
||||||
|
kerning first=75 second=111 amount=-1
|
||||||
|
kerning first=111 second=120 amount=-1
|
||||||
|
kerning first=87 second=44 amount=-5
|
||||||
|
kerning first=67 second=84 amount=-1
|
||||||
|
kerning first=84 second=111 amount=-7
|
||||||
|
kerning first=84 second=83 amount=-1
|
||||||
|
kerning first=102 second=113 amount=-1
|
||||||
|
kerning first=39 second=101 amount=-2
|
||||||
|
kerning first=80 second=88 amount=-2
|
||||||
|
kerning first=66 second=84 amount=-1
|
||||||
|
kerning first=65 second=87 amount=-1
|
||||||
|
kerning first=122 second=100 amount=-1
|
||||||
|
kerning first=75 second=118 amount=-1
|
||||||
|
kerning first=73 second=65 amount=1
|
||||||
|
kerning first=70 second=118 amount=-1
|
||||||
|
kerning first=73 second=88 amount=1
|
||||||
|
kerning first=82 second=89 amount=-2
|
||||||
|
kerning first=65 second=34 amount=-4
|
||||||
|
kerning first=120 second=99 amount=-1
|
||||||
|
kerning first=84 second=99 amount=-3
|
||||||
|
kerning first=84 second=65 amount=-4
|
||||||
|
kerning first=112 second=39 amount=-1
|
||||||
|
kerning first=76 second=39 amount=-10
|
||||||
|
kerning first=78 second=65 amount=1
|
||||||
|
kerning first=88 second=45 amount=-5
|
||||||
|
kerning first=34 second=111 amount=-3
|
||||||
|
kerning first=114 second=99 amount=-1
|
||||||
|
kerning first=86 second=125 amount=1
|
||||||
|
kerning first=70 second=111 amount=-1
|
||||||
|
kerning first=89 second=120 amount=-1
|
||||||
|
kerning first=90 second=119 amount=-1
|
||||||
|
kerning first=89 second=89 amount=1
|
||||||
|
kerning first=89 second=117 amount=-1
|
||||||
|
kerning first=75 second=117 amount=-1
|
||||||
|
kerning first=76 second=65 amount=1
|
||||||
|
kerning first=34 second=34 amount=-1
|
||||||
|
kerning first=89 second=110 amount=-1
|
||||||
|
kerning first=88 second=101 amount=-1
|
||||||
|
kerning first=107 second=103 amount=-1
|
||||||
|
kerning first=34 second=115 amount=-3
|
||||||
|
kerning first=80 second=44 amount=-14
|
||||||
|
kerning first=98 second=39 amount=-1
|
||||||
|
kerning first=70 second=65 amount=-7
|
||||||
|
kerning first=89 second=116 amount=-1
|
||||||
|
kerning first=70 second=46 amount=-10
|
||||||
|
kerning first=98 second=34 amount=-1
|
||||||
|
kerning first=70 second=84 amount=1
|
||||||
|
kerning first=114 second=100 amount=-1
|
||||||
|
kerning first=88 second=79 amount=-1
|
||||||
|
kerning first=39 second=113 amount=-2
|
||||||
|
kerning first=65 second=118 amount=-2
|
||||||
|
kerning first=114 second=103 amount=-1
|
||||||
|
kerning first=77 second=65 amount=1
|
||||||
|
kerning first=120 second=103 amount=-1
|
||||||
|
kerning first=65 second=110 amount=-2
|
||||||
|
kerning first=114 second=121 amount=1
|
||||||
|
kerning first=89 second=100 amount=-2
|
||||||
|
kerning first=80 second=65 amount=-6
|
||||||
|
kerning first=121 second=111 amount=-1
|
||||||
|
kerning first=34 second=101 amount=-2
|
||||||
|
kerning first=122 second=111 amount=-1
|
||||||
|
kerning first=114 second=118 amount=1
|
||||||
|
kerning first=102 second=41 amount=1
|
||||||
|
kerning first=122 second=113 amount=-1
|
||||||
|
kerning first=89 second=122 amount=-1
|
||||||
|
kerning first=68 second=88 amount=-1
|
||||||
|
kerning first=81 second=89 amount=-1
|
||||||
|
kerning first=114 second=111 amount=-1
|
||||||
|
kerning first=46 second=34 amount=-10
|
||||||
|
kerning first=84 second=112 amount=-3
|
||||||
|
kerning first=76 second=34 amount=-10
|
||||||
|
kerning first=39 second=115 amount=-3
|
||||||
|
kerning first=76 second=118 amount=-4
|
||||||
|
kerning first=86 second=99 amount=-2
|
||||||
|
kerning first=84 second=84 amount=1
|
||||||
|
kerning first=120 second=111 amount=-1
|
||||||
|
kerning first=65 second=79 amount=-1
|
||||||
|
kerning first=87 second=101 amount=-1
|
||||||
|
kerning first=67 second=125 amount=-1
|
||||||
|
kerning first=120 second=113 amount=-1
|
||||||
|
kerning first=118 second=46 amount=-6
|
||||||
|
kerning first=88 second=103 amount=-1
|
||||||
|
kerning first=111 second=122 amount=-1
|
||||||
|
kerning first=77 second=84 amount=-1
|
||||||
|
kerning first=114 second=46 amount=-6
|
||||||
|
kerning first=34 second=39 amount=-1
|
||||||
|
kerning first=65 second=121 amount=-2
|
||||||
|
kerning first=114 second=44 amount=-6
|
||||||
|
kerning first=69 second=84 amount=1
|
||||||
|
kerning first=89 second=46 amount=-8
|
||||||
|
kerning first=97 second=39 amount=-1
|
||||||
|
kerning first=34 second=100 amount=-2
|
||||||
|
kerning first=70 second=100 amount=-1
|
||||||
|
kerning first=84 second=120 amount=-3
|
||||||
|
kerning first=90 second=118 amount=-1
|
||||||
|
kerning first=70 second=114 amount=-1
|
||||||
|
kerning first=34 second=112 amount=-1
|
||||||
|
kerning first=89 second=86 amount=1
|
||||||
|
kerning first=86 second=113 amount=-2
|
||||||
|
kerning first=88 second=71 amount=-1
|
||||||
|
kerning first=122 second=99 amount=-1
|
||||||
|
kerning first=66 second=89 amount=-2
|
||||||
|
kerning first=102 second=103 amount=-1
|
||||||
|
kerning first=88 second=67 amount=-1
|
||||||
|
kerning first=39 second=110 amount=-1
|
||||||
|
kerning first=88 second=117 amount=-1
|
||||||
|
kerning first=89 second=118 amount=-1
|
||||||
|
kerning first=97 second=118 amount=-1
|
||||||
|
kerning first=87 second=65 amount=-2
|
||||||
|
kerning first=89 second=67 amount=-1
|
||||||
|
kerning first=89 second=74 amount=-3
|
||||||
|
kerning first=102 second=101 amount=-1
|
||||||
|
kerning first=86 second=111 amount=-2
|
||||||
|
kerning first=65 second=119 amount=-1
|
||||||
|
kerning first=84 second=100 amount=-3
|
||||||
|
kerning first=120 second=100 amount=-1
|
||||||
|
kerning first=104 second=34 amount=-3
|
||||||
|
kerning first=86 second=41 amount=1
|
||||||
|
kerning first=111 second=34 amount=-3
|
||||||
|
kerning first=40 second=89 amount=1
|
||||||
|
kerning first=121 second=39 amount=1
|
||||||
|
kerning first=70 second=74 amount=-7
|
||||||
|
kerning first=68 second=90 amount=-1
|
||||||
|
kerning first=98 second=120 amount=-1
|
||||||
|
kerning first=110 second=34 amount=-3
|
||||||
|
kerning first=119 second=46 amount=-4
|
||||||
|
kerning first=69 second=102 amount=-1
|
||||||
|
kerning first=118 second=44 amount=-6
|
||||||
|
kerning first=84 second=114 amount=-2
|
||||||
|
kerning first=86 second=97 amount=-2
|
||||||
|
kerning first=40 second=87 amount=1
|
||||||
|
kerning first=65 second=109 amount=-2
|
||||||
|
kerning first=68 second=86 amount=-1
|
||||||
|
kerning first=86 second=93 amount=1
|
||||||
|
kerning first=65 second=67 amount=-1
|
||||||
|
kerning first=97 second=34 amount=-1
|
||||||
|
kerning first=34 second=65 amount=-4
|
||||||
|
kerning first=84 second=118 amount=-3
|
||||||
|
kerning first=112 second=34 amount=-1
|
||||||
|
kerning first=76 second=84 amount=-7
|
||||||
|
kerning first=107 second=99 amount=-1
|
||||||
|
kerning first=123 second=85 amount=-1
|
||||||
|
kerning first=102 second=125 amount=1
|
||||||
|
kerning first=65 second=63 amount=-3
|
||||||
|
kerning first=89 second=44 amount=-8
|
||||||
|
kerning first=80 second=118 amount=1
|
||||||
|
kerning first=112 second=122 amount=-1
|
||||||
|
kerning first=79 second=65 amount=-1
|
||||||
|
kerning first=80 second=121 amount=1
|
||||||
|
kerning first=118 second=34 amount=1
|
||||||
|
kerning first=87 second=45 amount=-2
|
||||||
|
kerning first=69 second=100 amount=-1
|
||||||
|
kerning first=87 second=103 amount=-1
|
||||||
|
kerning first=112 second=120 amount=-1
|
||||||
|
kerning first=86 second=65 amount=-3
|
||||||
|
kerning first=65 second=81 amount=-1
|
||||||
|
kerning first=68 second=44 amount=-4
|
||||||
|
kerning first=86 second=45 amount=-6
|
||||||
|
kerning first=39 second=34 amount=-1
|
||||||
|
kerning first=72 second=88 amount=1
|
||||||
|
kerning first=68 second=46 amount=-4
|
||||||
|
kerning first=65 second=89 amount=-5
|
||||||
|
kerning first=69 second=118 amount=-1
|
||||||
|
kerning first=89 second=38 amount=-1
|
||||||
|
kerning first=88 second=99 amount=-1
|
||||||
|
kerning first=65 second=71 amount=-1
|
||||||
|
kerning first=91 second=74 amount=-1
|
||||||
|
kerning first=75 second=101 amount=-1
|
||||||
|
kerning first=39 second=112 amount=-1
|
||||||
|
kerning first=70 second=113 amount=-1
|
||||||
|
kerning first=119 second=44 amount=-4
|
||||||
|
kerning first=72 second=89 amount=-1
|
||||||
|
kerning first=90 second=103 amount=-1
|
||||||
|
kerning first=65 second=86 amount=-3
|
||||||
|
kerning first=84 second=119 amount=-2
|
||||||
|
kerning first=34 second=110 amount=-1
|
||||||
|
kerning first=39 second=109 amount=-1
|
||||||
|
kerning first=75 second=81 amount=-1
|
||||||
|
kerning first=89 second=115 amount=-2
|
||||||
|
kerning first=89 second=87 amount=1
|
||||||
|
kerning first=114 second=101 amount=-1
|
||||||
|
kerning first=116 second=111 amount=-1
|
||||||
|
kerning first=90 second=100 amount=-1
|
||||||
|
kerning first=79 second=89 amount=-2
|
||||||
|
kerning first=84 second=122 amount=-2
|
||||||
|
kerning first=68 second=84 amount=-3
|
||||||
|
kerning first=76 second=86 amount=-7
|
||||||
|
kerning first=74 second=65 amount=-1
|
||||||
|
kerning first=107 second=101 amount=-1
|
||||||
|
kerning first=80 second=46 amount=-14
|
||||||
|
kerning first=89 second=93 amount=1
|
||||||
|
kerning first=89 second=65 amount=-5
|
||||||
|
kerning first=87 second=117 amount=-1
|
||||||
|
kerning first=89 second=81 amount=-1
|
||||||
|
kerning first=39 second=103 amount=-2
|
||||||
|
kerning first=86 second=101 amount=-2
|
||||||
|
kerning first=86 second=117 amount=-1
|
||||||
|
kerning first=84 second=113 amount=-3
|
||||||
|
kerning first=87 second=46 amount=-5
|
||||||
|
kerning first=47 second=47 amount=-9
|
||||||
|
kerning first=75 second=103 amount=-1
|
||||||
|
kerning first=89 second=84 amount=1
|
||||||
|
kerning first=84 second=110 amount=-3
|
||||||
|
kerning first=39 second=99 amount=-2
|
||||||
|
kerning first=88 second=121 amount=-1
|
||||||
|
kerning first=65 second=39 amount=-4
|
||||||
|
kerning first=110 second=39 amount=-3
|
||||||
|
kerning first=88 second=118 amount=-1
|
||||||
|
kerning first=86 second=114 amount=-1
|
||||||
|
kerning first=80 second=74 amount=-6
|
||||||
|
kerning first=84 second=97 amount=-6
|
||||||
|
kerning first=82 second=84 amount=-2
|
||||||
|
kerning first=91 second=85 amount=-1
|
||||||
|
kerning first=102 second=99 amount=-1
|
||||||
|
kerning first=66 second=86 amount=-1
|
||||||
|
kerning first=120 second=101 amount=-1
|
||||||
|
kerning first=102 second=93 amount=1
|
||||||
|
kerning first=75 second=100 amount=-1
|
||||||
|
kerning first=84 second=79 amount=-1
|
||||||
|
kerning first=44 second=39 amount=-10
|
||||||
|
kerning first=111 second=121 amount=-1
|
||||||
|
kerning first=75 second=121 amount=-1
|
||||||
|
kerning first=81 second=87 amount=-1
|
||||||
|
kerning first=107 second=113 amount=-1
|
||||||
|
kerning first=90 second=79 amount=-1
|
||||||
|
kerning first=89 second=114 amount=-1
|
||||||
|
kerning first=122 second=101 amount=-1
|
||||||
|
kerning first=111 second=118 amount=-1
|
||||||
|
kerning first=82 second=86 amount=-1
|
||||||
|
kerning first=70 second=101 amount=-1
|
||||||
|
kerning first=114 second=97 amount=-1
|
||||||
|
kerning first=70 second=97 amount=-1
|
||||||
|
kerning first=34 second=97 amount=-2
|
||||||
|
kerning first=89 second=102 amount=-1
|
||||||
|
kerning first=78 second=89 amount=-1
|
||||||
|
kerning first=70 second=44 amount=-10
|
||||||
|
kerning first=104 second=39 amount=-3
|
||||||
|
kerning first=84 second=45 amount=-10
|
||||||
|
kerning first=89 second=121 amount=-1
|
||||||
|
kerning first=109 second=34 amount=-3
|
||||||
|
kerning first=84 second=86 amount=1
|
||||||
|
kerning first=87 second=99 amount=-1
|
||||||
|
kerning first=32 second=84 amount=-2
|
||||||
|
kerning first=98 second=122 amount=-1
|
||||||
|
kerning first=89 second=112 amount=-1
|
||||||
|
kerning first=89 second=103 amount=-2
|
||||||
|
kerning first=65 second=116 amount=-1
|
||||||
|
kerning first=88 second=81 amount=-1
|
||||||
|
kerning first=102 second=34 amount=1
|
||||||
|
kerning first=109 second=39 amount=-3
|
||||||
|
kerning first=81 second=84 amount=-1
|
||||||
|
kerning first=121 second=97 amount=-1
|
||||||
|
kerning first=89 second=99 amount=-2
|
||||||
|
kerning first=89 second=125 amount=1
|
||||||
|
kerning first=81 second=86 amount=-1
|
||||||
|
kerning first=114 second=116 amount=2
|
||||||
|
kerning first=114 second=119 amount=1
|
||||||
|
kerning first=84 second=44 amount=-9
|
||||||
|
kerning first=102 second=39 amount=1
|
||||||
|
kerning first=44 second=34 amount=-10
|
||||||
|
kerning first=34 second=109 amount=-1
|
||||||
|
kerning first=84 second=101 amount=-3
|
||||||
|
kerning first=75 second=119 amount=-2
|
||||||
|
kerning first=84 second=81 amount=-1
|
||||||
|
kerning first=76 second=121 amount=-4
|
||||||
|
kerning first=69 second=101 amount=-1
|
||||||
|
kerning first=80 second=90 amount=-1
|
||||||
|
kerning first=89 second=97 amount=-2
|
||||||
|
kerning first=89 second=109 amount=-1
|
||||||
|
kerning first=90 second=99 amount=-1
|
||||||
|
kerning first=79 second=88 amount=-1
|
||||||
|
kerning first=70 second=103 amount=-1
|
||||||
|
kerning first=34 second=103 amount=-2
|
||||||
|
kerning first=84 second=67 amount=-1
|
||||||
|
kerning first=76 second=79 amount=-2
|
||||||
|
kerning first=34 second=113 amount=-2
|
||||||
|
kerning first=89 second=41 amount=1
|
||||||
|
kerning first=75 second=71 amount=-1
|
||||||
|
kerning first=76 second=87 amount=-3
|
||||||
|
kerning first=77 second=89 amount=-1
|
||||||
|
kerning first=90 second=113 amount=-1
|
||||||
|
kerning first=118 second=111 amount=-1
|
||||||
|
kerning first=118 second=97 amount=-1
|
||||||
|
kerning first=88 second=100 amount=-1
|
||||||
|
kerning first=89 second=111 amount=-2
|
||||||
|
kerning first=90 second=121 amount=-1
|
||||||
|
kerning first=89 second=113 amount=-2
|
||||||
|
kerning first=84 second=87 amount=1
|
||||||
|
kerning first=39 second=111 amount=-3
|
||||||
|
kerning first=39 second=100 amount=-2
|
||||||
|
kerning first=75 second=113 amount=-1
|
||||||
|
kerning first=88 second=111 amount=-1
|
||||||
|
kerning first=87 second=111 amount=-1
|
||||||
|
kerning first=89 second=83 amount=-1
|
||||||
|
kerning first=84 second=89 amount=1
|
||||||
|
kerning first=84 second=103 amount=-3
|
||||||
|
kerning first=70 second=117 amount=-1
|
||||||
|
kerning first=67 second=41 amount=-1
|
||||||
|
kerning first=89 second=71 amount=-1
|
||||||
|
kerning first=121 second=44 amount=-6
|
||||||
|
kerning first=97 second=121 amount=-1
|
||||||
|
kerning first=87 second=113 amount=-1
|
||||||
|
kerning first=73 second=84 amount=-1
|
||||||
|
kerning first=121 second=46 amount=-6
|
||||||
|
kerning first=75 second=99 amount=-1
|
||||||
|
kerning first=65 second=112 amount=-2
|
||||||
|
kerning first=65 second=85 amount=-1
|
||||||
|
kerning first=76 second=67 amount=-2
|
||||||
|
kerning first=76 second=81 amount=-2
|
||||||
|
kerning first=102 second=100 amount=-1
|
||||||
|
kerning first=75 second=79 amount=-1
|
||||||
|
kerning first=39 second=65 amount=-4
|
||||||
|
kerning first=65 second=84 amount=-4
|
||||||
|
kerning first=90 second=101 amount=-1
|
||||||
|
kerning first=84 second=121 amount=-3
|
||||||
|
kerning first=114 second=39 amount=1
|
||||||
|
kerning first=84 second=109 amount=-3
|
||||||
|
kerning first=123 second=74 amount=-1
|
||||||
|
kerning first=76 second=119 amount=-2
|
||||||
|
kerning first=84 second=117 amount=-2
|
||||||
|
kerning first=76 second=85 amount=-1
|
||||||
|
kerning first=76 second=71 amount=-2
|
||||||
|
kerning first=79 second=90 amount=-1
|
||||||
|
kerning first=107 second=100 amount=-1
|
||||||
|
kerning first=90 second=111 amount=-1
|
||||||
|
kerning first=79 second=44 amount=-4
|
||||||
|
kerning first=75 second=45 amount=-6
|
||||||
|
kerning first=79 second=86 amount=-1
|
||||||
|
kerning first=79 second=46 amount=-4
|
||||||
|
kerning first=76 second=89 amount=-10
|
||||||
|
kerning first=68 second=65 amount=-1
|
||||||
|
kerning first=79 second=84 amount=-3
|
||||||
|
kerning first=87 second=100 amount=-1
|
||||||
|
kerning first=84 second=32 amount=-2
|
||||||
|
kerning first=90 second=67 amount=-1
|
||||||
|
kerning first=69 second=103 amount=-1
|
||||||
|
kerning first=90 second=71 amount=-1
|
||||||
|
kerning first=86 second=44 amount=-8
|
||||||
|
kerning first=69 second=121 amount=-1
|
||||||
|
kerning first=87 second=114 amount=-1
|
||||||
|
kerning first=118 second=39 amount=1
|
||||||
|
kerning first=46 second=39 amount=-10
|
||||||
|
kerning first=72 second=84 amount=-1
|
||||||
|
kerning first=86 second=46 amount=-8
|
||||||
|
kerning first=69 second=113 amount=-1
|
||||||
|
kerning first=69 second=119 amount=-1
|
||||||
|
kerning first=73 second=89 amount=-1
|
||||||
|
kerning first=39 second=39 amount=-1
|
||||||
|
kerning first=69 second=117 amount=-1
|
||||||
|
kerning first=111 second=39 amount=-3
|
||||||
|
kerning first=90 second=81 amount=-1
|
BIN
src/web/static/fonts/bmfonts/RobotoBlack72White.png
Normal file
BIN
src/web/static/fonts/bmfonts/RobotoBlack72White.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
103
src/web/static/fonts/bmfonts/RobotoMono72White.fnt
Normal file
103
src/web/static/fonts/bmfonts/RobotoMono72White.fnt
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
info face="Roboto Mono" size=72 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2
|
||||||
|
common lineHeight=96 base=76 scaleW=512 scaleH=512 pages=1 packed=0
|
||||||
|
page id=0 file="RobotoMono72White.png"
|
||||||
|
chars count=98
|
||||||
|
char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=0 page=0 chnl=0
|
||||||
|
char id=10 x=0 y=0 width=45 height=99 xoffset=-1 yoffset=-2 xadvance=43 page=0 chnl=0
|
||||||
|
char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=43 page=0 chnl=0
|
||||||
|
char id=33 x=498 y=99 width=10 height=55 xoffset=16 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=34 x=434 y=319 width=20 height=19 xoffset=11 yoffset=21 xadvance=43 page=0 chnl=0
|
||||||
|
char id=35 x=175 y=265 width=41 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=36 x=200 y=0 width=35 height=69 xoffset=5 yoffset=15 xadvance=43 page=0 chnl=0
|
||||||
|
char id=37 x=0 y=155 width=42 height=56 xoffset=1 yoffset=22 xadvance=44 page=0 chnl=0
|
||||||
|
char id=38 x=42 y=155 width=41 height=56 xoffset=3 yoffset=22 xadvance=44 page=0 chnl=0
|
||||||
|
char id=39 x=502 y=211 width=7 height=19 xoffset=16 yoffset=21 xadvance=43 page=0 chnl=0
|
||||||
|
char id=40 x=45 y=0 width=21 height=78 xoffset=12 yoffset=16 xadvance=44 page=0 chnl=0
|
||||||
|
char id=41 x=66 y=0 width=22 height=78 xoffset=9 yoffset=16 xadvance=43 page=0 chnl=0
|
||||||
|
char id=42 x=256 y=319 width=37 height=37 xoffset=4 yoffset=32 xadvance=43 page=0 chnl=0
|
||||||
|
char id=43 x=219 y=319 width=37 height=40 xoffset=3 yoffset=32 xadvance=43 page=0 chnl=0
|
||||||
|
char id=44 x=421 y=319 width=13 height=22 xoffset=11 yoffset=67 xadvance=43 page=0 chnl=0
|
||||||
|
char id=45 x=17 y=360 width=29 height=8 xoffset=7 yoffset=49 xadvance=44 page=0 chnl=0
|
||||||
|
char id=46 x=496 y=319 width=12 height=13 xoffset=16 yoffset=65 xadvance=43 page=0 chnl=0
|
||||||
|
char id=47 x=319 y=0 width=31 height=58 xoffset=7 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=48 x=431 y=99 width=35 height=56 xoffset=4 yoffset=22 xadvance=43 page=0 chnl=0
|
||||||
|
char id=49 x=36 y=265 width=23 height=54 xoffset=6 yoffset=23 xadvance=44 page=0 chnl=0
|
||||||
|
char id=50 x=189 y=155 width=37 height=55 xoffset=2 yoffset=22 xadvance=44 page=0 chnl=0
|
||||||
|
char id=51 x=361 y=99 width=35 height=56 xoffset=2 yoffset=22 xadvance=43 page=0 chnl=0
|
||||||
|
char id=52 x=59 y=265 width=39 height=54 xoffset=2 yoffset=23 xadvance=44 page=0 chnl=0
|
||||||
|
char id=53 x=226 y=155 width=35 height=55 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=54 x=261 y=155 width=35 height=55 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=55 x=98 y=265 width=37 height=54 xoffset=3 yoffset=23 xadvance=44 page=0 chnl=0
|
||||||
|
char id=56 x=396 y=99 width=35 height=56 xoffset=5 yoffset=22 xadvance=43 page=0 chnl=0
|
||||||
|
char id=57 x=296 y=155 width=34 height=55 xoffset=4 yoffset=22 xadvance=43 page=0 chnl=0
|
||||||
|
char id=58 x=490 y=211 width=12 height=43 xoffset=18 yoffset=35 xadvance=43 page=0 chnl=0
|
||||||
|
char id=59 x=486 y=0 width=14 height=55 xoffset=16 yoffset=35 xadvance=43 page=0 chnl=0
|
||||||
|
char id=60 x=293 y=319 width=32 height=35 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=61 x=388 y=319 width=33 height=23 xoffset=5 yoffset=41 xadvance=43 page=0 chnl=0
|
||||||
|
char id=62 x=325 y=319 width=33 height=35 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=63 x=466 y=99 width=32 height=56 xoffset=6 yoffset=22 xadvance=43 page=0 chnl=0
|
||||||
|
char id=64 x=135 y=265 width=40 height=54 xoffset=1 yoffset=23 xadvance=42 page=0 chnl=0
|
||||||
|
char id=65 x=330 y=155 width=42 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=66 x=372 y=155 width=35 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=67 x=448 y=0 width=38 height=56 xoffset=3 yoffset=22 xadvance=43 page=0 chnl=0
|
||||||
|
char id=68 x=407 y=155 width=37 height=54 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=69 x=444 y=155 width=34 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=70 x=0 y=211 width=34 height=54 xoffset=6 yoffset=23 xadvance=44 page=0 chnl=0
|
||||||
|
char id=71 x=0 y=99 width=38 height=56 xoffset=3 yoffset=22 xadvance=44 page=0 chnl=0
|
||||||
|
char id=72 x=34 y=211 width=36 height=54 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=73 x=478 y=155 width=33 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=74 x=83 y=155 width=36 height=55 xoffset=2 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=75 x=70 y=211 width=38 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=76 x=108 y=211 width=34 height=54 xoffset=6 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=77 x=142 y=211 width=36 height=54 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=78 x=178 y=211 width=35 height=54 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=79 x=38 y=99 width=38 height=56 xoffset=3 yoffset=22 xadvance=43 page=0 chnl=0
|
||||||
|
char id=80 x=213 y=211 width=36 height=54 xoffset=6 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=81 x=242 y=0 width=40 height=64 xoffset=2 yoffset=22 xadvance=43 page=0 chnl=0
|
||||||
|
char id=82 x=249 y=211 width=36 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=83 x=76 y=99 width=38 height=56 xoffset=3 yoffset=22 xadvance=44 page=0 chnl=0
|
||||||
|
char id=84 x=285 y=211 width=40 height=54 xoffset=2 yoffset=23 xadvance=44 page=0 chnl=0
|
||||||
|
char id=85 x=119 y=155 width=36 height=55 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=86 x=325 y=211 width=41 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=87 x=366 y=211 width=42 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=88 x=408 y=211 width=41 height=54 xoffset=2 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=89 x=449 y=211 width=41 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=90 x=0 y=265 width=36 height=54 xoffset=3 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=91 x=88 y=0 width=16 height=72 xoffset=14 yoffset=16 xadvance=43 page=0 chnl=0
|
||||||
|
char id=92 x=350 y=0 width=30 height=58 xoffset=7 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=93 x=104 y=0 width=17 height=72 xoffset=13 yoffset=16 xadvance=44 page=0 chnl=0
|
||||||
|
char id=94 x=358 y=319 width=30 height=30 xoffset=7 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=95 x=46 y=360 width=34 height=8 xoffset=4 yoffset=74 xadvance=43 page=0 chnl=0
|
||||||
|
char id=96 x=0 y=360 width=17 height=12 xoffset=13 yoffset=22 xadvance=43 page=0 chnl=0
|
||||||
|
char id=97 x=251 y=265 width=35 height=42 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=98 x=380 y=0 width=34 height=57 xoffset=5 yoffset=21 xadvance=43 page=0 chnl=0
|
||||||
|
char id=99 x=286 y=265 width=35 height=42 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=100 x=414 y=0 width=34 height=57 xoffset=4 yoffset=21 xadvance=43 page=0 chnl=0
|
||||||
|
char id=101 x=321 y=265 width=36 height=42 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=102 x=282 y=0 width=37 height=58 xoffset=4 yoffset=19 xadvance=43 page=0 chnl=0
|
||||||
|
char id=103 x=114 y=99 width=34 height=56 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=104 x=148 y=99 width=34 height=56 xoffset=5 yoffset=21 xadvance=43 page=0 chnl=0
|
||||||
|
char id=105 x=155 y=155 width=34 height=55 xoffset=6 yoffset=22 xadvance=43 page=0 chnl=0
|
||||||
|
char id=106 x=121 y=0 width=26 height=71 xoffset=6 yoffset=22 xadvance=44 page=0 chnl=0
|
||||||
|
char id=107 x=182 y=99 width=36 height=56 xoffset=5 yoffset=21 xadvance=43 page=0 chnl=0
|
||||||
|
char id=108 x=218 y=99 width=34 height=56 xoffset=6 yoffset=21 xadvance=43 page=0 chnl=0
|
||||||
|
char id=109 x=428 y=265 width=39 height=41 xoffset=2 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=110 x=467 y=265 width=34 height=41 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=111 x=357 y=265 width=37 height=42 xoffset=3 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=112 x=252 y=99 width=34 height=56 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=113 x=286 y=99 width=34 height=56 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=114 x=0 y=319 width=29 height=41 xoffset=11 yoffset=36 xadvance=44 page=0 chnl=0
|
||||||
|
char id=115 x=394 y=265 width=34 height=42 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=116 x=216 y=265 width=35 height=51 xoffset=4 yoffset=27 xadvance=43 page=0 chnl=0
|
||||||
|
char id=117 x=29 y=319 width=33 height=41 xoffset=5 yoffset=37 xadvance=43 page=0 chnl=0
|
||||||
|
char id=118 x=62 y=319 width=39 height=40 xoffset=2 yoffset=37 xadvance=43 page=0 chnl=0
|
||||||
|
char id=119 x=101 y=319 width=43 height=40 xoffset=0 yoffset=37 xadvance=43 page=0 chnl=0
|
||||||
|
char id=120 x=144 y=319 width=40 height=40 xoffset=2 yoffset=37 xadvance=43 page=0 chnl=0
|
||||||
|
char id=121 x=320 y=99 width=41 height=56 xoffset=1 yoffset=37 xadvance=43 page=0 chnl=0
|
||||||
|
char id=122 x=184 y=319 width=35 height=40 xoffset=5 yoffset=37 xadvance=44 page=0 chnl=0
|
||||||
|
char id=123 x=147 y=0 width=26 height=71 xoffset=10 yoffset=19 xadvance=43 page=0 chnl=0
|
||||||
|
char id=124 x=235 y=0 width=7 height=68 xoffset=18 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=125 x=173 y=0 width=27 height=71 xoffset=10 yoffset=19 xadvance=44 page=0 chnl=0
|
||||||
|
char id=126 x=454 y=319 width=42 height=16 xoffset=1 yoffset=47 xadvance=44 page=0 chnl=0
|
||||||
|
char id=127 x=0 y=0 width=45 height=99 xoffset=-1 yoffset=-2 xadvance=43 page=0 chnl=0
|
||||||
|
kernings count=0
|
BIN
src/web/static/fonts/bmfonts/RobotoMono72White.png
Normal file
BIN
src/web/static/fonts/bmfonts/RobotoMono72White.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
492
src/web/static/fonts/bmfonts/RobotoSlab72White.fnt
Normal file
492
src/web/static/fonts/bmfonts/RobotoSlab72White.fnt
Normal file
|
@ -0,0 +1,492 @@
|
||||||
|
info face="Roboto Slab Regular" size=72 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2
|
||||||
|
common lineHeight=96 base=76 scaleW=512 scaleH=512 pages=1 packed=0
|
||||||
|
page id=0 file="RobotoSlab72White.png"
|
||||||
|
chars count=98
|
||||||
|
char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=0 page=0 chnl=0
|
||||||
|
char id=10 x=0 y=0 width=70 height=98 xoffset=0 yoffset=-1 xadvance=70 page=0 chnl=0
|
||||||
|
char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=18 page=0 chnl=0
|
||||||
|
char id=33 x=497 y=156 width=9 height=54 xoffset=4 yoffset=23 xadvance=17 page=0 chnl=0
|
||||||
|
char id=34 x=191 y=362 width=19 height=20 xoffset=5 yoffset=20 xadvance=28 page=0 chnl=0
|
||||||
|
char id=35 x=406 y=266 width=41 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0
|
||||||
|
char id=36 x=212 y=0 width=35 height=69 xoffset=2 yoffset=15 xadvance=39 page=0 chnl=0
|
||||||
|
char id=37 x=174 y=156 width=48 height=56 xoffset=2 yoffset=22 xadvance=52 page=0 chnl=0
|
||||||
|
char id=38 x=222 y=156 width=44 height=56 xoffset=2 yoffset=22 xadvance=46 page=0 chnl=0
|
||||||
|
char id=39 x=210 y=362 width=8 height=20 xoffset=5 yoffset=20 xadvance=17 page=0 chnl=0
|
||||||
|
char id=40 x=70 y=0 width=21 height=77 xoffset=3 yoffset=17 xadvance=23 page=0 chnl=0
|
||||||
|
char id=41 x=91 y=0 width=21 height=77 xoffset=-1 yoffset=17 xadvance=23 page=0 chnl=0
|
||||||
|
char id=42 x=100 y=362 width=31 height=33 xoffset=1 yoffset=23 xadvance=33 page=0 chnl=0
|
||||||
|
char id=43 x=0 y=362 width=37 height=40 xoffset=2 yoffset=32 xadvance=41 page=0 chnl=0
|
||||||
|
char id=44 x=492 y=320 width=13 height=21 xoffset=-1 yoffset=67 xadvance=14 page=0 chnl=0
|
||||||
|
char id=45 x=287 y=362 width=19 height=8 xoffset=4 yoffset=50 xadvance=27 page=0 chnl=0
|
||||||
|
char id=46 x=278 y=362 width=9 height=9 xoffset=4 yoffset=68 xadvance=17 page=0 chnl=0
|
||||||
|
char id=47 x=470 y=0 width=30 height=58 xoffset=-1 yoffset=23 xadvance=29 page=0 chnl=0
|
||||||
|
char id=48 x=139 y=156 width=35 height=56 xoffset=3 yoffset=22 xadvance=41 page=0 chnl=0
|
||||||
|
char id=49 x=305 y=266 width=25 height=54 xoffset=3 yoffset=23 xadvance=30 page=0 chnl=0
|
||||||
|
char id=50 x=357 y=156 width=36 height=55 xoffset=2 yoffset=22 xadvance=40 page=0 chnl=0
|
||||||
|
char id=51 x=0 y=156 width=34 height=56 xoffset=2 yoffset=22 xadvance=39 page=0 chnl=0
|
||||||
|
char id=52 x=330 y=266 width=39 height=54 xoffset=1 yoffset=23 xadvance=42 page=0 chnl=0
|
||||||
|
char id=53 x=393 y=156 width=33 height=55 xoffset=2 yoffset=23 xadvance=37 page=0 chnl=0
|
||||||
|
char id=54 x=34 y=156 width=35 height=56 xoffset=3 yoffset=22 xadvance=40 page=0 chnl=0
|
||||||
|
char id=55 x=369 y=266 width=37 height=54 xoffset=2 yoffset=23 xadvance=40 page=0 chnl=0
|
||||||
|
char id=56 x=69 y=156 width=35 height=56 xoffset=2 yoffset=22 xadvance=39 page=0 chnl=0
|
||||||
|
char id=57 x=104 y=156 width=35 height=56 xoffset=2 yoffset=22 xadvance=41 page=0 chnl=0
|
||||||
|
char id=58 x=500 y=0 width=9 height=40 xoffset=4 yoffset=37 xadvance=15 page=0 chnl=0
|
||||||
|
char id=59 x=447 y=266 width=13 height=52 xoffset=0 yoffset=37 xadvance=15 page=0 chnl=0
|
||||||
|
char id=60 x=37 y=362 width=31 height=35 xoffset=2 yoffset=39 xadvance=36 page=0 chnl=0
|
||||||
|
char id=61 x=160 y=362 width=31 height=23 xoffset=4 yoffset=40 xadvance=39 page=0 chnl=0
|
||||||
|
char id=62 x=68 y=362 width=32 height=35 xoffset=3 yoffset=39 xadvance=37 page=0 chnl=0
|
||||||
|
char id=63 x=480 y=98 width=31 height=55 xoffset=1 yoffset=22 xadvance=33 page=0 chnl=0
|
||||||
|
char id=64 x=247 y=0 width=60 height=68 xoffset=1 yoffset=25 xadvance=64 page=0 chnl=0
|
||||||
|
char id=65 x=426 y=156 width=51 height=54 xoffset=1 yoffset=23 xadvance=53 page=0 chnl=0
|
||||||
|
char id=66 x=0 y=212 width=44 height=54 xoffset=1 yoffset=23 xadvance=47 page=0 chnl=0
|
||||||
|
char id=67 x=191 y=98 width=42 height=56 xoffset=1 yoffset=22 xadvance=46 page=0 chnl=0
|
||||||
|
char id=68 x=44 y=212 width=46 height=54 xoffset=1 yoffset=23 xadvance=50 page=0 chnl=0
|
||||||
|
char id=69 x=90 y=212 width=42 height=54 xoffset=1 yoffset=23 xadvance=46 page=0 chnl=0
|
||||||
|
char id=70 x=132 y=212 width=42 height=54 xoffset=1 yoffset=23 xadvance=44 page=0 chnl=0
|
||||||
|
char id=71 x=233 y=98 width=43 height=56 xoffset=1 yoffset=22 xadvance=49 page=0 chnl=0
|
||||||
|
char id=72 x=174 y=212 width=52 height=54 xoffset=1 yoffset=23 xadvance=55 page=0 chnl=0
|
||||||
|
char id=73 x=477 y=156 width=20 height=54 xoffset=1 yoffset=23 xadvance=22 page=0 chnl=0
|
||||||
|
char id=74 x=266 y=156 width=39 height=55 xoffset=1 yoffset=23 xadvance=41 page=0 chnl=0
|
||||||
|
char id=75 x=226 y=212 width=48 height=54 xoffset=1 yoffset=23 xadvance=50 page=0 chnl=0
|
||||||
|
char id=76 x=274 y=212 width=39 height=54 xoffset=1 yoffset=23 xadvance=42 page=0 chnl=0
|
||||||
|
char id=77 x=313 y=212 width=64 height=54 xoffset=1 yoffset=23 xadvance=66 page=0 chnl=0
|
||||||
|
char id=78 x=377 y=212 width=52 height=54 xoffset=1 yoffset=23 xadvance=54 page=0 chnl=0
|
||||||
|
char id=79 x=276 y=98 width=47 height=56 xoffset=2 yoffset=22 xadvance=51 page=0 chnl=0
|
||||||
|
char id=80 x=429 y=212 width=43 height=54 xoffset=1 yoffset=23 xadvance=45 page=0 chnl=0
|
||||||
|
char id=81 x=307 y=0 width=48 height=64 xoffset=2 yoffset=22 xadvance=51 page=0 chnl=0
|
||||||
|
char id=82 x=0 y=266 width=46 height=54 xoffset=1 yoffset=23 xadvance=48 page=0 chnl=0
|
||||||
|
char id=83 x=323 y=98 width=38 height=56 xoffset=3 yoffset=22 xadvance=43 page=0 chnl=0
|
||||||
|
char id=84 x=46 y=266 width=45 height=54 xoffset=0 yoffset=23 xadvance=45 page=0 chnl=0
|
||||||
|
char id=85 x=305 y=156 width=52 height=55 xoffset=1 yoffset=23 xadvance=54 page=0 chnl=0
|
||||||
|
char id=86 x=91 y=266 width=50 height=54 xoffset=1 yoffset=23 xadvance=52 page=0 chnl=0
|
||||||
|
char id=87 x=141 y=266 width=67 height=54 xoffset=0 yoffset=23 xadvance=67 page=0 chnl=0
|
||||||
|
char id=88 x=208 y=266 width=49 height=54 xoffset=1 yoffset=23 xadvance=51 page=0 chnl=0
|
||||||
|
char id=89 x=257 y=266 width=48 height=54 xoffset=1 yoffset=23 xadvance=50 page=0 chnl=0
|
||||||
|
char id=90 x=472 y=212 width=38 height=54 xoffset=2 yoffset=23 xadvance=42 page=0 chnl=0
|
||||||
|
char id=91 x=180 y=0 width=16 height=72 xoffset=5 yoffset=16 xadvance=21 page=0 chnl=0
|
||||||
|
char id=92 x=0 y=98 width=31 height=58 xoffset=0 yoffset=23 xadvance=30 page=0 chnl=0
|
||||||
|
char id=93 x=196 y=0 width=16 height=72 xoffset=-1 yoffset=16 xadvance=19 page=0 chnl=0
|
||||||
|
char id=94 x=131 y=362 width=29 height=28 xoffset=1 yoffset=23 xadvance=30 page=0 chnl=0
|
||||||
|
char id=95 x=306 y=362 width=34 height=8 xoffset=3 yoffset=74 xadvance=40 page=0 chnl=0
|
||||||
|
char id=96 x=260 y=362 width=18 height=12 xoffset=1 yoffset=22 xadvance=20 page=0 chnl=0
|
||||||
|
char id=97 x=0 y=320 width=36 height=42 xoffset=3 yoffset=36 xadvance=41 page=0 chnl=0
|
||||||
|
char id=98 x=363 y=0 width=41 height=58 xoffset=-2 yoffset=20 xadvance=42 page=0 chnl=0
|
||||||
|
char id=99 x=36 y=320 width=34 height=42 xoffset=2 yoffset=36 xadvance=39 page=0 chnl=0
|
||||||
|
char id=100 x=404 y=0 width=40 height=58 xoffset=2 yoffset=20 xadvance=43 page=0 chnl=0
|
||||||
|
char id=101 x=70 y=320 width=34 height=42 xoffset=2 yoffset=36 xadvance=39 page=0 chnl=0
|
||||||
|
char id=102 x=444 y=0 width=26 height=58 xoffset=1 yoffset=19 xadvance=25 page=0 chnl=0
|
||||||
|
char id=103 x=31 y=98 width=34 height=57 xoffset=2 yoffset=36 xadvance=40 page=0 chnl=0
|
||||||
|
char id=104 x=65 y=98 width=44 height=57 xoffset=1 yoffset=20 xadvance=46 page=0 chnl=0
|
||||||
|
char id=105 x=109 y=98 width=20 height=57 xoffset=2 yoffset=20 xadvance=23 page=0 chnl=0
|
||||||
|
char id=106 x=112 y=0 width=18 height=73 xoffset=-2 yoffset=20 xadvance=20 page=0 chnl=0
|
||||||
|
char id=107 x=129 y=98 width=42 height=57 xoffset=1 yoffset=20 xadvance=44 page=0 chnl=0
|
||||||
|
char id=108 x=171 y=98 width=20 height=57 xoffset=1 yoffset=20 xadvance=22 page=0 chnl=0
|
||||||
|
char id=109 x=171 y=320 width=66 height=41 xoffset=1 yoffset=36 xadvance=68 page=0 chnl=0
|
||||||
|
char id=110 x=237 y=320 width=44 height=41 xoffset=1 yoffset=36 xadvance=46 page=0 chnl=0
|
||||||
|
char id=111 x=104 y=320 width=36 height=42 xoffset=2 yoffset=36 xadvance=40 page=0 chnl=0
|
||||||
|
char id=112 x=361 y=98 width=40 height=56 xoffset=1 yoffset=36 xadvance=43 page=0 chnl=0
|
||||||
|
char id=113 x=401 y=98 width=39 height=56 xoffset=2 yoffset=36 xadvance=40 page=0 chnl=0
|
||||||
|
char id=114 x=484 y=266 width=27 height=41 xoffset=2 yoffset=36 xadvance=30 page=0 chnl=0
|
||||||
|
char id=115 x=140 y=320 width=31 height=42 xoffset=3 yoffset=36 xadvance=36 page=0 chnl=0
|
||||||
|
char id=116 x=460 y=266 width=24 height=51 xoffset=1 yoffset=27 xadvance=26 page=0 chnl=0
|
||||||
|
char id=117 x=281 y=320 width=43 height=41 xoffset=0 yoffset=37 xadvance=44 page=0 chnl=0
|
||||||
|
char id=118 x=324 y=320 width=39 height=40 xoffset=0 yoffset=37 xadvance=40 page=0 chnl=0
|
||||||
|
char id=119 x=363 y=320 width=57 height=40 xoffset=1 yoffset=37 xadvance=59 page=0 chnl=0
|
||||||
|
char id=120 x=420 y=320 width=40 height=40 xoffset=1 yoffset=37 xadvance=42 page=0 chnl=0
|
||||||
|
char id=121 x=440 y=98 width=40 height=56 xoffset=0 yoffset=37 xadvance=41 page=0 chnl=0
|
||||||
|
char id=122 x=460 y=320 width=32 height=40 xoffset=3 yoffset=37 xadvance=38 page=0 chnl=0
|
||||||
|
char id=123 x=130 y=0 width=25 height=73 xoffset=1 yoffset=18 xadvance=25 page=0 chnl=0
|
||||||
|
char id=124 x=355 y=0 width=8 height=63 xoffset=4 yoffset=23 xadvance=16 page=0 chnl=0
|
||||||
|
char id=125 x=155 y=0 width=25 height=73 xoffset=-1 yoffset=18 xadvance=25 page=0 chnl=0
|
||||||
|
char id=126 x=218 y=362 width=42 height=16 xoffset=3 yoffset=47 xadvance=49 page=0 chnl=0
|
||||||
|
char id=127 x=0 y=0 width=70 height=98 xoffset=0 yoffset=-1 xadvance=70 page=0 chnl=0
|
||||||
|
kernings count=389
|
||||||
|
kerning first=86 second=45 amount=-1
|
||||||
|
kerning first=114 second=46 amount=-4
|
||||||
|
kerning first=40 second=87 amount=1
|
||||||
|
kerning first=70 second=99 amount=-1
|
||||||
|
kerning first=84 second=110 amount=-3
|
||||||
|
kerning first=114 second=116 amount=1
|
||||||
|
kerning first=39 second=65 amount=-4
|
||||||
|
kerning first=104 second=34 amount=-1
|
||||||
|
kerning first=89 second=71 amount=-1
|
||||||
|
kerning first=107 second=113 amount=-1
|
||||||
|
kerning first=78 second=88 amount=1
|
||||||
|
kerning first=109 second=39 amount=-1
|
||||||
|
kerning first=120 second=100 amount=-1
|
||||||
|
kerning first=84 second=100 amount=-3
|
||||||
|
kerning first=68 second=90 amount=-1
|
||||||
|
kerning first=68 second=44 amount=-4
|
||||||
|
kerning first=84 second=103 amount=-3
|
||||||
|
kerning first=34 second=97 amount=-2
|
||||||
|
kerning first=70 second=97 amount=-1
|
||||||
|
kerning first=76 second=81 amount=-2
|
||||||
|
kerning first=73 second=89 amount=-1
|
||||||
|
kerning first=84 second=44 amount=-8
|
||||||
|
kerning first=68 second=65 amount=-3
|
||||||
|
kerning first=97 second=34 amount=-2
|
||||||
|
kerning first=111 second=121 amount=-1
|
||||||
|
kerning first=79 second=90 amount=-1
|
||||||
|
kerning first=75 second=121 amount=-1
|
||||||
|
kerning first=75 second=118 amount=-1
|
||||||
|
kerning first=111 second=118 amount=-1
|
||||||
|
kerning first=89 second=65 amount=-9
|
||||||
|
kerning first=75 second=71 amount=-4
|
||||||
|
kerning first=39 second=99 amount=-2
|
||||||
|
kerning first=75 second=99 amount=-1
|
||||||
|
kerning first=90 second=121 amount=-1
|
||||||
|
kerning first=44 second=39 amount=-6
|
||||||
|
kerning first=89 second=46 amount=-7
|
||||||
|
kerning first=89 second=74 amount=-7
|
||||||
|
kerning first=34 second=103 amount=-2
|
||||||
|
kerning first=70 second=103 amount=-1
|
||||||
|
kerning first=112 second=39 amount=-1
|
||||||
|
kerning first=122 second=113 amount=-1
|
||||||
|
kerning first=86 second=113 amount=-2
|
||||||
|
kerning first=68 second=84 amount=-1
|
||||||
|
kerning first=89 second=110 amount=-1
|
||||||
|
kerning first=34 second=100 amount=-2
|
||||||
|
kerning first=68 second=86 amount=-1
|
||||||
|
kerning first=87 second=45 amount=-2
|
||||||
|
kerning first=39 second=34 amount=-4
|
||||||
|
kerning first=114 second=100 amount=-1
|
||||||
|
kerning first=84 second=81 amount=-1
|
||||||
|
kerning first=70 second=101 amount=-1
|
||||||
|
kerning first=68 second=89 amount=-2
|
||||||
|
kerning first=88 second=117 amount=-1
|
||||||
|
kerning first=112 second=34 amount=-1
|
||||||
|
kerning first=76 second=67 amount=-2
|
||||||
|
kerning first=76 second=34 amount=-5
|
||||||
|
kerning first=88 second=111 amount=-1
|
||||||
|
kerning first=66 second=86 amount=-1
|
||||||
|
kerning first=66 second=89 amount=-2
|
||||||
|
kerning first=122 second=101 amount=-1
|
||||||
|
kerning first=86 second=101 amount=-2
|
||||||
|
kerning first=76 second=121 amount=-5
|
||||||
|
kerning first=84 second=119 amount=-2
|
||||||
|
kerning first=84 second=112 amount=-3
|
||||||
|
kerning first=87 second=111 amount=-1
|
||||||
|
kerning first=69 second=118 amount=-1
|
||||||
|
kerning first=65 second=117 amount=-2
|
||||||
|
kerning first=65 second=89 amount=-9
|
||||||
|
kerning first=72 second=89 amount=-1
|
||||||
|
kerning first=119 second=44 amount=-4
|
||||||
|
kerning first=69 second=121 amount=-1
|
||||||
|
kerning first=84 second=109 amount=-3
|
||||||
|
kerning first=84 second=122 amount=-2
|
||||||
|
kerning first=89 second=99 amount=-2
|
||||||
|
kerning first=76 second=118 amount=-5
|
||||||
|
kerning first=90 second=99 amount=-1
|
||||||
|
kerning first=90 second=103 amount=-1
|
||||||
|
kerning first=79 second=89 amount=-2
|
||||||
|
kerning first=90 second=79 amount=-1
|
||||||
|
kerning first=84 second=115 amount=-4
|
||||||
|
kerning first=76 second=65 amount=1
|
||||||
|
kerning first=90 second=100 amount=-1
|
||||||
|
kerning first=118 second=46 amount=-4
|
||||||
|
kerning first=87 second=117 amount=-1
|
||||||
|
kerning first=118 second=34 amount=1
|
||||||
|
kerning first=69 second=103 amount=-1
|
||||||
|
kerning first=97 second=121 amount=-1
|
||||||
|
kerning first=39 second=111 amount=-2
|
||||||
|
kerning first=72 second=88 amount=1
|
||||||
|
kerning first=76 second=87 amount=-5
|
||||||
|
kerning first=69 second=119 amount=-1
|
||||||
|
kerning first=121 second=97 amount=-1
|
||||||
|
kerning first=75 second=45 amount=-8
|
||||||
|
kerning first=65 second=86 amount=-9
|
||||||
|
kerning first=46 second=34 amount=-6
|
||||||
|
kerning first=76 second=84 amount=-10
|
||||||
|
kerning first=116 second=111 amount=-1
|
||||||
|
kerning first=87 second=113 amount=-1
|
||||||
|
kerning first=69 second=100 amount=-1
|
||||||
|
kerning first=97 second=118 amount=-1
|
||||||
|
kerning first=65 second=85 amount=-2
|
||||||
|
kerning first=90 second=71 amount=-1
|
||||||
|
kerning first=68 second=46 amount=-4
|
||||||
|
kerning first=65 second=79 amount=-3
|
||||||
|
kerning first=98 second=122 amount=-1
|
||||||
|
kerning first=86 second=41 amount=1
|
||||||
|
kerning first=84 second=118 amount=-3
|
||||||
|
kerning first=70 second=118 amount=-1
|
||||||
|
kerning first=121 second=111 amount=-1
|
||||||
|
kerning first=81 second=87 amount=-1
|
||||||
|
kerning first=70 second=100 amount=-1
|
||||||
|
kerning first=102 second=93 amount=1
|
||||||
|
kerning first=114 second=101 amount=-1
|
||||||
|
kerning first=88 second=45 amount=-2
|
||||||
|
kerning first=39 second=103 amount=-2
|
||||||
|
kerning first=75 second=103 amount=-1
|
||||||
|
kerning first=88 second=101 amount=-1
|
||||||
|
kerning first=89 second=103 amount=-2
|
||||||
|
kerning first=110 second=39 amount=-1
|
||||||
|
kerning first=89 second=89 amount=1
|
||||||
|
kerning first=87 second=65 amount=-2
|
||||||
|
kerning first=119 second=46 amount=-4
|
||||||
|
kerning first=34 second=34 amount=-4
|
||||||
|
kerning first=88 second=79 amount=-2
|
||||||
|
kerning first=79 second=86 amount=-1
|
||||||
|
kerning first=76 second=119 amount=-3
|
||||||
|
kerning first=75 second=111 amount=-1
|
||||||
|
kerning first=65 second=116 amount=-4
|
||||||
|
kerning first=86 second=65 amount=-9
|
||||||
|
kerning first=70 second=84 amount=1
|
||||||
|
kerning first=75 second=117 amount=-1
|
||||||
|
kerning first=80 second=65 amount=-9
|
||||||
|
kerning first=34 second=112 amount=-1
|
||||||
|
kerning first=102 second=99 amount=-1
|
||||||
|
kerning first=118 second=97 amount=-1
|
||||||
|
kerning first=89 second=81 amount=-1
|
||||||
|
kerning first=118 second=111 amount=-1
|
||||||
|
kerning first=102 second=101 amount=-1
|
||||||
|
kerning first=114 second=44 amount=-4
|
||||||
|
kerning first=90 second=119 amount=-1
|
||||||
|
kerning first=75 second=81 amount=-4
|
||||||
|
kerning first=88 second=121 amount=-1
|
||||||
|
kerning first=34 second=110 amount=-1
|
||||||
|
kerning first=86 second=100 amount=-2
|
||||||
|
kerning first=122 second=100 amount=-1
|
||||||
|
kerning first=89 second=67 amount=-1
|
||||||
|
kerning first=90 second=118 amount=-1
|
||||||
|
kerning first=84 second=84 amount=1
|
||||||
|
kerning first=121 second=34 amount=1
|
||||||
|
kerning first=91 second=74 amount=-1
|
||||||
|
kerning first=88 second=113 amount=-1
|
||||||
|
kerning first=77 second=88 amount=1
|
||||||
|
kerning first=75 second=119 amount=-2
|
||||||
|
kerning first=114 second=104 amount=-1
|
||||||
|
kerning first=68 second=88 amount=-2
|
||||||
|
kerning first=121 second=44 amount=-4
|
||||||
|
kerning first=81 second=89 amount=-1
|
||||||
|
kerning first=102 second=39 amount=1
|
||||||
|
kerning first=74 second=65 amount=-2
|
||||||
|
kerning first=114 second=118 amount=1
|
||||||
|
kerning first=84 second=46 amount=-8
|
||||||
|
kerning first=111 second=34 amount=-1
|
||||||
|
kerning first=88 second=71 amount=-2
|
||||||
|
kerning first=88 second=99 amount=-1
|
||||||
|
kerning first=84 second=74 amount=-8
|
||||||
|
kerning first=39 second=109 amount=-1
|
||||||
|
kerning first=98 second=34 amount=-1
|
||||||
|
kerning first=86 second=114 amount=-1
|
||||||
|
kerning first=88 second=81 amount=-2
|
||||||
|
kerning first=70 second=74 amount=-11
|
||||||
|
kerning first=89 second=83 amount=-1
|
||||||
|
kerning first=87 second=41 amount=1
|
||||||
|
kerning first=89 second=97 amount=-3
|
||||||
|
kerning first=89 second=87 amount=1
|
||||||
|
kerning first=67 second=125 amount=-1
|
||||||
|
kerning first=89 second=93 amount=1
|
||||||
|
kerning first=80 second=118 amount=1
|
||||||
|
kerning first=107 second=100 amount=-1
|
||||||
|
kerning first=114 second=34 amount=1
|
||||||
|
kerning first=89 second=109 amount=-1
|
||||||
|
kerning first=89 second=45 amount=-2
|
||||||
|
kerning first=70 second=44 amount=-8
|
||||||
|
kerning first=34 second=39 amount=-4
|
||||||
|
kerning first=88 second=67 amount=-2
|
||||||
|
kerning first=70 second=46 amount=-8
|
||||||
|
kerning first=102 second=41 amount=1
|
||||||
|
kerning first=89 second=117 amount=-1
|
||||||
|
kerning first=89 second=111 amount=-4
|
||||||
|
kerning first=89 second=115 amount=-4
|
||||||
|
kerning first=114 second=102 amount=1
|
||||||
|
kerning first=89 second=125 amount=1
|
||||||
|
kerning first=89 second=121 amount=-1
|
||||||
|
kerning first=114 second=108 amount=-1
|
||||||
|
kerning first=47 second=47 amount=-8
|
||||||
|
kerning first=65 second=63 amount=-2
|
||||||
|
kerning first=75 second=67 amount=-4
|
||||||
|
kerning first=87 second=100 amount=-1
|
||||||
|
kerning first=111 second=104 amount=-1
|
||||||
|
kerning first=111 second=107 amount=-1
|
||||||
|
kerning first=75 second=109 amount=-1
|
||||||
|
kerning first=87 second=114 amount=-1
|
||||||
|
kerning first=111 second=120 amount=-1
|
||||||
|
kerning first=69 second=99 amount=-1
|
||||||
|
kerning first=65 second=84 amount=-6
|
||||||
|
kerning first=39 second=97 amount=-2
|
||||||
|
kerning first=121 second=46 amount=-4
|
||||||
|
kerning first=89 second=85 amount=-3
|
||||||
|
kerning first=75 second=79 amount=-4
|
||||||
|
kerning first=107 second=99 amount=-1
|
||||||
|
kerning first=102 second=100 amount=-1
|
||||||
|
kerning first=102 second=103 amount=-1
|
||||||
|
kerning first=75 second=110 amount=-1
|
||||||
|
kerning first=39 second=110 amount=-1
|
||||||
|
kerning first=69 second=84 amount=1
|
||||||
|
kerning first=84 second=111 amount=-3
|
||||||
|
kerning first=120 second=111 amount=-1
|
||||||
|
kerning first=84 second=114 amount=-3
|
||||||
|
kerning first=112 second=120 amount=-1
|
||||||
|
kerning first=79 second=84 amount=-1
|
||||||
|
kerning first=84 second=117 amount=-3
|
||||||
|
kerning first=89 second=79 amount=-1
|
||||||
|
kerning first=75 second=113 amount=-1
|
||||||
|
kerning first=39 second=113 amount=-2
|
||||||
|
kerning first=80 second=44 amount=-11
|
||||||
|
kerning first=79 second=88 amount=-2
|
||||||
|
kerning first=98 second=39 amount=-1
|
||||||
|
kerning first=65 second=118 amount=-4
|
||||||
|
kerning first=65 second=34 amount=-4
|
||||||
|
kerning first=88 second=103 amount=-1
|
||||||
|
kerning first=77 second=89 amount=-1
|
||||||
|
kerning first=39 second=101 amount=-2
|
||||||
|
kerning first=75 second=101 amount=-1
|
||||||
|
kerning first=88 second=100 amount=-1
|
||||||
|
kerning first=78 second=65 amount=-3
|
||||||
|
kerning first=87 second=44 amount=-4
|
||||||
|
kerning first=67 second=41 amount=-1
|
||||||
|
kerning first=86 second=93 amount=1
|
||||||
|
kerning first=84 second=83 amount=-1
|
||||||
|
kerning first=102 second=113 amount=-1
|
||||||
|
kerning first=34 second=111 amount=-2
|
||||||
|
kerning first=70 second=111 amount=-1
|
||||||
|
kerning first=86 second=99 amount=-2
|
||||||
|
kerning first=84 second=86 amount=1
|
||||||
|
kerning first=122 second=99 amount=-1
|
||||||
|
kerning first=84 second=89 amount=1
|
||||||
|
kerning first=70 second=114 amount=-1
|
||||||
|
kerning first=86 second=74 amount=-8
|
||||||
|
kerning first=89 second=38 amount=-1
|
||||||
|
kerning first=87 second=97 amount=-1
|
||||||
|
kerning first=76 second=86 amount=-9
|
||||||
|
kerning first=40 second=86 amount=1
|
||||||
|
kerning first=90 second=113 amount=-1
|
||||||
|
kerning first=39 second=39 amount=-4
|
||||||
|
kerning first=111 second=39 amount=-1
|
||||||
|
kerning first=90 second=117 amount=-1
|
||||||
|
kerning first=89 second=41 amount=1
|
||||||
|
kerning first=65 second=121 amount=-4
|
||||||
|
kerning first=89 second=100 amount=-2
|
||||||
|
kerning first=89 second=42 amount=-2
|
||||||
|
kerning first=76 second=117 amount=-2
|
||||||
|
kerning first=69 second=111 amount=-1
|
||||||
|
kerning first=46 second=39 amount=-6
|
||||||
|
kerning first=118 second=39 amount=1
|
||||||
|
kerning first=91 second=85 amount=-1
|
||||||
|
kerning first=80 second=90 amount=-1
|
||||||
|
kerning first=90 second=81 amount=-1
|
||||||
|
kerning first=69 second=117 amount=-1
|
||||||
|
kerning first=76 second=39 amount=-5
|
||||||
|
kerning first=90 second=67 amount=-1
|
||||||
|
kerning first=87 second=103 amount=-1
|
||||||
|
kerning first=84 second=120 amount=-3
|
||||||
|
kerning first=89 second=101 amount=-2
|
||||||
|
kerning first=102 second=125 amount=1
|
||||||
|
kerning first=76 second=85 amount=-2
|
||||||
|
kerning first=79 second=65 amount=-3
|
||||||
|
kerning first=65 second=71 amount=-3
|
||||||
|
kerning first=79 second=44 amount=-4
|
||||||
|
kerning first=97 second=39 amount=-2
|
||||||
|
kerning first=90 second=101 amount=-1
|
||||||
|
kerning first=65 second=87 amount=-5
|
||||||
|
kerning first=79 second=46 amount=-4
|
||||||
|
kerning first=87 second=99 amount=-1
|
||||||
|
kerning first=34 second=101 amount=-2
|
||||||
|
kerning first=40 second=89 amount=1
|
||||||
|
kerning first=76 second=89 amount=-8
|
||||||
|
kerning first=69 second=113 amount=-1
|
||||||
|
kerning first=120 second=103 amount=-1
|
||||||
|
kerning first=69 second=101 amount=-1
|
||||||
|
kerning first=69 second=102 amount=-1
|
||||||
|
kerning first=104 second=39 amount=-1
|
||||||
|
kerning first=80 second=121 amount=1
|
||||||
|
kerning first=86 second=46 amount=-8
|
||||||
|
kerning first=65 second=81 amount=-3
|
||||||
|
kerning first=86 second=44 amount=-8
|
||||||
|
kerning first=120 second=99 amount=-1
|
||||||
|
kerning first=98 second=120 amount=-1
|
||||||
|
kerning first=39 second=115 amount=-3
|
||||||
|
kerning first=121 second=39 amount=1
|
||||||
|
kerning first=88 second=118 amount=-1
|
||||||
|
kerning first=84 second=65 amount=-6
|
||||||
|
kerning first=65 second=39 amount=-4
|
||||||
|
kerning first=84 second=79 amount=-1
|
||||||
|
kerning first=65 second=119 amount=-4
|
||||||
|
kerning first=70 second=117 amount=-1
|
||||||
|
kerning first=75 second=100 amount=-1
|
||||||
|
kerning first=86 second=111 amount=-2
|
||||||
|
kerning first=122 second=111 amount=-1
|
||||||
|
kerning first=81 second=84 amount=-2
|
||||||
|
kerning first=107 second=103 amount=-1
|
||||||
|
kerning first=118 second=44 amount=-4
|
||||||
|
kerning first=87 second=46 amount=-4
|
||||||
|
kerning first=87 second=101 amount=-1
|
||||||
|
kerning first=70 second=79 amount=-2
|
||||||
|
kerning first=87 second=74 amount=-2
|
||||||
|
kerning first=123 second=74 amount=-1
|
||||||
|
kerning first=76 second=71 amount=-2
|
||||||
|
kerning first=39 second=100 amount=-2
|
||||||
|
kerning first=80 second=88 amount=-1
|
||||||
|
kerning first=84 second=121 amount=-3
|
||||||
|
kerning first=112 second=122 amount=-1
|
||||||
|
kerning first=84 second=71 amount=-1
|
||||||
|
kerning first=89 second=86 amount=1
|
||||||
|
kerning first=84 second=113 amount=-3
|
||||||
|
kerning first=120 second=113 amount=-1
|
||||||
|
kerning first=89 second=44 amount=-7
|
||||||
|
kerning first=84 second=99 amount=-3
|
||||||
|
kerning first=34 second=113 amount=-2
|
||||||
|
kerning first=80 second=46 amount=-11
|
||||||
|
kerning first=86 second=117 amount=-1
|
||||||
|
kerning first=110 second=34 amount=-1
|
||||||
|
kerning first=80 second=74 amount=-7
|
||||||
|
kerning first=120 second=101 amount=-1
|
||||||
|
kerning first=73 second=88 amount=1
|
||||||
|
kerning first=108 second=111 amount=-1
|
||||||
|
kerning first=34 second=115 amount=-3
|
||||||
|
kerning first=89 second=113 amount=-2
|
||||||
|
kerning first=82 second=86 amount=-3
|
||||||
|
kerning first=114 second=39 amount=1
|
||||||
|
kerning first=34 second=109 amount=-1
|
||||||
|
kerning first=84 second=101 amount=-3
|
||||||
|
kerning first=70 second=121 amount=-1
|
||||||
|
kerning first=123 second=85 amount=-1
|
||||||
|
kerning first=122 second=103 amount=-1
|
||||||
|
kerning first=86 second=97 amount=-2
|
||||||
|
kerning first=82 second=89 amount=-4
|
||||||
|
kerning first=66 second=84 amount=-1
|
||||||
|
kerning first=84 second=97 amount=-4
|
||||||
|
kerning first=86 second=103 amount=-2
|
||||||
|
kerning first=70 second=113 amount=-1
|
||||||
|
kerning first=84 second=87 amount=1
|
||||||
|
kerning first=75 second=112 amount=-1
|
||||||
|
kerning first=114 second=111 amount=-1
|
||||||
|
kerning first=39 second=112 amount=-1
|
||||||
|
kerning first=107 second=101 amount=-1
|
||||||
|
kerning first=82 second=84 amount=-3
|
||||||
|
kerning first=114 second=121 amount=1
|
||||||
|
kerning first=34 second=99 amount=-2
|
||||||
|
kerning first=70 second=81 amount=-2
|
||||||
|
kerning first=111 second=122 amount=-1
|
||||||
|
kerning first=84 second=67 amount=-1
|
||||||
|
kerning first=111 second=108 amount=-1
|
||||||
|
kerning first=89 second=84 amount=1
|
||||||
|
kerning first=76 second=79 amount=-2
|
||||||
|
kerning first=85 second=65 amount=-2
|
||||||
|
kerning first=44 second=34 amount=-6
|
||||||
|
kerning first=65 second=67 amount=-3
|
||||||
|
kerning first=109 second=34 amount=-1
|
||||||
|
kerning first=114 second=103 amount=-1
|
||||||
|
kerning first=78 second=89 amount=-1
|
||||||
|
kerning first=89 second=114 amount=-1
|
||||||
|
kerning first=89 second=112 amount=-1
|
||||||
|
kerning first=34 second=65 amount=-4
|
||||||
|
kerning first=70 second=65 amount=-11
|
||||||
|
kerning first=81 second=86 amount=-1
|
||||||
|
kerning first=114 second=119 amount=1
|
||||||
|
kerning first=89 second=102 amount=-1
|
||||||
|
kerning first=84 second=45 amount=-8
|
||||||
|
kerning first=86 second=125 amount=1
|
||||||
|
kerning first=70 second=67 amount=-2
|
||||||
|
kerning first=89 second=116 amount=-1
|
||||||
|
kerning first=102 second=34 amount=1
|
||||||
|
kerning first=114 second=99 amount=-1
|
||||||
|
kerning first=67 second=84 amount=-1
|
||||||
|
kerning first=114 second=113 amount=-1
|
||||||
|
kerning first=89 second=122 amount=-1
|
||||||
|
kerning first=89 second=118 amount=-1
|
||||||
|
kerning first=70 second=71 amount=-2
|
||||||
|
kerning first=114 second=107 amount=-1
|
||||||
|
kerning first=89 second=120 amount=-1
|
BIN
src/web/static/fonts/bmfonts/RobotoSlab72White.png
Normal file
BIN
src/web/static/fonts/bmfonts/RobotoSlab72White.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
File diff suppressed because one or more lines are too long
|
@ -248,7 +248,8 @@ IOE1W/Zqmqzq+4frwnzWwYv9/U1RwIs/qlFVnzliREOzW+om8EncSSd7fQ==
|
||||||
=fEAT
|
=fEAT
|
||||||
-----END PGP MESSAGE-----
|
-----END PGP MESSAGE-----
|
||||||
`,
|
`,
|
||||||
expectedOutput: `Signed by PGP fingerprint: e94e06dd0b3744a0e970de9d84246548df98e485
|
expectedOutput: `Signed by PGP key ID: DF98E485
|
||||||
|
PGP fingerprint: e94e06dd0b3744a0e970de9d84246548df98e485
|
||||||
Signed on Tue, 29 May 2018 15:44:52 GMT
|
Signed on Tue, 29 May 2018 15:44:52 GMT
|
||||||
----------------------------------
|
----------------------------------
|
||||||
${UTF8_TEXT}`,
|
${UTF8_TEXT}`,
|
||||||
|
@ -282,4 +283,30 @@ H2qMY1O7hezH3fp+EZzCAccJMtK7VPk13WAgMRH22HirG4aK1i75IVOtjBgObzDh
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "PGP Verify: ASCII, Alice",
|
||||||
|
input: `-----BEGIN PGP SIGNED MESSAGE-----
|
||||||
|
Hash: SHA256
|
||||||
|
|
||||||
|
A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools.
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
iLMEAQEIAB0WIQRLbJy6MLpYOr9qojE+2VNAUiMLOgUCXRTsvwAKCRA+2VNAUiML
|
||||||
|
OuaHBADMMNtsuN92Fb+UrDimsv6TDQpbJhDkwp9kZdKYP5HAmSYAhXBG7N+YCMw+
|
||||||
|
v2FSpUu9jJiPBm1K1SEwLufQVexoRv6RsBNolRFB07sArau0s0DnIXUchCZWvyTP
|
||||||
|
1KsjBnDr84U2b11H58g4DlTT4gQrz30rFuHz9AGmPAtDHbSXIA==
|
||||||
|
=vnk/
|
||||||
|
-----END PGP SIGNATURE-----`,
|
||||||
|
expectedOutput: `Signed by PGP key ID: DF98E485
|
||||||
|
PGP fingerprint: e94e06dd0b3744a0e970de9d84246548df98e485
|
||||||
|
Signed on Thu, 27 Jun 2019 16:20:15 GMT
|
||||||
|
----------------------------------
|
||||||
|
A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools.`,
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "PGP Verify",
|
||||||
|
"args": [ALICE_PUBLIC]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -60,7 +60,7 @@ module.exports = {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.m?js$/,
|
test: /\.m?js$/,
|
||||||
exclude: /node_modules\/(?!jsesc|crypto-api)/,
|
exclude: /node_modules\/(?!jsesc|crypto-api|bootstrap)/,
|
||||||
options: {
|
options: {
|
||||||
configFile: path.resolve(__dirname, "babel.config.js"),
|
configFile: path.resolve(__dirname, "babel.config.js"),
|
||||||
cacheDirectory: true,
|
cacheDirectory: true,
|
||||||
|
@ -119,9 +119,17 @@ module.exports = {
|
||||||
encoding: "base64"
|
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
|
{ // First party images are saved as files to be cached
|
||||||
test: /\.(png|jpg|gif)$/,
|
test: /\.(png|jpg|gif)$/,
|
||||||
exclude: /node_modules/,
|
exclude: /(node_modules|bmfonts)/,
|
||||||
loader: "file-loader",
|
loader: "file-loader",
|
||||||
options: {
|
options: {
|
||||||
name: "images/[name].[ext]"
|
name: "images/[name].[ext]"
|
||||||
|
|
Loading…
Reference in a new issue