TIFFメタデータ・フォーマット仕様および使用上のノート

イメージの読取り 「イメージを書く」
「ネイティブ・ストリーム・メタデータ形式」
「ネイティブ・イメージ・メタデータ形式」

イメージの読取り

TIFFイメージは、ImageReaderによって読み取られます。これは、そのパブリック・インタフェースおよび指定されたTIFFImageReadParamを介して制御できます。

色変換

ソース・イメージ・データのフォトメトリック・タイプがCIE L*a*b*またはYCbCrで、宛先のカラー・スペース・タイプがRGBの場合、ソース・イメージ・データは内部カラー・コンバータを使用してRGBに自動的に変換されます。

カラー・スペース

デフォルトで割り当てられるrawカラー空間(つまり、ユーザーが指定した ImageTypeSpecifierがない場合)は、次のうち最初に適用されます。

デフォルトのCMYKカラー・スペースに使用される正規化されたカラー座標変換は、次のように定義されます。

他の色空間を推測できないときに使用される一般的な色空間は、単にデータをロードできるようにするためだけに提供されます。 これは、どのような種類の正確な変換を提供することを意図していません。

データが前述で正しく処理されていないカラー・スペースにあることが判明している場合は、ImageTypeSpecifierをリーダーに提供し、問題のデータに適したカラー・スペースから導出する必要があります。

ICCプロファイル

ICCプロファイルがイメージメタデータ(BaselineTIFFTagSet)に含まれている場合。TAG_ICC_PROFILE、タグ番号34675)は、これを使用してロードされたイメージの色空間を作成しようとします。 これは、データ・レイアウトがコンポーネント・タイプで、ピクセル当たりのサンプル数がICCプロファイルで記述されたコンポーネント数と等しいか、1より大きい場合に使用されます。 ICCプロファイルを使用しない場合は、前述のいずれかの以降の手順で色空間が推測されます。

なんらかの理由で埋込みICCプロファイルが自動的に使用されない場合は、この手順に従って手動で使用できます。

  1. ImageReader.getImageMetadataからイメージ・メタデータを取得します
  2. ICCプロファイル・フィールドとその値を抽出します。
  3. ICC_Profile.getInstance(byte[])を使用して、ICCプロファイル・フィールド・データから作成されたICC_ProfileからICC_ColorSpaceを作成します。
  4. ICC_ColorSpaceを受け入れるいずれかのファクトリ・メソッドを使用して、新しい色空間からImageTypeSpecifierを作成します。
  5. 互換性のあるImageReadParamを作成し、ImageReadParam.setDestinationTypeを使用してImageTypeSpecifierを設定します。
  6. パラメータ・オブジェクトを適切なreadメソッドに渡します。

ICCプロファイル・フィールドに基づかない推測カラー・スペースがICCプロファイル・ベースのカラー・スペースと互換性がある場合、この推測カラー・スペースから導出された2番目のImageTypeSpecifierは、ImageReader.getImageTypesによって返されるイテレータに含まれます。 イテレータに複数のタイプが含まれている場合、最初のタイプはICCプロファイルに基づいて、2番目のタイプは推測カラー・スペースに基づきます。

メタデータ問題

デフォルトでは、TIFFイメージ・ファイル・ディレクトリ(IFD)で認識されたすべてのフィールドがネイティブ・イメージ・メタデータ・オブジェクトにロードされます。 どのフィールドがロードされるかは、リーダーが認識できるTIFFタグの設定、認識できないタグを持つフィールドを読み取るかどうか、すべてのメタデータを無視するかどうかによって制御できます。 リーダーには、ImageReader.setInput(Object,boolean,boolean)ignoreMetadataパラメータを使用して、通常どおりすべてのメタデータを無視するように通知されます。 どのTIFFTagTIFFImageReadParam.addAllowedTagSet(TIFFTagSet)およびTIFFImageReadParam.removeAllowedTagSet(TIFFTagSet)を介して認識されるか、または認識されないかが通知されます。 ignoreMetadatatrueの場合、イメージの読取りに必要なメタデータのみがネイティブ・イメージ・メタデータ・オブジェクトにロードされます。 ignoreMetadatafalseの場合、リーダーはデフォルトでネイティブ・イメージ・メタデータ・オブジェクトにロードされるのは、イメージの読取りに不可欠なフィールド、または許可されるTIFFTagSetの1つにTIFFTagが含まれているフィールドのみです。 許可されたTIFFTagSetにないタグを持つフィールドの読取りは、TIFFImageReadParam.setReadUnknownTags(boolean)がパラメータtrueで呼び出されたTIFFImageReadParamを渡すことによって強制できます。

TIFFDirectoryオブジェクトを使用すると、メタデータ値へのアクセスを簡略化できます。 TIFFDirectoryのインスタンスは、TIFFDirectory.createFromMetadataメソッドを使用して、TIFFリーダーによって返されるIIOMetadataオブジェクトから作成できます。

