From a1bcdf17a53fb98c476aae9e205817c4a80a363d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Sat, 16 Nov 2024 15:13:50 +0000 Subject: feat(jupyter): Add `Deno.jupyter.image` API (#26284) This commit adds `Deno.jupyter.image` API to display PNG and JPG images: ``` const data = Deno.readFileSync("./my-image.jpg"); Deno.jupyter.image(data); Deno.jupyter.image("./my-image.jpg"); ``` --- cli/js/40_jupyter.js | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) (limited to 'cli/js/40_jupyter.js') diff --git a/cli/js/40_jupyter.js b/cli/js/40_jupyter.js index ace50d6dc..198b6a350 100644 --- a/cli/js/40_jupyter.js +++ b/cli/js/40_jupyter.js @@ -177,6 +177,52 @@ function isCanvasLike(obj) { return obj !== null && typeof obj === "object" && "toDataURL" in obj; } +function isJpg(obj) { + // Check if obj is a Uint8Array + if (!(obj instanceof Uint8Array)) { + return false; + } + + // JPG files start with the magic bytes FF D8 + if (obj.length < 2 || obj[0] !== 0xFF || obj[1] !== 0xD8) { + return false; + } + + // JPG files end with the magic bytes FF D9 + if ( + obj.length < 2 || obj[obj.length - 2] !== 0xFF || + obj[obj.length - 1] !== 0xD9 + ) { + return false; + } + + return true; +} + +function isPng(obj) { + // Check if obj is a Uint8Array + if (!(obj instanceof Uint8Array)) { + return false; + } + + // PNG files start with a specific 8-byte signature + const pngSignature = [137, 80, 78, 71, 13, 10, 26, 10]; + + // Check if the array is at least as long as the signature + if (obj.length < pngSignature.length) { + return false; + } + + // Check each byte of the signature + for (let i = 0; i < pngSignature.length; i++) { + if (obj[i] !== pngSignature[i]) { + return false; + } + } + + return true; +} + /** Possible HTML and SVG Elements */ function isSVGElementLike(obj) { return obj !== null && typeof obj === "object" && "outerHTML" in obj && @@ -233,6 +279,16 @@ async function format(obj) { if (isDataFrameLike(obj)) { return extractDataFrame(obj); } + if (isJpg(obj)) { + return { + "image/jpeg": core.ops.op_base64_encode(obj), + }; + } + if (isPng(obj)) { + return { + "image/png": core.ops.op_base64_encode(obj), + }; + } if (isSVGElementLike(obj)) { return { "image/svg+xml": obj.outerHTML, @@ -314,6 +370,28 @@ const html = createTaggedTemplateDisplayable("text/html"); */ const svg = createTaggedTemplateDisplayable("image/svg+xml"); +function image(obj) { + if (typeof obj === "string") { + try { + obj = Deno.readFileSync(obj); + } catch { + // pass + } + } + + if (isJpg(obj)) { + return makeDisplayable({ "image/jpeg": core.ops.op_base64_encode(obj) }); + } + + if (isPng(obj)) { + return makeDisplayable({ "image/png": core.ops.op_base64_encode(obj) }); + } + + throw new TypeError( + "Object is not a valid image or a path to an image. `Deno.jupyter.image` supports displaying JPG or PNG images.", + ); +} + function isMediaBundle(obj) { if (obj == null || typeof obj !== "object" || Array.isArray(obj)) { return false; @@ -465,6 +543,7 @@ function enableJupyter() { md, html, svg, + image, $display, }; } -- cgit v1.2.3