Liking cljdoc? Tell your friends :D

com.blockether.imaging

Clojure imaging WITHOUT java.desktop — decoding, encoding, transforms, vector drawing, text, SVG rasterisation and plotting, all in-process through the JDK Foreign Function & Memory API over a first-party Rust cdylib (native/imaging-c: the image crate + resvg/usvg/tiny-skia).

Why it exists: javax.imageio, java.awt.image.BufferedImage and Graphics2D drag the whole AWT/Java2D/ImageIO subgraph — service loaders, sun.java2d.loops JNI blit primitives, the font manager — into a GraalVM native image, which is both huge and, on a headless server, pointless. This library replaces that surface with a ~5 MB cdylib and zero JNI: pure downcalls, no subprocess, no -Djava.awt.headless, no fontconfig.

Everything is bytes in, bytes out. An Img is an opaque handle on the Rust side; it is AutoCloseable (use with-open for tight loops) AND registered with a java.lang.ref.Cleaner, so forgetting to close leaks nothing permanently.

Run the JVM with --enable-native-access=ALL-UNNAMED so the foreign linker may load the library without a restricted-method warning.

The library is resolved ONCE, lazily, the first time it's needed:

  1. IMAGING_NATIVE_PATH env / com.blockether.imaging.native.path system property — an explicit path to the cdylib (used verbatim).
  2. A bundled classpath resource prebuilds/<platform>/<lib>, shipped by com.blockether/imaging-native-<platform>. This is the native-image path.
  3. A runtime download: the imaging-native-<platform> jar resolved through clojure.tools.deps (honouring Maven repos/mirrors/settings.xml), extracted + cached. Disable with IMAGING_DISABLE_DOWNLOAD=1.

<platform> in { linux-x64 linux-arm64 darwin-arm64 darwin-x64 windows-x64 }.

Clojure imaging WITHOUT `java.desktop` — decoding, encoding, transforms,
vector drawing, text, SVG rasterisation and plotting, all in-process through
the JDK Foreign Function & Memory API over a first-party Rust cdylib
(`native/imaging-c`: the `image` crate + `resvg`/`usvg`/`tiny-skia`).

Why it exists: `javax.imageio`, `java.awt.image.BufferedImage` and
`Graphics2D` drag the whole AWT/Java2D/ImageIO subgraph — service loaders,
`sun.java2d.loops` JNI blit primitives, the font manager — into a GraalVM
native image, which is both huge and, on a headless server, pointless. This
library replaces that surface with a ~5 MB cdylib and zero JNI: pure
downcalls, no subprocess, no `-Djava.awt.headless`, no fontconfig.

Everything is bytes in, bytes out. An `Img` is an opaque handle on the Rust
side; it is `AutoCloseable` (use `with-open` for tight loops) AND registered
with a `java.lang.ref.Cleaner`, so forgetting to close leaks nothing
permanently.

Run the JVM with `--enable-native-access=ALL-UNNAMED` so the foreign linker
may load the library without a restricted-method warning.

The library is resolved ONCE, lazily, the first time it's needed:
  1. IMAGING_NATIVE_PATH env / `com.blockether.imaging.native.path` system
     property — an explicit path to the cdylib (used verbatim).
  2. A bundled classpath resource `prebuilds/<platform>/<lib>`, shipped by
     `com.blockether/imaging-native-<platform>`. This is the native-image path.
  3. A runtime download: the `imaging-native-<platform>` jar resolved through
     `clojure.tools.deps` (honouring Maven repos/mirrors/settings.xml),
     extracted + cached. Disable with IMAGING_DISABLE_DOWNLOAD=1.

`<platform>` in { linux-x64 linux-arm64 darwin-arm64 darwin-x64 windows-x64 }.
raw docstring

adjustclj

(adjust img opts)

Photometric adjustment: :brightness :contrast :saturation :opacity (1.0 = unchanged).

Photometric adjustment: `:brightness` `:contrast` `:saturation` `:opacity`
(1.0 = unchanged).
sourceraw docstring

blankclj

(blank w h)
(blank w h c)

A new w x h image filled with c (default transparent).

A new `w` x `h` image filled with `c` (default transparent).
sourceraw docstring

blurclj

(blur img sigma)
source

close!clj

(close! img)

Free an image handle now. Idempotent; Img is also AutoCloseable.

Free an image handle now. Idempotent; `Img` is also `AutoCloseable`.
sourceraw docstring

colorclj

(color c)

A colour as the packed 0xRRGGBBAA long the cdylib takes. Accepts a packed integer, "#rgb" / "#rrggbb" / "#rrggbbaa", a basic CSS colour name (string or keyword), or [r g b] / [r g b a] with 0-255 channels (alpha may also be a 0.0-1.0 double).

A colour as the packed `0xRRGGBBAA` long the cdylib takes. Accepts a packed
integer, `"#rgb"` / `"#rrggbb"` / `"#rrggbbaa"`, a basic CSS colour name
(string or keyword), or `[r g b]` / `[r g b a]` with 0-255 channels (alpha may
also be a 0.0-1.0 double).
sourceraw docstring

convertclj

(convert data)
(convert data opts)

One-shot decode -> normalise -> re-encode: the "make this attachment safe to send" path, without ever materialising an image handle.