TIFFネイティブ・イメージ・メタデータの標準メタデータ形式へのマッピング

次の表に、TIFFネイティブ・イメージ・メタデータからの標準メタデータ形式javax_imageio_1.0要素の導出を示します。
標準メタデータ要素 TIFFフィールドからの導出
/Chroma/ColorSpaceType@name PhotometricInterpretation: WhiteIsZero、BlackIsZero、TransparencyMask = "GRAY"; RGB、PaletteColor => "RGB"; CMYK => "CMYK"; YCbCr => "YCbCr"; CIELab、ICCLab => "Lab".
/Chroma/NumChannels@value SamplesPerPixel
/Chroma/BlackIsZero@value "真" <=> PhotometricInterpretation => WhiteIsZero
/Chroma/Palette ColorMap
/Compression/CompressionTypeName@value 圧縮: Uncompressed => "none"; CCITT 1D => "CCITT RLE"; Group 3 Fax => "CCITT T.4"; Group 4 Fax => "CCITT T.6"; LZW => "LZW"; JPEG => "Old JPEG"; New JPEG => "JPEG"; Zlib =>> "ZLib"; PackBits => "PackBits"; Deflate => "Deflate"; Exif JPEG => "JPEG"。
/Compression/Lossless@value 圧縮: JPEGまたは新規JPEG => "FALSE"、それ以外の場合は"TRUE"。
/Data/PlanarConfiguration@value チャンキー=> "PixelInterleaved"; 平面=> "PlaneInterleaved".
/Data/SampleFormat@value PhotometricInterpretation PaletteColor => "索引"; SampleFormat符号なし整数データ=> "UnsignedIntegral"; SampleFormat 2の補数符号付き整数データ=> "SignedIntegral"; SampleFormat IEEE浮動小数点データ=> "実"; それ以外の場合、要素は発行されません。
/Data/BitsPerSample@value BitsPerSampleを空白区切りのリストとして指定します。
/Data/SampleMSB@value FillOrder: 左から右へ=> BitsPerSample-1のスペース区切りリスト、右から左へ=> 0sのスペース区切りリスト。
/Dimension/PixelAspectRatio@value (1/XResolution)/(1/YResolution)
/Dimension/ImageOrientation@value 概要
/Dimension/HorizontalPixelSize@value ResolutionUnitがNoneでない場合は、1/XResolution (ミリメートル単位)。
/Dimension/VerticalPixelSize@value ResolutionUnitがNoneでない場合は、1/YResolution (ミリメートル単位)。
/Dimension/HorizontalPosition@value ResolutionUnitが「なし」でない場合は、XPosition (ミリメートル単位)。
/Dimension/VerticalPosition@value ResolutionUnitが「なし」でない場合は、YPosition (ミリメートル単位)。
/Document/FormatVersion@value 6.0
/Document/SubimageInterpretation@value NewSubFileType: transparency => "TransparencyMask"; less-resolution => "ReducedResolution"; 単一ページ=> "SinglePage".
/Document/ImageCreationTime@value DateTime
/Text/TextEntry DocumentName、ImageDescription、Make、Model、PageName、Software、Artist、HostComputer、InkNames、Copyright: /Text/TextEntry@keyword = field name、/Text/TextEntry@value = field value。
例: TIFFソフトウェア・フィールド=> /Text/TextEntry@keyword = "Software"、/Text/TextEntry@value = イメージの作成に使用されたソフトウェア・パッケージの名前およびバージョン番号。
/Transparency/Alpha@value ExtraSamples: 関連付けられたアルファ=> "premultiplied"; 未関連付けのアルファ=> "nonpremultiplied"。

Exifイメージの読み取り

TIFFリーダーは、圧縮されていないExifイメージ、または圧縮されたExifイメージのAPP1マーカー・セグメントの内容を読み取るために使用できます。

非圧縮Exifイメージの読取り

非圧縮Exifイメージは、IFDおよびイメージ・データ・コンテンツの特定の順序を持つ1ページまたは2ページの非圧縮TIFFイメージです。 各ピクセルには、フォトメトリック解釈RGBまたは YCbCrを持つ3つの8ビットサンプルがあります。 イメージ・ストリームには1つのプライマリ・イメージが含まれている必要があり、1つのサムネイルが含まれている場合、そのサムネイルも圧縮解除する必要があります。 通常のImageReaderメソッドは、イメージ・データおよびメタデータの読取りに使用できます。

    ImageInputStream input;
    ImageReader tiffReader;
    ImageReadParam tiffReadParam;

    tiffReader.setInput(input);

    // Read primary image and IFD.
    BufferedImage image = tiffReader.read(0, tiffReadParam);
    IIOMetadata primaryIFD = tiffReader.getImageMetadata(0);

    // Read thumbnail if present.
    BufferedImage thumbnail = null;
    if (tiffReader.getNumImages(true) > 1) {
        thumbnail = tiffReader.read(1, tiffReadParam);
    }
