Skip to content

Make naming adhere to Rust API guidelines, docs, CLI help, dependencies bump #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
[package]
name = "dng"
description = "A pure rust library for reading / writing DNG files providing access to the raw data"
version = "1.5.2"
description = "A pure Rust library for reading/writing of DNG files, providing access to the RAW data"
version = "1.6.0"
keywords = ["dng", "raw", "tiff", "ifd", "exif"]
categories = ["command-line-utilities", "multimedia::images", "multimedia::encoding", "multimedia::video"]
categories = [
"command-line-utilities",
"multimedia::images",
"multimedia::encoding",
"multimedia::video",
]
repository = "https://github.com/apertus-open-source-cinema/dng-rs"
readme = "README.md"
license = "AGPL-3.0"
edition = "2021"

[features]
default = []
yaml = ["dep:fraction", "dep:lazy-regex", "dep:textwrap", "dep:yaml-peg"]
cli = ["yaml", "dep:clap"]

Expand All @@ -22,17 +28,16 @@ name = "compile_dng"
required-features = ["cli"]

[dependencies]
derivative = "2.2.0"
derivative = "2.2"

# these are needed for the yaml reading / writing
fraction = { version = "0.12.1", optional = true }
lazy-regex = { version = "2.3.1", optional = true }
textwrap = { version = "0.16.0", optional = true }
yaml-peg = { version = "1.0.5", optional = true }
# these are needed for the yaml reading/writing
fraction = { version = "0.15", optional = true }
lazy-regex = { version = "3.4", optional = true }
textwrap = { version = "0.16", optional = true }
yaml-peg = { version = "1.0", optional = true }

# this is only needed for the cli tools
clap = { version = "4.0.22", features = ["derive"], optional = true }

clap = { version = "4.5", features = ["derive"], optional = true }

[build-dependencies]
json = "0.12.4"
json = "0.12"
56 changes: 31 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,58 @@
# DNG-rs   [![crates-shield]][crates.io] [![docs-shield]][docs.rs]
# `dng`   [![crates-shield]][crates.io] [![docs-shield]][docs.rs]

[crates-shield]: https://img.shields.io/crates/v/dng.svg
[crates.io]: https://crates.io/crates/dng

[docs-shield]: https://img.shields.io/docsrs/dng.svg
[docs.rs]: https://docs.rs/dng

**A pure rust library for reading / writing DNG files providing access to the raw data in a zero-copy friendly way.**
Also containing code for reading / writing a human-readable YAML representation of DNG tags / the IFD structure.
DNG-rs also supports interacting with DCP (Dng Camera Profile) files, but that is on a best-effort basis since I
**A pure rust library for reading/writing DNG files providing access to the raw data in a zero-copy friendly way.**
Also containing code for reading/writing a human-readable YAML representation of DNG tags/the IFD structure.

The crate also supports interacting with DCP (DNG Camera Profile) files, but that is on a best-effort basis since I
was unable to find official documentation on that.

## Tools

This library also contains a pair of cli tools for converting a DNG into a human-readable YAML representation and back.

These are kind of similar to [dcpTool](https://dcptool.sourceforge.net/Usage.html)'s `-d` and `-c` but use YAML rather than XML.

```shell
$ target/debug/dump_dng -h
Dump the IFD metadata of a TIFF / DNG image to a human readable yaml representation
### `dump_dng`

```
Dump the IFD metadata of a TIFF/DNG image to a human readable YAML representation

Usage: dump_dng [OPTIONS] <FILE>

Arguments:
<FILE> input file to get the metadata from
<FILE> Input file to get the metadata from

Options:
-f, --dump-rational-as-float convert Rational and SRational types to float for better readability (this is lossy)
-e, --extract extract strips, tiles and larger blobs into a directory. also write the ifd chain as a yaml file there
-h, --help Print help information
-V, --version Print version information

-f, --dump-rational-as-float Convert (signed) rational types to float for better readability (this is lossy!)
-e, --extract Extract strips, tiles and larger blobs into a directory; also write the IFD chain as a YAML file there
-h, --help Print help
-V, --version Print version
```

```shell
$ target/debug/compile_dng -h
Assemble a DNG file from some of other dng files, plain raw files and metadata
### `compile_dng`

```
Assemble a DNG file from some of other DNG files, plain RAW files and metadata

Usage: compile_dng [OPTIONS] --yaml <YAML>

Options:
--yaml <YAML> input YAML file to get the metadata from
--dcp
-b, --big-endian
-h, --help Print help information
-V, --version Print version information
--yaml <YAML> Input YAML file to get the metadata from
--dcp Write the DCP magic bytes (DNG Camera profile) instead of the DNG ones
-b, --big-endian Write a big endian DNG (default: little endian)
-h, --help Print help
-V, --version Print version
```

example:
```shell
$ target/debug/dump_dng src/yaml/testdata/axiom_beta_simulated.dcp -f
Example:

```
$ dump_dng src/yaml/testdata/axiom_beta_simulated.dcp -f
UniqueCameraModel: "AXIOM Beta"
ProfileName: "AXIOM Beta spectral simulated"
ProfileEmbedPolicy: allow copying
Expand All @@ -67,5 +71,7 @@ ColorMatrix2: [
```

## Current Status

This library should be in a usable state for many applications. However, a more high-level API is not implemented (yet?).

For that (and support for other raw formats) you might want to use [rawloader](https://docs.rs/rawloader/latest/rawloader/).
15 changes: 11 additions & 4 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ fn parse_ifd_field_descriptor(mut json: JsonValue) -> (String, String) {
);
(name, definition)
}

fn doc_lines(lines: String) -> String {
let out = lines.lines().fold(String::new(), |mut out, s| {
let _ = write!(out, "/// {s}");
Expand All @@ -97,37 +98,41 @@ fn doc_lines(lines: String) -> String {
"/// ".into()
}
}

fn parse_dtype(mut json: JsonValue) -> String {
let entrys: String = json
.members_mut()
.map(|entry| parse_single_dtype(entry.take()) + ", ")
.collect();
format!("&[{entrys}]")
}

fn parse_single_dtype(json: JsonValue) -> String {
match json.as_str().unwrap() {
"BYTE" => "IfdValueType::Byte".to_string(),
"ASCII" => "IfdValueType::Ascii".to_string(),
"SHORT" => "IfdValueType::Short".to_string(),
"LONG" => "IfdValueType::Long".to_string(),
"RATIONAL" => "IfdValueType::Rational".to_string(),
"SBYTE" => "IfdValueType::SByte".to_string(),
"SBYTE" => "IfdValueType::SignedByte".to_string(),
"UNDEFINED" => "IfdValueType::Undefined".to_string(),
"SSHORT" => "IfdValueType::SShort".to_string(),
"SLONG" => "IfdValueType::SLong".to_string(),
"SRATIONAL" => "IfdValueType::SRational".to_string(),
"SSHORT" => "IfdValueType::SignedShort".to_string(),
"SLONG" => "IfdValueType::SignedLong".to_string(),
"SRATIONAL" => "IfdValueType::SignedRational".to_string(),
"FLOAT" => "IfdValueType::Float".to_string(),
"DOUBLE" => "IfdValueType::Double".to_string(),
_ => unreachable!(),
}
}

fn parse_count(json: JsonValue) -> String {
let str = json.as_str().unwrap();
match str.parse::<u32>() {
Ok(n) => format!("IfdCount::ConcreteValue({n})"),
Err(_) => "IfdCount::N".to_string(),
}
}

fn parse_interpretation(mut json: JsonValue) -> String {
let kind = json.remove("kind").take_string().unwrap();
match kind.as_str() {
Expand All @@ -153,6 +158,7 @@ fn parse_interpretation(mut json: JsonValue) -> String {
_ => "IfdTypeInterpretation::Default".to_string(),
}
}

fn parse_ifd_type(json: JsonValue) -> String {
match json.as_str().unwrap() {
"IFD" => "IfdType::Ifd".to_string(),
Expand All @@ -161,6 +167,7 @@ fn parse_ifd_type(json: JsonValue) -> String {
_ => unreachable!(),
}
}

fn parse_reverse_map(json: JsonValue) -> String {
let entries: String = json.entries().fold(String::new(), |mut output, (k, v)| {
let _ = write!(
Expand Down
9 changes: 4 additions & 5 deletions src/bin/compile_dng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,18 @@ use std::fs::{File, OpenOptions};
use std::io::Read;
use std::path::Path;

/// Assemble a DNG file from some of other dng files, plain raw files and metadata
#[derive(clap::Parser)]
#[command(author, version, about, long_about = None)]
#[command(author, version, about="Assemble a DNG file from some of other DNG files, plain RAW files and metadata", long_about = None)]
struct Args {
/// input YAML file to get the metadata from
/// Input YAML file to get the metadata from
#[arg(long)]
yaml: String,

// write the DCP magic bytes (DNG Camera profile) instead of the DNG ones
/// Write the DCP magic bytes (DNG Camera profile) instead of the DNG ones
#[arg(long, action)]
dcp: bool,

// write a big endian DNG (default: little endian)
/// Write a big endian DNG (default: little endian)
#[arg(short = 'b', long, action)]
big_endian: bool,
}
Expand Down
19 changes: 9 additions & 10 deletions src/bin/dump_dng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@ use std::io::Write;
use std::path::Path;
use std::sync::Arc;

/// Dump the IFD metadata of a TIFF / DNG image to a human readable yaml representation
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
#[command(author, version, about="Dump the IFD metadata of a TIFF/DNG image to a human readable YAML representation", long_about = None)]
struct Args {
/// input file to get the metadata from
/// Input file to get the metadata from
file: String,
/// convert Rational and SRational types to float for better readability (this is lossy)
/// Convert (signed) rational types to float for better readability (this is lossy!)
#[arg(short = 'f', long, action)]
dump_rational_as_float: bool,
/// extract strips, tiles and larger blobs into a directory. also write the ifd chain as a yaml file there
/// Extract strips, tiles and larger blobs into a directory; also write the IFD chain as a YAML file there
#[arg(short = 'e', long, action)]
extract: bool,
}
Expand All @@ -32,7 +31,7 @@ fn main() {
let matrix_prettify_visitor = move |entry: IfdEntryRef| -> Option<String> {
if entry
.tag
.get_known_name()
.known_name()
.map_or(true, |name| !name.to_lowercase().contains("matrix"))
{
return None;
Expand Down Expand Up @@ -85,7 +84,7 @@ fn main() {
let dng = dng.clone();
move |entry: IfdEntryRef| -> Option<String> {
if matches!(
entry.tag.get_type_interpretation(),
entry.tag.type_interpretation(),
Some(IfdTypeInterpretation::Blob)
) {
let bytes_vec: Option<Vec<u8>> = entry
Expand Down Expand Up @@ -116,7 +115,7 @@ fn main() {
}

if matches!(
entry.tag.get_type_interpretation(),
entry.tag.type_interpretation(),
Some(IfdTypeInterpretation::Offsets { .. })
) && !matches!(entry.value, IfdValue::List(_))
{
Expand Down Expand Up @@ -145,7 +144,7 @@ fn main() {
visitor: Some(Arc::new(extract_visitor)),
};

let ifd_yaml = yaml_dumper.dump_ifd(dng.get_ifd0());
let ifd_yaml = yaml_dumper.dump_ifd(dng.first_ifd());
OpenOptions::new()
.write(true)
.create(true)
Expand All @@ -159,7 +158,7 @@ fn main() {
dump_rational_as_float: args.dump_rational_as_float,
visitor: Some(Arc::new(matrix_prettify_visitor)),
};
let ifd_yaml = yaml_dumper.dump_ifd(dng.get_ifd0());
let ifd_yaml = yaml_dumper.dump_ifd(dng.first_ifd());
print!("{ifd_yaml}")
}
}
3 changes: 3 additions & 0 deletions src/byte_order_rw/byte_order_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct ByteOrderReader<R: Read> {
reader: R,
is_little_endian: bool,
}

impl<R: Read> ByteOrderReader<R> {
pub fn new(reader: R, is_little_endian: bool) -> Self {
Self {
Expand All @@ -31,6 +32,7 @@ macro_rules! generate_read_function {
}
};
}

impl<R: Read> ByteOrderReader<R> {
generate_read_function!(read_u8, u8);
generate_read_function!(read_i8, i8);
Expand All @@ -51,6 +53,7 @@ impl<R: Read> Deref for ByteOrderReader<R> {
&self.reader
}
}

impl<R: Read> DerefMut for ByteOrderReader<R> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.reader
Expand Down
3 changes: 3 additions & 0 deletions src/byte_order_rw/byte_order_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct ByteOrderWriter<W: Write> {
writer: W,
is_little_endian: bool,
}

impl<W: Write> ByteOrderWriter<W> {
pub fn new(writer: W, is_little_endian: bool) -> Self {
Self {
Expand All @@ -30,6 +31,7 @@ macro_rules! generate_write_function {
}
};
}

impl<W: Write> ByteOrderWriter<W> {
generate_write_function!(write_u8, u8);
generate_write_function!(write_i8, i8);
Expand All @@ -50,6 +52,7 @@ impl<W: Write> Deref for ByteOrderWriter<W> {
&self.writer
}
}

impl<W: Write> DerefMut for ByteOrderWriter<W> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.writer
Expand Down
Loading