機械翻訳について

TIFFメタデータ・フォーマットの仕様と使用上の注意

「イメージを読む」

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

イメージを読む

TIFFイメージはImageReaderによって読み取られます。ImageReaderは、公開されたTIFFImageReadParamを介して公開されているインタフェースと同様に制御されます。

色変換

ソース・イメージ・データが測光型CIE L*a*b*またはYCbCrを有し、かつ宛先色空間型がRGBである場合、ソース・イメージ・データは、内部カラー変換器を使用して自動的にRGBに変換される。

カラー・スペース

デフォルトで割り当てられた未加工の色空間、つまりユーザーが提供するImageTypeSpecifierがない場合、次のうち最初に適用される色空間が適用されます:

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

他の色空間が推測できないときに使用される汎用色空間は、データを読み込むためだけに用意されています。あらゆる種類の正確な変換を提供することを意図していません。

データが前述のものによって正しく処理されない色空間にあることが分かっている場合は、ImageTypeSpecifierをリーダーに供給し、問題のデータに適した色空間から導出する必要があります。

ICCプロファイル

ICCプロファイルがイメージ・メタデータ(BaselineTIFFTagSet.TAG_ICC_PROFILE、タグ番号34675)に含まれている場合、それを使用してロードされたイメージのカラー・スペースを作成しようとします。 データ・レイアウトがコンポーネント型であり、ピクセルあたりのサンプル数がICCプロファイルで記述されたコンポーネントの数と等しいか、または1つ多い場合に使用されます。 ICCプロファイルが使用されない場合、色空間はaboveで説明された後続のステップの1つで推定される。

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

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

「ICCプロファイル」フィールドに基づかない推定色空間がICCプロファイル・ベースの色空間と互換性がある場合、この推定色空間から派生した第2のImageTypeSpecifierImageReader.getImageTypesによって返されるIteratorに含まれる。 イテレータに複数の型が含まれている場合、最初の型はICCプロファイルに基づいており、2番目の型は推定色空間に基づいています。

メタデータの問題

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

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 "TRUE" <=>PhotometricInterpretation => WhiteIsZero
/Chroma/Palette ColorMap
/Compression/CompressionTypeName@value 圧縮: 非圧縮=> "none"; CCITT 1D => "CCITT RLE";グループ3ファックス=> "CCITT T.4";グループ4ファックス=> "CCITT T.6"; LZW => "LZW"; JPEG => "Old JPEG";新しいJPEG => "JPEG"; Zlib =>> "ZLib"; PackBits => "PackBits"; Deflate => "Deflate"; Exif JPEG => "JPEG"。
/Compression/Lossless@value 圧縮: JPEGまたは新しいJPEG => "FALSE";そうでなければ"TRUE"。
/Data/PlanarConfiguration@value Chunky => "PixelInterleaved"; Planar => "PlaneInterleaved"。
/Data/SampleFormat@value PhotometricInterpretation PaletteColor => "Index"; SampleFormat符号なし整数データ=> "UnsignedIntegral"; SampleFormat 2つの補数符号付き整数データ=> "SignedIntegral"; SampleFormat IEEE浮動小数点データ=> "Real";それ以外の要素は放出されません。
/Data/BitsPerSample@value BitsPerSampleは空白で区切られたリストです。
/Data/SampleMSB@value FillOrder: 左から右=> BitsPerSample-1のスペースで区切られたリスト。右から左=>スペースで区切られた0のリスト。
/Dimension/PixelAspectRatio@value (1/XResolution)/(1/YResolution)
/Dimension/ImageOrientation@value Orientation
/Dimension/HorizontalPixelSize@value ResolutionUnitがNoneでない場合、1/XResolution(ミリ・メートル)。
/Dimension/VerticalPixelSize@value ResolutionUnitがNoneでない場合、1/YResolution(ミリ・メートル)。
/Dimension/HorizontalPosition@value ResolutionUnitがNoneでない場合のミリ秒単位の単位。
/Dimension/VerticalPosition@value ResolutionUnitがNoneでない場合はYミリ・メートル単位で表します。
/Document/FormatVersion@value 6.0
/Document/SubimageInterpretation@value NewSubFileType: 透明度=> "TransparencyMask";縮小解像度=> "ReducedResolution";シングル・ページ=> "SinglePage"。
/Document/ImageCreationTime@value DateTime
/Text/TextEntry DocumentName, ImageDescription, Make, Model, PageName, Software, Artist, HostComputer, InkNames, Copyright: /Text/TextEntry@keyword =フィールド名、/Text/TextEntry@value =フィールド値。
例: TIFF Software field => /Text/TextEntry@keyword = "ソフトウェア"、/Text/TextEntry@value =イメージの作成に使用されたソフトウェア・パッケージの名前とバージョン番号。
/Transparency/Alpha@value ExtraSamples: 関連するアルファ=> "premultiplied";関連しないアルファ=> "nonpremultiplied"。

Exifイメージを読む

