blog.pagesd.info

Aller au contenu | Aller au menu | Aller à la recherche

vendredi 5 juin 2009

Ouvrir un document en dehors du navigateur

Quand on vient comme moi de changer de PC et que son nouveau Microsoft Internet Explorer a la facheuse habitude d'ouvrir les fichiers Word dans le navigateur plutôt que de lancer le Microsoft Word qui va bien, il ne faut pas hésiter à aller trifouiller dans le paramètres de l'explorateur de fichiers :

  • cliquer sur le menu "Outils"
  • choisr le sous-menu "Option des dossiers..."
  • aller sur l'onglet "Types de fichiers"
  • rechercher la ligne "DOC - Document Microsoft Word" (c'est dans l'ordre alphabétique)
  • cliquer sur le bouton "Avancé"
  • décocher "Parcourir dans une même fenêtre"
  • valider en cliquent sur le bouton "OK"
  • fermer la fenêtre "Option des dossiers" en cliquant sur le bouton "Fermer"

Pour faire bonne mesure et parce qu'on a rien à perdre, le mieux c'est ensuite de fermer les fenêtres de toutes les applications en cours, d'arrêter l'ordinateur puis de le redémarrer après avoir compté jusqu'à 5 (un, deux, trois, quatre et cinq).

Ou bien alors, avant d'en arriver à cette extrémité, on peut aussi en profiter pour répéter l'opération pour les fichiers Microsoft Excel, Adobe PDF, etcétéra...

lundi 29 septembre 2008

Répéter un formulaire PDF sur plusieurs pages

Pour parvenir à faire un état sur plusieurs pages en remplissant plusieurs fois le même formulaire PDF avec des données différentes, je n'ai finalement pas eu trop à me creuser la tête. J'ai simplement interrogé le tout nouveau site Stack Overflow qui m'a gentiment expliqué How do I programmatically create a PDF in my .NET application?

namespace Altrr.iText {

    using System;
    using System.Collections;
    using System.IO;
    using iTextSharp.text;
    using iTextSharp.text.pdf;

    /// <summary>
    /// Tests du composant iTextSharp
    /// </summary>
    public class Start {

        /// <summary>
        /// Point d'entrée principal de l'application.
        /// </summary>
        [STAThread]
        static void Main (string[] args) {

            string pdfSource = @"D:\Altrr\iText\register_form1.pdf";
            if (args.Length == 1) {
                pdfSource = args[0];
            }
            FillFields(pdfSource);

        }

        /// <summary>
        /// Remplis les différents champs d'un formulaire PDF
        /// </summary>
        static void FillFields (string pdfSource) {

            // Création d'un objet PDF Reader basé sur le formulaire PDF
            Console.WriteLine(pdfSource + " :");

            string pdfRempli = pdfSource.Replace(".pdf", "_test.pdf");
            Document doc = new Document();
            PdfCopy copy = new PdfCopy(doc, new FileStream(pdfRempli, FileMode.Create));
            doc.Open();

            for (int i = 0; i < 10; i++) {

                PdfReader pdfReader = new PdfReader(pdfSource);

                MemoryStream temp = new MemoryStream();
                PdfStamper pdfStamper = new PdfStamper(pdfReader, temp);

                AcroFields fields = pdfStamper.AcroFields;
                fields.SetField("person.name", i.ToString() + "Laura Specimen");
                fields.SetField("person.address", i.ToString() + "Paulo Soares Way 1");
                fields.SetField("person.postal_code", "F00b4R", "FOOBAR");
                fields.SetField("person.email", i.ToString() + "laura@lowagie.com");
                fields.SetField("person.programming", "JAVA");
                fields.SetField("person.language", "FR");
                fields.SetField("person.preferred", "EN");
                fields.SetField("person.knowledge.English", "On");
                fields.SetField("person.knowledge.French", "On");
                fields.SetField("person.knowledge.Dutch", "Off");

                pdfStamper.FormFlattening = true;
                pdfStamper.Close();

                PdfReader tempReader = new PdfReader(temp.ToArray());

                copy.AddPage(copy.GetImportedPage(tempReader, pdfReader.NumberOfPages));
                copy.FreeReader(tempReader);
            }
            doc.Close();
        }

    }
}

Pour que ça marche, j'ai dû ajouter un using iTextSharp.text; sans quoi le type 'Document' n'est pas référencé.

Et j'utilise aussi pdfStamper.FormFlattening = true; pour que le formulaire PDF ne soit plus modifiable. En fait, cela fait que le fichier PDF final redevient un simple PDF sans plus aucun champ saisissable.

