Skip to content

Commit 1dab89f

Browse files
committed
Generalize write for pre-encoded tags
1 parent 45f617b commit 1dab89f

File tree

4 files changed

+106
-12
lines changed

4 files changed

+106
-12
lines changed

src/encoder/mod.rs

Lines changed: 97 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::{
1212
decoder::ifd::Entry,
1313
error::{TiffResult, UsageError},
1414
tags::{
15-
CompressionMethod, ExtraSamples, IfdPointer, PhotometricInterpretation, ResolutionUnit,
16-
SampleFormat, Tag, Type,
15+
ByteOrder, CompressionMethod, ExtraSamples, IfdPointer, PhotometricInterpretation,
16+
ResolutionUnit, SampleFormat, Tag, Type, ValueBuffer,
1717
},
1818
Directory, TiffError, TiffFormatError,
1919
};
@@ -291,6 +291,21 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
291291
})
292292
}
293293

294+
/// Write a tag-value pair with prepared byte data.
295+
///
296+
/// Note that the library will _not_ attempt to verify that the data type or the count of the
297+
/// opaque value is permissible for the given tag. The data will be associated with the tag
298+
/// exactly as-is.
299+
///
300+
/// An error is returned if the byte order of the presented value does not match the byte order
301+
/// of the underlying file (i.e. the [native byte order](`crate::tags::ByteOrder::native`)).
302+
pub fn write_tag_value(&mut self, tag: Tag, value: &ValueBuffer) -> TiffResult<()> {
303+
let entry = self.write_entry_buf(value)?;
304+
self.directory.extend([(tag, entry)]);
305+
306+
Ok(())
307+
}
308+
294309
/// Write a single ifd tag.
295310
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> {
296311
// Encodes the value if necessary. In the current bytes all integers are taken as native
@@ -301,12 +316,12 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
301316
value.write(&mut writer)?;
302317
}
303318

304-
let entry = Self::write_value(
319+
let entry = Self::write_entry_inner(
305320
self.writer,
306-
&DirectoryEntry {
321+
DirectoryEntry {
307322
data_type: <T>::FIELD_TYPE,
308323
count: value.count().try_into()?,
309-
data: bytes,
324+
data: bytes.into(),
310325
},
311326
)?;
312327

@@ -317,13 +332,70 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
317332

318333
/// Write some data to the tiff file, the offset of the data is returned.
319334
///
320-
/// This could be used to write tiff strips.
335+
/// This could be used to write tiff strips. All of the data will be written into the file as a
336+
/// slice of bytes. It can not be used as an entry in a directory directly, where very short
337+
/// values are instead represented in the offset field directly. Use [`Self::write_entry`]
338+
/// instead.
321339
pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
322340
let offset = self.writer.offset();
323341
value.write(self.writer)?;
324342
Ok(offset)
325343
}
326344

345+
/// Write some data directly to the tiff file, the offset of the data is returned.
346+
///
347+
/// All of the data will be written into the file as a slice of bytes. It can not be used as an
348+
/// entry in a directory directly, where very short values are instead represented in the
349+
/// offset field directly. Use [`Self::write_entry_buf`] instead.
350+
///
351+
/// An error is returned if the byte order of the presented value does not match the byte order
352+
/// of the underlying file (i.e. the [native byte order](`crate::tags::ByteOrder::native`)).
353+
pub fn write_data_buf(&mut self, value: &ValueBuffer) -> TiffResult<u64> {
354+
self.check_value_byteorder(value)?;
355+
let offset = self.writer.offset();
356+
self.writer.write_bytes(value.as_bytes())?;
357+
Ok(offset)
358+
}
359+
360+
/// Write a directory value into the file.
361+
///
362+
/// Returns an [`Entry`]. If the value is short enough it will *not* be written in the file but
363+
/// stored in the entry's offset field.
364+
pub fn write_entry<T: TiffValue>(&mut self, value: T) -> TiffResult<Entry> {
365+
Self::write_entry_inner(
366+
self.writer,
367+
DirectoryEntry {
368+
data_type: T::FIELD_TYPE,
369+
count: K::convert_offset(value.count() as u64)?,
370+
// FIXME: not optimal we always allocate here. But the only other method we have
371+
// available is `T::write(&mut TiffWriter<W>)` and we must pass that into
372+
// `write_entry_inner` but then have a combinatorial explosion of instantiations
373+
// which is absurd.
374+
data: value.data(),
375+
},
376+
)
377+
}
378+
379+
/// Write a directory value into the file.
380+
///
381+
/// An error is returned if the byte order of the presented value does not match the byte order
382+
/// of the underlying file (i.e. the [native byte order](`crate::tags::ByteOrder::native`)).
383+
///
384+
/// Returns an [`Entry`]. If the value is short enough it will *not* be written in the file but
385+
/// stored in the entry's offset field.
386+
pub fn write_entry_buf(&mut self, value: &ValueBuffer) -> TiffResult<Entry> {
387+
self.check_value_byteorder(value)?;
388+
389+
Self::write_entry_inner(
390+
self.writer,
391+
DirectoryEntry {
392+
data_type: value.data_type(),
393+
count: K::convert_offset(value.count())?,
394+
data: value.as_bytes().into(),
395+
},
396+
)
397+
}
398+
327399
/// Insert previously written key-value entries.
328400
///
329401
/// It is the caller's responsibility to ensure that the data referred to by the entries is
@@ -377,17 +449,21 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
377449
Ok(offset)
378450
}
379451

