ConvertImageFormat.mjs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /**
  2. * @author j433866 [j433866@gmail.com]
  3. * @copyright Crown Copyright 2019
  4. * @license Apache-2.0
  5. */
  6. import Operation from "../Operation.mjs";
  7. import OperationError from "../errors/OperationError.mjs";
  8. import { isImage } from "../lib/FileType.mjs";
  9. import { toBase64 } from "../lib/Base64.mjs";
  10. import jimp from "jimp";
  11. /**
  12. * Convert Image Format operation
  13. */
  14. class ConvertImageFormat extends Operation {
  15. /**
  16. * ConvertImageFormat constructor
  17. */
  18. constructor() {
  19. super();
  20. this.name = "Convert Image Format";
  21. this.module = "Image";
  22. 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.";
  23. this.infoURL = "https://wikipedia.org/wiki/Image_file_formats";
  24. this.inputType = "ArrayBuffer";
  25. this.outputType = "ArrayBuffer";
  26. this.presentType = "html";
  27. this.args = [
  28. {
  29. name: "Output Format",
  30. type: "option",
  31. value: [
  32. "JPEG",
  33. "PNG",
  34. "BMP",
  35. "TIFF"
  36. ]
  37. },
  38. {
  39. name: "JPEG Quality",
  40. type: "number",
  41. value: 80,
  42. min: 1,
  43. max: 100
  44. },
  45. {
  46. name: "PNG Filter Type",
  47. type: "option",
  48. value: [
  49. "Auto",
  50. "None",
  51. "Sub",
  52. "Up",
  53. "Average",
  54. "Paeth"
  55. ]
  56. },
  57. {
  58. name: "PNG Deflate Level",
  59. type: "number",
  60. value: 9,
  61. min: 0,
  62. max: 9
  63. }
  64. ];
  65. }
  66. /**
  67. * @param {ArrayBuffer} input
  68. * @param {Object[]} args
  69. * @returns {byteArray}
  70. */
  71. async run(input, args) {
  72. const [format, jpegQuality, pngFilterType, pngDeflateLevel] = args;
  73. const formatMap = {
  74. "JPEG": jimp.MIME_JPEG,
  75. "PNG": jimp.MIME_PNG,
  76. "BMP": jimp.MIME_BMP,
  77. "TIFF": jimp.MIME_TIFF
  78. };
  79. const pngFilterMap = {
  80. "Auto": jimp.PNG_FILTER_AUTO,
  81. "None": jimp.PNG_FILTER_NONE,
  82. "Sub": jimp.PNG_FILTER_SUB,
  83. "Up": jimp.PNG_FILTER_UP,
  84. "Average": jimp.PNG_FILTER_AVERAGE,
  85. "Paeth": jimp.PNG_FILTER_PATH // Incorrect spelling in Jimp library
  86. };
  87. const mime = formatMap[format];
  88. if (!isImage(new Uint8Array(input))) {
  89. throw new OperationError("Invalid file format.");
  90. }
  91. let image;
  92. try {
  93. image = await jimp.read(input);
  94. } catch (err) {
  95. throw new OperationError(`Error opening image file. (${err})`);
  96. }
  97. try {
  98. switch (format) {
  99. case "JPEG":
  100. image.quality(jpegQuality);
  101. break;
  102. case "PNG":
  103. image.filterType(pngFilterMap[pngFilterType]);
  104. image.deflateLevel(pngDeflateLevel);
  105. break;
  106. }
  107. const imageBuffer = await image.getBufferAsync(mime);
  108. return imageBuffer.buffer;
  109. } catch (err) {
  110. throw new OperationError(`Error converting image format. (${err})`);
  111. }
  112. }
  113. /**
  114. * Displays the converted image using HTML for web apps
  115. *
  116. * @param {ArrayBuffer} data
  117. * @returns {html}
  118. */
  119. present(data) {
  120. if (!data.byteLength) return "";
  121. const dataArray = new Uint8Array(data);
  122. const type = isImage(dataArray);
  123. if (!type) {
  124. throw new OperationError("Invalid file type.");
  125. }
  126. return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
  127. }
  128. }
  129. export default ConvertImageFormat;