[Writer] Publipostage avec images

Vos meilleures macros et portions de code sont publiées dans cette section.
Aucun support sur une question de programmation ici !

Modérateur : Vilains modOOs

Règles du forum
Aucune question dans cette section !
Celle-ci rassemble les meilleures macros et portions de code. Vous pouvez en revanche commenter ou argumenter le code exposé. Vous pouvez même remercier l'auteur (cela fait toujours plaisir) en indiquant par exemple dans quel cadre ou contexte vous en avez eu l'utilité.
Si vous avez à poster quelque chose, faites-le depuis la section Macros et API et demandez à un modérateur de l'y déplacer.
Avatar de l’utilisateur
c-stefan
Membre fOOndateur
Membre fOOndateur
Messages : 1146
Inscription : 16 janv. 2006 15:07
Localisation : Lisieux
Contact :

[Writer] Publipostage et fusion de documents

Message par c-stefan »

Ce suprême de code rassemble les meilleurs exemples pour étendre les possibilités de publipostage.
Pierre-Yves Samyn
Grand Maître de l'OOffice
Grand Maître de l'OOffice
Messages : 11276
Inscription : 02 mai 2006 10:42

[Writer] Publipostage avec images

Message par Pierre-Yves Samyn »

Problématique : on dispose d'une table avec un champ contenant des Url pointant vers des images ; on souhaite réaliser un publipostage utilisant ce champ pour afficher l'image dans un contrôle Image.

Plusieurs approches sont possibles, notamment la décompression puis modification des fichiers XML constituant un document OpenOffice.

La bibliothèque jointe est un exemple (limité...) d'une autre solution réalisable par macro.

L'objectif n'est pas de recréer un assistant de publipostage mais de proposer un minimum de fonctionnalités, avec un minimum de contraintes de mise en oeuvre : la lettre-type doit simplement comprendre un contrôle image (Picto) de même nom que le champ comprenant l'url et le formulaire sous-jacent doit être fondé sur la même table.

Il ne s'agit donc pas ici d'une extension (la forme extension n'a été choisie que pour faciliter l'installation) et donc, volontairement, pas de commande de menu ajoutée, ni d'icône, etc.

Outre l'atteinte de l'objectif (créer un document de publipostage incluant les images), l'intérêt est ici pour moi de regrouper dans cette procédure quelques techniques "classiques" de manipulation des documents OpenOffice :
  • Création de document à partir d'un modèle
  • Navigation et écriture dans un document via les curseurs
  • Manipulation de formats
  • Manipulation de contrôles de formulaire
  • Manipulation de champs insérés dans le document
  • Manipulation de jeu de données (connexion, jeu d'enregisrements)
D'où la présentation de ce code dans la section "Suprême de code"...


Le document joint présente plus en détail l'installation, l'utilisation et les limitations.

Ces dernières pourraient bien entendu être levées... Encore une fois, l'objectif n'est pas de recréer un assistant "complet".

Par voie de conséquence il est inutile de poster pour demander l'évolution de ce code qui n'est qu'un exemple :D

La bibliothèque (télécharger)
Le document de présentation (télécharger)


Ci-dessous le code (mais il sera nécessaire de télécharger la bibliothèque pour disposer de la boîte de dialogue).

Code : Tout sélectionner

option explicit

global PysDlg as object, PysConnexion as object


Sub PysLancerPublipostage

dim PysLettreType as object, PysNewDoc as object, PysTexte as object, PysCurseur as object
dim PysForm as object, PysCtrlImg as object, PysTables as object
dim PysRowSet as object, PysEnum as object, PysChamp as object, PysRange as object


dim PysProp(0) as new com.sun.star.beans.PropertyValue

dim PysUrl as string
dim PysNbForms as integer

PysProp(0).name = "AsTemplate"
PysProp(0).value = true

PysLettreType = thiscomponent
PysUrl = PysLettreType.url

' Ne travaille que si le document a été enregistré (car on l'utilise par insertion > fichier)

if PysUrl <> "" and not(PysLettreType.isModified) then