Pour l'instant, je vais déjà utiliser ça comme ça parce que l'échéance pour la mise à jour de l'état approche à grands pas. Puis quand j'aurais fini j'essaierai de comprendre un peu mieux ce qui se passe et comment ça marche.

vendredi 26 septembre 2008

Remplir les champs d'un formulaire PDF

Après avoir réussi à lister les champs d'un formulaire PDF, ce coup-ci je fais un premier essai pour mettre des données dans mon formulaire. Pour commencer, je me contente d'y mettre un compteur numérique, ce qui va me permettre de repérer chaque champ dans le formulaire.

namespace Altrr.iText {

    using System;
    using System.Collections;
    using System.IO;
    using iTextSharp.text.pdf;

    /// <summary>
    /// Tests du composant iTextSharp
    /// </summary>
    public class Start {

        /// <summary>
        /// Point d'entrée principal de l'application.
        /// </summary>
        [STAThread]
        static void Main (string[] args) {

            string pdfSource = @"D:\Altrr\iText\register_form1.pdf";
            if (args.Length == 1) {
                pdfSource = args[0];
            }
            FillFields(pdfSource);

        }

        /// <summary>
        /// Remplis les différents champs d'un formulaire PDF
        /// </summary>
        static void FillFields (string pdfSource) {

            // Création d'un objet PDF Reader basé sur le formulaire PDF
            Console.WriteLine(pdfSource + " :");
            PdfReader pdfReader = new PdfReader(pdfSource);

            // Création d'un objet PDF Stamper à partir du formulaire PDF
            string pdfRempli = pdfSource.Replace(".pdf", "_test.pdf");
            PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(pdfRempli, FileMode.Create));
            AcroFields fields = pdfStamper.AcroFields;

            // Boucle pour remplir les différents champs
            int i = 0;
            foreach (DictionaryEntry field in fields.Fields) {
                // nom du champ
                string key = field.Key.ToString();
                // type du champ (et selon le cas liste des valeurs possibles)
                string type = "";
                i++;
                string data = i.ToString();
                string[] list = null;
                switch (fields.GetFieldType(key)) {
                    case AcroFields.FIELD_TYPE_CHECKBOX:
                        type = "CheckBox";
                        list = fields.GetAppearanceStates(key);
                        break;
                    case AcroFields.FIELD_TYPE_COMBO:
                        type = "Combo";
                        list = fields.GetListOptionExport(key);
                        break;
                    case AcroFields.FIELD_TYPE_LIST:
                        type = "List";
                        list = fields.GetListOptionExport(key);
                        break;
                    case AcroFields.FIELD_TYPE_NONE:
                        type = "None";
                        break;
                    case AcroFields.FIELD_TYPE_PUSHBUTTON:
                        type = "PushButton";
                        break;
                    case AcroFields.FIELD_TYPE_RADIOBUTTON:
                        type = "RadioButton";
                        list = fields.GetAppearanceStates(key);
                        break;
                    case AcroFields.FIELD_TYPE_SIGNATURE:
                        type = "Signature";
                        break;
                    case AcroFields.FIELD_TYPE_TEXT:
                        type = "Text";
                        break;
                }
                if (list != null) {
                    data = list[list.Length - 1];
                }
                Console.WriteLine("- " + key + " : " + data);
                fields.SetField(key, data);
            }
            // Fermeture du formulaire PDF rempli
            pdfStamper.Close();
        }

    }
}

Maintenant, il me reste à trouver comment éviter de créer physiquement un nouveau fichier à chaque fois. Etant donné que c'est destiné à être utilisé dans une application ASP.NET, je préfèrerais trouver une méthode qui me permette de renvoyer directement le résultat vers le poste client. Normalement, c'est le genre de chose qui devrait se trouver facilement sur Google (à condition que cela soit réalisable).

Et ce qui serait bien, c'est de voir s'il est possible à partir du formulaire modèle de remplir plusieurs pages avec des données différentes données. De cette façon, je pourrais gérer des impressions groupées de plusieurs documents à la fois.

jeudi 25 septembre 2008

Lister les champs d'un formulaire PDF

Aujourd'hui, j'ai eu besoin de re-faire un état Crystal Report. Il s'agit d'un document officiel dont le format a changé du jour au lendemain et qui doit être en production pour le 1° février. Vu les délais et surtout étant donné que maintenant plus personne ne maitrise Crystal Report chez nous, ça risquait d'être un peu juste.

