Maison  >  Article  >  interface Web  >  Explication détaillée des solutions alternatives au Html vers Pdf

Explication détaillée des solutions alternatives au Html vers Pdf

高洛峰
高洛峰original
2017-03-27 14:58:371632parcourir

Contexte

Le projet nécessite de générer un document pdf à partir d'une page HTML (résultat du paiement). La page contient des images et des tableaux. Il semble que l'iTextSharp open source ne puisse pas y faire face

Après quelques recherches, j'ai trouvé wkhtmltopdf, un outil de conversion open source en ligne de commande qui prend en charge la spécification d'URL. ou html local. Le chemin du fichier a bien fonctionné après l'essai. J'ai également spécialement écrit un outil utilisant wkhtmltopdf pour sauvegarder le pdf des articles du jardin du blog au niveau local. temps plus tard

Mais, envoyez Après deux jours de tests pour les clients, les résultats d'exécution n'étaient pas idéaux et des erreurs inconnues se sont produites. Ce qui est étrange, c'est qu'il n'y a eu aucun problème dans l'environnement de test, mais des erreurs fréquentes se sont produites. le milieu officiel. Finalement, le client a abandonné cette solution
Attaché WkhtmlToXSharp C# wrapper wrapper (utilisant P/Invoke) pour l'excellente bibliothèque de conversion Html vers PDF wkhtmltopdf.
* **
OK, allons droit au but, une solution alternative : Hook

Appelez la fonction d'impression IE, utilisez l'imprimante XPS, générez d'abord le fichier HTML dans un document xps, puis générez un pdf

Nouveau projet WinForm, faites-le glisser dans le contrôle WebBrowser, le code spécifie l'URL du chemin du fichier html local, attendez que le document soit chargé, WebBrowser.Print(); OK, exécutez, une boîte de dialogue permettant de sélectionner une imprimante apparaîtra, comme le montre la figure 1. Après avoir cliqué sur imprimer, une boîte de dialogue Enregistrer sous apparaîtra. Entrez le chemin xps et enregistrez-le (Figure 2), et vous obtiendrez un document xps.
另类解决Html to Pdf 的方案详解

Image 1 : Sélectionner l'imprimante
另类解决Html to Pdf 的方案详解

Image 2 : Entrez le chemin xps

Comme vous pouvez le voir d'en haut , L'impression ici nécessite une interaction avec l'interface utilisateur, cliquez manuellement sur Imprimer et entrez le chemin XPS à enregistrer.
Ensuite, j'ai cherché sur Internet : Comment imprimer et générer directement des fichiers xps sans afficher la boîte de dialogue. J'ai beaucoup lu sur stackoverflow et codeproject, mais je n'ai pas trouvé de moyen. Plus tard, j'ai accidentellement lu un article écrit par Yuanzi et utilisé la méthode du hook et l'automatisation de l'interface utilisateur pour terminer les actions d'impression et de sauvegarde. Je pense que cette solution est réalisable