' Création d'un nouveau document fondé sur la letre-type
	
	PysNewDoc = stardesktop.loadComponentFromUrl(PysUrl, "_blank", 0, PysProp())
	
' Chargement de la bibliotheque Tools car utilisation des fonctions IndexinArray et ToggleWindow
	GlobalScope.BasicLibraries.LoadLibrary("Tools")
	
	PysPatienter
	ToggleWindow false

' Ne travaille que si le document comprend un formulaire
	
	if PysNewDoc.DrawPage.Forms.count <> 0 then
		PysForm = PysNewDoc.DrawPage.Forms.getByIndex(0)

' Ne travaille que si le formulaire ne comprend qu'un seul contrôle (picto)	
		if PysForm.count = 1 then

' Accès à ce contrôle	
			PysCtrlImg = PysForm.getByIndex(0)

' On remonte du formulaire à la "connexion" utile pour :
' - créer le jeu d'enregistrements
' - accéder aux formats de données

			PysConnexion = PysForm.ActiveConnection 
	
			if IsNull(PysConnexion) then
				MsgBox("Connexion impossible", 16)
			else
				PysTables = PysConnexion.Tables

' Ne travaille que si la source ne comprend qu'une table
				if PysTables.count = 1 then

' Création du jeu d'enregistrements correspondant	
					PysRowSet = createUnoService("com.sun.star.sdb.RowSet")
					with PysRowSet
						.activeConnection = PysConnexion
						.CommandType = "com.sun.star.sdb.CommandType.Table"
						.Command = PysConnexion.Tables.getByIndex(0).Name					
						.execute
					end with

' Utilise la fonction (Tools) pour tester si la source de données comprend
' un champ de même nom que le contrôle Image

					if IndexinArray(PysCtrlImg.name, PysRowSet.Columns.ElementNames) <> -1 then

' Toutes les conditions sont réunies, on peut "boucler" sur les enregistrements
	
						with PysRowSet
							.beforeFirst
							PysNbForms = 0
							while .next

' Pour le premier enregistrement, le document a déjà été créé, sinon
' on va à la fin du document, on insère un paragraphe avec saut de page
' puis on insère la lettre-type à la fin

								if PysNbForms <> 0 then
									PysTexte = PysNewDoc.text
									PysCurseur = PysTexte.createTextCursor
									PysCurseur.gotoEnd(False)
									PysTexte.insertControlCharacter(PysCurseur, com.sun.star.text.ControlCharacter.PARAGRAPH_BREAK, false)
									PysCurseur.breakType = com.sun.star.style.BreakType.PAGE_BEFORE
									PysCurseur.insertDocumentFromURL(PysUrl, array())
									PysForm = PysNewDoc.DrawPage.Forms.getByIndex(PysNbForms)
									PysCtrlImg = PysForm.getByIndex(0)
								end if

' Assignation de l'url de l'image (contenue dans le champ de même nom dans la table)
								PysCtrlImg.ImageUrl =  convertToUrl(.columns.getByName(PysCtrlImg.name).String)
								PysNbForms = PysNbForms + 1
	
'Boucle sur les champs du document (et non de la source...)

								PysEnum = PysNewDoc.TextFields.createEnumeration
' Tant qu'il y en a...
								do while PysEnum.hasMoreElements
									PysChamp = PysEnum.nextElement

' S'il s'agit d'un champ lié à la base de données
									if PysChamp.supportsService("com.sun.star.text.TextField.Database") then

' Vérifie qu'il y a bien un champ du même nom
										if IndexinArray(PysChamp.TextFieldMaster.DataColumnName, PysRowSet.Columns.ElementNames) <> -1 then
		
' Récupération de la position du champ dans le document
										    PysRange=PysChamp.anchor.start

' Création d'un curseur d'écriture pour remplacer le champ par le texte contenu
' dans la base
										    PysCurseur = PysRange.text.createTextCursorByRange(PysRange)