380-
fn write_value(
452+
// Does it really make sense that this would be a (potentially) compressible write? For actual
453+
// directory values their size must match the data written so there's going to be a silent
454+
// problem if that actually occurs.
455+
fn write_entry_inner(
381456
writer: &mut TiffWriter<W>,
382-
value: &DirectoryEntry<K::OffsetType>,
457+
value: DirectoryEntry<K::OffsetType>,
383458
) -> TiffResult<Entry> {
384-
let &DirectoryEntry {
459+
let DirectoryEntry {
385460
data: ref bytes,
386461
ref count,
387462
data_type,
388463
} = value;
389464

390465
let in_entry_bytes = mem::size_of::<K::OffsetType>();
466+
391467
let mut offset_bytes = [0; 8];
392468

393469
if bytes.len() > in_entry_bytes {
@@ -406,6 +482,7 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
406482
// and some oversight, we can not clone the `count: K::OffsetType` and thus not convert it.
407483
// Instead, write it to a buffer...
408484
let mut count_bytes = [0; 8];
485+
debug_assert!(in_entry_bytes == 4 || in_entry_bytes == 8);
409486
// Nominally Cow but we only expect `Cow::Borrowed`.
410487
count_bytes[..count.bytes()].copy_from_slice(&count.data());
411488

@@ -419,6 +496,15 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
419496
})
420497
}
421498

499+
fn check_value_byteorder(&self, value: &ValueBuffer) -> TiffResult<()> {
500+
// FIXME: we should enable writing files with any chosen byte order.
501+
if value.byte_order() != ByteOrder::native() {
502+
Err(TiffError::UsageError(UsageError::ByteOrderMismatch))
503+
} else {
504+
Ok(())
505+
}
506+
}
507+
422508
/// Provides the number of bytes written by the underlying TiffWriter during the last call.
423509
fn last_written(&self) -> u64 {
424510
self.writer.last_written()
@@ -824,10 +910,10 @@ impl<'a, W: Write + Seek, C: ColorType, K: TiffKind> Drop for ImageEncoder<'a, W
824910
}
825911
}
826912

827-
struct DirectoryEntry<S> {
913+
struct DirectoryEntry<'data, S> {
828914
data_type: Type,
829915
count: S,
830-
data: Vec<u8>,
916+
data: std::borrow::Cow<'data, [u8]>,
831917
}
832918

833919
/// Trait to abstract over Tiff/BigTiff differences.

src/encoder/tiff_value.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ use super::writer::TiffWriter;
1616
pub trait TiffValue {
1717
const BYTE_LEN: u8;
1818
const FIELD_TYPE: Type;
19+
20+
// FIXME: If we want this to mean 'count' in the sense of `Entry` it should return `u32` or
21+
// `u64`, probably the latter with `convert_offset` taking care of the check against `u32::MAX`
22+
// for classic tiff.
1923
fn count(&self) -> usize;
24+
2025
fn bytes(&self) -> usize {
2126
self.count() * usize::from(Self::BYTE_LEN)
2227
}

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ quick_error! {
208208
ReconfiguredAfterImageWrite {
209209
display("attempted to reconfigure the encoder after image writing has started")
210210
}
211+
ByteOrderMismatch {
212+
display("attempted to use data with a byte order that does not match the required one")
213+
}
211214
}
212215
}
213216

src/tags.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ impl ValueBuffer {
366366
self.byte_order
367367
}
368368

369-
pub fn tiff_type(&self) -> Type {
369+
pub fn data_type(&self) -> Type {
370370
self.ty
371371
}
372372

0 commit comments

Comments
 (0)