Options: :format :quality :max-width :max-height :width :height :background (flatten alpha onto it) :filter :max-bytes (retry at lower quality/scale until the output fits). Returns a byte[].

One-shot decode -> normalise -> re-encode: the "make this attachment safe to
send" path, without ever materialising an image handle.

Options: `:format` `:quality` `:max-width` `:max-height` `:width` `:height`
`:background` (flatten alpha onto it) `:filter` `:max-bytes` (retry at lower
quality/scale until the output fits). Returns a byte[].
sourceraw docstring

convolveclj

(convolve img size kernel)
(convolve img size kernel {:keys [scale offset] :or {scale 0 offset 0}})

Correlate img with an odd size x size kernel (a seq of size*size weights, row-major) — blur, sharpen, emboss, edge detect: Pillow's ImageFilter.Kernel, and the same arithmetic.

:scale divides the weighted sum (0 or nil = no division) and :offset is added afterwards; the result is rounded half-up and clamped to 0..255. Edge samples clamp to the border pixel and the source ALPHA is carried through untouched, so blurring never eats a mask.

Correlate `img` with an odd `size` x `size` `kernel` (a seq of `size*size`
weights, row-major) — blur, sharpen, emboss, edge detect: Pillow's
`ImageFilter.Kernel`, and the same arithmetic.

`:scale` divides the weighted sum (0 or nil = no division) and `:offset` is
added afterwards; the result is rounded half-up and clamped to 0..255. Edge
samples clamp to the border pixel and the source ALPHA is carried through
untouched, so blurring never eats a mask.
sourceraw docstring

cropclj

(crop img x y w h)
source

decodeclj

(decode data)

Decode PNG/JPEG/WebP/GIF/BMP/TIFF/ICO/QOI/PNM/TGA/HDR/EXR bytes — or an SVG/SVGZ document, sized the way a browser would (see svg-canvas) — into an Img handle. Close it (with-open) when done.

Decode PNG/JPEG/WebP/GIF/BMP/TIFF/ICO/QOI/PNM/TGA/HDR/EXR bytes — or an
SVG/SVGZ document, sized the way a browser would (see `svg-canvas`) — into an
`Img` handle. Close it (`with-open`) when done.
sourceraw docstring

decode-gifclj

(decode-gif data)

Decode an animated (or still) GIF into {:width :height :loop-count :frames}, where each frame is {:delay-ms :rgba} with :rgba the straight RGBA8 rows as a byte[] (width*height*4). :loop-count is the iteration count, -1 for loop forever. The native multi-frame path — an animated GIF no longer needs a hand-rolled Clojure codec.

Decode an animated (or still) GIF into `{:width :height :loop-count :frames}`,
where each frame is `{:delay-ms :rgba}` with `:rgba` the straight RGBA8 rows
as a byte[] (`width*height*4`). `:loop-count` is the iteration count, -1 for
loop forever. The native multi-frame path — an animated GIF no longer needs a
hand-rolled Clojure codec.
sourceraw docstring

decode-videoclj

(decode-video data)
(decode-video data opts)

Decode an MP4's H.264 track into {:format :codec :encoding :width :height :source-width :source-height :frame-count :total-frames :duration-s :fps :frames}, where each frame is {:index :timestamp-s :data} and :data is a byte[].

:encoding decides what those bytes are: "rgba" (default) is straight RGBA8 rows, width*height*4, ready for from-pixels; anything encode understands ("png", "jpeg", "webp", ...) is an ENCODED still, which is far smaller and what a terminal or browser wants anyway.

Frames come out in DISPLAY order (B-frames are reordered), and RGBA is converted with the BT.601 limited-range matrix.

Options bound the work — the whole clip is decoded into memory otherwise: :max-frames (stop after N), :stride (keep every Nth frame), :max-dimension (downscale to fit), :filter, plus :quality for lossy :encoding.

Decode an MP4's H.264 track into
`{:format :codec :encoding :width :height :source-width :source-height
:frame-count :total-frames :duration-s :fps :frames}`, where each frame is
`{:index :timestamp-s :data}` and `:data` is a byte[].

`:encoding` decides what those bytes are: `"rgba"` (default) is straight
RGBA8 rows, `width*height*4`, ready for `from-pixels`; anything `encode`
understands (`"png"`, `"jpeg"`, `"webp"`, ...) is an ENCODED still, which is
far smaller and what a terminal or browser wants anyway.

Frames come out in DISPLAY order (B-frames are reordered), and RGBA is
converted with the BT.601 limited-range matrix.

Options bound the work — the whole clip is decoded into memory otherwise:
`:max-frames` (stop after N), `:stride` (keep every Nth frame),
`:max-dimension` (downscale to fit), `:filter`, plus `:quality` for lossy
`:encoding`.
sourceraw docstring

document->markdownclj

(document->markdown data)
(document->markdown data opts)

The Markdown of any document, as a String — read-document when all you want is the text. Same options.

The Markdown of any document, as a String — `read-document` when all you
want is the text. Same options.
sourceraw docstring

document-formatclj

(document-format data)
(document-format data opts)

Identify a document WITHOUT converting it: {:format :source :formats}.