' Nécessaire de formater les champs Date ou Numérique
' avant d'écrire le contenu du champ
										    select case .columns.getByName(PysChamp.TextFieldMaster.DataColumnName).TypeName
										    	case "DATE","DATETIME","TIME"
												    PysCurseur.string =_
												    	PysFormat(.columns.getByName(PysChamp.TextFieldMaster.DataColumnName).String,_
												    			 .columns.getByName(PysChamp.TextFieldMaster.DataColumnName).FormatKey)
										    	case "DECIMAL","NUMERIC"
												    PysCurseur.string =_
												    	PysFormat(val(.columns.getByName(PysChamp.TextFieldMaster.DataColumnName).String),_
												    			 .columns.getByName(PysChamp.TextFieldMaster.DataColumnName).FormatKey)
										    	case else
														PysCurseur.string =.columns.getByName(PysChamp.TextFieldMaster.DataColumnName).String
										    end select

' Supprime le champ
											PysChamp.dispose
										end if
									end if
								loop

' Met à jour la collection des champs du document
								PysNewDoc.TextFields.refresh
							wend
							.dispose
						end with
					else
						MsgBox("Aucun champ nommé " & PysCtrlImg.name & " ne figure dans la source de données", 16)
					end if
				else
					MsgBox("Table non trouvée", 16)
				end if
			end if
		else
			MsgBox("Impossible de trouver le contrôle image dans le formulaire", 16)
		end if
	else
		MsgBox("Aucun formulaire n'a été trouvé", 16)
	end if
	
	ToggleWindow true
	PysFinPatienter
	msgbox "Terminé...", 64, "Publipostage"

else
	MsgBox("La lettre type doit avoir été enregistrée", 16)
end if

End Sub


sub PysPatienter

' Affiche un dialogue d'attente à l'utiliateur

dim  PysBibli as Object, PysMonDialog as object, PysControle as object
	DialogLibraries.LoadLibrary("PysPublipostage")
	PysBibli=DialogLibraries.GetByName("PysPublipostage")
	PysMonDialog=PysBibli.GetByName("PysDlgPatienter")
	PysDlg=CreateUnoDialog(PysMonDialog)
	PysDlg.setVisible(True)
	PysDlg.model.PysTextMsg.Text="Veuillez patienter..."

' Nécessaire pour laisser le temps à la connexion à la source de données de s'établir
	wait 20
end sub

sub PysFinPatienter
	PysDlg.setVisible(False)
	PysDlg.Dispose
end sub



function PysFormat(PysChamp, PysKey as long) as string

' A partir du numéro de format (PysKey) on recherche dans les formats, on obtient ainsi la chaîne représentant le format
' Par exemple : # ##0,00 [$€-40C];[RED]-# ##0,00 [$€-40C]
' Appel de la fonction Calc FORMAT (TEXT en GB) pour formater le contenu du champ avec ce format

' Nota : on ne peut utiliser la propriété NumberFormats de "thiscomponent" (la lettre-type) car les formats sont différents.
' On appelle donc logiquement le NumberFormatsSupplier de la "database" à laquelle on remonte via la Connexion.

dim PysService as object

PysService = CreateUnoService("com.sun.star.sheet.FunctionAccess")
PysFormat = PysService.callFunction("TEXT", array(PysChamp, PysConnexion.Parent.NumberFormatsSupplier.NumberFormats.getByKey(PysKey).FormatString))

end function

Avatar de l’utilisateur
Hubert Lambert
SuppOOrter
SuppOOrter
Messages : 1214
Inscription : 06 avr. 2016 09:26

[Writer] Publipostage avec images (bis)

Message par Hubert Lambert »

Bonjour,

Dans la suite logique de ce message, j’ai eu envie de généraliser la macro proposée pour la rendre utilisable quel que soit le type de document (étiquettes ou lettre), le type d’image, le nombre d’images par document, l’emplacement de l’image (cadre, tableau…), etc.

Comme je souhaiterais faire évoluer cette macro en fonction des besoins des utilisateurs, il me semble que la section Projets se prête mieux que la section Macros et API pour la publication du code. Mais libre aux modérateurs de déplacer le fil s’ils l’estiment utile.

