cyberchef 10.22.1 → 10.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/CHANGELOG.md +182 -0
  2. package/CONTRIBUTING.md +40 -0
  3. package/Dockerfile +2 -0
  4. package/Gruntfile.js +2 -28
  5. package/README.md +1 -1
  6. package/babel.config.js +0 -6
  7. package/package.json +64 -63
  8. package/src/core/Chef.mjs +8 -1
  9. package/src/core/Ingredient.mjs +5 -2
  10. package/src/core/Operation.mjs +6 -1
  11. package/src/core/Recipe.mjs +10 -5
  12. package/src/core/config/Categories.json +20 -4
  13. package/src/core/config/OperationConfig.json +550 -26
  14. package/src/core/config/modules/Ciphers.mjs +6 -0
  15. package/src/core/config/modules/Crypto.mjs +6 -0
  16. package/src/core/config/modules/Default.mjs +8 -0
  17. package/src/core/config/modules/Shellcode.mjs +2 -0
  18. package/src/core/config/scripts/fixCryptoApiImports.mjs +54 -0
  19. package/src/core/config/scripts/fixSnackBarMarkup.mjs +28 -0
  20. package/src/core/lib/AudioBytes.mjs +103 -0
  21. package/src/core/lib/AudioMetaSchema.mjs +82 -0
  22. package/src/core/lib/AudioParsers.mjs +630 -0
  23. package/src/core/lib/BigIntUtils.mjs +73 -0
  24. package/src/core/lib/Extract.mjs +5 -0
  25. package/src/core/lib/Modhex.mjs +2 -0
  26. package/src/core/lib/ParityBit.mjs +50 -0
  27. package/src/core/lib/QRCode.mjs +30 -10
  28. package/src/core/lib/RC6.mjs +625 -0
  29. package/src/core/operations/A1Z26CipherDecode.mjs +1 -1
  30. package/src/core/operations/AddTextToImage.mjs +116 -64
  31. package/src/core/operations/AnalyseUUID.mjs +109 -6
  32. package/src/core/operations/BlurImage.mjs +10 -12
  33. package/src/core/operations/ContainImage.mjs +50 -40
  34. package/src/core/operations/ConvertImageFormat.mjs +33 -39
  35. package/src/core/operations/CoverImage.mjs +39 -37
  36. package/src/core/operations/CropImage.mjs +35 -21
  37. package/src/core/operations/DisassembleARM.mjs +193 -0
  38. package/src/core/operations/DitherImage.mjs +8 -8
  39. package/src/core/operations/EscapeUnicodeCharacters.mjs +0 -17
  40. package/src/core/operations/ExtractAudioMetadata.mjs +175 -0
  41. package/src/core/operations/ExtractEmailAddresses.mjs +2 -3
  42. package/src/core/operations/ExtractLSB.mjs +17 -11
  43. package/src/core/operations/ExtractRGBA.mjs +12 -10
  44. package/src/core/operations/FlaskSessionDecode.mjs +80 -0
  45. package/src/core/operations/FlaskSessionSign.mjs +89 -0
  46. package/src/core/operations/FlaskSessionVerify.mjs +136 -0
  47. package/src/core/operations/FlipImage.mjs +14 -10
  48. package/src/core/operations/GenerateImage.mjs +39 -32
  49. package/src/core/operations/ImageBrightnessContrast.mjs +10 -10
  50. package/src/core/operations/ImageFilter.mjs +14 -13
  51. package/src/core/operations/ImageHueSaturationLightness.mjs +22 -20
  52. package/src/core/operations/ImageOpacity.mjs +6 -8
  53. package/src/core/operations/InvertImage.mjs +4 -6
  54. package/src/core/operations/Jq.mjs +12 -4
  55. package/src/core/operations/NormaliseImage.mjs +5 -7
  56. package/src/core/operations/OffsetChecker.mjs +1 -1
  57. package/src/core/operations/ParityBit.mjs +128 -0
  58. package/src/core/operations/ParseEthernetFrame.mjs +112 -0
  59. package/src/core/operations/ParseIPv4Header.mjs +23 -6
  60. package/src/core/operations/ParseQRCode.mjs +13 -13
  61. package/src/core/operations/PseudoRandomIntegerGenerator.mjs +164 -0
  62. package/src/core/operations/RC6Decrypt.mjs +119 -0
  63. package/src/core/operations/RC6Encrypt.mjs +119 -0
  64. package/src/core/operations/RandomizeColourPalette.mjs +11 -11
  65. package/src/core/operations/RegularExpression.mjs +2 -1
  66. package/src/core/operations/RenderMarkdown.mjs +35 -4
  67. package/src/core/operations/ResizeImage.mjs +30 -23
  68. package/src/core/operations/RotateImage.mjs +8 -9
  69. package/src/core/operations/SQLBeautify.mjs +21 -3
  70. package/src/core/operations/SharpenImage.mjs +94 -62
  71. package/src/core/operations/SplitColourChannels.mjs +47 -21
  72. package/src/core/operations/TextIntegerConverter.mjs +123 -0
  73. package/src/core/operations/UnescapeUnicodeCharacters.mjs +17 -0
  74. package/src/core/operations/ViewBitPlane.mjs +16 -20
  75. package/src/core/operations/index.mjs +22 -0
  76. package/src/node/index.mjs +55 -0
  77. package/src/web/HTMLIngredient.mjs +24 -43
  78. package/src/web/HTMLOperation.mjs +8 -2
  79. package/src/web/Manager.mjs +1 -0
  80. package/src/web/html/index.html +6 -6
  81. package/src/web/static/fonts/bmfonts/Roboto72White.fnt +491 -485
  82. package/src/web/static/fonts/bmfonts/RobotoBlack72White.fnt +494 -488
  83. package/src/web/static/fonts/bmfonts/RobotoMono72White.fnt +110 -103
  84. package/src/web/static/fonts/bmfonts/RobotoSlab72White.fnt +498 -492
  85. package/src/web/stylesheets/layout/_banner.css +30 -0
  86. package/src/web/stylesheets/layout/_modals.css +5 -0
  87. package/src/web/stylesheets/utils/_overrides.css +7 -0
  88. package/src/web/waiters/ControlsWaiter.mjs +82 -0
  89. package/src/web/waiters/InputWaiter.mjs +12 -6
  90. package/src/web/waiters/OperationsWaiter.mjs +30 -13
  91. package/src/web/waiters/RecipeWaiter.mjs +2 -2
  92. package/tests/browser/02_ops.js +23 -3
  93. package/tests/node/index.mjs +1 -0
  94. package/tests/node/tests/lib/BigIntUtils.mjs +150 -0
  95. package/tests/node/tests/operations.mjs +11 -10
  96. package/tests/operations/index.mjs +13 -0
  97. package/tests/operations/tests/A1Z26CipherDecode.mjs +33 -0
  98. package/tests/operations/tests/AnalyseUUID.mjs +66 -0
  99. package/tests/operations/tests/DisassembleARM.mjs +377 -0
  100. package/tests/operations/tests/ExtractAudioMetadata.mjs +287 -0
  101. package/tests/operations/tests/ExtractEmailAddresses.mjs +38 -12
  102. package/tests/operations/tests/Fernet.mjs +18 -3
  103. package/tests/operations/tests/FlaskSession.mjs +246 -0
  104. package/tests/operations/tests/GenerateQRCode.mjs +67 -0
  105. package/tests/operations/tests/JWTSign.mjs +83 -8
  106. package/tests/operations/tests/Jq.mjs +32 -0
  107. package/tests/operations/tests/Modhex.mjs +20 -0
  108. package/tests/operations/tests/ParityBit.mjs +147 -0
  109. package/tests/operations/tests/ParseEthernetFrame.mjs +45 -0
  110. package/tests/operations/tests/RC6.mjs +487 -0
  111. package/tests/operations/tests/RegularExpression.mjs +75 -0
  112. package/tests/operations/tests/RenderMarkdown.mjs +110 -0
  113. package/tests/operations/tests/SQLBeautify.mjs +54 -0
  114. package/tests/operations/tests/TextIntegerConverter.mjs +199 -0
  115. package/tests/samples/Audio.mjs +73 -0
  116. package/tests/samples/Images.mjs +0 -12
  117. package/webpack.config.js +10 -7
  118. package/src/core/lib/ImageManipulation.mjs +0 -251
@@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
9
9
  import { isImage } from "../lib/FileType.mjs";
10
10
  import { toBase64 } from "../lib/Base64.mjs";
11
11
  import { isWorkerEnvironment } from "../Utils.mjs";
12
- import Jimp from "jimp/es/index.js";
12
+ import { Jimp, JimpMime, ResizeStrategy } from "jimp";
13
13
 
14
14
  /**
15
15
  * Resize Image operation
16
16
  */
17
17
  class ResizeImage extends Operation {
18
-
19
18
  /**
20
19
  * ResizeImage constructor
21
20
  */
@@ -24,7 +23,8 @@ class ResizeImage extends Operation {
24
23
 
25
24
  this.name = "Resize Image";
26
25
  this.module = "Image";
27
- this.description = "Resizes an image to the specified width and height values.";
26
+ this.description =
27
+ "Resizes an image to the specified width and height values.";
28
28
  this.infoURL = "https://wikipedia.org/wiki/Image_scaling";
29
29
  this.inputType = "ArrayBuffer";
30
30
  this.outputType = "ArrayBuffer";
@@ -34,23 +34,23 @@ class ResizeImage extends Operation {
34
34
  name: "Width",
35
35
  type: "number",
36
36
  value: 100,
37
- min: 1
37
+ min: 1,
38
38
  },
39
39
  {
40
40
  name: "Height",
41
41
  type: "number",
42
42
  value: 100,
43
- min: 1
43
+ min: 1,
44
44
  },
45
45
  {
46
46
  name: "Unit type",
47
47
  type: "option",
48
- value: ["Pixels", "Percent"]
48
+ value: ["Pixels", "Percent"],
49
49
  },
50
50
  {
51
51
  name: "Maintain aspect ratio",
52
52
  type: "boolean",
53
- value: false
53
+ value: false,
54
54
  },
55
55
  {
56
56
  name: "Resizing algorithm",
@@ -60,10 +60,10 @@ class ResizeImage extends Operation {
60
60
  "Bilinear",
61
61
  "Bicubic",
62
62
  "Hermite",
63
- "Bezier"
63
+ "Bezier",
64
64
  ],
65
- defaultIndex: 1
66
- }
65
+ defaultIndex: 1,
66
+ },
67
67
  ];
68
68
  }
69
69
 
@@ -80,11 +80,11 @@ class ResizeImage extends Operation {
80
80
  resizeAlg = args[4];
81
81
 
82
82
  const resizeMap = {
83
- "Nearest Neighbour": Jimp.RESIZE_NEAREST_NEIGHBOR,
84
- "Bilinear": Jimp.RESIZE_BILINEAR,
85
- "Bicubic": Jimp.RESIZE_BICUBIC,
86
- "Hermite": Jimp.RESIZE_HERMITE,
87
- "Bezier": Jimp.RESIZE_BEZIER
83
+ "Nearest Neighbour": ResizeStrategy.NEAREST_NEIGHBOR,
84
+ Bilinear: ResizeStrategy.BILINEAR,
85
+ Bicubic: ResizeStrategy.BICUBIC,
86
+ Hermite: ResizeStrategy.HERMITE,
87
+ Bezier: ResizeStrategy.BEZIER,
88
88
  };
89
89
 
90
90
  if (!isImage(input)) {
@@ -99,23 +99,31 @@ class ResizeImage extends Operation {
99
99
  }
100
100
  try {
101
101
  if (unit === "Percent") {
102
- width = image.getWidth() * (width / 100);
103
- height = image.getHeight() * (height / 100);
102
+ width = image.width * (width / 100);
103
+ height = image.height * (height / 100);
104
104
  }
105
105
 
106
106
  if (isWorkerEnvironment())
107
107
  self.sendStatusMessage("Resizing image...");
108
108
  if (aspect) {
109
- image.scaleToFit(width, height, resizeMap[resizeAlg]);
109
+ image.scaleToFit({
110
+ w: width,
111
+ h: height,
112
+ mode: resizeMap[resizeAlg],
113
+ });
110
114
  } else {
111
- image.resize(width, height, resizeMap[resizeAlg]);
115
+ image.resize({
116
+ w: width,
117
+ h: height,
118
+ mode: resizeMap[resizeAlg],
119
+ });
112
120
  }
113
121
 
114
122
  let imageBuffer;
115
- if (image.getMIME() === "image/gif") {
116
- imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG);
123
+ if (image.mime === "image/gif") {
124
+ imageBuffer = await image.getBuffer(JimpMime.png);
117
125
  } else {
118
- imageBuffer = await image.getBufferAsync(Jimp.AUTO);
126
+ imageBuffer = await image.getBuffer(image.mime);
119
127
  }
120
128
  return imageBuffer.buffer;
121
129
  } catch (err) {
@@ -139,7 +147,6 @@ class ResizeImage extends Operation {
139
147
 
140
148
  return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
141
149
  }
142
-
143
150
  }
144
151
 
145
152
  export default ResizeImage;
@@ -9,13 +9,12 @@ import OperationError from "../errors/OperationError.mjs";
9
9
  import { isImage } from "../lib/FileType.mjs";
10
10
  import { toBase64 } from "../lib/Base64.mjs";
11
11
  import { isWorkerEnvironment } from "../Utils.mjs";
12
- import Jimp from "jimp/es/index.js";
12
+ import { Jimp, JimpMime } from "jimp";
13
13
 
14
14
  /**
15
15
  * Rotate Image operation
16
16
  */
17
17
  class RotateImage extends Operation {
18
-
19
18
  /**
20
19
  * RotateImage constructor
21
20
  */
@@ -24,7 +23,8 @@ class RotateImage extends Operation {
24
23
 
25
24
  this.name = "Rotate Image";
26
25
  this.module = "Image";
27
- this.description = "Rotates an image by the specified number of degrees.";
26
+ this.description =
27
+ "Rotates an image by the specified number of degrees.";
28
28
  this.infoURL = "";
29
29
  this.inputType = "ArrayBuffer";
30
30
  this.outputType = "ArrayBuffer";
@@ -33,8 +33,8 @@ class RotateImage extends Operation {
33
33
  {
34
34
  name: "Rotation amount (degrees)",
35
35
  type: "number",
36
- value: 90
37
- }
36
+ value: 90,
37
+ },
38
38
  ];
39
39
  }
40
40
 
@@ -62,10 +62,10 @@ class RotateImage extends Operation {
62
62
  image.rotate(degrees);
63
63
 
64
64
  let imageBuffer;
65
- if (image.getMIME() === "image/gif") {
66
- imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG);
65
+ if (image.mime === "image/gif") {
66
+ imageBuffer = await image.getBuffer(JimpMime.png);
67
67
  } else {
68
- imageBuffer = await image.getBufferAsync(Jimp.AUTO);
68
+ imageBuffer = await image.getBuffer(image.mime);
69
69
  }
70
70
  return imageBuffer.buffer;
71
71
  } catch (err) {
@@ -89,7 +89,6 @@ class RotateImage extends Operation {
89
89
 
90
90
  return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
91
91
  }
92
-
93
92
  }
94
93
 
95
94
  export default RotateImage;
@@ -3,8 +3,7 @@
3
3
  * @copyright Crown Copyright 2016
4
4
  * @license Apache-2.0
5
5
  */
6
-
7
- import vkbeautify from "vkbeautify";
6
+ import { format } from "sql-formatter";
8
7
  import Operation from "../Operation.mjs";
9
8
 
10
9
  /**
@@ -39,7 +38,26 @@ class SQLBeautify extends Operation {
39
38
  */
40
39
  run(input, args) {
41
40
  const indentStr = args[0];
42
- return vkbeautify.sql(input, indentStr);
41
+ // Extract and replace bind variables like :Bind1 with __BIND_0__
42
+ const bindRegex = /:\w+/g;
43
+ const bindMap = {};
44
+ let bindCounter=0;
45
+ const placeholderInput = input.replace(bindRegex, (match) => {
46
+ const placeholder = `__BIND_${bindCounter++}__`;
47
+ bindMap[placeholder] = match;
48
+ return placeholder;
49
+ });
50
+ // Format the SQL with chosen options
51
+ let formatted= format(placeholderInput, {
52
+ language: "mysql", // Use MySQL as the default dialect for better compatibility with real-world SQL
53
+ useTabs: indentStr==="\t", // true if tab, false if spaces
54
+ tabWidth: indentStr.length || 4, // fallback if empty
55
+ indentStyle: "standard" // fine for most SQL
56
+ });
57
+ // Replace placeholders back with original bind variables
58
+ formatted = formatted.replace(/__BIND_\d+__/g, match => bindMap[match] || match);
59
+
60
+ return formatted;
43
61
  }
44
62
 
45
63
  }
@@ -8,15 +8,13 @@ import Operation from "../Operation.mjs";
8
8
  import OperationError from "../errors/OperationError.mjs";
9
9
  import { isImage } from "../lib/FileType.mjs";
10
10
  import { toBase64 } from "../lib/Base64.mjs";
11
- import { gaussianBlur } from "../lib/ImageManipulation.mjs";
12
11
  import { isWorkerEnvironment } from "../Utils.mjs";
13
- import Jimp from "jimp/es/index.js";
12
+ import { Jimp, JimpMime } from "jimp";
14
13
 
15
14
  /**
16
15
  * Sharpen Image operation
17
16
  */
18
17
  class SharpenImage extends Operation {
19
-
20
18
  /**
21
19
  * SharpenImage constructor
22
20
  */
@@ -35,22 +33,22 @@ class SharpenImage extends Operation {
35
33
  name: "Radius",
36
34
  type: "number",
37
35
  value: 2,
38
- min: 1
36
+ min: 1,
39
37
  },
40
38
  {
41
39
  name: "Amount",
42
40
  type: "number",
43
41
  value: 1,
44
42
  min: 0,
45
- step: 0.1
43
+ step: 0.1,
46
44
  },
47
45
  {
48
46
  name: "Threshold",
49
47
  type: "number",
50
48
  value: 10,
51
49
  min: 0,
52
- max: 100
53
- }
50
+ max: 100,
51
+ },
54
52
  ];
