/// <summary> /// Flush the TIFF file header into the stream. /// </summary> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that fires if the user has requested to abort this operation.</param> /// <returns></returns> public async Task FlushAsync(CancellationToken cancellationToken = default) { EnsureNotDisposed(); cancellationToken.ThrowIfCancellationRequested(); Debug.Assert(_writer != null); if (_requireBigTiff && !_useBigTiff) { throw new InvalidOperationException("Must use BigTIFF format. But it is disabled."); } byte[] buffer = ArrayPool <byte> .Shared.Rent(SmallBufferSize); try { Array.Clear(buffer, 0, 16); TiffFileHeader.Write(buffer, _imageFileDirectoryOffset, BitConverter.IsLittleEndian, _useBigTiff); await _writer !.WriteAsync(0, new ArraySegment <byte>(buffer, 0, _useBigTiff ? 16 : 8), cancellationToken).ConfigureAwait(false); await _writer.FlushAsync(cancellationToken).ConfigureAwait(false); } finally { ArrayPool <byte> .Shared.Return(buffer); } }
/// <summary> /// Try to parse TIFF file header from the specified buffer. /// </summary> /// <param name="data">The buffer to use.</param> /// <param name="header">The parsed TIFF file header.</param> /// <returns>True if the TIFF file header is successfully parsed. Otherwise, false.</returns> public static bool TryParse(ReadOnlySpan <byte> data, out TiffFileHeader header) { if (data.Length < 8) { header = default; return(false); } // Read byte order short byteOrderFlag = MemoryMarshal.Read <short>(data); bool reverseEndianRequired; if (byteOrderFlag == LittleEndianByteOrderFlag) { reverseEndianRequired = !BitConverter.IsLittleEndian; } else if (byteOrderFlag == BigEndianByteOrderFlag) { reverseEndianRequired = BitConverter.IsLittleEndian; } else { header = default; return(false); } // Read TIFF version short version = MemoryMarshal.Read <short>(data.Slice(2)); if (reverseEndianRequired) { version = BinaryPrimitives.ReverseEndianness(version); } if (version == StandardTiffVersion) { // Standard TIFF int imageFileDirectoryOffset32 = MemoryMarshal.Read <int>(data.Slice(4)); if (reverseEndianRequired) { imageFileDirectoryOffset32 = BinaryPrimitives.ReverseEndianness(imageFileDirectoryOffset32); } // An IFD can be at any location in the file after the header but must begin on a word boundary. if (imageFileDirectoryOffset32 < 8 || (imageFileDirectoryOffset32 & 0b1) == 1) { header = default; return(false); } header = new TiffFileHeader(byteOrderFlag, imageFileDirectoryOffset32); return(true); } else if (version == BigTiffVersion) { // BigTIFF data = data.Slice(4); if (data.Length < 12) { header = default; return(false); } short byteSizeOfOffsets = MemoryMarshal.Read <short>(data); short bigTiffConstant = MemoryMarshal.Read <short>(data.Slice(2)); long imageFileDirectoryOffset64 = MemoryMarshal.Read <long>(data.Slice(4)); if (reverseEndianRequired) { byteSizeOfOffsets = BinaryPrimitives.ReverseEndianness(byteSizeOfOffsets); // Uncomment this when we support BigTIFF constant other than zero. // bigTiffConstant = BinaryPrimitives.ReverseEndianness(bigTiffConstant); imageFileDirectoryOffset64 = BinaryPrimitives.ReverseEndianness(imageFileDirectoryOffset64); } if (byteSizeOfOffsets != 8) { // Unsupported byte size of offsets header = default; return(false); } if (bigTiffConstant != 0) { header = default; return(false); } // An IFD can be at any location in the file after the header but must begin on a word boundary. if (imageFileDirectoryOffset64 < 16 || (imageFileDirectoryOffset64 & 0b1) == 1) { header = default; return(false); } header = new TiffFileHeader(byteOrderFlag, BigTiffVersion, byteSizeOfOffsets, imageFileDirectoryOffset64); return(true); } else { header = default; return(false); } }