Mais heureusement, l'organisme qui demande ce nouvel état a eu la bonne idée de le fournir sous forme de formulaire PDF prêt à remplir. Comme ce n'est normalement pas un état trop demandé (ce qui fait qu'on n'a pas vraiment besoin de la puissance de feu de Crystal Report), j'en ai profité pour tester s'il était possible de remplir ce formulaire depuis ASP.NET.

Apparemment, la meilleure solution bon marché pour cela semble être iTextSharp. C'est gratuit et ça semble vraiment très simple à utiliser. Sauf en ce qui concerne la mise à jour des formulaires PDF où il m'a fallu beaucoup creuser pour réussir à trouver des exemples concrets :

Une fois tout ça trouvé, ça a de suite été plus facile de faire un premier essai qui marche :

namespace Altrr.iText {

    using System;
    using System.Collections;
    using iTextSharp.text.pdf;

    /// <summary>
    /// Tests du composant iTextSharp
    /// </summary>
    public class Start {

        /// <summary>
        /// Point d'entrée principal de l'application.
        /// </summary>
        [STAThread]
        static void Main (string[] args) {

            string pdfSource = @"D:\Altrr\iText\register_form1.pdf";
            if (args.Length == 1) {
                pdfSource = args[0];
            }
            ListFieldNames(pdfSource);

        }

        /// <summary>
        /// Affiche la liste des champs d'un formulaire PDF
        /// </summary>
        static void ListFieldNames (string pdfSource) {

            // Création d'un objet PDF Reader basé sur le formulaire PDF
            Console.WriteLine(pdfSource + " :");
            PdfReader pdfReader = new PdfReader(pdfSource);
            AcroFields fields = pdfReader.AcroFields;

            // Boucle sur les différents champs du formulaire
            foreach (DictionaryEntry field in fields.Fields) {
                // nom du champ
                string key = field.Key.ToString();
                // type du champ (et selon le cas liste des valeurs possibles)
                string type = "";
                string data = "";
                switch (fields.GetFieldType(key)) {
                    case AcroFields.FIELD_TYPE_CHECKBOX:
                        type = "CheckBox";
                        data = String.Join(", ", fields.GetAppearanceStates(key));
                        break;
                    case AcroFields.FIELD_TYPE_COMBO:
                        type = "Combo";
                        data = String.Join(", ", fields.GetListOptionExport(key));
                        break;
                    case AcroFields.FIELD_TYPE_LIST:
                        type = "List";
                        data = String.Join(", ", fields.GetListOptionExport(key));
                        break;
                    case AcroFields.FIELD_TYPE_NONE:
                        type = "None";
                        break;
                    case AcroFields.FIELD_TYPE_PUSHBUTTON:
                        type = "PushButton";
                        break;
                    case AcroFields.FIELD_TYPE_RADIOBUTTON:
                        type = "RadioButton";
                        data = String.Join(", ", fields.GetAppearanceStates(key));
                        break;
                    case AcroFields.FIELD_TYPE_SIGNATURE:
                        type = "Signature";
                        break;
                    case AcroFields.FIELD_TYPE_TEXT:
                        type = "Text";
                        break;
                }
                Console.Write("- " + key + " : " + type);
                if (data != "") {
                    Console.WriteLine(" (valeurs possibles : " + data + ")");
                } else {
                    Console.WriteLine("");
                }
            }
        }

    }
}

Ce qui avec le fichier d'exemple me donne :

D:\Altrr\iText\register_form1.pdf :
- person.name : Text
- person.postal_code : Text
- person.knowledge.Dutch : CheckBox (valeurs possibles : Off, On)
- person.language : Combo (valeurs possibles : EN, FR, NL)
- person.knowledge.French : CheckBox (valeurs possibles : Off, On)
- person.programming : List (valeurs possibles : JAVA, C, CS, VB)
- person.email : Text
- person.address : Text
- person.preferred : RadioButton (valeurs possibles : Off, NL, EN, FR)
- person.knowledge.English : CheckBox (valeurs possibles : Off, On)

Il y a quand même un problème, parce qu'avec mon formulaire PDF, j'obtiens une liste des champs totalement dans le désordre, c'est à dire ni dans l'ordre de saisie, ni même dans l'ordre alphabétique. Et comme les noms des champs sont moyennement clairs, c'est assez galère pour savoir à quoi correspond chaque champ.

Ca vaudrait peut être le coup d'essayer la fonction pdfReader.AcroFields.GetFieldPositions(key) qui renvoie la position du champ pour essayer de trier cette liste de haut en bas et de gauche à droite. Ou alors, simplement remplir chaque champ avec une valeur numérique croissante et voir ce que ça donne.