Après la conversion vers PDF/A, nous nous intéressons aujourd’hui à une autre fonctionnalité liée au format de fichier PDF implémentable dans Alfresco : l’insertion d’un texte en filigrane. Cette insertion est rendue possible par un simple développement utilisant les API mises à disposition par Alfresco, la configuration du client web, et la librairie iText.

iText [en] est une API JAVA permettant de générer des documents PDF, ainsi que de modifier des documents PDF existants. Cette API est disponible gratuitement, sous licence MPL et LGPL.

Les actions dans Alfresco

Une action au sens Alfresco est une unité de travail, exécutée sur un document ou un espace, et que l’on peut actionner depuis l’interface standard (depuis la fiche de propriétés d’un élément, sélectionner « Lancer une action ») ou automatiser via les règles de gestion.

Il est possible, via une configuration, de mettre à disposition des utilisateurs une nouvelle action écrite en Java comme indiqué dans le wiki Alfresco [en]. Ce développement est facilité par les classes de base Alfresco, ce qui nous permet de nous consacrer pleinement à l’application du filigrane.

package com.atolcd.repo.action.executer;
 
import java.io.File;
import java.io.FileOutputStream;
import java.util.List;
 
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.TempFileProvider;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
 
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
 
public class PDFWatermarkActionExecuter extends ActionExecuterAbstractBase
	implements InitializingBean {
 
    public static final String NAME = "watermark";
    public static final String PARAM_TEXT = "watermark-text";
 
    private ContentService contentService;
 
    public void setContentService(ContentService contentService) {
	this.contentService = contentService;
    }
 
    public void afterPropertiesSet() throws Exception {
	Assert.notNull(contentService);
    }
 
    @Override
    protected void executeImpl(Action action, NodeRef actionedUponNodeRef) {
	ContentReader reader = contentService.getReader(actionedUponNodeRef,
		ContentModel.PROP_CONTENT);
 
	if (MimetypeMap.MIMETYPE_PDF.equals(reader.getMimetype())) {
	    PdfReader pdfReader = null;
	    PdfStamper stamper = null;
	    try {
		pdfReader = new PdfReader(reader.getContentInputStream());
		File newPdf = TempFileProvider.createTempFile("tmp",
			"watermark");
		FileOutputStream fos = new FileOutputStream(newPdf);
		stamper = new PdfStamper(pdfReader, fos);
		BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA,
			BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
		PdfContentByte under;
		int total = pdfReader.getNumberOfPages() + 1;
		for (int i = 1; i < total; i++) {
		    under = stamper.getUnderContent(i);
		    under.beginText();
		    under.setFontAndSize(bf, 18);
		    under.setTextMatrix(0, 0.5f, -0.5f, 0, 30, 30);
		    under.showText((String) action
			    .getParameterValue(PARAM_TEXT));
		    under.endText();
		}
		stamper.close();
 
		ContentWriter writer = contentService.getWriter(
			actionedUponNodeRef, ContentModel.PROP_CONTENT, true);
		writer.putContent(newPdf);
	    } catch (Exception e) {
		e.printStackTrace();
	    }
	}
    }
 
    @Override
    protected void addParameterDefinitions(List paramList) {
	paramList
		.add(new ParameterDefinitionImpl(PARAM_TEXT,
			DataTypeDefinition.TEXT, true,
			getParamDisplayLabel(PARAM_TEXT)));
    }
}

La classe PDFWatermarkActionExecuter est réduite à sa plus simple expression : une méthode addParameterDefinitions permettant de gérer les paramètres et une méthode executeImpl chargée de l’application de notre filigrane : un texte passé en paramètre de l’action, avec une position, une orientation, une police et une taille fixées.

Il serait possible d’améliorer cette action en permettant à l’utilisateur de paramétrer plus finement (police, taille, etc…) le filigrane, voire d’utiliser une image en complément (ou à la place) du texte.

Il est à noter qu’un autre développement est nécessaire afin d’obtenir une interface adaptée à la saisie dudit paramètre (encore une fois, direction le wiki[en]).

Module

L’action est ici proposée sous forme de module, facilement installable dans une instance Alfresco [en], comportant :

  • le code Java chargé de l’ajout du filigrane sur toutes les pages d’un document PDF,
  • le morceau d’interface permettant de passer en paramètre le texte que l’on souhaite insérer,
  • la configuration ajoutant l’action dans l’interface standard.

Télécharger le projet Eclipse du module.