Generate PDF with selenium chrome driver - java

To generate PDF from a HTML file, I want to use selenium Chrome driver.
I tried it with command line :
chrome.exe --headless --disable-gpu --print-to-pdf file:///C:invoiceTemplate2.html
and it works perfectly, So I wanted to do that with JAVA and here's my code :
System.setProperty("webdriver.chrome.driver", "C:/work/chromedriver.exe");
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless", "--disable-gpu", "--print-to-pdf",
"file:///C:/invoiceTemplate2.html");
WebDriver driver = new ChromeDriver(options);
driver.quit();
The server is started with no problem, but chrome is opened with multiple tabs with the arguments I specified in Options.
Any solution to this ? thx.

This can indeed be done with Selenium and ChromeDriver (tested with Chrome version 85), but using the "print-to-pdf" option when starting Chrome from the webdriver is not the solution.
The thing to do is to use the command execution functionality of ChromeDriver:
https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/remote/RemoteWebDriver.html#execute-java.lang.String-java.util.Map-
There is a command called Page.printToPDF that provides PDF output functionality. A dictionary containing the item "data", with the resulting PDF in base-64-encoded format, is returned.
Unfortunately, I do not have a full Java example, but in this answer, there is a C# example (Selenium methods are named differently in C# compared to Java, but the principle should be the same):
https://stackoverflow.com/a/63970792/2416627
The Page.printToPDF command in Chrome is documented here:
https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF

UPDATE:
we noticed that the original workaround was not always working properly, and we went for a Selenium + ChromeDriver:
public void generatePdf(Path inputPath, Path outputPath) throws Exception
{
try
{
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless", "--disable-gpu", "--run-all-compositor-stages-before-draw");
ChromeDriver chromeDriver = new ChromeDriver(options);
chromeDriver.get(inputPath.toString());
Map<String, Object> params = new HashMap();
String command = "Page.printToPDF";
Map<String, Object> output = chromeDriver.executeCdpCommand(command, params);
try
{
FileOutputStream fileOutputStream = new FileOutputStream(outputPath.toString());
byte[] byteArray = java.util.Base64.getDecoder().decode((String) output.get("data"));
fileOutputStream.write(byteArray);
fileOutputStream.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
catch (Exception e)
{
e.printStackTrace(System.err);
throw e;
}
}
If this will be called frequently I suggest reusing the driver object because it takes a while to initialize.
Remember to close or quit the driver to avoid leaving Zombie chrome processes behind and also remember to install ChromeDriver in your machine.
Original Solution:
Not being able to get the desired outcome using ChromeDriver my workaround was to call the headless chrome in the command-line from my Java program.
This is working on Windows but just changing the contents of the paths used in the command variable should make it work in Linux too.
public void generatePdf(Path inputPath, Path outputPath) throws Exception {
try {
String chromePath = "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe";
String command = chromePath + " --headless --disable-gpu --run-all-compositor-stages-before-draw --print-to-pdf=" + outputPath.toString() + " " + inputPath.toString();
// Runs "chrome" Windows command
Process process = Runtime.getRuntime().exec(command);
process.waitFor(); // Waits for the command's execution to finish
}catch (Exception e){
e.printStackTrace(System.err);
throw e;
}finally{
// Deletes files on exit
input.toFile().deleteOnExit();
output.toFile().deleteOnExit();
}
}
Note: both input and output paths are temporary files created with NIO.

The code will help you save the page in PDF format on Selenium c#
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
protected void PDFconversion(ChromeDriver driver, string root, string rootTemp)
{
//Grid.Rows.Add(TxtBxName.Text, TxtBxAddress.Text);
try
{
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
Thread.Sleep(500);
js.ExecuteScript("setTimeout(function() { window.print(); }, 0);");
Thread.Sleep(500);
driver.SwitchTo().Window(driver.WindowHandles.Last());
Thread.Sleep(500);
string JSPath = "document.querySelector('body>print-preview-app').shadowRoot.querySelector('#sidebar').shadowRoot.querySelector('#destinationSettings').shadowRoot.querySelector('#destinationSelect').shadowRoot.querySelector('print-preview-settings-section:nth-child(9)>div>select>option:nth-child(3)')";
Thread.Sleep(500);
IWebElement PrintBtn = (IWebElement)js.ExecuteScript($"return {JSPath}");
Thread.Sleep(500);
PrintBtn.Click();
string JSPath1 = "document.querySelector('body>print-preview-app').shadowRoot.querySelector('#sidebar').shadowRoot.querySelector('print-preview-button-strip').shadowRoot.querySelector('cr-button.action-button')";
Thread.Sleep(1000);
IWebElement PrintBtn1 = (IWebElement)js.ExecuteScript($"return {JSPath1}");
PrintBtn1.Click();
Thread.Sleep(1000);
SendKeys.Send("{HOME}");
SendKeys.Send(rootTemp + "\\" + "result.pdf"); // Path
SendKeys.Send("{TAB}");
SendKeys.Send("{TAB}");
SendKeys.Send("{TAB}");
SendKeys.Send("{ENTER}");
Thread.Sleep(1000);
}
catch (Exception ex){}
}

You have to do two things.
First: Make a screenshot using selenium.
Second: Convert that screenshot using any pdf tool, like itext. Here I am showing a complete example of how to do this.
Step 1: Download the jar of itext from here and add the jar file to your build path.
Step 2: Add this code to your project.
ChromeOptions options = new ChromeOptions();
options.addArguments("disable-infobars");
options.addArguments("--print-to-pdf");
WebDriver driver = new ChromeDriver(options);
driver.get("file:///C:/invoiceTemplate2.html");
try {
File screenshot = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(screenshot, new File("screenshot.png"));
Document document = new Document(PageSize.A4, 20, 20, 20, 20);
PdfWriter.getInstance(document, new FileOutputStream("webaspdf.pdf"));
document.open();
Image image = Image.getInstance("screenshot.png");
document.add(image);
document.close();
}
catch (Exception e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
Note: To use the mentioned itext package, add the required imports to your code.
import com.itextpdf.text.Document;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;

Related

How to handle keyboard operation on selenium chrome browser running on virtual ubuntu 22

I am trying to select "OK" button against my browser certificate pop-up using robot class. The same piece of code works well on windows but failing on ubuntu.
Code:
public void load() {
setUrl();
Thread threadNavigation = new Thread() {
#Override
public void run() {
driver.navigate().to(url);
}
};
Thread threadCertificateHandler = new Thread() {
#Override
public void run() {
try {
Thread.sleep(3000);
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_ENTER);
robot.delay(2000);
robot.keyRelease(KeyEvent.VK_ENTER);
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
}
};
// Start the threads.
threadNavigation.start();
threadCertificateHandler.start();
// Wait for them both to finish
try {
threadNavigation.join();
threadCertificateHandler.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Code Explanation: As soon as url is navigated using selenium, a certificate pop-up is displayed before page is loaded. The approach I have used here - 1 thread used here to get application url and another thread is used to handle the pop-up.
Attaching screenshot of the pop-up.
ChromeDriver Init Code:
public void getStandaloneHubNodeServerDriver(String browserType,String platformType, String url) throws MalformedURLException {
DesiredCapabilities caps = new DesiredCapabilities();
logger.log(Level.INFO, () -> "Setting browser address #: " + url);
ChromeOptions options = new ChromeOptions();
options.addArguments("start-maximized"); // open Browser in maximized mode
options.addArguments("disable-infobars"); // disabling infobars
options.addArguments("--disable-extensions"); // disabling extensions
options.addArguments("--disable-dev-shm-usage"); // overcome limited resource problems
options.addArguments("--no-sandbox"); // Bypass OS security model
options.setAcceptInsecureCerts(true);
options.setUnhandledPromptBehaviour(UnexpectedAlertBehaviour.ACCEPT);
options.setExperimentalOption("excludeSwitches",Arrays.asList("disable-popup-blocking"));
//threadLocalDriver.set(new RemoteWebDriver(new URL(url),options));
driver = new RemoteWebDriver(new URL(url),options);
driver.manage().window().maximize();
}
The same code works like a charm on local windows but when executed on remote VM running on linux, it does not perform the "Enter" event. Is there's any alternative to Robot class that can be used for linux?
Robot class is generally inconsistent as it blindly performs keyboard or mouse actions. I have figured out a solution to select the browser certificate without Robot class. This works only on Firefox though. If you are happy to switch from Chrome to Firefox, try the below code:
FirefoxOptions options = new FirefoxOptions();
options.addArguments("-profile");
options.addArguments("C:/Users/username/AppData/Roaming/Mozilla/Firefox/Profiles/zyz.default");
WebDriver driver = new FirefoxDriver(options);
In the above code, driver instance is loaded with the default Firefox browser profile. This profile will contain the certificate which will be accepted automatically and pop-up will not even be triggered. Try this and see if you can get away from using Robot class.

chromedriver NullPointerException error on linux

I'm currently learning selenium. I have followed a step by step walkthrough on how to start selenium-chromedriver. However I am experiencing this error here and I am have no idea how to solve it.
The following are the source code the walkthrough has given me.
package driverUtilities;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
public class DriverUtilities {
private static DriverUtilities instanceOfDriverUtilities;
private WebDriver driver;
public static DriverUtilities getInstanceOfDriverUtilities() {
if (instanceOfDriverUtilities == null) {
instanceOfDriverUtilities = new DriverUtilities();
}
return instanceOfDriverUtilities;
}
public WebDriver getDriver() {
if (driver == null) {
CreateDriver();
}
return driver;
}
private String GetDriverName() {
Properties config = new Properties();
String driverName = "";
try {
config.load(new FileInputStream("config.properties"));
} catch (FileNotFoundException e) {
System.out.println("Config file is not present");
e.printStackTrace();
} catch (IOException e) {
System.out.println("Error when loading config file");
e.printStackTrace();
}
for (String key : config.stringPropertyNames()) {
if (key.equals("browser")) {
driverName = config.getProperty(key);
}
}
return driverName;
}
private void CreateDriver() {
String driverName = GetDriverName();
switch (driverName) {
case "Google Chrome":
System.setProperty("webdriver.chrome.driver", "chromedriver");
this.driver = new ChromeDriver();
break;
case "Firefox":
System.setProperty("webdriver.gecko.driver", "geckodriver.exe");
this.driver = new FirefoxDriver();
break;
default:
break;
}
}
}
This is the Driver Utilities class
package test;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import driverUtilities.DriverUtilities;
public class Mod08_Slide_16_Navigation_Commands {
#Test
public void navigationCommands() {
DriverUtilities myDriverUtilities = new DriverUtilities();
WebDriver driver = myDriverUtilities.getDriver();
System.out.println("Start the Test Case");
// Load the website - http://www.bbc.co.uk
driver.get("http://www.bbc.co.uk");
System.out.println("\nLoad the website - http://www.bbc.co.uk");
// Refresh the page
driver.navigate().refresh();
System.out.println("\nRefresh the page");
// Load the website - http://www.google.co.uk
driver.get("http://www.google.co.uk");
System.out.println("\nLoad the website - http://www.google.co.uk");
// Go back to the website - http://www.bbc.co.uk
driver.navigate().back();
System.out.println("\nGo back to the website - http://www.bbc.co.uk");
// Go forward to the website - http://www.google.co.uk
driver.navigate().forward();
System.out.println("\nGo forward to the website - http://www.google.co.uk");
// Close the browser window
driver.close();
System.out.println("\nClose the browser window");
System.out.println("\nEnd of Test Case \"Navigation Commands\"");
}
}
This is the java test class I am trying to make work of
# Change the browser to use for testing purposes, accepted values are Google Chrome and Firefox
browser=Google Chrome
I also have the pom.xml file and a config.properties file and will share it if its required.
Note: My assumptions is that the setproperty function is not locating the correct path to chromedriver. However, I'm a arch linux user thus I'm not sure if the value inside the setproperty function is set to "chromedriver" or "/usr/bin/chromedriver"
Note 1: I have navigated to the .metadata of the Eclipse IDE and deleted it after reading some stackoverflow posts. However, this did not work*
Note 2: I am completely new to Selenium however, I have some experiences with Java and understand that nullpointerexception occurs when I declare a variable but did not create an object and assign that variable to it.
Edit 1: I have included the config.properties as specified
One suggestion I have is to hard-code the creation of the driver (without config.properties) and once you have the code working, you can work on optimizations such as dynamically creating the web driver based on a key value like you are doing here. To do this, simply replace this line
WebDriver driver = myDriverUtilities.getDriver();
with
System.setProperty("webdriver.chrome.driver", "C:\\Program Files\\WebDrivers\\chromedriver.exe");
WebDriver driver = new ChromeDriver();
Obviously, make sure the path to the chromedriver.exe is resolved correctly for your system. By the way, unless your web drivers are in the root folder of your code project, that path is not correct. I will double check that is the case.
I ran your code with this recommendation and it worked. That tells me either your web driver path is not correct, or there is something funky with your config.properties.
Lastly, this loop isn't correct either
for (String key : config.stringPropertyNames()) {
if (key.equals("browser")) {
driverName = config.getProperty(key);
}
}
You don't want to loop through all your browser keys. If you have more than one "key", it will iterate through them and return the last one found. Instead, you need to call getProperty() once your properties file is loaded:
String propValue = props.getProperty("browser");
Then, once you have the correct propValue, you should pass it to your switch to properly instantiate the web driver.
switch(propValue) {
case "chrome":
this.driver = new ChromeDriver();
break;
case "firefox":
this.driver = new GeckoDriver();
break;
default:
// throw some exception here
throw new IllegalArgumentException(propValue + " is not a supported browser.");
}

Error using gecko driver script for Selenium 3.11

I am using Selenium version 3.11, gecko driver v0.20 and Firefox version 59. I used the system.setproperty script but I'm still getting this error:
Exception in thread "main" java.lang.IllegalStateException: The path to the driver executable must be set by the webdriver.gecko.driver system property
I also tried this with Firefox v40.
Please help me sort out this issue. Thanks.
The syntax i used is as follows :
System.setProperty("webdriver. gecko.driver","C:\geckodriver.exe");
Try to do something like that:
public void loadSystemProperties() {
try {
InputStream in = getClass().getResourceAsStream("/geckodriver");
File file = stream2file(in);
System.setProperty("webdriver.gecko.driver", file.getAbsolutePath());
LOGGER.info("Geckdriver found at {}", file.getAbsoluteFile());
} catch (IOException e) {
LOGGER.error(e.getMessage());
}
}
static File stream2file(InputStream in) throws IOException {
String PREFIX = "stream2file";
String SUFFIX = ".tmp";
final File tempFile = File.createTempFile(PREFIX, SUFFIX);
tempFile.deleteOnExit();
try (FileOutputStream out = new FileOutputStream(tempFile)) {
IOUtils.copy(in, out);
}
tempFile.setExecutable(true);
return tempFile;
}
I'm using commons-io, version 2.6. In addition, my geckodriver is in my resource folder.
Thanks for your response.My issue stands resolved after setting the geckodriver path in the environment variable settings.
But can anyone help me with one question: which latest firefox version will support firebug and xpath addons?

Selenium 2.0 Robot class on remote host

I just write a test which is supposed to download pdf files via webApp(Yep, I know, I should not do it on selenium, but You know, Orders.)
What do I need?
For diffrent scenarios I have to download difrent pdf, rename it and place to custom catalog. So, I have to handle with system modal window.
Everything works great, so test is run on remote host, and when I click to download the file I handle the system modal window(I used robotil package, it is extended robot class which allow us use robot class on remote host) so I use robotil class to type path to file, and file name on system modal and then click "Enter" to confirm and save the file. This is everything I need and it works, so where is the problem? here: SOMEONE should be logged to remote host, if Im logged via rdp and looks at screen(and doing my stuff on my host) then everything is great, but for the case when no one is logged, it looks like that during tests webbrowswer do not have a FOCUS, so everytime robotil class do some action this action is not focused on webbroswer(as it should).
test class:
#Test
public void compareDeposits() throws Exception {
HomePage homePage = new HomePage(driver);
PageFactory.initElements(driver, homePage);
PrintDepositsPage printDepositsPage = (PrintDepositsPage) homePage.openViaUrl(Data.baseUrl).openViewViaTopMenu(
ETopMenuItem.PrintDeposits);
((PrintDepositsPage) printDepositsPage).goToPrintedDepositsTab();
printDepositsPage.getPrintedDepositsDateRangeFromInput().click();
printDepositsPage.getPrintedDepositsDateRangeFromInput().clear();
printDepositsPage.getPrintedDepositsGoButton().click();
printDepositsPage.getFirstRecordOnPrintedDepositsTab().click();
handler.getRobot().mouseClick(371, 274, InputEvent.BUTTON1_MASK);// get focus
printDepositsPage.getPrintButtonEnabled().click();
handler.downloadFile("DepositTest");
handler object declaration:
class SystemModalWindowHandler {
private RemoteWebDriver driver;
private Date date = new Date();
private DateFormat dateFormat = new SimpleDateFormat("yyy/mm/dd");
private String extendedTestName = dateFormat.format(date).replace("/", ".") + ".pdf";
private Robotil robotil = new Robotil("xxxxx", 6667);
public Robotil getRobot(){
return robotil;
}
public void downloadFile(String testFileName) throws AWTException, InterruptedException {
boolean continueBool = true;
while (continueBool) {
String pathToTestFile = new String("C:\\DiffPdfData\\" + testFileName + "\\"
+ extendedTestName);
Thread.sleep(3000);
for (int i = 0; i < pathToTestFile.length(); i++) {
System.out.println(KeyStroke.getKeyStroke(pathToTestFile.charAt(i)) + " = "
+ (int) pathToTestFile.charAt(i));
if ((int) pathToTestFile.charAt(i) == 58) {
robotil.pressKey(KeyEvent.VK_SHIFT);
robotil.pressAndReleaseKey(KeyEvent.VK_SEMICOLON);
robotil.releaseKey(KeyEvent.VK_SHIFT);
}
else {
robotil.pressAndReleaseKey(KeyEvent.getExtendedKeyCodeForChar((int) pathToTestFile.charAt(i)));
}
}
robotil.pressAndReleaseKey(KeyEvent.VK_ENTER);
continueBool = false;
}
is there any way to get focus on webbrowser when no one is logged in?.
I believe that using the mentioned strategy you won't be able to accomplish it without logged-in user. So I suggest you to use a simpler solution.
You can configure Firefox the directly download the files - File types and download actions
If you don't want to hardcode the setting for your browser, you can setup a specific FF profile only for your tests, where you can configure where you want the files to be downloaded.
FirefoxProfile firefoxProfile = new FirefoxProfile();
firefoxProfile.setPreference("browser.download.folderList",2);
firefoxProfile.setPreference("browser.download.manager.showWhenStarting",false);
firefoxProfile.setPreference("browser.download.dir","c:\\downloads");
firefoxProfile.setPreference("browser.helperApps.neverAsk.saveToDisk","text/csv");
WebDriver driver = new FirefoxDriver(firefoxProfile);
Chrome Driver:
String downloadFilepath = "/path/to/download";
HashMap<String, Object> chromePrefs = new HashMap<String, Object>();
chromePrefs.put("profile.default_content_settings.popups", 0);
chromePrefs.put("download.default_directory", downloadFilepath);
ChromeOptions options = new ChromeOptions();
options.setExperimentalOption("prefs", chromePrefs);
DesiredCapabilities cap = DesiredCapabilities.chrome();
cap.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
cap.setCapability(ChromeOptions.CAPABILITY, options);
WebDriver driver = new ChromeDriver(cap);

Uploading file using selenium javascript error

I have browse button to browse for the file. After browsing there is a import button which will actually import the file.
I'm able to browse the path using the following code:
public static void uploadFiles(String object, String data) {
try {
String filemode="";
Capabilities cap = ((RemoteWebDriver) driver).getCapabilities();
String browsername = cap.getBrowserName();
//System.out.println(browsername);
if (browsername.contains("chrome")){
filemode= "Open";
}
else if (browsername.contains("firefox")){
filemode= "File Upload";
}
else if (browsername.contains("explorer")){
filemode = "Choose File to Upload";
}
String EXE_FILE=DriverScript.EXE_FILENAME;
String[] command={EXE_FILE,filemode,data};
Runtime.getRuntime().exec(command);
Thread.sleep(5000);
} catch (Exception e) {
}
}
But when I click on the import button after that "JavaScript error (WARNING: The server did not provide any stacktrace information)" exception is thrown. EXE_FILE is the path to Fileload.exe which is used for browsing
Uploading a file using Selenium:
WebElement upload = driver.findElement(By.id("identifier of input tag"));
upload.sendKeys("path to file");
Remove capability INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS if you're using it in your test code and manually set your IE protected mode settings to be the same for all zones.
It should fix the problem.

Categories