ホームページ  >  記事  >  ウェブフロントエンド  >  HtmlからPDFへの代替ソリューションの詳細な説明

HtmlからPDFへの代替ソリューションの詳細な説明

高洛峰
高洛峰オリジナル
2017-03-27 14:58:371624ブラウズ

背景

このプロジェクトでは、HTML ページ (支払い結果) から PDF ドキュメントを生成する必要があります。このページには写真とテーブルがありますが、オープンソースのiTextSharpでは対応できないようです

いくつか検索した結果、URLまたはローカルHTMLのパスの指定をサポートするコマンドラインのオープンソース変換ツールであるwkhtmltopdfを見つけました。ファイルを試してみたところ、効果は良好でした。ブログ投稿の PDF をローカルにバックアップするためのツールも特別に作成しました。後で時間があるときにこのツールを共有します。 2日間テストを行ったところ、走行効果が思わしくなく、不明なエラーが発生しました。そして不思議なことに、テスト環境では問題がないのに、正式環境ではエラーが頻繁に発生します。最終的に、顧客はこのソリューションを諦めました

添付されているのは、WkhtmlToXSharp

C#
ラッパー ラッパー (P/Invoke を使用)の優れた Html から PDF への変換ライブラリ wkhtmltopdf ライブラリです*** さて、本題に入りましょう。別の解決策: フック
IE 印刷機能を呼び出し、XPS プリンタを使用し、最初に HTML ファイルを
Print

() に生成します。OK、実行すると、図に示すように、プリンタを選択するためのダイアログ ボックスが表示されます。 1. [印刷] をクリックすると、[名前を付けて保存] ダイアログ ボックスが表示され、xps パスを入力して保存すると (図 2)、xps ドキュメントが作成されます。

図 1: プリンターを選択します
另类解决Html to Pdf 的方案详解

図 2: xps パスを入力します

上記からわかるように、ここで印刷するには UI との対話が必要で、手動で [印刷] をクリックし、xps パスを入力します保存。
次に、インターネットで検索しました: ダイアログボックスを表示せずに xps ファイルを直接印刷して生成する方法 stackoverflow と codeproject をよく読みましたが、方法が見つかりませんでした。その後、偶然、Yuanzi の前任者の記事を読みました。フックメソッドと UI オートメーションを使用して、印刷と保存のアクションを完了しました。この解決策は実現可能だと思います另类解决Html to Pdf 的方案详解

次はコーディングしてみましょう

    //调用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;
    }
次のステップは、xps を PDF に変換することです。 Spire.Pdf を使用すると、公式デモがあります。ここではこれ以上説明しません


写真と真実があります

XPS の自動選択に関するフックコードが完了していません

Document

ライターさん、お願いします私にアドバイスを!

以上がHtmlからPDFへの代替ソリューションの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。