TIFFリーダーを使用して、圧縮されていないExifイメージ、または圧縮されたExifイメージのAPP1マーカー・セグメントの内容を読み取ることができます。
非圧縮Exifイメージを読む
圧縮されていないExifイメージは、1ページまたは2ページの圧縮されていないTIFFイメージで、そのIFDおよびイメージ・データの内容が特定の順序になっています。 各画素は、測光解釈RGBまたはYCbCrを有する3つの8ビット・サンプルを有します。 イメージ・ストリームには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イメージは、APP1マーカー・セグメントが挿入された3バンドのISO/IEC 10918-1のベースラインDCT JPEGストリームです。 長さの後のマーカー・セグメントのパラメータは、完全なTIFFストリームに続く6バイトのシーケンス{'E', 'x', 'i', 'f', 0x00, 0x00} です。 埋め込まれた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によって書き込まれます。ImageWriterは、公開されているImageWriteParamを介して公開されているインタフェースと同様に制御されます。 TIFF ImageWritergetDefaultWriteParam()メソッドによって返されたImageWriteParamに対して、canWriteTiles()およびcanWriteCompressed()メソッドはtrueを返します。canOffsetTiles()canWriteProgressive()メソッドはfalseを返します。

TIFFライターは、タイル・イメージの書き込み、イメージの挿入、空のイメージの書き込みまたは挿入、イメージ・データの置換など、多くのオプション機能をサポートしています。 ピクセルは、空のイメージまたは空でないイメージのいずれかで、データが圧縮されていない場合にのみ置き換えられます。

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

圧縮

圧縮モードは、圧縮モードをMODE_EXPLICITに設定した後、ImageWriteParamsetCompressionType()メソッドによって設定することができます。 本来サポートされている圧縮形式のセットを次の表に示します:
サポートされている圧縮型
圧縮タイプ 説明 参照
CCITT RLE 修正ハフマン圧縮 TIFF 6.0仕様、セクション10
CCITT T.4 CCITT T.4二値符号化/グループ3ファクシミリ圧縮 TIFF 6.0仕様、セクション11
CCITT T.6 CCITT T.6二値符号化/グループ4ファクシミリ圧縮 TIFF 6.0仕様、セクション11
LZW LZW圧縮 TIFF 6.0仕様、セクション13
JPEG "新しい" JPEG-in-TIFF圧縮 TIFFテクニカル・ノート#2
ZLib "Deflate/Inflate" compression (この表の後の注記を参照) 「Adobe Photoshop® TIFFテクニカル・ノート」 (PDF)
PackBits バイト指向、ランレングス圧縮 TIFF 6.0仕様、セクション9
Deflate "Zip-in-TIFF" compression (この表の後の注記を参照) 「ZLIB圧縮データ・フォーマット仕様」, 「DEFLATE圧縮データ・フォーマット仕様」
Exif JPEG Exif固有のJPEG圧縮(この表の後の注記を参照) 「Exif 2.2仕様」 (PDF)、セクション4.5.5、"サムネイル・データの基本構造"

TIFF 6.0仕様のセクション22で説明されている古いスタイルのJPEG圧縮はサポートされていません。

CCITT圧縮型は、二値の(1-bit)イメージにのみ適用されます。 JPEG圧縮型は、バイト・グレイ・スケールの(1-band)およびRGB (3-band)イメージにのみ適用されます。

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

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

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

色変換

ソース・イメージ・データ色空間型がRGBであり、宛先測光型がCIE L * a * b *またはYCbCrである場合、ソース・イメージ・データは内部カラー変換器を使用してRGBから自動的に変換される。

ICCプロファイル

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

メタデータの問題

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

二値イメージの場合、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フィールドがIFDに書き込まれ、JPEGTablesフィールドがライターに提供されたメタデータ・オブジェクトに含まれている場合にのみ、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ネイティブ・イメージ・メタデータへのマッピング
「TIFFネイティブ・イメージ・メタデータ」要素の標準メタデータ形式javax_imageio_1.0からの派生は、次の表に示されています。

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 /Compression/CompressionTypeName@value: "none" =>非圧縮; "CCITT RLE" => CCITT 1D; "CCITT T.4" =>グループ3ファックス; "CCITT T.6" =>グループ4ファックス; "LZW" => LZW; "Old JPEG" => JPEG; "JPEG" =>新JPEG; "ZLib" => ZLib; "PackBits" => PackBits; "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が設定されている場合はセンチメートル。それ以外はNone。
Orientation /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, Make, Model, PageName, Software, Artist, HostComputer, InkNames, Copyright /Text/TextEntry: /Text/TextEntry@keywordは、TIFFフィールドのいずれかの名前です(例:"ソフトウェア")。このフィールドは、content /Text/TextEntry@valueとcount 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ストリームの構造は、1次イメージ・データが存在しないことを除いて、「非圧縮Exifイメージ構造」と同じです。つまり、1次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 -->
]>
@since 9