Le "bis" du titre fait référence à la macro de Pierre-Yves Samyn citée dans le message précité, qui y propose une réécriture complète de la fusion de documents. Celle-ci propose une autre voie, qui "colle" au plus près du code existant (en l'occurrence le service MailMerge) et se focalise exclusivement sur l'intégration des images via une ébauche d'assistant.

Mode d’emploi
1. Dézipper les fichiers joints dans un même répertoire et les importer (Alt-F11 → Gérer → Bibliothèques → Importer et choisir le fichier "script.xlb").
2. Ouvrir un document modèle ou en créer un de manière classique; pour les images, insérer à l’endroit voulu un cadre qui ne contiendra que le champ correspondant à l’url.
3. Depuis le document modèle ouvert, exécuter la macro "publipostage_avec_images" (Alt-F11 → Mes Macros → PublipostageAvecImages → Module1 → Exécuter).
4. Choisir les enregistrements et les options, puis lancer.

Si aucun enregistrement n’est sélectionné, tous seront utilisés.
Le document modèle peut être créé à partir de l'assistant inclus dans la macro, par glisser-déposer des en-têtes de champs.
Les url des images peuvent être absolues ou relatives.
PublipostageAvecImages.png
Bugs connus
En raison du bug décrit ici, la sélection d’enregistrements est défaillante sous OpenOffice : il ne sera possible que de fusionner l’ensemble des éléments (sans sélection donc) ou un seul à la fois.

Cordialement.
Pièces jointes
PublipostageAvecImages.zip
v0.2
(4.23 Kio) Téléchargé 472 fois
AOOo 4.1.7 sur Win10
AOOo 4.1.x sur Linux Mint
LibreOffice 5.x/6.x sur Linux Mint
--
| « Nos défauts devraient nous donner une qualité : l'indulgence pour les défauts des autres » (Rivarol)
Avatar de l’utilisateur
Dude
IdOOle de la suite
IdOOle de la suite
Messages : 25142
Inscription : 03 mars 2006 08:45
Localisation : 127.0.0.1
Contact :

[Writer] Publipostage simplissime pour débuter

Message par Dude »

Réalisation d'un publipostage visant à fusionner dans un ODT unique plusieurs documents ainsi qu'un export dans des PDF distincts.
Bibliography est la source de données utilisée afin de simplifier à l'extrême l'exemple.
Image
publipostage_biblio.odt
(20.84 Kio) Téléchargé 64 fois

Code : Tout sélectionner

Sub Main
	oListener = createUnoListener("pub_", "com.sun.star.text.XMailMergeListener")
	oUdp = ThisComponent.DocumentProperties.getUserDefinedProperties()
	oPub = createUnoService("com.sun.star.text.MailMerge")
	with oPub
		.OutputType = com.sun.star.text.MailMergeType.FILE
		.DataSourceName = oUdp.MRG_DataSource
		.CommandType = oUdp.MRG_SourceType
		.Command = oUdp.MRG_Source
		.DocumentURL = ThisComponent.getURL()
		.SaveAsSingleFile = True
		.Filter = oUdp.MRG_SQLFilter
		.addMailMergeEventListener(oListener)
		.execute(Array())
		.removeMailMergeEventListener(oListener)
	end with
End Sub

Sub pub_notifyMailMergeEvent(oEvt)
	Dim aFiltre(0) as new com.sun.star.beans.PropertyValue
	aFiltre(0).Name = "FilterName"
	aFiltre(0).Value = "writer_pdf_Export"
	oPub = oEvt.Source
	oDoc = oEvt.Model
	oId = oDoc.TextFieldMasters.getByName("com.sun.star.text.fieldmaster.DataBase.Bibliography.biblio.Identifier")
	oChamp = oId.DependentTextFields(0)
	sID = oChamp.CurrentPresentation
	oEvt.Model.storeToURL(oPub.DocumentURL &"_"& sID &".pdf", aFiltre)
End Sub

Sub pub_disposing(oEvt)
	'RAS
End Sub
Verrouillé