2016-08-24 2 views
2

iText(2.1.7および5.5.9で試しました)でpdfにトランスフォームjava.awt.Imageを埋め込む次の問題が発生しました。iTextバイナリトランスペアレントバグ

ARGB ImageからiText Imageに変換する際に、iTextがバイナリトランスペアレント画像を正しく処理できないと思います。

提供される画像が100%と0%の透明ピクセル(バイナリ透明度)のみを含み、すべてのピクセルが黒色である場合(opaqeと透明なもの - つまり画像のすべてのピクセルがcolor = 0%または100%)、透明ピクセルの色の値は内部的には黒であると検出されます(これは私の意見では間違っています)。これは生成されたpdfの目に見えない画像につながります。 forceBW = falseの場合 と:

テスト・ケース:ここ

import java.awt.AlphaComposite; 
import java.awt.BasicStroke; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics2D; 
import java.awt.RenderingHints; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.net.MalformedURLException; 
import java.net.URL; 

import com.itextpdf.text.BadElementException; 
import com.itextpdf.text.Document; 
import com.itextpdf.text.DocumentException; 
import com.itextpdf.text.Image; 
import com.itextpdf.text.PageSize; 
import com.itextpdf.text.Paragraph; 
import com.itextpdf.text.pdf.PdfContentByte; 
import com.itextpdf.text.pdf.PdfWriter; 

public class BinaryTransparencyBug { 


    private static Image bkgnd; 


    public static void main(String[] args) throws Exception { 

     bkgnd = Image.getInstance(new URL("http://gitlab.itextsupport.com/itext/sandbox/raw/master/resources/images/berlin2013.jpg")); 
     bkgnd.scaleAbsolute(PageSize.A4); 
     bkgnd.setAbsolutePosition(0, 0); 


     Document document = new Document(); 
     File file = new File("target/binary_transparency_bug.pdf"); 
     FileOutputStream outputStream = new FileOutputStream(file); 
     PdfWriter writer = PdfWriter.getInstance(document, outputStream); 
     document.open(); 


     addBackground(writer); 
     document.add(new Paragraph("Binary transparency bug test case")); 
     document.add(new Paragraph("OK: Visible image (opaque pixels are red, non opaque pixels are black)")); 
     document.add(com.itextpdf.text.Image.getInstance(createBinaryTransparentAWTImage(Color.red,false,null), null)); 
     document.newPage(); 

     addBackground(writer); 
     document.add(new Paragraph("Suspected bug: invisible image (both opaque an non opaque pixels have the same color)")); 
     document.add(com.itextpdf.text.Image.getInstance(createBinaryTransparentAWTImage(Color.black,false,null), null)); 
     document.newPage(); 

     addBackground(writer); 
     document.add(new Paragraph("Analysis: Aliasing makes the problem disappear, because this way the image is not binary transparent any more")); 
     document.add(com.itextpdf.text.Image.getInstance(createBinaryTransparentAWTImage(Color.black,true,null), null)); 
     document.newPage(); 

     addBackground(writer); 
     document.add(new Paragraph("Analysis: Setting the color of the transparent pixels to anything but black makes the problem go away, too")); 
     document.add(com.itextpdf.text.Image.getInstance(createBinaryTransparentAWTImage(Color.black,false,Color.red), null)); 

     document.close(); 

    } 

    private static void addBackground(PdfWriter writer) 
      throws BadElementException, MalformedURLException, IOException, DocumentException { 
     PdfContentByte canvas = writer.getDirectContentUnder(); 
     canvas.saveState(); 
     canvas.addImage(bkgnd); 
     canvas.restoreState(); 
    } 

    // Create an ARGB AWT Image that has only 100% transparent and 0% 
    // transparent pixels. 
    // All 100% opaque pixels have the provided color "color" 
    // All transparent pixels have the Color "backgroundColor" 
    public static BufferedImage createBinaryTransparentAWTImage(Color color, boolean alias, Color backgroundColor) { 
     Dimension size = new Dimension(200, 200); 
     BufferedImage awtimg = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB); 
     Graphics2D g2d = awtimg.createGraphics(); 

     if (backgroundColor!=null) 
     { 
      //Usually it doen't make much sense to set the color of transparent pixels... 
      //but in this case it changes the behavior of com.itextpdf.text.Image.getInstance fundamentally! 
      g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 0f));  
      g2d.setColor(backgroundColor); 
      g2d.fillRect(0, 0, size.width, size.height); 
     } 
     g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f)); 
     g2d.setColor(color); 
     if (alias) 
     { 
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
      g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
     } 

     BasicStroke bs = new BasicStroke(2); 
     g2d.setStroke(bs); 
     for (int i = 0; i < 5; i++) { 
      g2d.drawLine((size.width + 2)/4 * i, 0, (size.width + 2)/4 * i, size.height - 1); 
      g2d.drawLine(0, (size.height + 2)/4 * i, size.width - 1, (size.height + 2)/4 * i); 
     } 
     return awtimg; 
    } 
} 

答えて

2

が問題の修正のための私の提案です:Image.getInstance(AWTImage、色、forcebw)で

color = null:

for (int j = 0; j < size; j++) { 
    byte alpha = smask[j] = (byte) (pixels[j] >> 24 & 0xff); 
    /* bugfix by Chris Nokleberg */ 
    if (!shades) { 
     if (alpha != 0 && alpha != -1) { 
      //as soon as there is any pixel with alpha not 0% or 100% 
      //switch to smask 
      shades = true; 
     } else if (transparency == null) { 
      //in binary transparency mode, determine the transparentPixel Color to be the 
      //value of the first Pixel we find with 100% transparency 
      if (alpha == 0) { 
       transparentPixel = pixels[j] & 0xffffff; 
       transparency = new int[6]; 
       transparency[0] = transparency[1] = transparentPixel >> 16 & 0xff; 
       transparency[2] = transparency[3] = transparentPixel >> 8 & 0xff; 
       transparency[4] = transparency[5] = transparentPixel & 0xff; 
       // vvv--- added by mkl 
       // Check whether this value for transparent pixels 
       // has already been used for a non-transparent one 
       // before this position 
       for (int jj = 0; jj < j; jj++) 
       { 
        if ((pixels[jj] & 0xffffff) == transparentPixel) 
        { 
         // found a prior use of the transparentPixel color 
         // and, therefore, cannot make use of this color 
         // for transparency; we could still use an image 
         // mask but for simplicity let's use a soft mask 
         // which already is implemented here 
         shades = true; 
         break; 
        } 
       } 
       // ^^^--- added by mkl 
      } 
     } else if (((pixels[j] & 0xffffff) != transparentPixel) && (alpha==0)) { 
      //TB: The above if seems incorrect to me. (EDIT: it was if ((pixels[j] & 0xffffff) != transparentPixel) 
      //As soon as we find any pixel that has differnt color from 
      //transparentPixel-Color and alpha 0% or 100% 
      //switch of binary transparency mode. 
      //IMHO this should only be done if alpha==0! 
      //so the if clause should be 
      //((pixels[j] & 0xffffff) != transparentPixel) && (alpha==0) 
      shades = true; 
     } 
     //TB: Proposed fix: 
     else if ((pixels[j] & 0xffffff) == transparentPixel && alpha!=0) { 
      //switch of binary transparency mode, if we find any pixel with the transparentPixel-Color, 
      //but which is not transparent 
      shades = true; 
     }      
    } 
    ... 
} 
+0

GitHubレポのプルリクエストとして提出できますか?それはあなたのパッチを提出するのに推奨される場所です。 http://github.com/itext/itextpdf –

+0

解決策は完全ではありません。 – mkl

+1

コメント(!)の提案は、 'alpha == 0'と' alpha == -1'の状況を区別することを正しく提案していますが、 'transparentPixel'の前に' transparentPixel'が使われていないことをまだ確認していません。決定されました。 – mkl