RciTools RPDF - Génération de documents PDF depuis Oracle
50-Génération d'un document PDF de 100 pages
Pour terminer la page courante, on utilise l'instruction
EndPage.
Pour commencer une nouvelle page, c'est l'instruction
BeginPage.
L'instruction New "exécute" l'instruction BeginPage
sans qu'il soit nécessaire de l'appeler dans la programmation.
De même l'instruction Show, "exécute" l'instruction
EndPage, pour la page en cours de génération.
Dans l'exemple, on écrit sur chaque page un certain nombre de lignes.
Du point de vue des performances, avec Oracle XE et un PC cadencé à 2 gigas,
cette génération de 100 pages PDF est effectuée en moins d'une seconde :
en 0.734 secondes
Sur un serveur Oracle Entreprise mutualisé, sous Linux, cela a pris seulement
0.563 secondes.
Cette vitesse n'est possible que parce que tout la programmation est réalisée en
PL/SQL, sans de mécanisme d'appel de processus "extérieur".
Le PDF généré par RciTools RPDF comporte des commentaires, en particulier
sur le temps de génération, en micro-secondes.
declare
maxi integer := 100;
begin
RPDF.New; -- Initialisation de la 1ère page
for m in 1..maxi loop
RPDF.Write (t=>'Page numéro ' || m, tSize=>24);
for p in 1..42 loop
RPDF.Write(t=>'L' || p || ' page ' || m, x=>50, tSize
=> 9);
end loop;
if m <> maxi then
RPDF.EndPage; -- Termine la page PDF en cours
RPDF.BeginPage; -- Commence une nouvelle page PDF
end if;
end loop;
RPDF.Show; -- Termine la page PDF en cours, et le document PDF
end;
51-Détermination du format de page
Dans cet exemple l'utilisateur a sélectionné un format de document parmi une
liste de formats possibles, ainsi qu'un sens.
Le résultat de ces choix se situe dans deux champs Oracle Application Express,
nommés P2_Format et
P2_Sens.
Il va être possible d'utiliser ces choix pour initialiser les deux variables
générales nommées wD.CurrentPageLandscape et
wD.CurrentPageDim, à des valeurs autres que les
valeurs par défaut..
Dans cet exemple on génère 20 pages et on utilise une variante de l'instruction
BeginPage avec l'argument
endPrevious fixé à true
Cela correspond exactement à utiliser d'abord EndPage
puis ensuite BeginPage, mais en une seule
instruction.
declare
wF varchar2(100); wS varchar2 (100);
begin
wF := :P2_Format;
wS := :P2_Sens;
if wS = 'horizontal' then
RPDF.wD.CurrentPageLandscape := true;
else
RPDF.wD.CurrentPageLandscape := false;
end if;
RPDF.wD.CurrentPageDim := wF;
RPDF.New; -- Initialisation de la 1ère page
for m in 1..20 loop
RPDF.Write(t=>'Page ' || m || ', format: ' || wF || '-' || wS);
-- Nouvelle page PDF avec fin de la précédente
if m <> 20 then
RPDF.BeginPage (endPrevious =>true);
end if;
end loop;
RPDF.Show; -- Termine la page PDF en cours, et le document PDF
end;
52-Document de dimensions non standard
L'instruction AddPageFormat permet de définir un
nouveau format de page PDF, en associant un nom name à une largeur et hauteur de
page (width et height).
Ce n'est qu'au moment de la fin de la génération du document PDF que
l'information sur la taille des papes est écrite.
Ceci explique que l'ordre des instruction New ,
AddPageFormat et enfin la spécification du format à
utiliser ( wD.CurrentPageDim := ....) puissent être
réalisés dans un ordre quelconque (avant toutefois l'appel de l'instruction
Show)
declare
wF varchar2(100); wS varchar2 (100);
begin
RPDF.wD.CurrentPageDim := 'myformat1';
RPDF.New;
RPDF.AddPageFormat (name=>'myformat1', width=>2000, height=>2000);
for m in 1..20 loop
RPDF.Write(t=>'Page ' || m , y=> 1950);
if m <> 20 then RPDF.BeginPage (endPrevious =>true); end if;
end loop;
RPDF.Show; -- Termine la page PDF en cours, et le document PDF
end;
60-Génération automatique de tableaux PDF à partir de requêtes SQL
La bibliothèque RPDF comporte des outils permettant de créer facilement des
tableaux en PDF, depuis des requêtes SQL, non prédéfinies à l'avance. Ces
requêtes peuvent en particulier être dynamiques, en fonction de calculs
intermédiaires.
L'utilisation type de cette fonctionnalité est par exemple un ensemble de lignes
de facture, pour une facture donnée, ou une liste de produits commandés dans le
cadre d'une saisie de commande via le Web.
La première instruction est DoSQL, qui implémente
un moteur de résolution de requêtes SQL, mettant en oeuvre, de façon
transparente, le package Oracle nommé DBMS_SQL.
Nous avons développé et enrichi ce moteur DoSQL, initialement pour notre
logiciel d'infocentre nommé RciTools Oracle HTML, depuis 1998. Cette
implémentation nous permet d'utiliser ce même moteur dans le cadre de la
génération de PDF, ici, et pour la génération de documents Excel, en texte CSV
ou en format "riche" Excel XML.
Dans cet exemple, nous utiliserons DoSQL avec trois
arguments :
- rSQL une chaîne de caractères
comportant la requête SQL. Celle-ci peut porter sur une ou plusieurs tables,
vues, avec ou non des "jointures". Elle peut interroger en particulier des vues
matérialisées, travaillant au travers de "Data base links". C'est utile, en
particulier avec un poste de travail Oracle XE, pour interroger de grandes bases
Oracle en production.
- CS une structure en
mémoire (de type record PL/SQL) destinée à recevoir, sous une forme "tableur"
les données de la requête
- res une variable renvoyant la
valeur 0 si l'exécution de la requête SQL s'est correctement passée, et dans le
cas contraire le code d'erreur Oracle
La deuxième instruction est Write, que nous avons
déjà décrite dans sa forme utilisant une chaîne de caractères.
Ici, nous appelons Write en lui "passant" les
données résultantes d'une requête SQL, au travers d'une structure de type
CellsSpace.
Write va fonctionner en plusieurs "passes".
La première "passe" effectue une analyse des données, en fonction des attributs
typographiques "courants".
Un calcul est effectué de la largeur des colonnes en fonction de leur type
(texte, nombres décimaux ou entiers, dates) en tenant compte également des
"masques" de formatage pour chaque type de données.
Les "colonnes" de type numériques sont analysées de façon à déterminer si les
nombres sont entiers ou décimaux, de façon à adapter l'affichage au contenu.
Enfin Write de charge du calcul des positions de
lignes et de colonnes, ainsi que du contrôle de la pagination.
Dans l'exemple, deux requêtes SQL légèrement différentes sont calculées et
affichées.
Le "hasard" (voulu, pour l'exemple) des contenus fait que la 6 ème colonne
peut comporter des valeurs décimales dans la première requête, alors qu'elle ne
comporte que des valeurs entières dans la deuxième requête.
Write en tient compte dans l'affichage.
Avec Oracle XE, cet exemple a utilisé 0.511 secondes pour l'exécution des deux requêtes SQL et la génération du PDF.
declare
CS1 RPDF.CellsSpace;
r integer;
w varchar2(4000);
begin
RPDF.New;
RPDF.DoSQL (rSQL=>'Select * from Emp', CS=>CS1,res=>r);
RPDF.Write ( cs=> CS1, tSize=>9, y=>500);
w := 'Select * from Emp where empNo <> 9020 order by 2';
RPDF.DoSQL (rSQL=>w, CS=>CS1,res=>r);
RPDF.Write ( cs=> CS1, tSize=>8, y=>300);
RPDF.Show; -- Termine la page PDF en cours, et le document PDF
end;
61-Génération automatique de sauts de page PDF en fonction du résultat d'une requête SQL
Dans cette exemple, nous exécutons une requête de plusieurs centaines de lignes.
Il faut fonc que le résultat s'affiche sur plusieurs pages. C'est le rôle de
l'argument autoPages de l'instruction
Write.
Dans la requête SQL, le nombre de lignes a été ici limité à 500 (sur plus
de 36000) par l'argument rowsMax
500 lignes occupent ici 11 pages, générées en 9.727 secondes,
soit 0.88 secondes par page.
declare
CS1 RPDF.CellsSpace;
r integer; w varchar2(4000);
begin
RPDF.New;
w := 'Select dep, nom from France order by dep,nom';
RPDF.DoSQL (rSQL=>w, CS=>CS1, res=>r , rowsMax =>500);
RPDF.Tsize (9);
RPDF.Write ( cs=> CS1, autoPages=>true);
RPDF.Show; -- Termine la page PDF en cours, et le
document PDF
end;
La génération en PDF du résultat de la requête SQL en plusieurs colonnes, (et plusieurs pages) , est obtenu grâce à l'argument autoColumns de l'instruction Write :
declare
CS1 RPDF.CellsSpace;
r integer; w varchar2(4000);
begin
RPDF.New;
w := 'Select dep, nom from France order by dep,nom';
RPDF.DoSQL (rSQL=>w, CS=>CS1, res=>r , rowsMax =>500);
RPDF.Tsize (9);
RPDF.Write ( cs=> CS1, autoPages=>true, autoColumns=>true);
RPDF.Show; -- Termine la page PDF en cours, et le document PDF
end;

et même en quatre colonnes, en ayant indiqué une format A4 horizontal (paysage) :

Voici le source PL/SQL de ce dernier exemple :
declare
CS1 RPDF.CellsSpace;
r integer; w varchar2(4000);
begin
RPDF.New;
RPDF.wD.CurrentPageLandscape := true;
RPDF.wD.CurrentPageDim := 'a4';
w := 'Select cle from France order by nom,dep';
RPDF.DoSQL (rSQL=>w, CS=>CS1, res=>r , rowsMax =>500);
RPDF.Tsize (9);
RPDF.Write ( cs=> CS1, autoPages=>true, autoColumns=>true);
RPDF.Show; -- Termine la page PDF en cours, et le document PDF
end;
68-Calculs et mesure de performances

Il est important d'optimiser la génération d'états en général et de documents
PDF en particulier.
Un outil de mesure de temps est intégré au package RPDF.
Il permet de placer des "points de contrôle" à l'intérieur d'un procédure
PL/SQL, de façon à déterminer les parties à optimiser.
C'est la fonction GetET (Elapsed
Time) qui renvoie le temps écoulé, en millisecondes, depuis
l'utilisation de l'instruction New.
declare
CS1 RPDF.CellsSpace;
r integer; w varchar2(4000);
begin
RPDF.New;
RPDF.Write (t=>'après Init: ' || RPDF.GetET, y=>100, tsize=>9, x=>10);
RPDF.DoSQL (rSQL=>'Select ename from Emp', CS=>CS1,res=>r);
RPDF.Write (cs=>CS1, tSize=>9, y=>500);
RPDF.Write (t=>'après tab 1 : ' || RPDF.GetET, y=>85, tsize=>9, x=>10);
w := 'Select * from Emp where empNo <> 9020 order by 2';
RPDF.DoSQL (rSQL=>w, CS=>CS1,res=>r);
RPDF.Write ( cs=> CS1, tSize=>8, y=>300);
RPDF.Write (t=>'après tab 2 : ' || RPDF.GetET, y=>70, tsize=>9, x=>10);
RPDF.Write (t=>'Durée totale : ' || RPDF.GetET, y=>55, tsize=>9, x=>10);
RPDF.Show; -- Termine la page PDF en cours, et le document PDF
end;
Retour au sommaire de génération PDF
Tous droits réservés, RCI Informatique SAS, 2004-2006
rci@wanadoo.fr
www.rci-informatique.fr