55
53
  }
56
54
 
@@ -79,67 +77,102 @@ class SharpenImage extends Operation {
79
77
  const blurMask = image.clone();
80
78
 
81
79
  if (isWorkerEnvironment())
82
- self.sendStatusMessage("Sharpening image... (Blurring cloned image)");
83
- const blurImage = gaussianBlur(image.clone(), radius);
84
-
80
+ self.sendStatusMessage(
81
+ "Sharpening image... (Blurring cloned image)",
82
+ );
83
+ const blurImage = image.clone().gaussian(radius);
85
84
 
86
85
  if (isWorkerEnvironment())
87
- self.sendStatusMessage("Sharpening image... (Creating unsharp mask)");
88
- blurMask.scan(0, 0, blurMask.bitmap.width, blurMask.bitmap.height, function(x, y, idx) {
89
- const blurRed = blurImage.bitmap.data[idx];
90
- const blurGreen = blurImage.bitmap.data[idx + 1];
91
- const blurBlue = blurImage.bitmap.data[idx + 2];
92
-
93
- const normalRed = this.bitmap.data[idx];
94
- const normalGreen = this.bitmap.data[idx + 1];
95
- const normalBlue = this.bitmap.data[idx + 2];
96
-
97
- // Subtract blurred pixel value from normal image
98
- this.bitmap.data[idx] = (normalRed > blurRed) ? normalRed - blurRed : 0;
99
- this.bitmap.data[idx + 1] = (normalGreen > blurGreen) ? normalGreen - blurGreen : 0;
100
- this.bitmap.data[idx + 2] = (normalBlue > blurBlue) ? normalBlue - blurBlue : 0;
101
- });
86
+ self.sendStatusMessage(
87
+ "Sharpening image... (Creating unsharp mask)",
88
+ );
89
+ blurMask.scan(
90
+ 0,
91
+ 0,
92
+ blurMask.bitmap.width,
93
+ blurMask.bitmap.height,
94
+ function (x, y, idx) {
95
+ const blurRed = blurImage.bitmap.data[idx];
96
+ const blurGreen = blurImage.bitmap.data[idx + 1];
97
+ const blurBlue = blurImage.bitmap.data[idx + 2];
98
+
99
+ const normalRed = this.bitmap.data[idx];
100
+ const normalGreen = this.bitmap.data[idx + 1];
101
+ const normalBlue = this.bitmap.data[idx + 2];
102
+
103
+ // Subtract blurred pixel value from normal image
104
+ this.bitmap.data[idx] =
105
+ normalRed > blurRed ? normalRed - blurRed : 0;
106
+ this.bitmap.data[idx + 1] =
107
+ normalGreen > blurGreen ? normalGreen - blurGreen : 0;
108
+ this.bitmap.data[idx + 2] =
109
+ normalBlue > blurBlue ? normalBlue - blurBlue : 0;
110
+ },
111
+ );
102
112
 
103
113
  if (isWorkerEnvironment())
104
- self.sendStatusMessage("Sharpening image... (Merging with unsharp mask)");
105
- image.scan(0, 0, image.bitmap.width, image.bitmap.height, function(x, y, idx) {
106
- let maskRed = blurMask.bitmap.data[idx];
107
- let maskGreen = blurMask.bitmap.data[idx + 1];
108
- let maskBlue = blurMask.bitmap.data[idx + 2];
109
-
110
- const normalRed = this.bitmap.data[idx];
111
- const normalGreen = this.bitmap.data[idx + 1];
112
- const normalBlue = this.bitmap.data[idx + 2];
113
-
114
- // Calculate luminance
115
- const maskLuminance = (0.2126 * maskRed + 0.7152 * maskGreen + 0.0722 * maskBlue);
116
- const normalLuminance = (0.2126 * normalRed + 0.7152 * normalGreen + 0.0722 * normalBlue);
117
-
118
- let luminanceDiff;
119
- if (maskLuminance > normalLuminance) {
120
- luminanceDiff = maskLuminance - normalLuminance;
121
- } else {
122
- luminanceDiff = normalLuminance - maskLuminance;
123
- }
124
-
125
- // Scale mask colours by amount
126
- maskRed = maskRed * amount;
127
- maskGreen = maskGreen * amount;
128
- maskBlue = maskBlue * amount;
129
-
130
- // Only change pixel value if the difference is higher than threshold
131
- if ((luminanceDiff / 255) * 100 >= threshold) {
132
- this.bitmap.data[idx] = (normalRed + maskRed) <= 255 ? normalRed + maskRed : 255;
133
- this.bitmap.data[idx + 1] = (normalGreen + maskGreen) <= 255 ? normalGreen + maskGreen : 255;
134
- this.bitmap.data[idx + 2] = (normalBlue + maskBlue) <= 255 ? normalBlue + maskBlue : 255;
135
- }
136
- });
114
+ self.sendStatusMessage(
115
+ "Sharpening image... (Merging with unsharp mask)",
116
+ );
117
+ image.scan(
118
+ 0,
119
+ 0,
120
+ image.bitmap.width,
121
+ image.bitmap.height,
122
+ function (x, y, idx) {
123
+ let maskRed = blurMask.bitmap.data[idx];
124
+ let maskGreen = blurMask.bitmap.data[idx + 1];
125
+ let maskBlue = blurMask.bitmap.data[idx + 2];
126
+
127
+ const normalRed = this.bitmap.data[idx];
128
+ const normalGreen = this.bitmap.data[idx + 1];
129
+ const normalBlue = this.bitmap.data[idx + 2];
130
+
131
+ // Calculate luminance
132
+ const maskLuminance =
133
+ 0.2126 * maskRed +
134
+ 0.7152 * maskGreen +
135
+ 0.0722 * maskBlue;
136
+ const normalLuminance =
137
+ 0.2126 * normalRed +
138
+ 0.7152 * normalGreen +
139
+ 0.0722 * normalBlue;
140
+
141
+ let luminanceDiff;
142
+ if (maskLuminance > normalLuminance) {
143
+ luminanceDiff = maskLuminance - normalLuminance;
144
+ } else {
145
+ luminanceDiff = normalLuminance - maskLuminance;
146
+ }
147
+
148
+ // Scale mask colours by amount
149
+ maskRed = maskRed * amount;
150
+ maskGreen = maskGreen * amount;
151
+ maskBlue = maskBlue * amount;
152
+
153
+ // Only change pixel value if the difference is higher than threshold
154
+ if ((luminanceDiff / 255) * 100 >= threshold) {
155
+ this.bitmap.data[idx] =
156
+ normalRed + maskRed <= 255 ?
157
+ normalRed + maskRed :
158
+ 255;
159
+ this.bitmap.data[idx + 1] =
160
+ normalGreen + maskGreen <= 255 ?
161
+ normalGreen + maskGreen :
162
+ 255;
163
+ this.bitmap.data[idx + 2] =
164
+ normalBlue + maskBlue <= 255 ?
165
+ normalBlue + maskBlue :
166
+ 255;
167
+ }
168
+ },
169
+ );
137
170
 
138
171
  let imageBuffer;
139
- if (image.getMIME() === "image/gif") {
140
- imageBuffer = await image.getBufferAsync(Jimp.MIME_PNG);
172
+ if (image.mime === "image/gif") {
173
+ imageBuffer = await image.getBuffer(JimpMime.png);
141
174
  } else {
142
- imageBuffer = await image.getBufferAsync(Jimp.AUTO);
175
+ imageBuffer = await image.getBuffer(image.mime);
143
176
  }
144
177
  return imageBuffer.buffer;
145
178
  } catch (err) {
@@ -163,7 +196,6 @@ class SharpenImage extends Operation {
163
196
 
164
197
  return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
165
198
  }
166
-
167
199
  }
168
200
 
169
201
  export default SharpenImage;
@@ -7,14 +7,13 @@
7
7
  import Operation from "../Operation.mjs";
8
8
  import OperationError from "../errors/OperationError.mjs";
9
9
  import Utils from "../Utils.mjs";
10
- import {isImage} from "../lib/FileType.mjs";
11
- import Jimp from "jimp/es/index.js";
10
+ import { isImage } from "../lib/FileType.mjs";
11
+ import { Jimp, JimpMime } from "jimp";
12
12
 
13
13
  /**
14
14
  * Split Colour Channels operation
15
15
  */
16
16
  class SplitColourChannels extends Operation {
17
-
18
17
  /**
19
18
  * SplitColourChannels constructor
20
19
  */
@@ -23,7 +22,8 @@ class SplitColourChannels extends Operation {
23
22
 
24
23
  this.name = "Split Colour Channels";
25
24
  this.module = "Image";
26
- this.description = "Splits the given image into its red, green and blue colour channels.";
25
+ this.description =
26
+ "Splits the given image into its red, green and blue colour channels.";
27
27
  this.infoURL = "https://wikipedia.org/wiki/Channel_(digital_image)";
28
28
  this.inputType = "ArrayBuffer";
29
29
  this.outputType = "List<File>";
@@ -48,26 +48,44 @@ class SplitColourChannels extends Operation {
48
48
  const split = parsedImage
49
49
  .clone()
50
50
  .color([
51
- {apply: "blue", params: [-255]},
52
- {apply: "green", params: [-255]}
51
+ { apply: "blue", params: [-255] },
52
+ { apply: "green", params: [-255] },
53
53
  ])
54
- .getBufferAsync(Jimp.MIME_PNG);
55
- resolve(new File([new Uint8Array((await split).values())], "red.png", {type: "image/png"}));
54
+ .getBuffer(JimpMime.png);
55
+ resolve(
56
+ new File(
57
+ [new Uint8Array((await split).values())],
58
+ "red.png",
59
+ { type: "image/png" },
60
+ ),
61
+ );
56
62
  } catch (err) {
57
- reject(new OperationError(`Could not split red channel: ${err}`));
63
+ reject(
64
+ new OperationError(`Could not split red channel: ${err}`),
65
+ );
58
66
  }
59
67
  });
60
68
 
61
69
  const green = new Promise(async (resolve, reject) => {
62
70
  try {
63
- const split = parsedImage.clone()
71
+ const split = parsedImage
72
+ .clone()
64
73
  .color([
65
- {apply: "red", params: [-255]},
66
- {apply: "blue", params: [-255]},
67
- ]).getBufferAsync(Jimp.MIME_PNG);
68
- resolve(new File([new Uint8Array((await split).values())], "green.png", {type: "image/png"}));
74
+ { apply: "red", params: [-255] },
75
+ { apply: "blue", params: [-255] },
76
+ ])
77
+ .getBuffer(JimpMime.png);
78
+ resolve(
79
+ new File(
80
+ [new Uint8Array((await split).values())],
81
+ "green.png",
82
+ { type: "image/png" },
83
+ ),
84
+ );
69
85
  } catch (err) {
70
- reject(new OperationError(`Could not split green channel: ${err}`));
86
+ reject(
87
+ new OperationError(`Could not split green channel: ${err}`),
88
+ );
71
89
  }
72
90
  });
