3

ITextXMLWorkerでの埋め込み画像の処理。

XMLWorkerで埋め込み(Base64)画像を処理する方法はありますか?バージョン5.3.5では、使用したImageProviderが機能しなくなったため(以前は例外が発生していました)、次のようにImageRetrieveにパッチを適用しましたが、これは次のXMLWorkerアップデートで明らかになります。

package com.itextpdf.tool.xml.net;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;

import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.codec.Base64;
import com.itextpdf.tool.xml.net.exc.NoImageException;
import com.itextpdf.tool.xml.pipeline.html.ImageProvider;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @author redlab_b
 *
 */
public class ImageRetrieve {
    final static Pattern INLINE_PATTERN = Pattern.compile("^/data:image/(png|jpg|gif);base64,(.*)");

    private final ImageProvider provider;
    /**
     * @param imageProvider the provider to use.
     *
     */
    public ImageRetrieve(final ImageProvider imageProvider) {
        this.provider = imageProvider;
    }
    /**
     *
     */
    public ImageRetrieve() {
        this.provider = null;
    }
    /**
     * @param src an URI that can be used to retrieve an image
     * @return an iText Image object
     * @throws NoImageException if there is no image
     * @throws IOException if an IOException occurred
     */
    public com.itextpdf.text.Image retrieveImage(final String src) throws NoImageException, IOException {
        com.itextpdf.text.Image img = null;
        if (null != provider) {
            img = provider.retrieve(src);
        }

        if (null == img) {
            String path = null;
            if (src.startsWith("http")) {
                // full url available
                path = src;
            } else if (null != provider){
                String root = this.provider.getImageRootPath();
                if (null != root) {
                    if (root.endsWith("/") && src.startsWith("/")) {
                        root = root.substring(0, root.length() - 1);
                    }
                    path = root + src;
                }
            } else {
                path = src;
            }
            if (null != path) {
                try {
                  Matcher m;
                    if (path.startsWith("http")) {
                        img = com.itextpdf.text.Image.getInstance(path);
                    } else if ((m = INLINE_PATTERN.matcher(path)).matches()) {
                      // Let's handle the embedded image without saving it
                      try {
                        byte[] data = Base64.decode(m.group(2));
                        return Image.getInstance(data);
                      } catch (Exception ex) {
                        throw new NoImageException(src, ex);
                      }
                    } else {
                        img = com.itextpdf.text.Image.getInstance(new File(path).toURI().toURL());
                    }
                    if (null != provider && null != img) {
                        provider.store( src, img);
                    }
                } catch (BadElementException e) {
                    throw new NoImageException(src, e);
                } catch (MalformedURLException e) {
                    throw new NoImageException(src, e);
                }
            } else {
                throw new NoImageException(src);
            }
        }
        return img;
    }



}
4

2 に答える 2

5

この質問をしてからほぼ 1 年が経ちますが、とにかくこの回答が役立つかもしれません。

最近、同様の問題に遭遇しました。私の目標は、データベースに保存された画像を生成されたpdfに含めることでした。

これを行うために、クラスを拡張し、そのメソッドを次のようcom.itextpdf.tool.xml.pipeline.html.AbstractImageProviderにオーバーライドしました。retrieve()

public class MyImageProvider extends AbstractImageProvider {

  @Override
  public Image retrieve(final String src) {
    Image img = super.retrieve(src);
    if (img == null) {
      try {
        byte [] data = getMyImageSomehow(src);
        img = Image.getInstance(data);
        super.store(src, img);
      }
      catch (Exception e) {
        //handle exceptions
      }
    }
    return img;
  }

  @Override
  public String getImageRootPath() {
    return "http://sampleurl/img";
  }
}

次に、XMLWorker [1] のパイプラインを構築するときに、クラスのインスタンスをコンテキストに渡します。

htmlPipelineContext.setImageProvider(new MyImageProvider());

これでうまくいくはずです。しかし、落とし穴があります。xmlworker ライブラリのどこかで、この htmlPipelineContext が複製されています。そして、この操作中に ImageProvider の実装が失われます。これは、HtmlPipelineContext の clone() メソッド内で発生しています。行 274-280 を見てください (私は 5.4.4 バージョンを参照しています):

final String rootPath =  imageProvider.getImageRootPath();
newCtx.setImageProvider(new AbstractImageProvider() {
  public String getImageRootPath() {
    return rootPath;
  }
});

これは、HtmlPipelineContext.clone() の javadoc [2] でも説明されています。

この HtmlPipelineContext のクローンを作成します。クローンには内部値ではなく、初期値のみが含まれます。現在のコンテキストの状態はクローンにコピーされないことに注意してください。(...) ImageProvider (同じ ImageRootPath を持つ新しい AbstractImageProvider) 、 (...) のような構成上重要なものだけがコピーされます。

おかしくないですか?抽象化して拡張するように設計されたクラスを取得しますが、最終的には、このクラスはプロパティ ホルダーとしてのみ機能することがわかります。

これに対する私の回避策:

public class MySpecialImageProviderAwareHtmlPipelineContext extends HtmlPipelineContext {

  MySpecialImageProviderAwareHtmlPipelineContext () {
    super(null);
  }

  public HtmlPipelineContext clone () {
    HtmlPipelineContext ctx = null;
    try {
      ctx = super.clone();
      ctx.setImageProvider(new MyImageProvider());
    } catch (Exception e) {
      //handle exception
    }
    return ctx;
  }

}

次に、HtmlPipelineContext の代わりにこれを使用します。


[1] http://demo.itextsupport.com/xmlworker/itextdoc/flatsite.html#itextdoc-menu-7

[2] http://api.itextpdf.com/xml/com/itextpdf/tool/xml/pipeline/html/HtmlPipelineContext.html#clone()

于 2013-11-15T17:41:56.637 に答える
1

そしてうまくいけば、あなたのソリューションは後のバージョン (少なくとも 5.5.6) で採用されたようです。

于 2015-07-03T09:13:43.030 に答える