Heim  >  Artikel  >  Web-Frontend  >  Ausführliche Erklärung alternativer Lösungen von HTML zu PDF

Ausführliche Erklärung alternativer Lösungen von HTML zu PDF

高洛峰
高洛峰Original
2017-03-27 14:58:371624Durchsuche

Hintergrund

Das Projekt erfordert die Generierung eines PDF-Dokuments aus einer HTML-Seite (Zahlungsergebnis). Die Seite enthält Bilder und Tabellen, und es scheint, dass das Open-Source-Programm iTextSharp damit nicht zurechtkommt.

Nach einigem Suchen habe ich wkhtmltopdf gefunden, ein Open-Source-Befehlszeilen-Konvertierungstool, das dies unterstützt Angabe von URL oder lokalem HTML. Der Pfad von Datei hat nach dem Test gut funktioniert. Ich habe auch speziell ein Tool mit wkhtmltopdf geschrieben, um das PDF der Blog-Gartenbeiträge lokal zu sichern Habe später Zeit

Aber, senden Nach zwei Testtagen für Kunden waren die Laufergebnisse nicht ideal und es traten einige unbekannte Fehler auf. Merkwürdig ist, dass es in der Testumgebung keine Probleme gab, aber häufig Fehler auftraten im offiziellen Umfeld. Schließlich gab der Kunde diese Lösung auf
Angehängter WkhtmlToXSharp C# Wrapper-Wrapper (mit P/Invoke) für die hervorragende HTML-zu-PDF-Konvertierungsbibliothek wkhtmltopdf-Bibliothek.
* **
OK, kommen wir zum Punkt, eine alternative Lösung: Hook

Rufen Sie die IE-Druckfunktion auf, verwenden Sie den XPS-Drucker, generieren Sie zuerst die HTML-Datei in ein XPS-Dokument und dann ein PDF

Neues WinForm-Projekt, in das WebBrowser-Steuerelement ziehen, der Code gibt die URL zum lokalen HTML-Dateipfad an, warten, bis das Dokument geladen ist, WebBrowser.Drucken(); OK, ausführen, ein Dialogfeld zur Auswahl eines Druckers wird angezeigt, wie in Abbildung 1 dargestellt. Nachdem Sie auf „Drucken“ geklickt haben, wird ein Dialogfeld zum Speichern unter angezeigt. Geben Sie den XPS-Pfad ein und speichern Sie ihn (Abbildung 2). Sie erhalten ein XPS-Dokument.
另类解决Html to Pdf 的方案详解

Bild 1: Drucker auswählen
另类解决Html to Pdf 的方案详解

Bild 2: XPS-Pfad eingeben

Wie Sie oben sehen können , Das Drucken hier erfordert eine Interaktion mit der Benutzeroberfläche. Klicken Sie manuell auf Drucken und geben Sie den XPS-Pfad zum Speichern ein.
Als nächstes habe ich im Internet gesucht: Wie man XPS-Dateien direkt druckt und generiert, ohne das Dialogfeld anzuzeigen. Ich habe viel über Stackoverflow und Codeproject gelesen, konnte aber keinen Weg finden. Später habe ich versehentlich einen Artikel von Yuanzi gelesen und die Hook-Methode und die UI-Automatisierung verwendet, um die Druck- und Speicheraktionen abzuschließen.

Als nächstes codieren wir es

    //调用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;
    }

Weiter Bezüglich der Konvertierung von XPS in PDF habe ich Spire.Pdf verwendet, es gibt eine offizielle Demo, hier keine weitere Erklärung

Es gibt Bilder und die Wahrheit
另类解决Html to Pdf 的方案详解

Über Automatik Auswahl von XPS Dokument Ich habe den Writer-Hook-Code noch nicht fertiggestellt, bitte geben Sie mir einen Rat!

Das obige ist der detaillierte Inhalt vonAusführliche Erklärung alternativer Lösungen von HTML zu PDF. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn