>  기사  >  웹 프론트엔드  >  Html to Pdf의 대체 솔루션에 대한 자세한 설명

Html to Pdf의 대체 솔루션에 대한 자세한 설명

高洛峰
高洛峰원래의
2017-03-27 14:58:371579검색

배경

이 프로젝트에는 HTML 페이지(결제 결과)에서 PDF 문서를 생성해야 합니다. 페이지에 그림과 가 있는데, 오픈 소스 iTextSharp가 이를 처리할 수 없는 것 같습니다.

몇몇 검색 끝에 wkhtmltopdf를 지원하는 명령줄 오픈 소스 변환 도구를 찾았습니다. URL 또는 로컬 HTML 지정 file의 경로는 시험 후에 잘 작동했습니다. 또한 블로그 정원 게시물의 PDF를 로컬에 백업하기 위해 특별히 wkhtmltopdf를 사용하는 도구를 작성했습니다. 나중에 시간을 갖고

그런데 보내기 이틀 동안 고객을 대상으로 테스트한 결과가 이상적이지 않았으며 알 수 없는 오류도 발생했습니다. 이상한 점은 테스트 환경에서는 문제가 없었는데 오류가 자주 발생했다는 것입니다. 공식 환경에서. 마침내 고객은
뛰어난 HTML-PDF 변환 라이브러리 wkhtmltopdf 라이브러리에 대한 WkhtmlToXSharp C# 래퍼 래퍼(P/Invoke 사용)
첨부된
이 솔루션을 포기했습니다.

* **

자, 대체 솔루션인 Hook

IE 인쇄 기능을 호출하고 XPS 프린터를 사용하여 먼저 HTML 파일을 xps 문서로 생성한 다음 PDF를 생성합니다.

새 WinForm 프로젝트, WebBrowser 컨트롤로 드래그, 코드는 Url을 로컬 HTML 파일 경로로 지정하고 문서가 로드될 때까지 기다립니다. WebBrowser.
Print另类解决Html to Pdf 的方案详解(); OK, 실행하면 그림 1과 같이 프린터 선택 대화 상자가 나타납니다. 인쇄를 클릭하면 다른 이름으로 저장 대화 상자가 나타납니다. xps 경로를 입력하고 저장하면(그림 2) xps 문서가 생성됩니다.


另类解决Html to Pdf 的方案详解그림 1: 프린터 선택

그림 2: xps 경로 입력

위에서 보는 바와 같이 , 여기서 인쇄하려면 UI와의 상호 작용이 필요합니다. 수동으로 인쇄를 클릭하고 xps 경로를 입력하여 저장하세요.

다음으로 인터넷에서 대화 상자를 표시하지 않고 xps 파일을 직접 인쇄하고 생성하는 방법을 검색했습니다. stackoverflow 및 codeproject에서 많은 것을 읽었지만 방법을 찾을 수 없었습니다. 나중에 우연히 Yuanzi의 전임자의 기사를 읽었는데, 후크 메소드와 UI 자동화를 사용하여 인쇄 및 저장 작업을 완료하는 것이 이 솔루션이 가능하다고 생각합니다

다음에 코딩해 보겠습니다

    //调用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를 사용했습니다. 공식 데모가 있습니다. 여기에는 추가 설명이 없습니다
另类解决Html to Pdf 的方案详解그림과 진실이 있습니다

자동에 대해 XPS
문서 선택 아직 Writer Hook 코드를 완성하지 못했는데 조언 부탁드립니다!

위 내용은 Html to Pdf의 대체 솔루션에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.