73
91
 
@@ -75,12 +93,21 @@ class SplitColourChannels extends Operation {
75
93
  try {
76
94
  const split = parsedImage
77
95
  .color([
78
- {apply: "red", params: [-255]},
79
- {apply: "green", params: [-255]},
80
- ]).getBufferAsync(Jimp.MIME_PNG);
81
- resolve(new File([new Uint8Array((await split).values())], "blue.png", {type: "image/png"}));
96
+ { apply: "red", params: [-255] },
97
+ { apply: "green", params: [-255] },
98
+ ])
99
+ .getBuffer(JimpMime.png);
100
+ resolve(
101
+ new File(
102
+ [new Uint8Array((await split).values())],
103
+ "blue.png",
104
+ { type: "image/png" },
105
+ ),
106
+ );
82
107
  } catch (err) {
83
- reject(new OperationError(`Could not split blue channel: ${err}`));
108
+ reject(
109
+ new OperationError(`Could not split blue channel: ${err}`),
110
+ );
84
111
  }
85
112
  });
86
113
 
@@ -96,7 +123,6 @@ class SplitColourChannels extends Operation {
96
123
  async present(files) {
97
124
  return await Utils.displayFilesAsHTML(files);
98
125
  }
99
-
100
126
  }
101
127
 
102
128
  export default SplitColourChannels;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * @author p-leriche [philip.leriche@cantab.net]
