亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

使用 wkhtmltopdf 庫打印時如何接收 DOM 元素的高度?

使用 wkhtmltopdf 庫打印時如何接收 DOM 元素的高度?

HUX布斯 2023-05-11 16:15:36
當我在使用 wkhtmltopdf 庫打印時嘗試使用 Javascript 獲取 offsetHeight 或任何 DOM 元素時,高度永遠不會確定并且始終等于 0。當我在任何瀏覽器中執行相同的 JS 代碼時,它可以正常工作并產生特定的高度的元素。google了半天,發現可能和wkhtmltopdf有關,其中document和window的寬高都為0。 wkhtmltopdf 配置參數,但 offsetHeight 仍然為 0。使用 wkhtmltopdf 打印時是否有任何已知的解決方法來接收 DOM 元素的高度?我用的是最新穩定版的打印庫(0.12.6)
查看完整描述

1 回答

?
慕標5832272

TA貢獻1966條經驗 獲得超4個贊

我過去使用過 wkHtml2Pdf。
我的建議是立即停止,因為 wkhtmltopdf 使用的是非常舊的瀏覽器版本,無論如何您都可能會遇到問題。此外,wkHtmlToPdf 不能正常工作(而且性能很差)。

相反,您可以使用更好的選擇。
該選項是將 Chrome DevTools 與遠程調試協議一起使用:
https ://chromedevtools.github.io/devtools-protocol/

基本上像這樣運行 Chrome

chrome.exe --remote-debugging-port=9222

可選配

$"--user-data-dir=\"{directoryInfo.FullName}\"";

"--headless --disable-gpu";

這是我在服務器上啟動 Chrome 進程的方式(C# 代碼)

public IChromeProcess Create(int port, bool headless)

{

    string path = System.IO.Path.GetRandomFileName();

    System.IO.DirectoryInfo directoryInfo = System.IO.Directory.CreateDirectory(

        System.IO.Path.Combine(

            System.IO.Path.GetTempPath(), path)

    );


    string remoteDebuggingArg = $"--remote-debugging-port={port}";

    string userDirectoryArg = $"--user-data-dir=\"{directoryInfo.FullName}\"";

    const string headlessArg = "--headless --disable-gpu";


    // https://peter.sh/experiments/chromium-command-line-switches/

    System.Collections.Generic.List<string> chromeProcessArgs = 

        new System.Collections.Generic.List<string>

    {

        remoteDebuggingArg,

        userDirectoryArg,

        // Indicates that the browser is in "browse without sign-in" (Guest session) mode. 

        // Should completely disable extensions, sync and bookmarks.

        "--bwsi", 

        "--no-first-run"

    };



    if (false)

    {

        string proxyProtocol = "socks5";

        proxyProtocol = "http";

        proxyProtocol = "https";

        string proxyIP = "68.183.233.181";

        string proxyPort = "3128";

        string proxyArg = "--proxy-server=\"" + proxyProtocol + "://" + proxyIP + ":" + proxyPort + "\"";

        chromeProcessArgs.Add(proxyArg);

    }



    if (headless)

        chromeProcessArgs.Add(headlessArg);


    if(IsRoot)

        chromeProcessArgs.Add("--no-sandbox");


    string args = string.Join(" ", chromeProcessArgs);

    System.Diagnostics.ProcessStartInfo processStartInfo = new System.Diagnostics.ProcessStartInfo(ChromePath, args);

    System.Diagnostics.Process chromeProcess = System.Diagnostics.Process.Start(processStartInfo);


    string remoteDebuggingUrl = "http://localhost:" + port;

    return new LocalChromeProcess(new System.Uri(remoteDebuggingUrl), () => DirectoryCleaner.Delete(directoryInfo), chromeProcess);

}

我在這里使用這個 C# 庫與 DevTools 交互(通過 WebSockets):
https://github.com/MasterDevs/ChromeDevTools

如果你在服務器上使用 NodeJS,你可以使用這個:https:
//github.com/cyrus-and/chrome-remote-interface
或者 TypeScript: https:
//github.com/TracerBench/chrome-debugging-client

為了生成 PDF,您需要發出 PrintToPDF 命令:

Dim cm2inch As UnitConversion_t = Function(ByVal centimeters As Double) centimeters * 0.393701

Dim mm2inch As UnitConversion_t = Function(ByVal milimeters As Double) milimeters * 0.0393701


Dim printCommand2 As PrintToPDFCommand = New PrintToPDFCommand() With {

    .Scale = 1,

    .MarginTop = 0,

    .MarginLeft = 0,

    .MarginRight = 0,

    .MarginBottom = 0,

    .PrintBackground = True,

    .Landscape = False,

    .PaperWidth = mm2inch(conversionData.PageWidth),

    .PaperHeight = mm2inch(conversionData.PageHeight) ' 

}

要創建光柵圖形,您需要發出 CaptureScreenshot-Command :


Dim screenshot As MasterDevs.ChromeDevTools.CommandResponse(Of CaptureScreenshotCommandResponse) = Await chromeSession.SendAsync(New CaptureScreenshotCommand With {

    .Format = "png"

})

System.Diagnostics.Debug.WriteLine("Screenshot taken.")

conversionData.PngData = System.Convert.FromBase64String(screenshot.Result.Data)

請注意,要使屏幕截圖正常工作,您需要通過 SetDeviceMetricsOverride-Command 設置寬度和高度:


Await chromeSession.SendAsync(New SetDeviceMetricsOverrideCommand With {

    .Width = conversionData.ViewPortWidth,

    .Height = conversionData.ViewPortHeight,

    .Scale = 1

})

您可能必須將 overflow:hidden 放在 HTML 或一些子元素上,這樣您就不會截取滾動條;)


