-- images-handling.lua -- SPDX-FileCopyrightText: 2023–2025 toastal -- SPDX-License-Identifier: MPL-2.0 Plugin.require_version("4.4.0") function append_dimensions(og_elem, src) local local_path = Sys.join_path_unix(build_dir, src) if Sys.file_exists(local_path) then local exif_output = Sys.get_program_output(format("exiftool %s -s -ImageSize | gawk -F ':' '{print $2}'", local_path)) if exif_output then local trimmed_exif_output = String.trim(exif_output) local dims = Regex.split(trimmed_exif_output, "x") if size(dims) == 2 then HTML.set_attribute(og_elem, "width", dims[1]) HTML.set_attribute(og_elem, "height", dims[2]) else Log.warning(format("Invalid size `%s` for `%s`", trimmed_exif_output, local_path)) end else Log.warning(format("Missing image size for `%s`", local_path)) end end end function is_smaller_size_by(ext, percent, old, new) if (not Value.is_int(old)) or (not Value.is_int(new)) or new < 1 then return false else local diff = 1.0 - ((new + 0.0) / (old + 0.0)) return (percent + 0.0) < diff end end function append_2x(og_elem, og_src) local has_srcset = HTML.get_attribute(og_elem, "srcset") -- bail if srcset exists if not has_srcset then local extension = Sys.get_extension(og_src) local hi_path = Sys.strip_extensions(og_src) .. "@2x." .. extension -- BUG: this includes `/` as well as no Windows support 🤷 local hi_local_path = Sys.join_path_unix(build_dir, hi_path) if Sys.get_file_size(hi_local_path) then HTML.set_attribute(og_elem, "srcset", hi_path .. " 2x") end end end function append_source(picture, og_src, og_file_size, extension, mime_type) if Sys.get_extension(og_src) ~= extension then local alt_path = Sys.strip_extensions(og_src) .. "." .. extension -- BUG: this includes `/` as well as no Windows support 🤷 local alt_local_path = Sys.join_path_unix(build_dir, alt_path) local srcset = "" if Sys.file_exists(alt_local_path) then local asset_file_size = Sys.get_file_size(alt_local_path) local alt_path_2x = Sys.strip_extensions(og_src) .. "@2x." .. extension -- BUG: this includes `/` as well as no Windows support 🤷 local alt_local_path_2x = Sys.join_path_unix(build_dir, alt_path_2x) local alt_2x_exists = Sys.file_exists(alt_local_path_2x) if is_smaller_size_by(extension, 0.05, og_file_size, asset_file_size) then local source = HTML.create_element("source") local srcset = alt_path if alt_2x_exists then srcset = srcset .. ", " .. alt_path_2x .. " 2x" end HTML.set_attribute(source, "srcset", srcset) HTML.set_attribute(source, "type", mime_type) HTML.prepend_child(picture, source) else -- If we ain’t using it, then it’s best to delete it Sys.delete_file(alt_local_path) if alt_2x_exists then Sys.delete_file(alt_local_path_2x) end end end end end function process_img(elem) local parent = HTML.parent(elem) local picture = HTML.create_element("picture") local og_class = HTML.get_attribute(elem, "class") if og_class then HTML.set_attribute(picture, "class", og_class) end HTML.delete_attribute(elem, "class") HTML.append_child(picture, elem) -- append empty alt if missing if Value.is_nil(HTML.get_attribute(elem, "alt")) then HTML.set_attribute(elem, "alt", "") end local src = HTML.get_attribute(elem, "src") local file_size = Sys.get_file_size(Sys.join_path(build_dir, src)) if Value.is_int(file_size) then append_dimensions(elem, src) append_2x(elem, src) append_source(picture, src, file_size, "jxl", "image/jxl") else Log.warning(format("Image not found or wrong permissions or empty for “%s”; skipping.", src)) end HTML.prepend_child(parent, picture) end Table.iter_values(process_img, HTML.select(page, "img[src^=\"/\"]"))