3 votes

Gestion des images intégrées dans IText XMLWorker

Gestion des images intégrées dans IText XMLWorker.

Existe-t-il un moyen de gérer les images incorporées (Base64) dans XMLWorker ? Dans la version 5.3.5 l'ImageProvider que j'ai utilisé ne fonctionne plus (une exception est levée avant), j'ai donc patché ImageRetrieve comme suit, mais il est évident que cela ne fonctionnera plus dans la prochaine mise à jour de 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;
    }

}

5voto

bartnikiewi.cz Points 131

Cela fait presque un an que vous avez posé cette question, mais peut-être que cette réponse vous aidera quand même.

J'ai récemment rencontré un problème similaire. Mon but était d'inclure dans le pdf généré une image stockée dans la base de données.

Pour ce faire, j'ai étendu la fonction com.itextpdf.tool.xml.pipeline.html.AbstractImageProvider et de remplacer sa classe retrieve() méthode comme celle-ci :

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";
  }
}

Ensuite, lorsque je construis des pipelines pour XMLWorker [1], je passe une instance de ma classe au contexte :

htmlPipelineContext.setImageProvider(new MyImageProvider());

Nous nous attendons à ce que cela fonctionne. Mais il y a un hic ! Quelque part, au plus profond de la bibliothèque xmlworker, ce htmlPipelineContext est cloné. Et pendant cette opération, notre implémentation d'ImageProvider est perdue. Cela se passe dans la méthode clone() de HtmlPipelineContext. Jetez un coup d'œil aux lignes 274-280 (je me réfère à la version 5.4.4) :

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

Ceci est même décrit dans la javadoc de HtmlPipelineContext.clone() [2] :

Créer un clone de ce HtmlPipelineContext, le clone ne contient que les valeurs initiales, pas les valeurs internes. Attention, l'état du contexte actuel n'est pas copié dans le clone. Seuls les éléments de configuration importants comme (...) ImageProvider (new AbstractImageProvider with same ImageRootPath) , (...) sont copiés.

N'est-ce pas drôle ? Vous obtenez une classe qui est conçue pour s'étendre en la rendant abstraite, mais à la fin, il s'avère que cette classe ne sert qu'à détenir des propriétés.

Ma solution pour résoudre ce problème :

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;
  }

}

Je l'utilise alors à la place de 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()](http://api.itextpdf.com/xml/com/itextpdf/tool/xml/pipeline/html/HtmlPipelineContext.html#clone())

1voto

Dfaure Points 391

Et heureusement, votre solution semble avoir été adoptée dans la version ultérieure (5.5.6 au moins).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X