順便說一下,如果您需要特定版本的 Windows 版 Chrome(Chromium,因為出于安全原因舊版 Chrome 不可用),您可以從 Chocolatey-Repository 獲取它們:https: //chocolatey.org/packages/chromium /#版本歷史


這是我的完整測試代碼供參考(減去一些類)


Imports MasterDevs.ChromeDevTools

Imports MasterDevs.ChromeDevTools.Protocol.Chrome.Browser

Imports MasterDevs.ChromeDevTools.Protocol.Chrome.Page

Imports MasterDevs.ChromeDevTools.Protocol.Chrome.Target


Namespace Portal_Convert.CdpConverter



    Public Class ChromiumBasedConverter



        Private Delegate Function UnitConversion_t(ByVal value As Double) As Double





        Public Shared Sub KillHeadlessChromes(ByVal writer As System.IO.TextWriter)

            Dim allProcesses As System.Diagnostics.Process() = System.Diagnostics.Process.GetProcesses()

            Dim exeName As String = "\chrome.exe"


            If System.Environment.OSVersion.Platform = System.PlatformID.Unix Then

                exeName = "/chrome"

            End If


            For i As Integer = 0 To allProcesses.Length - 1

                Dim proc As System.Diagnostics.Process = allProcesses(i)

                Dim commandLine As String = ProcessUtils.GetCommandLine(proc)

                If String.IsNullOrEmpty(commandLine) Then Continue For

                commandLine = commandLine.ToLowerInvariant()

                If commandLine.IndexOf(exeName, System.StringComparison.InvariantCultureIgnoreCase) = -1 Then Continue For


                If commandLine.IndexOf("--headless", System.StringComparison.InvariantCultureIgnoreCase) <> -1 Then

                    writer.WriteLine($"Killing process {proc.Id} with command line ""{commandLine}""")

                    ProcessUtils.KillProcessAndChildren(proc.Id)

                End If

            Next


            writer.WriteLine($"Finished killing headless chromes")

        End Sub



        Public Shared Sub KillHeadlessChromes()

            KillHeadlessChromes(System.Console.Out)

        End Sub



        Private Shared Function __Assign(Of T)(ByRef target As T, value As T) As T

            target = value

            Return value

        End Function



        Public Shared Function KillHeadlessChromesWeb() As System.Collections.Generic.List(Of String)

            Dim ls As System.Collections.Generic.List(Of String) = New System.Collections.Generic.List(Of String)()

            Dim sb As System.Text.StringBuilder = New System.Text.StringBuilder()


            Using sw As System.IO.StringWriter = New System.IO.StringWriter(sb)

                KillHeadlessChromes(sw)

            End Using


            Using tr As System.IO.TextReader = New System.IO.StringReader(sb.ToString())

                Dim thisLine As String = Nothing


                While (__Assign(thisLine, tr.ReadLine())) IsNot Nothing

                    ls.Add(thisLine)

                End While

            End Using


            sb.Length = 0

            sb = Nothing

            Return ls

        End Function



        Private Shared Async Function InternalConnect(ByVal ci As ConnectionInfo, ByVal remoteDebuggingUri As String) As System.Threading.Tasks.Task

            ci.ChromeProcess = New RemoteChromeProcess(remoteDebuggingUri)

            ci.SessionInfo = Await ci.ChromeProcess.StartNewSession()

        End Function



        Private Shared Async Function ConnectToChrome(ByVal chromePath As String, ByVal remoteDebuggingUri As String) As System.Threading.Tasks.Task(Of ConnectionInfo)

            Dim ci As ConnectionInfo = New ConnectionInfo()


            Try

                Await InternalConnect(ci, remoteDebuggingUri)

            Catch ex As System.Exception


                If ex.InnerException IsNot Nothing AndAlso Object.ReferenceEquals(ex.InnerException.[GetType](), GetType(System.Net.WebException)) Then


                    If (CType(ex.InnerException, System.Net.WebException)).Status = System.Net.WebExceptionStatus.ConnectFailure Then

                        Dim chromeProcessFactory As MasterDevs.ChromeDevTools.IChromeProcessFactory = New MasterDevs.ChromeDevTools.ChromeProcessFactory(New FastStubbornDirectoryCleaner(), chromePath)

                        Dim persistentChromeProcess As MasterDevs.ChromeDevTools.IChromeProcess = chromeProcessFactory.Create(9222, True)


                        ' await cannot be used inside catch ...

                        ' Await InternalConnect(ci, remoteDebuggingUri)

                        InternalConnect(ci, remoteDebuggingUri).Wait()

                        Return ci

                    End If

                End If


                System.Console.WriteLine(chromePath)

                System.Console.WriteLine(ex.Message)

                System.Console.WriteLine(ex.StackTrace)


                If ex.InnerException IsNot Nothing Then

                    System.Console.WriteLine(ex.InnerException.Message)

                    System.Console.WriteLine(ex.InnerException.StackTrace)

                End If


                System.Console.WriteLine(ex.[GetType]().FullName)

                Throw

            End Try


            Return ci

        End Function



        Private Shared Async Function ClosePage(ByVal chromeSession As MasterDevs.ChromeDevTools.IChromeSession, ByVal frameId As String, ByVal headLess As Boolean) As System.Threading.Tasks.Task

            Dim closeTargetTask As System.Threading.Tasks.Task(Of MasterDevs.ChromeDevTools.CommandResponse(Of CloseTargetCommandResponse)) = chromeSession.SendAsync(New CloseTargetCommand() With {

                .TargetId = frameId

            })


            ' await will block forever if headless    

            If Not headLess Then

                Dim closeTargetResponse As MasterDevs.ChromeDevTools.CommandResponse(Of CloseTargetCommandResponse) = Await closeTargetTask

                System.Console.WriteLine(closeTargetResponse)

            Else

                System.Console.WriteLine(closeTargetTask)

            End If

        End Function



        Public Shared Async Function ConvertDataAsync(ByVal conversionData As ConversionData) As System.Threading.Tasks.Task

            Dim chromeSessionFactory As MasterDevs.ChromeDevTools.IChromeSessionFactory = New MasterDevs.ChromeDevTools.ChromeSessionFactory()



            Using connectionInfo As ConnectionInfo = Await ConnectToChrome(conversionData.ChromePath, conversionData.RemoteDebuggingUri)

                Dim chromeSession As MasterDevs.ChromeDevTools.IChromeSession = chromeSessionFactory.Create(connectionInfo.SessionInfo.WebSocketDebuggerUrl)


                Await chromeSession.SendAsync(New SetDeviceMetricsOverrideCommand With {

                    .Width = conversionData.ViewPortWidth,

                    .Height = conversionData.ViewPortHeight,

                    .Scale = 1

                })


                Dim navigateResponse As MasterDevs.ChromeDevTools.CommandResponse(Of NavigateCommandResponse) = Await chromeSession.SendAsync(New NavigateCommand With {

                    .Url = "about:blank"

                })


                System.Console.WriteLine("NavigateResponse: " & navigateResponse.Id)

                Dim setContentResponse As MasterDevs.ChromeDevTools.CommandResponse(Of SetDocumentContentCommandResponse) = Await chromeSession.SendAsync(New SetDocumentContentCommand() With {

                    .FrameId = navigateResponse.Result.FrameId,

                    .Html = conversionData.Html

                })


                Dim cm2inch As UnitConversion_t = Function(ByVal centimeters As Double) centimeters * 0.393701

                Dim mm2inch As UnitConversion_t = Function(ByVal milimeters As Double) milimeters * 0.0393701


                Dim printCommand2 As PrintToPDFCommand = New PrintToPDFCommand() With {

                    .Scale = 1,

                    .MarginTop = 0,

                    .MarginLeft = 0,

                    .MarginRight = 0,

                    .MarginBottom = 0,

                    .PrintBackground = True,

                    .Landscape = False,

                    .PaperWidth = mm2inch(conversionData.PageWidth),

                    .PaperHeight = mm2inch(conversionData.PageHeight) ' 

                }


                '.PaperWidth = cm2inch(conversionData.PageWidth),

                '.PaperHeight = cm2inch(conversionData.PageHeight)



                If conversionData.ChromiumActions.HasFlag(ChromiumActions_t.GetVersion) Then


                    Try

                        System.Diagnostics.Debug.WriteLine("Getting browser-version")

                        Dim version As MasterDevs.ChromeDevTools.CommandResponse(Of GetVersionCommandResponse) = Await chromeSession.SendAsync(New GetVersionCommand())

                        System.Diagnostics.Debug.WriteLine("Got browser-version")

                        conversionData.Version = version.Result

                    Catch ex As System.Exception

                        conversionData.Exception = ex

                        System.Diagnostics.Debug.WriteLine(ex.Message)

                    End Try

                End If


                If conversionData.ChromiumActions.HasFlag(ChromiumActions_t.ConvertToImage) Then


                    Try

                        System.Diagnostics.Debug.WriteLine("Taking screenshot")

                        Dim screenshot As MasterDevs.ChromeDevTools.CommandResponse(Of CaptureScreenshotCommandResponse) = Await chromeSession.SendAsync(New CaptureScreenshotCommand With {

                            .Format = "png"

                        })

                        System.Diagnostics.Debug.WriteLine("Screenshot taken.")

                        conversionData.PngData = System.Convert.FromBase64String(screenshot.Result.Data)

                    Catch ex As System.Exception

                        conversionData.Exception = ex

                        System.Diagnostics.Debug.WriteLine(ex.Message)

                    End Try

                End If


                If conversionData.ChromiumActions.HasFlag(ChromiumActions_t.ConvertToPdf) Then


                    Try

                        System.Diagnostics.Debug.WriteLine("Printing PDF")

                        Dim pdf As MasterDevs.ChromeDevTools.CommandResponse(Of PrintToPDFCommandResponse) = Await chromeSession.SendAsync(printCommand2)

                        System.Diagnostics.Debug.WriteLine("PDF printed.")

                        conversionData.PdfData = System.Convert.FromBase64String(pdf.Result.Data)

                    Catch ex As System.Exception

                        conversionData.Exception = ex

                        System.Diagnostics.Debug.WriteLine(ex.Message)

                    End Try

                End If



                System.Console.WriteLine("Closing page")

                Await ClosePage(chromeSession, navigateResponse.Result.FrameId, True)

                System.Console.WriteLine("Page closed")


            End Using ' connectionInfo


        End Function ' ConvertDataAsync



        Public Shared Sub ConvertData(ByVal conversionData As ConversionData)

            ConvertDataAsync(conversionData).Wait()

        End Sub



    End Class



End Namespace

請注意,如果有人使用 C#,最好使用此庫: https:

//github.com/BaristaLabs/chrome-dev-tools-runtime

,它使用較少的外部依賴項,并且是 NetCore。我使用另一個只是因為我必須將它移植到舊的框架版本......


查看完整回答
反對 回復 2023-05-11
  • 1 回答
  • 0 關注
  • 217 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號