Exifサムネイルは、サムネイルではなく、TIFFストリーム内の個別のページとして扱われます。つまり、tiffReader.hasThumbnails(0)falseを返します。

圧縮されたExifイメージの読取り

圧縮されたExifイメージは、3バンドISO/IEC 10918-1ベースラインDCT JPEGストリームで、APP1マーカー・セグメントが挿入されています。 長さの後のマーカー・セグメントのパラメータは、6バイトのシーケンス{'E', 'x', 'i', 'f', 0x00, 0x00}の後に完全なTIFFストリームが続きます。 埋込みTIFFストリームには、オプションでサムネイルIFDと圧縮または非圧縮のサムネイル・イメージ・データが続くJPEGイメージを記述するプライマリIFDが含まれています。 埋込みTIFFストリームには、プライマリIFDに関連付けられたイメージ・データも、JPEGストリーム自体で見つかった情報を複製する記述フィールドも含まれていないことに注意してください。

APP1マーカー・セグメントのパラメータ・コンテンツは、JPEGリーダーによって返されるイメージ・メタデータ・オブジェクトから抽出されたjavax_imageio_jpeg_image_1.0ネイティブ・イメージ・メタデータ・ツリー内の関連するNodeのユーザー・オブジェクトから取得できます。 このAPP1 Exifノードは"markerSequence"という名前のノードの子であり、unknownという名前と、整数値0xE1 (String"225")を持つMarkerTagという名前の属性を持ちます。 このノードのユーザー・オブジェクトは、6バイトの{'E', 'x', 'i', 'f', '0', '0'}で始まるバイト配列になります。 プライマリIFDとサムネイルIFDおよびイメージは、通常のImageReaderメソッドによってユーザー・オブジェクトから読み取ることができます。


    ImageReader jpegReader;
    ImageReader tiffReader;

    // Obtain the APP1 Exif marker data from the JPEG image metadata.
    IIOMetadata jpegImageMetadata = jpegReader.getImageMetadata(0);
    String nativeFormat = jpegImageMetadata.getNativeMetadataFormatName();
    Node jpegImageMetadataTree = jpegImageMetadata.getAsTree(nativeFormat);

    // getExifMarkerData() returns the byte array which is the user object
    // of the APP1 Exif marker node.
    byte[] app1Params = getExifMarkerData(jpegImageMetadataTree);
    if (app1Params == null) {
        throw new IIOException("APP1 Exif marker not found.");
    }

    // Set up input, skipping Exif ID 6-byte sequence.
    MemoryCacheImageInputStream app1ExifInput
        = new MemoryCacheImageInputStream
            (new ByteArrayInputStream(app1Params, 6, app1Params.length - 6));
    tiffReader.setInput(app1ExifInput);

    // Read primary IFD.
    IIOMetadata primaryIFD = tiffReader.getImageMetadata(0);

    // Read thumbnail if present.
    BufferedImage thumbnail = null;
    if (tiffReader.getNumImages(true) > 1) {
        thumbnail = tiffReader.read(1, tiffReadParam);
    }

    // Read the primary image.
    BufferedImage image = jpegReader.read(0);
tiffReader.getNumImages(true)は、埋込みTIFFストリーム内のIFDの数(空のイメージに対応するものを含む)を返すことに注意してください。 埋込みTIFFストリームのプライマリ・イメージは常に空であるため、tiffReader.read(0, readParam)をコールすると例外がスローされます。プライマリ・イメージは、JPEGリーダー自体を使用して取得する必要があります。

イメージの書込み

TIFFイメージは、ImageWriterによって書き込まれます。これは、そのパブリック・インタフェースおよび指定されたImageWriteParamを介して制御できます。 TIFF ImageWritergetDefaultWriteParam()メソッドによって戻されたImageWriteParamの場合、canWriteTiles()およびcanWriteCompressed()メソッドはtrueを返し、canOffsetTiles()およびcanWriteProgressive()メソッドはfalseを返します。 TIFFライターは、タイル・イメージの記述、イメージの挿入、空のイメージの書込みまたは挿入、イメージ・データの置換など、多くのオプション機能をサポートしています。 ピクセルは、空または空でないイメージで置換できますが、データが圧縮されていない場合のみ置換されます。

タイルが書き込まれる場合、各ディメンションはTIFF仕様ごとに16の最も近い倍数に丸められます。 JPEG-in-TIFF圧縮が使用されていて、タイルが書き込まれている場合、各タイル次元は、その次元内のJPEG最小コード単位(MCU)の8倍の最も近い倍数に丸められます。 JPEG-in-TIFF圧縮が使用され、ストリップが書き込まれている場合、ストリップ当たりの行数は、両方のディメンションで最大MCUの8倍に丸められます。

