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;

 

 


62-Génération de pages PDF avec colonnes automatiques depuis une requête SQL

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;
 


Voici le même résultat du même bloc de programme PL/SQL, avec une requête SQL pour laquelle  le calcul automatique des largeurs de colonnes permet de "placer" trois colonnes :

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