:format is one of :doc :docx :odt :pdf :ppt :pptx :rtf :epub :excel :ods :odp :csv, or nil when nothing recognised the input. :source says which evidence won — "content" (the container's own signature), "name" (the extension of :name) or "format" (you said so).

The signature is asked FIRST, because a signature cannot lie and an extension routinely does. CSV carries no signature at all, so a :name is its only route.

Options: :name (file name or extension), :format (force it).

Identify a document WITHOUT converting it: `{:format :source :formats}`.

`:format` is one of `:doc :docx :odt :pdf :ppt :pptx :rtf :epub :excel :ods
:odp :csv`, or nil when nothing recognised the input. `:source` says which
evidence won — `"content"` (the container's own signature), `"name"` (the
extension of `:name`) or `"format"` (you said so).

The signature is asked FIRST, because a signature cannot lie and an
extension routinely does. CSV carries no signature at all, so a `:name` is
its only route.

Options: `:name` (file name or extension), `:format` (force it).
sourceraw docstring

docxclj

(docx spec)

A .docx document as bytes (hand-written WordprocessingML).

{:properties {…} :page {:width-pt 595 :height-pt 842 :landscape false :margin-pt 72} :blocks [{:type :heading :level 1 :text "Q3"} {:type :paragraph :style :Quote :align :center :bullet true :runs [{:text "revenue "} {:text "up" :bold true :color "#c00"}]} {:type :table :header true :borders true :rows [{:cells [{:text "a"} {:runs [{:text "b" :bold true}]}]}]} {:type :image :data <bytes> :width-pt 240 :alt "chart"} {:type :page-break}]}

:body is accepted as a synonym for :blocks. Font sizes are points, spacing/indent *-pt keys are points; the module converts to half-points and twips.

A `.docx` document as bytes (hand-written WordprocessingML).

`{:properties {…}
  :page {:width-pt 595 :height-pt 842 :landscape false :margin-pt 72}
  :blocks [{:type :heading :level 1 :text "Q3"}
           {:type :paragraph :style :Quote :align :center :bullet true
            :runs [{:text "revenue "} {:text "up" :bold true :color "#c00"}]}
           {:type :table :header true :borders true
            :rows [{:cells [{:text "a"} {:runs [{:text "b" :bold true}]}]}]}
           {:type :image :data <bytes> :width-pt 240 :alt "chart"}
           {:type :page-break}]}`

`:body` is accepted as a synonym for `:blocks`. Font sizes are points,
spacing/indent `*-pt` keys are points; the module converts to half-points and
twips.
sourceraw docstring

draw!clj

(draw! img ops)

Run a batch of vector drawing ops on img, IN PLACE — this is the Graphics2D replacement. One call per batch keeps FFI chatter out of drawing loops; ops are applied in order and inherit the batch's style defaults.

Style keys (batch level or per op): :fill :stroke :stroke-width :dash :cap (:butt :round :square) :join (:miter :round :bevel) :size :family :weight :italic :letter-spacing :line-height.

Ops (:op): :clear — fill everything with :fill :clip — clip to :x :y :w :h (:op :reset-clip to drop it) :line:x1 :y1 :x2 :y2 (or :points) :polyline / :polygon:points [[x y] ...], :close :rect:x :y :w :h, optional :radius (rounded) :circle:cx :cy :r :ellipse:cx :cy :rx :ry :arc / :wedge:cx :cy :r :r-inner :start :end (degrees) :path:d (SVG path data), :fill-rule :text:text :x :y :anchor (:start|:middle|:end) :baseline :rotate :image:image (an Img), :x :y :w :h :opacity

Returns img.

Run a batch of vector drawing ops on `img`, IN PLACE — this is the
`Graphics2D` replacement. One call per batch keeps FFI chatter out of drawing
loops; ops are applied in order and inherit the batch's style defaults.

Style keys (batch level or per op): `:fill` `:stroke` `:stroke-width` `:dash`
`:cap` (`:butt` `:round` `:square`) `:join` (`:miter` `:round` `:bevel`)
`:size` `:family` `:weight` `:italic` `:letter-spacing` `:line-height`.

Ops (`:op`):
  `:clear`     — fill everything with `:fill`
  `:clip`      — clip to `:x :y :w :h` (`:op :reset-clip` to drop it)
  `:line`      — `:x1 :y1 :x2 :y2` (or `:points`)
  `:polyline` / `:polygon` — `:points [[x y] ...]`, `:close`
  `:rect`      — `:x :y :w :h`, optional `:radius` (rounded)
  `:circle`    — `:cx :cy :r`     `:ellipse` — `:cx :cy :rx :ry`
  `:arc` / `:wedge` — `:cx :cy :r :r-inner :start :end` (degrees)
  `:path`      — `:d` (SVG path data), `:fill-rule`
  `:text`      — `:text :x :y :anchor (:start|:middle|:end) :baseline :rotate`
  `:image`     — `:image` (an `Img`), `:x :y :w :h :opacity`

Returns `img`.
sourceraw docstring

encodeclj

(encode img)
(encode img fmt)
(encode img fmt quality)

Encode to fmt (:png :jpeg :webp :gif :bmp :tiff :ico :qoi :pnm :tga); returns a byte[]. quality (1-100, default 85) applies to the lossy formats.

Encode to `fmt` (`:png` `:jpeg` `:webp` `:gif` `:bmp` `:tiff` `:ico` `:qoi`
`:pnm` `:tga`); returns a byte[]. `quality` (1-100, default 85) applies to the
lossy formats.
sourceraw docstring

encode-gifclj

(encode-gif spec)

Encode an animated GIF from {:width :height :loop-count :frames}, each frame {:delay-ms :rgba} (straight RGBA8 byte[]). :loop-count -1/omitted = loop forever. Returns a byte[].

Encode an animated GIF from `{:width :height :loop-count :frames}`, each frame
`{:delay-ms :rgba}` (straight RGBA8 byte[]). `:loop-count` -1/omitted = loop
forever. Returns a byte[].
sourceraw docstring

flattenclj

(flatten img)
(flatten img bg)

Composite onto an opaque background (default white).

Composite onto an opaque background (default white).
sourceraw docstring

flipclj

(flip img mode)

:horizontal/:h or :vertical/:v.

`:horizontal`/`:h` or `:vertical`/`:v`.
sourceraw docstring

fontsclj

(fonts)

Every font family known to the shared font database, as a vector of strings. The four Noto faces (sans regular/bold/italic + mono) are embedded, so text and SVG rendering never depend on system fonts.

Every font family known to the shared font database, as a vector of strings.
The four Noto faces (sans regular/bold/italic + mono) are embedded, so text
and SVG rendering never depend on system fonts.
sourceraw docstring

from-pixelsclj

(from-pixels rgba w h)

An image from straight (non-premultiplied) RGBA8 rows — w*h*4 bytes.

An image from straight (non-premultiplied) RGBA8 rows — `w*h*4` bytes.
sourceraw docstring

get-pixelclj

(get-pixel img x y)

Packed 0xRRGGBBAA of one pixel, or nil when out of bounds.

Packed `0xRRGGBBAA` of one pixel, or nil when out of bounds.
sourceraw docstring

grayscaleclj

(grayscale img)
source

heightclj

(height img)
source

img?clj

(img? x)
source

infoclj

(info img)

{:width :height :is-opaque :is-grayscale} — read off the handle's own RGBA8 pixels (:is-grayscale = every pixel has r = g = b), not off the file it came from; probe answers that one.

`{:width :height :is-opaque :is-grayscale}` — read off the handle's own RGBA8
pixels (`:is-grayscale` = every pixel has r = g = b), not off the file it came
from; `probe` answers that one.
sourceraw docstring

invertclj

(invert img)
source

mp4?clj

(mp4? data)

True when data looks like an ISO-BMFF/MP4 container (a ftyp box at byte 4). A sniff, not a validation — probe-video is the one that reads tracks.

True when `data` looks like an ISO-BMFF/MP4 container (a `ftyp` box at byte
4). A sniff, not a validation — `probe-video` is the one that reads tracks.
sourceraw docstring

officeclj

(office kind spec)

Build an Office Open XML document from a declarative spec and return its bytes. kind is :xlsx, :docx or :pptx. Pure Rust — rust_xlsxwriter for workbooks, hand-written WordprocessingML/PresentationML for the rest; no POI, no JVM document model, and it works in a native image.

See xlsx, docx and pptx for the per-kind spec.

Build an Office Open XML document from a declarative spec and return its
bytes. `kind` is `:xlsx`, `:docx` or `:pptx`. Pure Rust — rust_xlsxwriter for
workbooks, hand-written WordprocessingML/PresentationML for the rest; no POI,
no JVM document model, and it works in a native image.

See `xlsx`, `docx` and `pptx` for the per-kind spec.
sourceraw docstring

optimizeclj

(optimize data)
(optimize data opts)

Shrink an ENCODED image with the real, format-specific optimisers — oxipng (filter/heuristic search, colour-type and palette reduction, optional zopfli) for PNG, jpegtran-style lossless metadata stripping for JPEG, and a differencing re-encoder (gifsicle's algorithm) for GIF. Takes and returns encoded bytes.

LOSSLESS unless you ask otherwise: the pixels of a PNG or a JPEG come back bit-identical unless :lossy, a :quality, a different :format, a :max-width/:max-height or an unmeetable :max-bytes licenses a re-encode.

Options: :format (default: keep the input's) :level 0-6 effort :lossy (palette-quantise PNG / re-encode JPEG) :colors :dither :quality :progressive :strip (default true) :background :max-width :max-height (downscale to fit, never enlarge) :max-bytes (keep trying until it fits) :force (return the optimised bytes even when they are bigger).

Returns a byte[] — the smaller of optimised and original unless :force.

Shrink an ENCODED image with the real, format-specific optimisers — oxipng
(filter/heuristic search, colour-type and palette reduction, optional zopfli)
for PNG, jpegtran-style lossless metadata stripping for JPEG, and a
differencing re-encoder (gifsicle's algorithm) for GIF. Takes and returns
encoded bytes.

LOSSLESS unless you ask otherwise: the pixels of a PNG or a JPEG come back
bit-identical unless `:lossy`, a `:quality`, a different `:format`, a
`:max-width`/`:max-height` or an unmeetable `:max-bytes` licenses a re-encode.

Options: `:format` (default: keep the input's) `:level` 0-6 effort
`:lossy` (palette-quantise PNG / re-encode JPEG) `:colors` `:dither`
`:quality` `:progressive` `:strip` (default true) `:background`
`:max-width` `:max-height` (downscale to fit, never enlarge)
`:max-bytes` (keep trying until it fits)
`:force` (return the optimised bytes even when they are bigger).

Returns a byte[] — the smaller of optimised and original unless `:force`.
sourceraw docstring

paste!clj

(paste! dst src x y)
(paste! dst src x y {:keys [blend] :or {blend true}})

Draw src into dst at (x,y), IN PLACE. :blend false replaces the destination pixels instead of alpha-compositing.

Draw `src` into `dst` at (x,y), IN PLACE. `:blend` false replaces the
destination pixels instead of alpha-compositing.
sourceraw docstring

pixelsclj

(pixels img)

The image's straight RGBA8 rows as a byte[].

The image's straight RGBA8 rows as a byte[].
sourceraw docstring

plotclj

(plot spec)

Render a plot specification to an image. See com.blockether.imaging.plot for the spec builders (line, bar, scatter, hist, pie, heatmap, …) and the full vocabulary.

Render a plot specification to an image. See `com.blockether.imaging.plot` for
the spec builders (`line`, `bar`, `scatter`, `hist`, `pie`, `heatmap`, …) and
the full vocabulary.
sourceraw docstring

pptxclj

(pptx spec)

A .pptx presentation as bytes (hand-written PresentationML).

{:width 12192000 :height 6858000 ; EMU, 16:9 by default :properties {…} :layouts [{:name "Title Slide" :type :title :placeholders [{:type :ctrTitle :idx 0 :left :top :width :height :size :align}]}] :slides [{:layout 0 :background "#ffffff" :notes "…" :shapes [{:kind :textbox|:auto|:picture|:table|:chart|:connector :left :top :width :height :rotation :preset :roundRect :fill "#3366cc" :line {:color :width} :image {:data <bytes> :crop {:left :top :right :bottom}} :chart {:type :column :title "Revenue" :categories ["Q1" "Q2"] :legend {:position :bottom} :series [{:name "Sales" :values [3 7] :fill "4472C4"}]} :text-frame {:paragraphs [{:align :level :bullet :runs [{:text "hi" :bold true :size 1800}]}]}}]}]}

A chart :type is one of :column :bar :line :area :pie :doughnut :radar :scatter :bubble plus the *-stacked / *-percent-stacked variants. Each chart becomes its own ppt/charts/chartN.xml part, so PowerPoint and python-pptx see a real chart rather than a picture of one.

Lengths are EMU (914400 per inch), font sizes centipoints (1800 = 18 pt) — exactly how python-pptx normalises its own Length/Pt values.

A `.pptx` presentation as bytes (hand-written PresentationML).

`{:width 12192000 :height 6858000            ; EMU, 16:9 by default
  :properties {…}
  :layouts [{:name "Title Slide" :type :title
             :placeholders [{:type :ctrTitle :idx 0
                             :left :top :width :height :size :align}]}]
  :slides [{:layout 0 :background "#ffffff" :notes "…"
            :shapes [{:kind :textbox|:auto|:picture|:table|:chart|:connector
                      :left :top :width :height :rotation
                      :preset :roundRect :fill "#3366cc" :line {:color :width}
                      :image {:data <bytes> :crop {:left :top :right :bottom}}
                      :chart {:type :column :title "Revenue"
                              :categories ["Q1" "Q2"]
                              :legend {:position :bottom}
                              :series [{:name "Sales" :values [3 7]
                                        :fill "4472C4"}]}
                      :text-frame {:paragraphs [{:align :level :bullet
                                                 :runs [{:text "hi" :bold true
                                                         :size 1800}]}]}}]}]}`

A chart `:type` is one of `:column :bar :line :area :pie :doughnut :radar
:scatter :bubble` plus the `*-stacked` / `*-percent-stacked` variants. Each
chart becomes its own `ppt/charts/chartN.xml` part, so PowerPoint and
`python-pptx` see a real chart rather than a picture of one.

Lengths are EMU (`914400` per inch), font sizes centipoints (`1800` = 18 pt) —
exactly how `python-pptx` normalises its own `Length`/`Pt` values.
sourceraw docstring

probeclj

(probe data)

Identify encoded bytes without a full decode: {:format :width :height :bytes :is-animated :frames :color :channels :bits :is-grayscale :has-alpha}. The colour keys describe the SOURCE file — a decoded Img is always RGBA8, so this is the only way to learn that a PNG was 8-bit grayscale (:color "l8") or 16-bit. :color is one of "l8" "la8" "rgb8" "rgba8" "l16" "la16" "rgb16" "rgba16" "rgb32f" "rgba32f"; indexed/palette sources report what they expand into. SVG documents report :format "svg" and rasterise as "rgba8".

Identify encoded bytes without a full decode:
`{:format :width :height :bytes :is-animated :frames :color :channels :bits
:is-grayscale :has-alpha}`. The colour keys describe the SOURCE file — a
decoded `Img` is always RGBA8, so this is the only way to learn that a PNG
was 8-bit grayscale (`:color "l8"`) or 16-bit. `:color` is one of `"l8"`
`"la8"` `"rgb8"` `"rgba8"` `"l16"` `"la16"` `"rgb16"` `"rgba16"`
`"rgb32f"` `"rgba32f"`; indexed/palette sources report what they expand
into. SVG documents report `:format "svg"` and rasterise as `"rgba8"`.
sourceraw docstring

probe-videoclj

(probe-video data)

Identify an MP4 without decoding a frame: {:format :codec :codec-kind :is-decodable :width :height :frames :duration-s :fps :timescale :has-audio}. :frames is the sample count and :width /:height the container's declared track size — the bitstream's own size is what decode-video reports. :is-decodable is false for a video track this build cannot decode (HEVC, AV1, VP9); only H.264/AVC decodes.

Identify an MP4 without decoding a frame:
`{:format :codec :codec-kind :is-decodable :width :height :frames :duration-s
:fps :timescale :has-audio}`. `:frames` is the sample count and `:width`
/`:height` the container's declared track size — the bitstream's own size is
what `decode-video` reports. `:is-decodable` is false for a video track this
build cannot decode (HEVC, AV1, VP9); only H.264/AVC decodes.
sourceraw docstring

put-pixel!clj

(put-pixel! img x y c)
source

quantizeclj

(quantize img)
(quantize img {:keys [colors dither] :or {colors 256 dither false}})

Median-cut colour quantisation with optional Floyd-Steinberg dithering — the palette engine behind optimize's lossy PNG and GIF paths.

Returns {:width :height :palette :indices :transparent :rgba}: :palette a vector of 0xRRGGBB ints, :indices one palette index per pixel (byte[]), :transparent the palette slot used for fully transparent pixels (or nil), :rgba the quantised image back as straight RGBA8 rows.

Median-cut colour quantisation with optional Floyd-Steinberg dithering — the
palette engine behind `optimize`'s lossy PNG and GIF paths.

Returns `{:width :height :palette :indices :transparent :rgba}`: `:palette` a
vector of `0xRRGGBB` ints, `:indices` one palette index per pixel (byte[]),
`:transparent` the palette slot used for fully transparent pixels (or nil),
`:rgba` the quantised image back as straight RGBA8 rows.
sourceraw docstring

rank-filterclj

(rank-filter img size rank)

Per-channel rank filter over an odd size x size window: every channel (alpha included) is sorted independently and the rank-th smallest kept. :min / :median / :max are the named ranks — Pillow's MinFilter, MedianFilter and MaxFilter; an integer picks the rank directly.

Per-channel rank filter over an odd `size` x `size` window: every channel
(alpha included) is sorted independently and the `rank`-th smallest kept.
`:min` / `:median` / `:max` are the named ranks — Pillow's `MinFilter`,
`MedianFilter` and `MaxFilter`; an integer picks the rank directly.
sourceraw docstring

read-documentclj

(read-document data)
(read-document data opts)

Convert ANY document a human sent into GitHub-Flavored Markdown: {:format :source :chars :markdown :assets}, and with :blocks the STRUCTURE behind that Markdown too.

Word (.doc .docx .odt .rtf), PDF, EPUB, presentations (.ppt .pptx .odp) and spreadsheets (.xls .xlsx .xlsm .xlsb .ods .csv) all come back as one Markdown string, with their headings, lists, tables and links intact — which is what an LLM can actually read. Nothing here shells out to a headless office suite: anydoc parses every format in pure Rust inside the cdylib.

Options: :format (force the parser, e.g. :csv), :name (the file name, the only route for signature-less CSV), :assets (also return every embedded binary as {:id :media-type :origin-part :size :bytes} with :bytes a byte[]), :max-assets, :blocks.

:blocks true additionally returns:

  • :text — the document as PLAIN text. Same content, none of Markdown's punctuation, so a matcher never trips over **March** or a |, and an offset means a position in the DOCUMENT rather than in a rendering of it.
  • :blocks — one entry per heading, paragraph, list item, table row, code block and note, in reading order: {:index :kind :text :start :end :line :path} where :start/:end are CHARACTER offsets into :text, :line is 1-based, and :path is the heading breadcrumb the block sits under. A heading adds :level/:anchor, a list item :marker/:list-depth/ :checked, a code block :lang, a note :note/:note-kind, a quoted block :quote-depth, and a table row :row/:is-header/:cells, each cell {:column :name :text :start :end} — so a hit can be reported as row 4, column Revenue.
  • :pages — the page count for a PDF, nil for everything else.

A PDF has no document model, so it never reports assets; its blocks come from per-page extraction instead, and every one of them carries :page.

Convert ANY document a human sent into GitHub-Flavored Markdown:
`{:format :source :chars :markdown :assets}`, and with `:blocks` the
STRUCTURE behind that Markdown too.

Word (`.doc` `.docx` `.odt` `.rtf`), PDF, EPUB, presentations (`.ppt`
`.pptx` `.odp`) and spreadsheets (`.xls` `.xlsx` `.xlsm` `.xlsb` `.ods`
`.csv`) all come back as one Markdown string, with their headings, lists,
tables and links intact — which is what an LLM can actually read. Nothing
here shells out to a headless office suite: `anydoc` parses every format in
pure Rust inside the cdylib.

Options: `:format` (force the parser, e.g. `:csv`), `:name` (the file name,
the only route for signature-less CSV), `:assets` (also return every
embedded binary as `{:id :media-type :origin-part :size :bytes}` with
`:bytes` a `byte[]`), `:max-assets`, `:blocks`.

`:blocks true` additionally returns:

- `:text` — the document as PLAIN text. Same content, none of Markdown's
  punctuation, so a matcher never trips over `**March**` or a `|`, and an
  offset means a position in the DOCUMENT rather than in a rendering of it.
- `:blocks` — one entry per heading, paragraph, list item, table row, code
  block and note, in reading order: `{:index :kind :text :start :end :line
  :path}` where `:start`/`:end` are CHARACTER offsets into `:text`, `:line`
  is 1-based, and `:path` is the heading breadcrumb the block sits under.
  A heading adds `:level`/`:anchor`, a list item `:marker`/`:list-depth`/
  `:checked`, a code block `:lang`, a note `:note`/`:note-kind`, a quoted
  block `:quote-depth`, and a table row `:row`/`:is-header`/`:cells`, each
  cell `{:column :name :text :start :end}` — so a hit can be reported as
  row 4, column Revenue.
- `:pages` — the page count for a PDF, nil for everything else.

A PDF has no document model, so it never reports assets; its blocks come
from per-page extraction instead, and every one of them carries `:page`.
sourceraw docstring

read-officeclj

(read-office data)
(read-office data opts)

Read an Office document back. Workbooks (.xlsx .xlsm .xlsb .xls .ods) come back as {:kind :sheets [{:name :rows [[cell …] …]}]} with cells already coerced to strings/numbers/booleans; .docx comes back as {:kind :paragraphs [...]}.

A .pptx is read back as the mirror image of what pptx writes — the whole DrawingML shape tree, not just its text:

{:kind :width :height :properties :slides [{:name :index :layout :notes :paragraphs :shapes [{:kind :name :left :top :width :height :rotation :fill {…} :line {…} :text-frame {…} :table {…} :chart {…} :image {…}}]}]}

so a deck can be read, edited and written again without losing its shapes.

Options: :sheet (name or index — read just that one), :header (treat the first row as keys and return maps), :max-rows, and :with-images (base64 every embedded picture of a presentation; off by default).

Read an Office document back. Workbooks (`.xlsx` `.xlsm` `.xlsb` `.xls`
`.ods`) come back as `{:kind :sheets [{:name :rows [[cell …] …]}]}` with cells
already coerced to strings/numbers/booleans; `.docx` comes back as
`{:kind :paragraphs [...]}`.

A `.pptx` is read back as the mirror image of what `pptx` writes — the whole
DrawingML shape tree, not just its text:

`{:kind :width :height :properties
  :slides [{:name :index :layout :notes :paragraphs
            :shapes [{:kind :name :left :top :width :height :rotation
                      :fill {…} :line {…}
                      :text-frame {…} :table {…} :chart {…} :image {…}}]}]}`

so a deck can be read, edited and written again without losing its shapes.

Options: `:sheet` (name or index — read just that one), `:header` (treat the
first row as keys and return maps), `:max-rows`, and `:with-images` (base64
every embedded picture of a presentation; off by default).
sourceraw docstring

register-font!clj

(register-font! font)

Add a TTF/OTF/TTC (bytes/File/path/stream) to the font database for this process; returns the vector of families it provided.

Add a TTF/OTF/TTC (bytes/File/path/stream) to the font database for this
process; returns the vector of families it provided.
sourceraw docstring

render-svgclj

(render-svg svg)
(render-svg svg opts)

Rasterise an SVG/SVGZ document (bytes, markup String, File, path, stream) with resvg. Options: :width :height :scale :max-width :max-height :background :dpi :font-family :font-size :base-url.

The canvas is REPAIRED by default: a document with a zero, negative or absent size is sized the way a BROWSER would (see svg-canvas) instead of failing resvg's size check. Pass :repair false for resvg's literal answer.

Rasterise an SVG/SVGZ document (bytes, markup String, File, path, stream) with
resvg. Options: `:width` `:height` `:scale` `:max-width` `:max-height`
`:background` `:dpi` `:font-family` `:font-size` `:base-url`.

The canvas is REPAIRED by default: a document with a zero, negative or absent
size is sized the way a BROWSER would (see `svg-canvas`) instead of failing
resvg's size check. Pass `:repair false` for resvg's literal answer.
sourceraw docstring

resizeclj

(resize img w h)
(resize img w h filter)

Resample to exactly w x h. filter: :nearest :triangle/:bilinear :catmullrom/:bicubic :gaussian :lanczos3 (default), passed through.

Resample to exactly `w` x `h`. `filter`: `:nearest` `:triangle`/`:bilinear`
`:catmullrom`/`:bicubic` `:gaussian` `:lanczos3` (default), passed through.
sourceraw docstring

rotateclj

(rotate img degrees)
(rotate img degrees {:keys [expand background]})

Rotate counter-clockwise by degrees. :expand true grows the canvas to hold the rotated image; :background fills behind it.

Rotate counter-clockwise by `degrees`. `:expand` true grows the canvas to hold
the rotated image; `:background` fills behind it.
sourceraw docstring

save!clj

(save! img file)
(save! img file {:keys [format quality] :as _opts})

Encode img and write it to file; the format comes from the extension unless :format says otherwise. Returns the file.

Encode `img` and write it to `file`; the format comes from the extension
unless `:format` says otherwise. Returns the file.
sourceraw docstring

svg-canvasclj

(svg-canvas svg)
(svg-canvas svg opts)

How a BROWSER would size an SVG/SVGZ document (bytes, markup String, File, path, stream): {:width :height :source :is-repaired :svg}.

resvg answers only for documents that declare a usable size: it refuses a zero or negative one, and measures a size-less document from the ORIGIN, so a figure drawn at x=50 gets 50px of dead margin. A browser falls back to the viewBox, then to the ink actually painted. :source says which rule won — "declared" (resvg's own resolved size), "view_box", "content" or "default" — and :svg is the REPAIRED markup (a byte[], nil when the document needed no repair), which render-svg/decode rasterise as drawn.

render-svg with :repair true does exactly this internally. Options: :min-dimension (512) and :max-dimension (4096) bound an ink-framed canvas, plus the parse options of render-svg.

How a BROWSER would size an SVG/SVGZ document (bytes, markup String, File,
path, stream): `{:width :height :source :is-repaired :svg}`.

resvg answers only for documents that declare a usable size: it refuses a zero
or negative one, and measures a size-less document from the ORIGIN, so a
figure drawn at x=50 gets 50px of dead margin. A browser falls back to the
`viewBox`, then to the ink actually painted. `:source` says which rule won —
`"declared"` (resvg's own resolved size), `"view_box"`, `"content"` or
`"default"` — and `:svg` is the REPAIRED markup (a byte[], nil when the
document needed no repair), which `render-svg`/`decode` rasterise as drawn.

`render-svg` with `:repair true` does exactly this internally. Options:
`:min-dimension` (512) and `:max-dimension` (4096) bound an ink-framed canvas,
plus the parse options of `render-svg`.
sourceraw docstring

text-measureclj

(text-measure spec)

Measure a text run without drawing it — same :text :size :family :weight :italic :letter-spacing :line-height keys as the :text draw op. Returns {:width :height :x :y :lines}, the ink box relative to the first baseline.

Measure a text run without drawing it — same `:text :size :family :weight
:italic :letter-spacing :line-height` keys as the `:text` draw op. Returns
`{:width :height :x :y :lines}`, the ink box relative to the first baseline.
sourceraw docstring

thumbnailclj

(thumbnail img max-w max-h)

Scale DOWN to fit inside max-w x max-h, preserving aspect ratio.

Scale DOWN to fit inside `max-w` x `max-h`, preserving aspect ratio.
sourceraw docstring

versionclj

(version)

The cdylib's version string (crate version + backing engines).

The cdylib's version string (crate version + backing engines).
sourceraw docstring

video->gifclj

(video->gif data)
(video->gif data opts)

Transcode an MP4 straight to an animated GIF byte[] — the frames never cross the FFI boundary, so this is the cheap way to make a clip watchable anywhere a GIF is (a terminal, a chat wire that allows image/gif, a browser).

Options: the decode-video ones (:max-frames :stride :max-dimension :filter) plus :fps (playback rate; default the source rate divided by :stride) and :loop-count (-1/omitted = forever). GIF stores its delay in CENTIseconds, so the frame rate is quantised to 1/100 s.

Transcode an MP4 straight to an animated GIF byte[] — the frames never cross
the FFI boundary, so this is the cheap way to make a clip watchable anywhere
a GIF is (a terminal, a chat wire that allows `image/gif`, a browser).

Options: the `decode-video` ones (`:max-frames` `:stride` `:max-dimension`
`:filter`) plus `:fps` (playback rate; default the source rate divided by
`:stride`) and `:loop-count` (-1/omitted = forever). GIF stores its delay in
CENTIseconds, so the frame rate is quantised to 1/100 s.
sourceraw docstring

widthclj

(width img)
source

xlsxclj

(xlsx spec)

A .xlsx workbook as bytes, written by rust_xlsxwriter.

{:properties {:title :subject :author :manager :company :category :keywords :comments :status} :sheets [{:name "Sales" :columns [{:first 0 :last 3 :width 18 :format {…} :hidden false}] :rows [{:index 0 :height 22 :format {…}}] :freeze [row col] :autofilter [r1 c1 r2 c2] :merges [{:range [r1 c1 r2 c2] :value … :format {…}}] :images [{:row :col :data <bytes> :scale-x :scale-y}] :cells [{:row 0 :col 0 :value "Region" :format {:bold true}}]}]}

A cell's :type is optional — a string/number/boolean/nil is written as itself; the explicit types are string number boolean formula datetime date time url blank and rich (a run list of [format? text]). A :format map (:bold :italic :underline :font :size :color :bg-color :align :valign :wrap :rotation :indent :border :num-format :locked …) is deduplicated into one workbook format automatically.

A `.xlsx` workbook as bytes, written by rust_xlsxwriter.

`{:properties {:title :subject :author :manager :company :category :keywords
               :comments :status}
  :sheets [{:name "Sales"
            :columns [{:first 0 :last 3 :width 18 :format {…} :hidden false}]
            :rows [{:index 0 :height 22 :format {…}}]
            :freeze [row col] :autofilter [r1 c1 r2 c2]
            :merges [{:range [r1 c1 r2 c2] :value … :format {…}}]
            :images [{:row :col :data <bytes> :scale-x :scale-y}]
            :cells [{:row 0 :col 0 :value "Region" :format {:bold true}}]}]}`

A cell's `:type` is optional — a string/number/boolean/nil is written as
itself; the explicit types are `string` `number` `boolean` `formula`
`datetime` `date` `time` `url` `blank` and `rich` (a run list of
`[format? text]`). A `:format` map (`:bold :italic :underline :font :size
:color :bg-color :align :valign :wrap :rotation :indent :border :num-format
:locked` …) is deduplicated into one workbook format automatically.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close