圧縮

圧縮タイプは、圧縮モードをMODE_EXPLICITに設定した後、ImageWriteParamsetCompressionType()メソッドを使用して設定できます。 次の表に、内部でサポートされている圧縮タイプのセットを示します。
サポートされる圧縮タイプ
圧縮タイプ 説明 リファレンス
CCITT RLE 変更されたHuffman圧縮 TIFF 6.0仕様、セクション10
CCITT T.4 CCITT T.4 bilevelエンコード/グループ3 facsimile圧縮 TIFF 6.0仕様、セクション11
CCITT T.6 CCITT T.6 bilevelエンコード/グループ4 facsimile圧縮 TIFF 6.0仕様、セクション11
LZW LZW圧縮 TIFF 6.0仕様、セクション13
JPEG 「新しい」JPEG-in-TIFF圧縮 TIFFテクニカルノート#2
ZLib 「Deflate/Inflate」圧縮(この表のあとのノートを参照)
PackBits バイト指向、実行長圧縮 TIFF 6.0仕様、セクション9
Deflate "Zip-in-TIFF"圧縮(この表の後のノートを参照) ZLIB Compressed Data Format Specification, DEFLATE Compressed Data Format Specification
Exif JPEG Exif固有のJPEG圧縮(この表のあとのノートを参照) Exif 2.2仕様(PDF)、4.5.5項「サムネイル・データの基本構造」

TIFF 6.0仕様のセクション22で説明されている旧式のJPEG圧縮はサポートされていません

CCITT圧縮タイプは、Bilevel (1ビット)イメージにのみ適用されます。 JPEG圧縮タイプは、バイト・グレースケール(1バンド)およびRGB(3バンド)イメージにのみ適用できます。

ZLibとデフレート圧縮は、TIFF圧縮フィールドの値を除いて同じです。ZLibの場合、「圧縮」フィールドの値は8ですが、デフレートの場合、値は32946 (0x80b2)です。 どちらの場合も、各イメージ・セグメント(ストリップまたはタイル)は単一の完全なzlibデータ・ストリームとして書き込まれます。

"Exif JPEG"は、JPEGネイティブ・イメージ・メタデータ・ツリーに含めるAPP1 Exifマーカー・セグメントの内容を記述するときに使用される圧縮タイプです。 この圧縮タイプが使用されるときに出力に追加される内容は、空のイメージまたは空でないイメージが書き込まれるかどうかの関数です。 イメージが空の場合は、圧縮されたExifプライマリIFDの仕様に準拠するTIFF IFDが追加されます。 イメージが空でない場合は、圧縮されたExifサムネイルIFDおよびイメージの仕様に準拠した完全なIFDおよびイメージが追加されます。 空のイメージのデータは、後でTIFFライターのピクセル置換機能を使用して追加されないことに注意してください。

ZLib/DeflateまたはJPEG圧縮を使用する場合、圧縮品質を設定できます。 ZLib/Deflateの場合、指定された浮動小数点品質値は[1, 9]の範囲にスケール変更され、デフレート圧縮レベルを導出するために整数に切り捨てられます。 JPEGの場合、浮動小数点品質の値は、通常の方法で解釈するJPEGライター・プラグインに直接渡されます。

色変換

ソース・イメージ・データの色空間タイプがRGBで、宛先フォトメトリック・タイプがCIE L*a*b*またはYCbCrの場合、ソース・イメージ・データは内部カラー・コンバータを使用してRGBから自動的に変換されます。

ICCプロファイル

次のいずれかの場合、ICC Profileフィールドが書き込まれます。

メタデータ問題

ライターのいくつかの動作は、ユーザーが提供する可能性のあるイメージ・メタデータの内容によって影響を受けるか、または影響を受ける可能性があります。

Bilevelイメージの場合、FillOrderおよびT4Optionsフィールドは出力データに影響します。 FillOrderの値が2 (BaselineTIFFTagSet.FILL_ORDER_RIGHT_TO_LEFT)の場合は右から左にデータが入力され、それ以外の場合は左から右にデータが入力されます。 T4Optionsの値は、データを1Dまたは2Dでエンコードするかどうか、およびEOLパディングを使用するかどうかを指定します。

すべてのイメージについて、RowsPerStripフィールドの値は、イメージがタイル化されていない場合のストリップ当たりの行数の設定に使用されます。 ストリップ当たりのデフォルト行数は、8または8キロバイト以下になる行数のいずれか大きい方です。

すべてのイメージについて、タイル・モードがImageWriteParam.MODE_COPY_FROM_METADATAの場合、タイル・ディメンションはTileWidthおよびTileLengthフィールド値を使用して設定できます。 このモードが設定されていてもフィールドがそうでない場合、それぞれのデフォルト値はイメージの幅と高さになります。

JPEG-in-TIFF圧縮を使用する場合、ライターに提供されたメタデータ・オブジェクトにJPEGTablesフィールドが含まれている場合にのみ、JPEGTablesフィールドがIFDに書き込まれ、JPEGストリームが各ストリップまたはタイルに短縮されます。 JPEGTablesフィールドの内容が有効な表のみのJPEGストリームである場合、それが使用されます。それ以外の場合、フィールドの内容はデフォルトの視覚的に損失のない表に置き換えられます。 そのようなJPEGTablesフィールドがメタデータに存在しない場合、JPEGTablesフィールドは出力に書き込まれず、各ストリップまたはタイルは独立した自己完結型のJPEGストリームとして書き込まれます。

Deflate/ZLibまたはLZW圧縮を使用する場合、イメージにサンプル当たり8ビットがある場合、Predictorフィールドに値2 (BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING)があると水平方向の相違予測子が使用されます。 予測が要求されるが、イメージにサンプル当たり8ビットがない場合、フィールドは値1 (BaselineTIFFTagSet.PREDICTOR_NONE)になるようにリセットされます。

一部のフィールドを追加または変更できます。

一部のフィールドは削除される可能性があります。

指定されたメタデータ内に存在するその他のフィールドは解釈されず、指定されたとおりに書き込まれます。

Exifイメージが書き込まれる場合、フィールドセットが存在し、その値が変更され、結果がExif 2.2の仕様と一致するようになります。

TIFFストリームに書き込むイメージ・メタデータを設定するには、TIFF IFDを表すTIFFDirectoryクラスを使用します。 TIFF IFDのフィールドは、TIFFFieldのインスタンスで表されます。 書き込まれる各フィールドについて、TIFFFieldTIFFDirectoryに追加し、後者はTIFFDirectory.getAsMetadataを起動してIIOMetadataオブジェクトに変換できます。 取得されたIIOMetadataオブジェクトは、TIFFライターに渡すことができます。

TIFFネイティブ・イメージ・メタデータへの標準メタデータ形式のマッピング

次の表に、標準メタデータ形式javax_imageio_1.0からのTIFFネイティブ・イメージ・メタデータ要素の導出を示します。
TIFFフィールド 標準メタデータ要素からの導出
PhotometricInterpretation /Chroma/ColorSpaceType@name: "GRAY"および/Chroma/BlackIsZero@value = "FALSE" => WhiteIsZero; "GRAY"および/Document/SubimageInterpretation@value = "TransparencyMask" => TransparencyMask; "RGB"および/Chroma/Palette present => PaletteColor; "GRAY" => BlackIsZero; "RGB" => RGB; "YCbCr" => YCbCr; "CMYK" => CMYK; "Lab" => CIELab.
SamplesPerPixel /Chroma/NumChannels@value
ColorMap /Chroma/Palette
圧縮 /Compression/CompressionTypeName@value: "none" => Uncompressed; "CCITT RLE" => CCITT 1D; "CCITT T.4" => Group 3 Fax; "CCITT T.6" => Group 4 Fax; "LZW" => LZW; "Old JPEG" => JPEG; "JPEG" => New JPEG; "ZLib" => ZLib; "PackBits" => PackBits; "Deflate" => Deflate.
PlanarConfiguration /Data/PlanarConfiguration@value: "PixelInterleaved" => Chunky; "PlaneInterleaved" => Planar
SampleFormat /Data/SampleFormat@value: "SignedIntegral" => 2つの補数符号付き整数データ、"UnsignedIntegral" =>符号なし整数データ、"Real" => IEEE浮動小数点データ、"Index" =>符号なし整数データ。
BitsPerSample /Data/BitsPerSample@value: char配列に解析されるスペース区切りリスト。
FillOrder /Data/SampleMSB@value: スペース区切りリストのすべての値が0s =>右から左、それ以外の場合は=>左から右です。
XResolution (10 / /Dimension/HorizontalPixelSize@value)または(10 / (/Dimension/VerticalPixelSize@value * /Dimension/PixelAspectRatio@value))
YResolution (10 / /Dimension/VerticalPixelSize@value)または(10 / (/Dimension/HorizontalPixelSize@value / /Dimension/PixelAspectRatio@value))
ResolutionUnit XResolutionまたはYResolutionが設定されている場合はセンチメートル、それ以外の場合はなし。
概要 /Dimension/ImageOrientation@value
XPosition /Dimension/HorizontalPosition@value/10
YPosition /Dimension/VerticalPosition@value/10
NewSubFileType /Document/SubimageInterpretation@value: "TransparencyMask" =>透明度マスク、"ReducedResolution" =>縮小解像度、"SinglePage" =>単一ページ。
DateTime /Document/ImageCreationTime@value
DocumentName、ImageDescription、メイク、モデル、PageName、ソフトウェア、アーティスト、HostComputer、InkNames、著作権 /Text/TextEntry: /Text/TextEntry@keywordがいずれかのTIFFフィールドの名前(ソフトウェアなど)である場合、フィールドはコンテンツ/Text/TextEntry@valueおよびカウント1で追加されます。
ExtraSamples /Transparency/Alpha@value: "premultiplied" =>関連するアルファ、カウント1、"nonpremultiplied" =>関連付けられていないアルファ、カウント1。

Exifイメージの記述

TIFFライターは、圧縮されていないExifイメージ、または圧縮されたExifイメージのAPP1マーカー・セグメントの内容を記述するために使用できます。

圧縮されていないExifイメージの書込み

一連のイメージを記述する場合、各イメージは通常、{IFD、 IFD Value、 Image Data}として記録されます。 Exif仕様では、圧縮されていないExifイメージを次のように構造化する必要があります。
  1. イメージファイルヘッダー
  2. プライマリIFD
  3. プライマリIFD値
  4. サムネイルIFD
  5. サムネイルIFD値
  6. サムネイル画像データ
  7. プライマリイメージデータ
最後に記録されるプライマリ・イメージ・データの要件を満たすには、プライマリ・イメージは最初は空のイメージとして書き込まれ、サムネイルIFDおよびイメージ・データが書き込まれた後にピクセル置換によって追加される必要があります。

    ImageWriter tiffWriter;
    ImageWriteParam tiffWriteParam;
    IIOMetadata tiffStreamMetadata;
    IIOMetadata primaryIFD;
    BufferedImage image;
    BufferedImage thumbnail;

    // Specify uncompressed output.
    tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);

    if (thumbnail != null) {
        // Write the TIFF header.
        tiffWriter.prepareWriteSequence(tiffStreamMetadata);

        // Append the primary IFD.
        tiffWriter.prepareInsertEmpty(-1, // append
                new ImageTypeSpecifier(image),
                image.getWidth(),
                image.getHeight(),
                primaryIFD,
                null, // thumbnails
                tiffWriteParam);
        tiffWriter.endInsertEmpty();

        // Append the thumbnail image data.
        tiffWriter.writeToSequence(new IIOImage(thumbnail, null, null),
                tiffWriteParam);

        // Insert the primary image data.
        tiffWriter.prepareReplacePixels(0, new Rectangle(image.getWidth(),
                image.getHeight()));
        tiffWriter.replacePixels(image, tiffWriteParam);
        tiffWriter.endReplacePixels();

        // End writing.
        tiffWriter.endWriteSequence();
    } else {
        // Write only the primary IFD and image data.
        tiffWriter.write(tiffStreamMetadata,
                new IIOImage(image, null, primaryIFD),
                tiffWriteParam);
    }

圧縮されたExifイメージの書込み

圧縮されたExifイメージのAPP1セグメント内の埋込みTIFFストリームの構造は、非圧縮Exifイメージ構造と同じですが、プライマリ・イメージ・データはありません。つまり、プライマリIFDがイメージ・データを参照しません。

    ImageWriter tiffWriter;
    ImageWriteParam tiffWriteParam;
    IIOMetadata tiffStreamMetadata;
    BufferedImage image;
    BufferedImage thumbnail;
    IIOMetadata primaryIFD;
    ImageOutputStream output;

    // Set up an output to contain the APP1 Exif TIFF stream.
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    MemoryCacheImageOutputStream app1ExifOutput =
        new MemoryCacheImageOutputStream(baos);
    tiffWriter.setOutput(app1ExifOutput);

    // Set compression for the thumbnail.
    tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    tiffWriteParam.setCompressionType("Exif JPEG");

    // Write the APP1 Exif TIFF stream.
    if (thumbnail != null) {
        // Write the TIFF header.
        tiffWriter.prepareWriteSequence(tiffStreamMetadata);

        // Append the primary IFD.
        tiffWriter.prepareInsertEmpty(-1, // append
                new ImageTypeSpecifier(image),
                image.getWidth(),
                image.getHeight(),
                primaryIFD,
                null, // thumbnails
                tiffWriteParam);
        tiffWriter.endInsertEmpty();

        // Append the thumbnail IFD and image data.
        tiffWriter.writeToSequence(new IIOImage(thumbnail, null,
                null), tiffWriteParam);

        // End writing.
        tiffWriter.endWriteSequence();
    } else {
        // Write only the primary IFD.
        tiffWriter.prepareWriteEmpty(tiffStreamMetadata,
                new ImageTypeSpecifier(image),
                image.getWidth(),
                image.getHeight(),
                primaryIFD,
                null, // thumbnails
                tiffWriteParam);
        tiffWriter.endWriteEmpty();
    }

    // Flush data into byte stream.
    app1ExifOutput.flush();

    // Create APP1 parameter array.
    byte[] app1Parameters = new byte[6 + baos.size()];

    // Add APP1 Exif ID bytes.
    app1Parameters[0] = (byte) 'E';
    app1Parameters[1] = (byte) 'x';
    app1Parameters[2] = (byte) 'i';
    app1Parameters[3] = (byte) 'f';
    app1Parameters[4] = app1Parameters[5] = (byte) 0;

    // Append TIFF stream to APP1 parameters.
    System.arraycopy(baos.toByteArray(), 0, app1Parameters, 6, baos.size());

    // Create the APP1 Exif node to be added to native JPEG image metadata.
    IIOMetadataNode app1Node = new IIOMetadataNode("unknown");
    app1Node.setAttribute("MarkerTag", String.valueOf(0xE1));
    app1Node.setUserObject(app1Parameters);

    // Append the APP1 Exif marker to the "markerSequence" node.
    IIOMetadata jpegImageMetadata =
        jpegWriter.getDefaultImageMetadata(new ImageTypeSpecifier(image),
            jpegWriteParam);
    String nativeFormat = jpegImageMetadata.getNativeMetadataFormatName();
    Node tree = jpegImageMetadata.getAsTree(nativeFormat);
    NodeList children = tree.getChildNodes();
    int numChildren = children.getLength();
    for (int i = 0; i < numChildren; i++) {
        Node child = children.item(i);
        if (child.getNodeName().equals("markerSequence")) {
            child.appendChild(app1Node);
            break;
        }
    }
    jpegImageMetadata.setFromTree(nativeFormat, tree);

    // Write the JPEG image data including the APP1 Exif marker.
    jpegWriter.setOutput(output);
    jpegWriter.write(new IIOImage(image, null, jpegImageMetadata));
前述で作成した"unknown"ノードは、ネイティブJPEGイメージ・メタデータの"markerSequence"ノードに追加され、プライマリ・イメージがJPEGライターを使用して書き込まれるときにJPEGストリームに書き込まれます。

ストリーム・メタデータ

TIFFネイティブ・ストリーム・メタデータ形式のDTDは、次のとおりです。
<!DOCTYPE "javax_imageio_tiff_stream_1.0" [

  <!ELEMENT "javax_imageio_tiff_stream_1.0" (ByteOrder)>

    <!ELEMENT "ByteOrder" EMPTY>
      <!-- The stream byte order -->
      <!ATTLIST "ByteOrder" "value" #CDATA #REQUIRED>
        <!-- One of "BIG_ENDIAN" or "LITTLE_ENDIAN" -->
        <!-- Data type: String -->
]>

イメージ・メタデータ

TIFFネイティブ・イメージ・メタデータ形式のDTDは、次のとおりです。
<!DOCTYPE "javax_imageio_tiff_image_1.0" [

  <!ELEMENT "javax_imageio_tiff_image_1.0" (TIFFIFD)*>

    <!ELEMENT "TIFFIFD" (TIFFField | TIFFIFD)*>
      <!-- An IFD (directory) containing fields -->
      <!ATTLIST "TIFFIFD" "tagSets" #CDATA #REQUIRED>
        <!-- Data type: String -->
      <!ATTLIST "TIFFIFD" "parentTagNumber" #CDATA #IMPLIED>
        <!-- The tag number of the field pointing to this IFD -->
        <!-- Data type: Integer -->
      <!ATTLIST "TIFFIFD" "parentTagName" #CDATA #IMPLIED>
        <!-- A mnemonic name for the field pointing to this IFD, if known
             -->
        <!-- Data type: String -->

      <!ELEMENT "TIFFField" (TIFFBytes | TIFFAsciis |
        TIFFShorts | TIFFSShorts | TIFFLongs | TIFFSLongs |
        TIFFRationals | TIFFSRationals |
        TIFFFloats | TIFFDoubles | TIFFUndefined)>
        <!-- A field containing data -->
        <!ATTLIST "TIFFField" "number" #CDATA #REQUIRED>
          <!-- The tag number asociated with the field -->
          <!-- Data type: String -->
        <!ATTLIST "TIFFField" "name" #CDATA #IMPLIED>
          <!-- A mnemonic name associated with the field, if known -->
          <!-- Data type: String -->

        <!ELEMENT "TIFFBytes" (TIFFByte)*>
          <!-- A sequence of TIFFByte nodes -->

          <!ELEMENT "TIFFByte" EMPTY>
            <!-- An integral value between 0 and 255 -->
            <!ATTLIST "TIFFByte" "value" #CDATA #IMPLIED>
              <!-- The value -->
              <!-- Data type: String -->
            <!ATTLIST "TIFFByte" "description" #CDATA #IMPLIED>
              <!-- A description, if available -->
              <!-- Data type: String -->

        <!ELEMENT "TIFFAsciis" (TIFFAscii)*>
          <!-- A sequence of TIFFAscii nodes -->

          <!ELEMENT "TIFFAscii" EMPTY>
            <!-- A String value -->
            <!ATTLIST "TIFFAscii" "value" #CDATA #IMPLIED>
              <!-- The value -->
              <!-- Data type: String -->

        <!ELEMENT "TIFFShorts" (TIFFShort)*>
          <!-- A sequence of TIFFShort nodes -->

          <!ELEMENT "TIFFShort" EMPTY>
            <!-- An integral value between 0 and 65535 -->
            <!ATTLIST "TIFFShort" "value" #CDATA #IMPLIED>
              <!-- The value -->
              <!-- Data type: String -->
            <!ATTLIST "TIFFShort" "description" #CDATA #IMPLIED>
              <!-- A description, if available -->
              <!-- Data type: String -->

        <!ELEMENT "TIFFSShorts" (TIFFSShort)*>
          <!-- A sequence of TIFFSShort nodes -->

          <!ELEMENT "TIFFSShort" EMPTY>
            <!-- An integral value between -32768 and 32767 -->
            <!ATTLIST "TIFFSShort" "value" #CDATA #IMPLIED>
              <!-- The value -->
              <!-- Data type: String -->
            <!ATTLIST "TIFFSShort" "description" #CDATA #IMPLIED>
              <!-- A description, if available -->
              <!-- Data type: String -->

        <!ELEMENT "TIFFLongs" (TIFFLong)*>
          <!-- A sequence of TIFFLong nodes -->

          <!ELEMENT "TIFFLong" EMPTY>
            <!-- An integral value between 0 and 4294967295 -->
            <!ATTLIST "TIFFLong" "value" #CDATA #IMPLIED>
              <!-- The value -->
              <!-- Data type: String -->
            <!ATTLIST "TIFFLong" "description" #CDATA #IMPLIED>
              <!-- A description, if available -->
              <!-- Data type: String -->

        <!ELEMENT "TIFFSLongs" (TIFFSLong)*>
          <!-- A sequence of TIFFSLong nodes -->

          <!ELEMENT "TIFFSLong" EMPTY>
            <!-- An integral value between -2147483648 and 2147482647 -->
            <!ATTLIST "TIFFSLong" "value" #CDATA #IMPLIED>
              <!-- The value -->
              <!-- Data type: String -->
            <!ATTLIST "TIFFSLong" "description" #CDATA #IMPLIED>
              <!-- A description, if available -->
              <!-- Data type: String -->

        <!ELEMENT "TIFFRationals" (TIFFRational)*>
          <!-- A sequence of TIFFRational nodes -->

          <!ELEMENT "TIFFRational" EMPTY>
            <!-- A rational value consisting of an unsigned numerator and
                 denominator -->
            <!ATTLIST "TIFFRational" "value" #CDATA #IMPLIED>
              <!-- The numerator and denominator, separated by a slash -->
              <!-- Data type: String -->

        <!ELEMENT "TIFFSRationals" (TIFFSRational)*>
          <!-- A sequence of TIFFSRational nodes -->

          <!ELEMENT "TIFFSRational" EMPTY>
            <!-- A rational value consisting of a signed numerator and
                 denominator -->
            <!ATTLIST "TIFFSRational" "value" #CDATA #IMPLIED>
              <!-- The numerator and denominator, separated by a slash -->
              <!-- Data type: String -->

        <!ELEMENT "TIFFFloats" (TIFFFloat)*>
          <!-- A sequence of TIFFFloat nodes -->

          <!ELEMENT "TIFFFloat" EMPTY>
            <!-- A single-precision floating-point value -->
            <!ATTLIST "TIFFFloat" "value" #CDATA #IMPLIED>
              <!-- The value -->
              <!-- Data type: String -->

        <!ELEMENT "TIFFDoubles" (TIFFDouble)*>
          <!-- A sequence of TIFFDouble nodes -->

          <!ELEMENT "TIFFDouble" EMPTY>
            <!-- A double-precision floating-point value -->
            <!ATTLIST "TIFFDouble" "value" #CDATA #IMPLIED>
              <!-- The value -->
              <!-- Data type: String -->

        <!ELEMENT "TIFFUndefined" EMPTY>
          <!-- Uninterpreted byte data -->
          <!ATTLIST "TIFFUndefined" "value" #CDATA #IMPLIED>
            <!-- A list of comma-separated byte values -->
            <!-- Data type: String -->
]>
導入されたバージョン:
9