cause :

Because the project needs to realize the silent printing effect of web pages , Then the silent printing effect cannot be achieved by directly using the browser printing function .

The preview interface will pop up when the browser prints ( Here's the picture ), Unable to achieve silent printing .

Solution :

Google browser provides a way to html Print directly to pdf And save it as a file , And then pdf Print silently .

Before invoking Google commands , Need to get the current Google installation location :

public static class ChromeFinder
{
#region Get application directory
private static void GetApplicationDirectories(ICollection<string> directories)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
const string subDirectory = "Google\\Chrome\\Application";
directories.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), subDirectory));
directories.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), subDirectory));
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
directories.Add("/usr/local/sbin");
directories.Add("/usr/local/bin");
directories.Add("/usr/sbin");
directories.Add("/usr/bin");
directories.Add("/sbin");
directories.Add("/bin");
directories.Add("/opt/google/chrome");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
throw new Exception("Finding Chrome on MacOS is currently not supported, please contact the programmer.");
}
#endregion
#region Get the current program directory
private static string GetAppPath()
{
var appPath = AppDomain.CurrentDomain.BaseDirectory;
if (appPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
return appPath;
return appPath + Path.DirectorySeparatorChar;
}
#endregion
#region lookup
/// <summary>
/// Try to find Google Apps
/// </summary>
/// <returns></returns>
public static string Find()
{
// about Windows, Let's first check the registry . This is the safest way , Non default installation locations are also considered . Please note that ,Chrome x64 At present (2019 year 2 month ) Also installed in the program file (x86) in , And use the same registry key !
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var key = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome","InstallLocation", string.Empty);
if (key != null)
{
var path = Path.Combine(key.ToString(), "chrome.exe");
if (File.Exists(path)) return path;
}
}
// Collect common executable file names
var exeNames = new List<string>(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
exeNames.Add("chrome.exe");
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
exeNames.Add("google-chrome");
exeNames.Add("chrome");
exeNames.Add("chromium");
exeNames.Add("chromium-browser");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
exeNames.Add("Google Chrome.app/Contents/MacOS/Google Chrome");
exeNames.Add("Chromium.app/Contents/MacOS/Chromium");
}
// Check the running Directory
var currentPath = GetAppPath();
foreach (var exeName in exeNames)
{
var path = Path.Combine(currentPath, exeName);
if (File.Exists(path)) return path;
}
// Find Google program files in the general software installation directory
var directories = new List<string>();
GetApplicationDirectories(directories);
foreach (var exeName in exeNames)
{
foreach (var directory in directories)
{
var path = Path.Combine(directory, exeName);
if (File.Exists(path)) return path;
}
}
return null;
}
#endregion
}

1、 Command mode :

Start the Google process by command , Incoming web address 、pdf Save location and other information , take html convert to pdf:

/// <summary>
/// function cmd command
/// </summary>
/// <param name="command"></param>
private void RunCMD(string command)
{
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.UseShellExecute = false; // Whether to use the operating system shell start-up
p.StartInfo.RedirectStandardInput = true;// Accept input from the calling program
p.StartInfo.RedirectStandardOutput = true;// Get the output information from the calling program
p.StartInfo.RedirectStandardError = true;// Redirect standard error output
p.StartInfo.CreateNoWindow = true;// Don't show program window
p.Start();// Start the program
// towards cmd Window sends input information
p.StandardInput.WriteLine(command + "&exit");
p.StandardInput.AutoFlush = true;
//p.StandardInput.WriteLine("exit");
// Write the command to be executed to the standard input . Use here & Is the symbol of a batch command , Whether the previous command is executed successfully or not (exit) command , If not implemented exit command , Call back ReadToEnd() The method will fake death
// Similar symbols and && and || The former means that the following command will not be executed until the previous command is executed successfully , The latter means that the following command will not be executed until the previous command fails
// obtain cmd The output of the window
p.StandardOutput.ReadToEnd();
p.WaitForExit();// Wait for the program to finish executing and exit the process
p.Close();
} public void GetPdf(string url, List<string> args = null)
{
var chromeExePath = ChromeFinder.Find();
if (string.IsNullOrEmpty(chromeExePath))
{
MessageBox.Show(" Failed to get Google browser address ");
return;
}
var outpath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tmppdf");
if (!Directory.Exists(outpath))
{
Directory.CreateDirectory(outpath);
}
outpath = Path.Combine(outpath, DateTime.Now.Ticks + ".pdf");
if (args == null)
{
args = new List<string>();
args.Add("--start-in-incognito");// Stealth mode
args.Add("--headless");// No interface mode
args.Add("--disable-gpu");// Ban gpu Speed up
args.Add("--print-to-pdf-no-header");// Print generation pdf No header, no footers
args.Add($"--print-to-pdf=\"{outpath}\" \"{url}\"");// Print generation pdf To the specified directory
}
string command = $"\"{chromeExePath}\"";
if (args != null && args.Count > 0)
{
foreach (var item in args)
{
command += $" {item} ";
}
}
Stopwatch sw = new Stopwatch();
sw.Start();
RunCMD(command);
sw.Stop();
MessageBox.Show(sw.ElapsedMilliseconds + "ms");
}

The main command parameters include :

a)  --headless: No interface

b) --print-to-pdf-no-header : Print generation pdf Do not include headers and footers

c) --print-to-pdf: Print the page as pdf, The parameter value is the output address

Existing problems :

    • In this way, multiple Google processes will be generated ( As many as 5 individual ), And frequently create processes when performance is poor , Will lead to pdf slower
    • In some cases , The process created by Google : Failed to exit completely , Cause subsequent generation pdf unexecuted .

The exception process parameters are similar :--type=crashpad-handler "--user-data-dir=xxx" /prefetch:7 --monitor-self-annotation=ptype=crashpad-handler "--database=xx" "--metrics-dir=xx" --url=https://clients2.google.com/cr/report --annotation=channel= --annotation=plat=Win64 --annotation=prod=Chrome

that , There is no way to reuse Google processes , And can generate pdf Operation? ? Then you need to use the second way .

2、Chrome DevTools Protocol The way

The main steps of this method are :

  • Create a Google process without interface
#region Start the Google browser process 
/// <summary>
/// Start the Google process , If it has been started, it will not be started
/// </summary>
/// <exception cref="ChromeException"></exception>
private void StartChromeHeadless()
{
if (IsChromeRunning)
{
return;
} var workingDirectory = Path.GetDirectoryName(_chromeExeFileName);
_chromeProcess = new Process();
var processStartInfo = new ProcessStartInfo
{
FileName = _chromeExeFileName,
Arguments = string.Join(" ", DefaultChromeArguments),
CreateNoWindow = true,
};
_chromeProcess.ErrorDataReceived += _chromeProcess_ErrorDataReceived;
_chromeProcess.EnableRaisingEvents = true;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardError = true;
_chromeProcess.StartInfo = processStartInfo;
_chromeProcess.Exited += _chromeProcess_Exited;
try
{
_chromeProcess.Start();
}
catch (Exception exception)
{
throw;
}
_chromeWaitEvent = new ManualResetEvent(false);
_chromeProcess.BeginErrorReadLine();
if (_conversionTimeout.HasValue)
{
if (!_chromeWaitEvent.WaitOne(_conversionTimeout.Value))
throw new Exception($" exceed {_conversionTimeout.Value}ms, Can't connect to Chrome development tool ");
}
_chromeWaitEvent.WaitOne();
_chromeProcess.ErrorDataReceived -= _chromeProcess_ErrorDataReceived;
_chromeProcess.Exited -= _chromeProcess_Exited;
}
/// <summary>
/// Exit event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void _chromeProcess_Exited(object sender, EventArgs e)
{
try
{
if (_chromeProcess == null) return;
var exception = Marshal.GetExceptionForHR(_chromeProcess.ExitCode);
throw new Exception($"Chrome Unexpected exit , {exception}");
}
catch (Exception exception)
{
_chromeEventException = exception;
_chromeWaitEvent.Set();
}
}/// <summary>
/// When Chrome Raised when data is sent to the error output
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private void _chromeProcess_ErrorDataReceived(object sender, DataReceivedEventArgs args)
{
try
{
if (args.Data == null || string.IsNullOrEmpty(args.Data) || args.Data.StartsWith("[")) return;
if (!args.Data.StartsWith("DevTools listening on")) return;
// DevTools listening on ws://127.0.0.1:50160/devtools/browser/53add595-f351-4622-ab0a-5a4a100b3eae
var uri = new Uri(args.Data.Replace("DevTools listening on ", string.Empty));
ConnectToDevProtocol(uri);
_chromeProcess.ErrorDataReceived -= _chromeProcess_ErrorDataReceived;
_chromeWaitEvent.Set();
}
catch (Exception exception)
{
_chromeEventException = exception;
_chromeWaitEvent.Set();
}
}
#endregion
  • Get the browser from the process output information ws Connection address , And create ws Connect ; Send... To the Google browser process ws news : Open a TAB
WebSocket4Net.WebSocket _browserSocket = null;
/// <summary>
/// Create connection
/// </summary>
/// <param name="uri"></param>
private void ConnectToDevProtocol(Uri uri)
{
// establish socket Connect
// Browser connection :ws://127.0.0.1:50160/devtools/browser/53add595-f351-4622-ab0a-5a4a100b3eae
_browserSocket = new WebSocket4Net.WebSocket(uri.ToString());
_browserSocket.MessageReceived += WebSocket_MessageReceived;
JObject jObject = new JObject();
jObject["id"] = 1;
jObject["method"] = "Target.createTarget";
jObject["params"] = new JObject();
jObject["params"]["url"] = "about:blank";
_browserSocket.Send(jObject.ToString());
// Create page cards Socket Connect
// Page card connection :ws://127.0.0.1:50160/devtools/browser/53add595-f351-4622-ab0a-5a4a100b3eae
var pageUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}/devtools/page/ Page card id";
}
  • according to devtools The agreement is created to the current page card ws Connect

    WebSocket4Net.WebSocket _pageSocket = null;
    private void WebSocket_MessageReceived(object sender, WebSocket4Net.MessageReceivedEventArgs e)
    {
    string msg = e.Message;
    var pars = JObject.Parse(msg);
    string id = pars["id"].ToString();
    switch (id)
    {
    case "1":
    var pageUrl = $"{_browserUrl.Scheme}://{_browserUrl.Host}:{_browserUrl.Port}/devtools/page/{pars["result"]["targetId"].ToString()}";
    _pageSocket = new WebSocket4Net.WebSocket(pageUrl);
    _pageSocket.MessageReceived += _pageSocket_MessageReceived;
    _pageSocket.Open();
    break;
    }
    }
  • Send a command to the page card , Jump to the need to generate pdf The page of
// Send refresh command 
JObject jObject = new JObject();
jObject["method"] = "Page.navigate"; // Method
jObject["id"] = "2"; //id
jObject["params"] = new JObject(); // Parameters
jObject["params"]["url"] = "http://www.baidu.com";
_pageSocket.Send(jObject.ToString());
  • Finally, the page card sends a command to generate pdf

    // Send refresh command 
    jObject = new JObject();
    jObject["method"] = "Page.printToPDF"; // Method
    jObject["id"] = "3"; //id
    jObject["params"] = new JObject(); // Parameter print parameter settings
    jObject["params"]["landscape"] = false;
    jObject["params"]["displayHeaderFooter"] = false;
    jObject["params"]["printBackground"] = false;
    _pageSocket.Send(jObject.ToString());

Details of command support , Detailed view DevTools Content of agreement

Reference resources :

DevTools agreement : Chrome DevTools Protocol - Page domain

Google parameter description :List of Chromium Command Line Switches « Peter Beverloo

【 The problem record 】- Google browser Html Generate PDF More articles about

  1. Generate PDF Total strategy 【2】 Already exist PDF Add content to

    Projects are changing , Demand is changing , It's always the programmer who hits the keyboard ..... PDF After generation , Sometimes you need to PDF Add something else to it , Like words , picture .... After several failed attempts , Finally got the right way to write code . Here it is ...

  2. PHP Generate PDF

    Web page generation is needed in a project PDF, That is to generate a PDF file , I've used it before HTML2PDF, It can only generate some simple HTML Code , complex HTML + css The effect is terrible , Baidu for a while , Find a ...

  3. Generate PDF Total strategy 【1】 First experience

    How many stepping holes have you experienced , How many similar blogs have you read , How many versions of Jar, To find the right way to write code , To realize the function points in the project manager's requirements analysis . Borrow this article once JavaEE Generate PDF The bumpy implementation process , Describe the procedures for small and medium-sized companies ...

  4. Django In the middle of PDF( One )

    Django In the middle of PDF( One ) Requirements describe :     An agreement reached between a website and its users , Each agreement contains unique information about the user , And you need to generate PDF And archive .PDF There needs to be an enterprise in the file LOGO. Text description and other information . Its presentation form ...

  5. Javascript take HTML Page generation PDF And download

    I've come across a demand recently , Need to generate the current page pdf, And download . For a few days , Sort it out by yourself , recorded , I think there should be someone who needs :) html2canvas brief introduction We can use it directly on the browser side html2canvas, To the whole ...

  6. java Generate PDF, Various formats 、 style 、 Watermarks are

    There are two places in the code that need pictures , Please replace . One is watermark . One is a finger . Needed JAR The package link :http://download.csdn.net/detail/justinytsoft/9688893 Here is a preview : ...

  7. js take HTML Page generation PDF And download

    I've come across a demand recently , Need to generate the current page pdf, And download . For a few days , Sort it out by yourself , recorded , I think there should be someone who needs :) Let's start with two plug-ins : html2Canvas brief introduction We can use it directly on the browser side html2 ...

  8. 【 original 】CRM 2015/2016,SSRS Generate PDF file , And send an email as an attachment

    The main steps are as follows : Generate a mail record Generate a ActivityParty Record Generate PDF file , And Base64 Add to ActivityMimeAttachment  In the middle Open the send mail window , For editing and sending mail ...

  9. itext Generate pdf Add header and footer to the file

    From the original :https://www.cnblogs.com/joann/p/5511905.html I just record all jar edition , It's a headache due to version conflict and incompatibility , Total needs 5 individual jar, among itextpd ...

  10. Java Use itext Generate pdf And download

    Usage method : 1. You need two jar package : iText-5.0.6.jar    // You must use this version , Otherwise, there is a lack of relevant methods TextAsian.jar // It is a package that must be referenced for the normal display of Chinese in the document TextAsi ...

Random recommendation

  1. jQuery-1.9.1 Source analysis series ( 6、 ... and ) Delay object application ——jQuery.ready

    Do you remember jQuery Initialization function jQuery.fn.init There is such a branch in //document ready Easy to write $(function(){…}) } else if ( jQuery.isFu ...

  2. CentOS7 install Ambari

    Environmental Science : CentOS7 Install two nodes :master.slave1. And configuration ssh Password-free login . step : obtain Ambari Public library file for (public repository): wget http://pu ...

  3. algorithm Common functions in

    Non modifying sequence operations (12 individual ) loop          Perform an operation on each element in the sequence          for_each() lookup          Find the first occurrence of a value in the sequence          fin ...

  4. iOS MJRefresh Pull up to load more

    1. Import MJRefresh package 2. Introduce... Into the class :#import "MJRefresh.h" 3. add to footerView Add and load more UI style : MJRefreshAutoNorm ...

  5. LindDotNetCore~ Fundamentals of entry

    Back to directory LindDotNetCore Basic introduction Running environment The configuration file Registration of services Registration of configuration files Use of services Use of profile Running environment vs2017+.netcore2.0,vs Need to upgrade to the latest package Configuration document ...

  6. install myeclipse after , Pop up on open :“ The revocation certificate for this site's security certificate is not available ”, How to solve ?

    1. When it pops up " Revocation information for this site's security certificate is not available . Whether or not to continue ?" When the dialog box for , Click on " Check the certificate ", Switch to " Details "TAB page , To find the "CRL branch ...

  7. c# word Document operation

    Reference resources https://blog.csdn.net/ruby97/article/details/7406806 Word The object model   (.Net Perspective) This article mainly aims at Visual St ...

  8. RelativeLayout in include Control overlay overlap problem

    RelativeLayout direct include the other one layout Yes, it will include The control in is the same as the current layout Controls in overlap , After checking the information Among them include The label must be marked with ( because include Middle finger ...

  9. NOIP2018 Rough record of zero explosion retirement

    \(Day\ -1\) Very decadent I took the exam this morning loli\(\ \ oi\) The last round of ,\(mhr\) An hour and fifteen minutes \(260\) branch , Hang and beat the master Find yourself \(T2\) The diameter of the tree is strange , I don't quite get it, but I think you are really terrific I'm afraid I'm not going to ...

  10. UnityHub Crack

    1. sign out UnityHub, Install well nodejs Execute the following command npm install -g asar 2. open UnityHub The installation directory is as follows C:\Program Files\Unity Hub\resour ...