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
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.
Image 1 : Sélectionner l'imprimante
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; }
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!