3
+ * @copyright Crown Copyright 2025
4
+ * @license Apache-2.0
5
+ */
6
+
7
+ import Operation from "../Operation.mjs";
8
+ import OperationError from "../errors/OperationError.mjs";
9
+
10
+ /* ---------- helper functions ---------- */
11
+
12
+ /**
13
+ * Convert text to BigInt (big-endian byte interpretation)
14
+ */
15
+ function textToBigInt(text) {
16
+ if (text.length === 0) return 0n;
17
+
18
+ let result = 0n;
19
+ for (let i = 0; i < text.length; i++) {
20
+ const charCode = BigInt(text.charCodeAt(i));
21
+ if (charCode > 255n) {
22
+ throw new OperationError(
23
+ `Character at position ${i} exceeds Latin-1 range (0-255).\n` +
24
+ "Only ASCII and Latin-1 characters are supported.");
25
+ }
26
+ result = (result << 8n) | charCode;
27
+ }
28
+ return result;
29
+ }
30
+
31
+ /**
32
+ * Convert BigInt to text (big-endian byte interpretation)
33
+ */
34
+ function bigIntToText(value) {
35
+ if (value === 0n) return "";
36
+
37
+ const bytes = [];
38
+ let num = value;
39
+
40
+ while (num > 0n) {
41
+ bytes.unshift(Number(num & 0xFFn));
42
+ num >>= 8n;
43
+ }
44
+
45
+ return String.fromCharCode(...bytes);
46
+ }
47
+
48
+ /* ---------- operation class ---------- */
49
+
50
+ /**
51
+ * Text/Integer Converter operation
52
+ */
53
+ class TextIntegerConverter extends Operation {
54
+ /**
55
+ * TextIntegerConverter constructor
56
+ */
57
+ constructor() {
58
+ super();
59
+
60
+ this.description =
61
+ "Converts between text strings and large integers (decimal or hexadecimal).<br><br>" +
62
+ "Text is interpreted as a big-endian sequence of character codes. For example:<br>" +
63
+ "ABC is 0x414243 (hex) is 4276803 (decimal)<br>" +
64
+ "<b>Input format detection:</b><br>" +
65
+ "Decimal: digits 0-9 only<br>" +
66
+ "Hexadecimal: 0x... prefix<br>" +
67
+ "Quoted or unquoted text: treated as string<br><br>" +
68
+ "<b>Character limitations:</b><br>" +
69
+ "Text input may only contain ASCII and Latin-1 characters (code point < 256).<br>" +
70
+ "Multi-byte Unicode characters will generate an error.<br><br>." ;
71
+ this.infoURL = "https://wikipedia.org/wiki/Endianness";
72
+ this.inputType = "string";
73
+ this.outputType = "string";
74
+ this.args = [
75
+ {
76
+ name: "Output format",
77
+ type: "option",
78
+ value: ["String", "Decimal", "Hexadecimal"]
79
+ }
80
+ ];
81
+ this.name = "Text-Integer Conversion";
82
+ this.module = "Default";
83
+ }
84
+
85
+ /**
86
+ * @param {string} input
87
+ * @param {Object[]} args
88
+ * @returns {string}
89
+ */
90
+ run(input, args) {
91
+ const outputFormat = args[0];
92
+ const trimmed = input.trim();
93
+
94
+ let bigIntValue;
95
+
96
+ if (!trimmed) {
97
+ // Null input - treat as zero
98
+ bigIntValue = 0;
99
+ } else if (/^0x[0-9a-f]+$/i.test(trimmed) ||
100
+ /^[+-]?[0-9]+$/.test(trimmed)) {
101
+ // Hex or decimal integer
102
+ bigIntValue = BigInt(trimmed);
103
+ } else if (/^["'].*["']$/.test(trimmed)) {
104
+ // Quoted string: Remove quotes and convert text to BigInt
105
+ const text = trimmed.slice(1, -1);
106
+ bigIntValue = textToBigInt(text);
107
+ } else {
108
+ // Assume it's unquoted text
109
+ bigIntValue = textToBigInt(trimmed);
110
+ }
111
+
112
+ // Convert to output format
113
+ if (outputFormat === "String") {
114
+ return bigIntToText(bigIntValue);
115
+ } else if (outputFormat === "Decimal") {
116
+ return bigIntValue.toString();
117
+ } else { // Hexadecimal
118
+ return "0x" + bigIntValue.toString(16);
119
+ }
120
+ }
121
+ }
122
+
123
+ export default TextIntegerConverter;