Codons-la ensuite

    //调用WebBrowser.Print的代码就忽略了,直接看钩子
    IntPtr hwndDialog;
    string pathFile;
    EnumBrowserFileSaveType saveType;

    // Imports of the User32 DLL. 
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr GetDlgItem(IntPtr hWnd, int nIDDlgItem);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern private bool SetWindowText(IntPtr hWnd, string lpString);
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool IsWindowVisible(IntPtr hWnd);

    //Win32 Api定义
    [DllImport("user32.dll")]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]
    static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfeter, string lpszClass, string lpszWindow);

    [DllImport("user32.dll")]
    static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, String lParam);

    [DllImport("user32.dll")]
    static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);


    //Win32消息定义
    const uint WM_SETTEXT = 0x000c;
    const uint WM_IME_KEYDOWN = 0x0290;
    const uint WM_LBUTTONDOWN = 0x0201;
    const uint WM_LBUTTONUP = 0x0202;

    // The thread procedure performs the message loop and place the data
    public void ThreadProc()
    {
        int maxRetry = 10;
        int retry = 0;
        IntPtr hWndPrint = FindWindow("#32770", "打印");
        IntPtr hWnd = FindWindow("#32770", "文件另存为");
        if (hWnd != IntPtr.Zero)
        {
            log.InfoFormat("got saveas dialog handle. Printer Dialog skipped.");
        }
        else
        {
            Thread.Sleep(200);
            hWndPrint = FindWindow("#32770", "打印");

            //这里有时候获取不到window,所以加了Sleep,多试几次
            while (hWndPrint == IntPtr.Zero && retry < maxRetry)
            {
                Thread.Sleep(200);
                log.InfoFormat("retry get Print dialog handle.retry:{0}", retry);
                hWndPrint = FindWindow("#32770", "打印");
                retry++;
            }
            if (hWndPrint == IntPtr.Zero)
            {
                //wait 1 second,retry again
                Thread.Sleep(1000);
                hWndPrint = FindWindow("#32770", "打印");
            }
            if (hWndPrint == IntPtr.Zero)
            {
                log.InfoFormat("Did not get Print dialog handle.retry:{0}", retry);
                return;
            }
            log.InfoFormat("got Print dialog handle.retry:{0}", retry);
            //select printer dialog
            IntPtr hChildP;
            hChildP = IntPtr.Zero;
            hChildP = FindWindowEx(hWndPrint, IntPtr.Zero, "Button", "打印(&P)");
            // 向保存按钮发送2个消息,以模拟click消息,借此来按下保存按钮
            PostMessage(hChildP, WM_LBUTTONDOWN, IntPtr.Zero, IntPtr.Zero);
            PostMessage(hChildP, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero);
            Application.DoEvents();
        }

        //hWnd = FindWindow("#32770", null);
        hWnd = FindWindow("#32770", "文件另存为");
        //To avoid race condition, we are forcing this thread to wait until Saveas dialog is displayed.
        retry = 0;
        while ((!IsWindowVisible(hWnd) || hWnd == IntPtr.Zero) && retry < maxRetry)
        {
            Thread.Sleep(200);
            log.InfoFormat("retry get saveas dialog handle.retry:{0}", retry);
            hWnd = FindWindow("#32770", null);
            retry++;
            Application.DoEvents();
        }
        log.InfoFormat("got saveas dialog handle.retry:{0}", retry);
        if (hWnd == IntPtr.Zero)
        {
            //wait 1 second,retry again
            Thread.Sleep(1000);
            hWnd = FindWindow("#32770", "文件另存为");
        }
        if (hWnd == IntPtr.Zero)
        {
            return;
        }
        Application.DoEvents();

        IntPtr hChild;
        // 由于输入框被多个控件嵌套,因此需要一级一级的往控件内找到输入框
        hChild = FindWindowEx(hWnd, IntPtr.Zero, "DUIViewWndClassName", String.Empty);
        hChild = FindWindowEx(hChild, IntPtr.Zero, "DirectUIHWND", String.Empty);
        hChild = FindWindowEx(hChild, IntPtr.Zero, "FloatNotifySink", String.Empty);
        hChild = FindWindowEx(hChild, IntPtr.Zero, "ComboBox", String.Empty);
        hChild = FindWindowEx(hChild, IntPtr.Zero, "Edit", String.Empty); // File name edit control
        // 向输入框发送消息,填充目标xps文件名
        SendMessage(hChild, WM_SETTEXT, IntPtr.Zero, pathFile);
        // 等待1秒钟
        System.Threading.Thread.Sleep(1000);
        // 找到对话框内的保存按钮
        hChild = IntPtr.Zero;
        hChild = FindWindowEx(hWnd, IntPtr.Zero, "Button", "保存(&S)");
        // 向保存按钮发送2个消息,以模拟click消息,借此来按下保存按钮
        PostMessage(hChild, WM_LBUTTONDOWN, IntPtr.Zero, IntPtr.Zero);
        PostMessage(hChild, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero);

        // Clean up GUI - we have clicked save button.
        //GC is going to do that cleanup job, so we are OK
        Application.DoEvents();
        //Terminate the thread.
        return;
    }
Suivant Concernant la conversion xps en pdf, j'ai utilisé Spire.Pdf, il y a une démo officielle, pas d'explication supplémentaire ici

Il y a des images et la vérité


另类解决Html to Pdf 的方案详解

A propos de l'automatique sélection de XPS
Document Je n'ai pas encore complété le code hook Writer, merci de me donner quelques conseils !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn