From aa9a9d1bdcfd4bf3ccc025780053a104b6fc3766 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ravi=20S=2E=20R=C4=81mphal?= <rramphal@gmail.com>
Date: Tue, 16 Jan 2024 06:29:32 -0600
Subject: [PATCH] feat: add support for DjVu image format

---
 README.md                   |   9 +++++----
 src/map.rs                  |   6 ++++++
 src/matchers/image.rs       |  16 ++++++++++++++++
 testdata/sample_multi.djvu  | Bin 0 -> 1155 bytes
 testdata/sample_single.djvu | Bin 0 -> 372 bytes
 tests/image.rs              |   4 ++++
 6 files changed, 31 insertions(+), 4 deletions(-)
 create mode 100644 testdata/sample_multi.djvu
 create mode 100644 testdata/sample_single.djvu

diff --git a/README.md b/README.md
index c094f25..cea0d80 100644
--- a/README.md
+++ b/README.md
@@ -5,11 +5,11 @@
 [![documentation](https://docs.rs/infer/badge.svg)](https://docs.rs/infer)
 
 Small crate to infer file and MIME type by checking the
-[magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)) signature. 
+[magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)) signature.
 
-Adaptation of [filetype](https://github.com/h2non/filetype) Go package ported to Rust. 
+Adaptation of [filetype](https://github.com/h2non/filetype) Go package ported to Rust.
 
-Does not require magic file database (i.e. `/etc/magic`). 
+Does not require magic file database (i.e. `/etc/magic`).
 
 ## Features
 
@@ -88,7 +88,7 @@ assert!(infer::is_image(&buf));
 ```
 
 ### Adds a custom file type matcher
-    
+
 ```rust
 fn custom_matcher(buf: &[u8]) -> bool {
     return buf.len() >= 3 && buf[0] == 0x10 && buf[1] == 0x11 && buf[2] == 0x12;
@@ -121,6 +121,7 @@ assert_eq!(kind.extension(), "foo");
 - **psd** - `image/vnd.adobe.photoshop`
 - **ico** - `image/vnd.microsoft.icon`
 - **ora** - `image/openraster`
+- **djvu** - `image/vnd.djvu`
 
 #### Video
 
diff --git a/src/map.rs b/src/map.rs
index 7d03c1a..cd0b9ad 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -209,6 +209,12 @@ matcher_map!(
         "ora",
         matchers::image::is_ora
     ),
+    (
+        MatcherType::Image,
+        "image/vnd.djvu",
+        "djvu",
+        matchers::image::is_djvu
+    ),
     // Video
     (
         MatcherType::Video,
diff --git a/src/matchers/image.rs b/src/matchers/image.rs
index 0ecf624..04e548c 100644
--- a/src/matchers/image.rs
+++ b/src/matchers/image.rs
@@ -194,6 +194,22 @@ pub fn is_ora(buf: &[u8]) -> bool {
         && buf[53] == 0x72
 }
 
+/// Returns whether a buffer is DjVu image data.
+pub fn is_djvu(buf: &[u8]) -> bool {
+    buf.len() > 14
+        && buf[0] == 0x41
+        && buf[1] == 0x54
+        && buf[2] == 0x26
+        && buf[3] == 0x54
+        && buf[4] == 0x46
+        && buf[5] == 0x4F
+        && buf[6] == 0x52
+        && buf[7] == 0x4D
+        && buf[12] == 0x44
+        && buf[13] == 0x4A
+        && buf[14] == 0x56
+}
+
 // GetFtyp returns the major brand, minor version and compatible brands of the ISO-BMFF data
 fn get_ftyp(buf: &[u8]) -> Option<(&[u8], &[u8], impl Iterator<Item = &[u8]>)> {
     if buf.len() < 16 {
diff --git a/testdata/sample_multi.djvu b/testdata/sample_multi.djvu
new file mode 100644
index 0000000000000000000000000000000000000000..a24483c6bd9db789836ea8e8656b1078ff54b23f
GIT binary patch
literal 1155
zcmZ<^Q44YN5AtPTU@3R;3iEaG1hE+m8X1^?lm`O?Q^^1S7xs6_--`@5d|N76?7zwW
z>WiXmQ|uD?6(inVS*Rc$Jy-v!J;V$q7N8lSo_=orK+RmtK`cHJ3_6TrjKNt+RSXP_
z&l<GOr6+BR{Bg`^y6j$|!#6gsT-D7c@W+19w(4nWixVW8!faix=D(Y$*y3l(((z5^
zgKFE}B@(7#Cbjk)8jDvIl|T41Yd+&ov8`F})Ev*=akGD;*liMX^x_Zg1N#5}|5tO0
z<|%mI67g#-JCOJP5BtwI;$cFz@9(GFFg-MhYw5f>H%)JCOAvYbZA*yXIrBd)Oh*pv
zk_p;>sImO#St%Emv;O`YZ`^N+KF`X_nt91N|No!OlO`%9@BFc=sfNA&|Nn1-f)^(#
z{@-@<VRh|Ke&fz6uNx2E-M0S!Z_?A}>g@4nPbr-~8MZY&{#p4l@%u-3%|$lecYShJ
zX3yQ5f13MNL<^>t$Zl?nE||<1dj0yJ#Vnd{xAkB2zH!dvTYld=hpY2MV!TRvT&**#
z+*7JAS++bp%pBCB&1zG7*8EP*yoA(wGYT*4zt|Y`ah>L;u=h303)XqZmAv`B%5#^{
zN>!WX(_YSxJ-31Vss+<)F0<{cBx+BIrQSK^?|i^6aL1i8^Eh@_g^9=P*S+}j!0V*_
z|Nn;^ln&YlmoIytefkAI*S3}oI>!HaneXk`^r^IT`RR|!|4#)Nt*iAE$uE3$eZt~r
zw$j%o8~*?Qw@b&ttp4??9ls){3y3u{Cak=-LSV}86CRC5dnEq<w{lw)b4wbU7S!>j
z1t!kXv@lH4!p@5hH!p}CxN4OAMrr@F=MQX8FLV8x<78d?S>5E_)x%rAXxs67i(OM;
d6s?|fd&_yRdpv^iKlV3lfAIfa?enQ6tN_JHIr0Di

literal 0
HcmV?d00001

diff --git a/testdata/sample_single.djvu b/testdata/sample_single.djvu
new file mode 100644
index 0000000000000000000000000000000000000000..1cf68668eb18dc6243dadd588556a3e06c1cb0b3
GIT binary patch
literal 372
zcmV-)0gL`YR3=nLPf|?)00C%3N>)`#PDW1v000UDQUpvG04xC(0aI#XdH?_cN`NZP
zXJWRx=<4F?dCcpKuaR5fEBoX5NEfO@J^%8}iT!}thkvlSpv?ZUG~mf7kM-N%Fv~Xx
zZR>A?Z1fQq;5OTb^9x?ry5+}u>EJu%LAGMP)S~}pgfAm>nk$b+6yCw>Ehhzm!)t4h
zn~3!F%PZ5_p6mdrzCcue|Nr_`dP1=AZC<045A4~OXNnNwclhj~xKelPMvNS&nVBu~
zp18`8=0pIeE87eYlY{W9vO$Ez8fCHPbr{P9>IS2Qj4*BlffO#&1})@-#=E23t*6We
z3?y&ES*(!U;>qAl?6EgQw3{jGNJ}TEAo#j}E?LpYspdO4I>_`TXej^jJ}h6+5BU=6
zmqB$)NibxOgQDt*JhZvuITx^D{$X&K_8gElW$Gl~s&->N09|&85!QeI|LFBzvW2#6
SB;~+A?U~l`hr{Qx**hP90lwM*

literal 0
HcmV?d00001

diff --git a/tests/image.rs b/tests/image.rs
index ecaf351..f15fd08 100644
--- a/tests/image.rs
+++ b/tests/image.rs
@@ -29,3 +29,7 @@ test_format!(Image, "image/avif", "avif", avif, "sample.avif");
 test_format!(Image, "image/jxl", "jxl", jxl, "spline_on_first_frame.jxl");
 
 test_format!(Image, "image/openraster", "ora", ora, "sample.ora");
+
+test_format!(Image, "image/vnd.djvu", "djvu", djvu1, "sample_single.djvu");
+
+test_format!(Image, "image/vnd.djvu", "djvu", djvu2, "sample_multi.djvu");