From 54a2b8fbfde6f00c62dfeb3b619b70156c8254e5 Mon Sep 17 00:00:00 2001 From: Ed Manlove Date: Fri, 11 Aug 2023 21:12:33 -0400 Subject: [PATCH 1/7] Initial commit and sketchings of `Print Page As PDF` Keyword Some initial ideas and sketches for the `Print Page As PDF` keyword. I wanted to seewhat it may look like and how it may work. So this is just some preliminary ideas. Seeing a need to handle Print Options, need to document options and what are valid values, how to deal with the return encoded base64 string, etc. --- atest/acceptance/keywords/print_page.robot | 18 ++++++++++++++++++ src/SeleniumLibrary/keywords/screenshot.py | 21 ++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 atest/acceptance/keywords/print_page.robot diff --git a/atest/acceptance/keywords/print_page.robot b/atest/acceptance/keywords/print_page.robot new file mode 100644 index 000000000..bf377898e --- /dev/null +++ b/atest/acceptance/keywords/print_page.robot @@ -0,0 +1,18 @@ +*** Settings *** +Documentation Suite description +Suite Setup Go To Page "non_ascii.html" +Resource ../resource.robot + +*** Test Cases *** +Print Page As PDF Without Print Options + Print Page As PDF + +Provide Print Options From Module + # ${print_options}= Evaluate sys.modules['selenium.webdriver'].common.print_page_options() sys, selenium.webdriver + ${print_options}= Evaluate selenium.webdriver.common.print_page_options.PrintOptions() + # Set To Dictionary ${print_options} scale 0.5 + # Evaluate ${print_options}.scale=0.5 + # Set Variable ${print_options.scale} 0.5 + # Evaluate ${print_options.scale}=0.5 + Evaluate setattr($print_options, 'scale', 0.5) + Print Page As PDF ${print_options} diff --git a/src/SeleniumLibrary/keywords/screenshot.py b/src/SeleniumLibrary/keywords/screenshot.py index 628361c8a..57f1d38cc 100644 --- a/src/SeleniumLibrary/keywords/screenshot.py +++ b/src/SeleniumLibrary/keywords/screenshot.py @@ -14,10 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. import os -from typing import Union +from typing import Optional, Union +from base64 import b64decode from robot.utils import get_link_path from selenium.webdriver.remote.webelement import WebElement +from selenium.webdriver.common.print_page_options import PrintOptions from SeleniumLibrary.base import LibraryComponent, keyword from SeleniumLibrary.utils.path_formatter import _format_path @@ -235,3 +237,20 @@ def _embed_to_log_as_file(self, path, width): f'', html=True, ) + + @keyword + # def print_page_as_pdf(self, print_options: Optional[PrintOptions]=None,): + # def print_page_as_pdf(self, print_options: Optional[Union[PrintOptions, dict]]=None): + def print_page_as_pdf(self, print_options: Union[PrintOptions, dict, None]=None): + """ Print the current page as a PDF + + """ + if not print_options: + print_options = PrintOptions() + print_options.page_ranges = ['-'] + + base64code = self.driver.print_page(print_options) + pdfdata = b64decode(base64code) + with open('test.pdf', mode='wb') as pdf: + pdf.write(pdfdata) + #self.debug(pdf_str) \ No newline at end of file From b462eabcad0f6888b2da293fb13f549fb10c38f2 Mon Sep 17 00:00:00 2001 From: Ed Manlove Date: Mon, 29 Apr 2024 17:33:28 -0400 Subject: [PATCH 2/7] Switched from single print_options argument to individual options --- src/SeleniumLibrary/keywords/screenshot.py | 60 +++++++++++++++++++--- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/src/SeleniumLibrary/keywords/screenshot.py b/src/SeleniumLibrary/keywords/screenshot.py index 57f1d38cc..3cc87d569 100644 --- a/src/SeleniumLibrary/keywords/screenshot.py +++ b/src/SeleniumLibrary/keywords/screenshot.py @@ -241,16 +241,64 @@ def _embed_to_log_as_file(self, path, width): @keyword # def print_page_as_pdf(self, print_options: Optional[PrintOptions]=None,): # def print_page_as_pdf(self, print_options: Optional[Union[PrintOptions, dict]]=None): - def print_page_as_pdf(self, print_options: Union[PrintOptions, dict, None]=None): + # def print_page_as_pdf(self, print_options: Union[PrintOptions, dict, None]=None): + # """ Print the current page as a PDF + # + # """ + # if not print_options: + # print_options = PrintOptions() + # print_options.page_ranges = ['-'] + def print_page_as_pdf(self, + background=None, + margin_bottom=None, + margin_left=None, + margin_right=None, + margin_top=None, + orientation=None, + page_height=None, + page_ranges=['-'], + page_width=None, + scale=None, + shrink_to_fit=None, + path_to_file=None, + ): """ Print the current page as a PDF + ``page_ranges`` defaults to `['-']` or "all" pages. ``page_ranges`` takes a list of + strings indicating the ranges. + + The page size defaults to 21.59 for ``page_width`` and 27.94 for ``page_height``. + This is the equivalent size of US-Letter. The assumed units on these parameters + is centimeters. + + The default margin for top, left, bottom, right is `1`. The assumed units on + these parameters is centimeters. + + The default ``orientation`` is `portrait`. ``orientation`` can be either `portrait` + or `landscape`. + + The default ``scale`` is `1`. ``scale`` must be greater than or equal to `0.1` and + less than or equal to `2`. + + ``background`` and ``scale_to_fite`` can be either `${True}` or `${False}`.. + + If all print options are None then a pdf will failed to print silently.s """ - if not print_options: - print_options = PrintOptions() - print_options.page_ranges = ['-'] + + print_options = PrintOptions() + print_options.background = background + print_options.margin_bottom = margin_bottom + print_options.margin_left = margin_left + print_options.margin_right = margin_right + print_options.margin_top = margin_top + print_options.orientation = orientation + print_options.page_height = page_height + print_options.page_ranges = page_ranges + print_options.page_width = page_width + print_options.scale = scale + print_options.shrink_to_fit = shrink_to_fit base64code = self.driver.print_page(print_options) pdfdata = b64decode(base64code) with open('test.pdf', mode='wb') as pdf: - pdf.write(pdfdata) - #self.debug(pdf_str) \ No newline at end of file + pdf.write(pdfdata) \ No newline at end of file From 81752a7d7d991493080fe95443584505b6f6b7c3 Mon Sep 17 00:00:00 2001 From: Ed Manlove Date: Sat, 11 May 2024 12:57:34 -0400 Subject: [PATCH 3/7] More modifications of the print page as pdf keyword --- atest/acceptance/keywords/print_page.robot | 18 ++--- src/SeleniumLibrary/keywords/screenshot.py | 82 +++++++++++++++++----- 2 files changed, 72 insertions(+), 28 deletions(-) diff --git a/atest/acceptance/keywords/print_page.robot b/atest/acceptance/keywords/print_page.robot index bf377898e..c45cde715 100644 --- a/atest/acceptance/keywords/print_page.robot +++ b/atest/acceptance/keywords/print_page.robot @@ -7,12 +7,12 @@ Resource ../resource.robot Print Page As PDF Without Print Options Print Page As PDF -Provide Print Options From Module - # ${print_options}= Evaluate sys.modules['selenium.webdriver'].common.print_page_options() sys, selenium.webdriver - ${print_options}= Evaluate selenium.webdriver.common.print_page_options.PrintOptions() - # Set To Dictionary ${print_options} scale 0.5 - # Evaluate ${print_options}.scale=0.5 - # Set Variable ${print_options.scale} 0.5 - # Evaluate ${print_options.scale}=0.5 - Evaluate setattr($print_options, 'scale', 0.5) - Print Page As PDF ${print_options} +#Provide Print Options From Module +# # ${print_options}= Evaluate sys.modules['selenium.webdriver'].common.print_page_options() sys, selenium.webdriver +# ${print_options}= Evaluate selenium.webdriver.common.print_page_options.PrintOptions() +# # Set To Dictionary ${print_options} scale 0.5 +# # Evaluate ${print_options}.scale=0.5 +# # Set Variable ${print_options.scale} 0.5 +# # Evaluate ${print_options.scale}=0.5 +# Evaluate setattr($print_options, 'scale', 0.5) +# Print Page As PDF ${print_options} diff --git a/src/SeleniumLibrary/keywords/screenshot.py b/src/SeleniumLibrary/keywords/screenshot.py index 3cc87d569..417858568 100644 --- a/src/SeleniumLibrary/keywords/screenshot.py +++ b/src/SeleniumLibrary/keywords/screenshot.py @@ -27,6 +27,7 @@ DEFAULT_FILENAME_PAGE = "selenium-screenshot-{index}.png" DEFAULT_FILENAME_ELEMENT = "selenium-element-screenshot-{index}.png" EMBED = "EMBED" +DEFAULT_FILENAME_PDF = "selenium-page-{index}.pdf" class ScreenshotKeywords(LibraryComponent): @@ -249,6 +250,7 @@ def _embed_to_log_as_file(self, path, width): # print_options = PrintOptions() # print_options.page_ranges = ['-'] def print_page_as_pdf(self, + filename :str = DEFAULT_FILENAME_PDF, background=None, margin_bottom=None, margin_left=None, @@ -260,7 +262,7 @@ def print_page_as_pdf(self, page_width=None, scale=None, shrink_to_fit=None, - path_to_file=None, + # path_to_file=None, ): """ Print the current page as a PDF @@ -280,25 +282,67 @@ def print_page_as_pdf(self, The default ``scale`` is `1`. ``scale`` must be greater than or equal to `0.1` and less than or equal to `2`. - ``background`` and ``scale_to_fite`` can be either `${True}` or `${False}`.. + ``background`` and ``scale_to_fit`` can be either `${True}` or `${False}`.. - If all print options are None then a pdf will failed to print silently.s + If all print options are None then a pdf will fail to print silently. """ print_options = PrintOptions() - print_options.background = background - print_options.margin_bottom = margin_bottom - print_options.margin_left = margin_left - print_options.margin_right = margin_right - print_options.margin_top = margin_top - print_options.orientation = orientation - print_options.page_height = page_height - print_options.page_ranges = page_ranges - print_options.page_width = page_width - print_options.scale = scale - print_options.shrink_to_fit = shrink_to_fit - - base64code = self.driver.print_page(print_options) - pdfdata = b64decode(base64code) - with open('test.pdf', mode='wb') as pdf: - pdf.write(pdfdata) \ No newline at end of file + if background is not None: + print_options.background = background + if margin_bottom is not None: + print_options.margin_bottom = margin_bottom + if margin_left is not None: + print_options.margin_left = margin_left + if margin_right is not None: + print_options.margin_right = margin_right + if margin_top is not None: + print_options.margin_top = margin_top + if orientation is not None: + print_options.orientation = orientation + if page_height is not None: + print_options.page_height = page_height + if page_ranges is not None: + print_options.page_ranges = page_ranges + if page_width is not None: + print_options.page_width = page_width + if scale is not None: + print_options.scale = scale + if shrink_to_fit is not None: + print_options.shrink_to_fit = shrink_to_fit + + # base64code = self.driver.print_page(print_options) + # pdfdata = b64decode(base64code) + # with open('test.pdf', mode='wb') as pdf: + # pdf.write(pdfdata) + + if not self.drivers.current: + self.info("Cannot print page to pdf because no browser is open.") + return + return self._print_page_as_pdf_to_file(filename, print_options) + + def _print_page_as_pdf_to_file(self, filename, options): + path = self._get_screenshot_path(filename) + self._create_directory(path) + pdfdata = self.driver.print_page(options) + if not pdfdata: + raise RuntimeError(f"Failed to print page.") + self._save_pdf_to_file(pdfdata, path) + return path + + def _save_pdf_to_file(self, pdfbase64, path): + pdfdata = b64decode(pdfbase64) + with open(path, mode='wb') as pdf: + pdf.write(pdfdata) + + def _get_pdf_path(self, filename): + directory = self.log_dir + filename = filename.replace("/", os.sep) + index = 0 + while True: + index += 1 + formatted = _format_path(filename, index) + path = os.path.join(directory, formatted) + # filename didn't contain {index} or unique path was found + if formatted == filename or not os.path.exists(path): + return path From 22d04d1296f6c0c530ee5119a507dd4b72e5ccdc Mon Sep 17 00:00:00 2001 From: Ed Manlove Date: Sat, 11 May 2024 20:36:50 -0400 Subject: [PATCH 4/7] Made corrections to the Print Page As PDF code and updated tests Had some bad sloppy code within the keyword functionality. Cleaned that up and added some tests. --- atest/acceptance/keywords/print_page.robot | 16 +++++++++++ src/SeleniumLibrary/keywords/screenshot.py | 31 ++++++++++++---------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/atest/acceptance/keywords/print_page.robot b/atest/acceptance/keywords/print_page.robot index c45cde715..a9d02a0d2 100644 --- a/atest/acceptance/keywords/print_page.robot +++ b/atest/acceptance/keywords/print_page.robot @@ -3,10 +3,26 @@ Documentation Suite description Suite Setup Go To Page "non_ascii.html" Resource ../resource.robot +Test Setup Remove Files ${OUTPUTDIR}/selenium-page-*.pdf + *** Test Cases *** Print Page As PDF Without Print Options Print Page As PDF +Verify Index Increments With Multiple Prints + [Setup] Remove Files ${OUTPUTDIR}/selenium-page-*.pdf + ${file_1} = Print Page As PDF + Should Be Equal ${file_1} ${OUTPUTDIR}${/}selenium-page-1.pdf + ${file_2} = Print Page As PDF + Should Be Equal ${file_2} ${OUTPUTDIR}${/}selenium-page-2.pdf + ${file_3} = Print Page As PDF + Should Be Equal ${file_3} ${OUTPUTDIR}${/}selenium-page-3.pdf + +Print With Full Options + Print Page As PDF page_ranges=['1'] background=${False} shrink_to_fit=${False} orientation=portrait + ... margin_top=${0.5} margin_left=${1.5} margin_bottom=${0.5} margin_right=${1.5} + ... page_height=${35.56} page_width=${21.59} + #Provide Print Options From Module # # ${print_options}= Evaluate sys.modules['selenium.webdriver'].common.print_page_options() sys, selenium.webdriver # ${print_options}= Evaluate selenium.webdriver.common.print_page_options.PrintOptions() diff --git a/src/SeleniumLibrary/keywords/screenshot.py b/src/SeleniumLibrary/keywords/screenshot.py index 417858568..c9d88f4b8 100644 --- a/src/SeleniumLibrary/keywords/screenshot.py +++ b/src/SeleniumLibrary/keywords/screenshot.py @@ -19,7 +19,7 @@ from robot.utils import get_link_path from selenium.webdriver.remote.webelement import WebElement -from selenium.webdriver.common.print_page_options import PrintOptions +from selenium.webdriver.common.print_page_options import PrintOptions, Orientation from SeleniumLibrary.base import LibraryComponent, keyword from SeleniumLibrary.utils.path_formatter import _format_path @@ -250,18 +250,18 @@ def _embed_to_log_as_file(self, path, width): # print_options = PrintOptions() # print_options.page_ranges = ['-'] def print_page_as_pdf(self, - filename :str = DEFAULT_FILENAME_PDF, - background=None, - margin_bottom=None, - margin_left=None, - margin_right=None, - margin_top=None, - orientation=None, - page_height=None, - page_ranges=['-'], - page_width=None, - scale=None, - shrink_to_fit=None, + filename: str = DEFAULT_FILENAME_PDF, + background: Optional[bool] = None, + margin_bottom: Optional[float] = None, + margin_left: Optional[float] = None, + margin_right: Optional[float] = None, + margin_top: Optional[float] = None, + orientation: Optional[Orientation] = None, + page_height: Optional[float] = None, + page_ranges: Optional[list] = None, + page_width: Optional[float] = None, + scale: Optional[float] = None, + shrink_to_fit: Optional[bool] = None, # path_to_file=None, ): """ Print the current page as a PDF @@ -287,6 +287,9 @@ def print_page_as_pdf(self, If all print options are None then a pdf will fail to print silently. """ + if page_ranges is None: + page_ranges = ['-'] + print_options = PrintOptions() if background is not None: print_options.background = background @@ -322,7 +325,7 @@ def print_page_as_pdf(self, return self._print_page_as_pdf_to_file(filename, print_options) def _print_page_as_pdf_to_file(self, filename, options): - path = self._get_screenshot_path(filename) + path = self._get_pdf_path(filename) self._create_directory(path) pdfdata = self.driver.print_page(options) if not pdfdata: From c3d90e5e1ceeb1cce24395849da574da41d07781 Mon Sep 17 00:00:00 2001 From: Ed Manlove Date: Sun, 12 May 2024 07:33:13 -0400 Subject: [PATCH 5/7] Cleanup of commented out code --- atest/acceptance/keywords/print_page.robot | 10 ---------- src/SeleniumLibrary/keywords/screenshot.py | 14 -------------- 2 files changed, 24 deletions(-) diff --git a/atest/acceptance/keywords/print_page.robot b/atest/acceptance/keywords/print_page.robot index a9d02a0d2..3b873516d 100644 --- a/atest/acceptance/keywords/print_page.robot +++ b/atest/acceptance/keywords/print_page.robot @@ -22,13 +22,3 @@ Print With Full Options Print Page As PDF page_ranges=['1'] background=${False} shrink_to_fit=${False} orientation=portrait ... margin_top=${0.5} margin_left=${1.5} margin_bottom=${0.5} margin_right=${1.5} ... page_height=${35.56} page_width=${21.59} - -#Provide Print Options From Module -# # ${print_options}= Evaluate sys.modules['selenium.webdriver'].common.print_page_options() sys, selenium.webdriver -# ${print_options}= Evaluate selenium.webdriver.common.print_page_options.PrintOptions() -# # Set To Dictionary ${print_options} scale 0.5 -# # Evaluate ${print_options}.scale=0.5 -# # Set Variable ${print_options.scale} 0.5 -# # Evaluate ${print_options.scale}=0.5 -# Evaluate setattr($print_options, 'scale', 0.5) -# Print Page As PDF ${print_options} diff --git a/src/SeleniumLibrary/keywords/screenshot.py b/src/SeleniumLibrary/keywords/screenshot.py index c9d88f4b8..1c19b50d8 100644 --- a/src/SeleniumLibrary/keywords/screenshot.py +++ b/src/SeleniumLibrary/keywords/screenshot.py @@ -240,15 +240,6 @@ def _embed_to_log_as_file(self, path, width): ) @keyword - # def print_page_as_pdf(self, print_options: Optional[PrintOptions]=None,): - # def print_page_as_pdf(self, print_options: Optional[Union[PrintOptions, dict]]=None): - # def print_page_as_pdf(self, print_options: Union[PrintOptions, dict, None]=None): - # """ Print the current page as a PDF - # - # """ - # if not print_options: - # print_options = PrintOptions() - # print_options.page_ranges = ['-'] def print_page_as_pdf(self, filename: str = DEFAULT_FILENAME_PDF, background: Optional[bool] = None, @@ -314,11 +305,6 @@ def print_page_as_pdf(self, if shrink_to_fit is not None: print_options.shrink_to_fit = shrink_to_fit - # base64code = self.driver.print_page(print_options) - # pdfdata = b64decode(base64code) - # with open('test.pdf', mode='wb') as pdf: - # pdf.write(pdfdata) - if not self.drivers.current: self.info("Cannot print page to pdf because no browser is open.") return From e29e1dda3903cb4ef20e83ecef5e2d8bed022f72 Mon Sep 17 00:00:00 2001 From: Ed Manlove Date: Sun, 12 May 2024 09:11:49 -0400 Subject: [PATCH 6/7] Updated number of keywords --- utest/test/api/test_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utest/test/api/test_plugins.py b/utest/test/api/test_plugins.py index 2a07a1156..c8241d8ba 100644 --- a/utest/test/api/test_plugins.py +++ b/utest/test/api/test_plugins.py @@ -22,7 +22,7 @@ def setUpClass(cls): def test_no_libraries(self): for item in [None, "None", ""]: sl = SeleniumLibrary(plugins=item) - self.assertEqual(len(sl.get_keyword_names()), 181) + self.assertEqual(len(sl.get_keyword_names()), 182) def test_parse_library(self): plugin = "path.to.MyLibrary" From e9061cc70053e1f1bf795f467fac4b7f3bc4be9c Mon Sep 17 00:00:00 2001 From: Ed Manlove Date: Mon, 13 May 2024 07:13:59 -0400 Subject: [PATCH 7/7] Switched up print page scenarios so first test was not a repeat of the second --- atest/acceptance/keywords/print_page.robot | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/atest/acceptance/keywords/print_page.robot b/atest/acceptance/keywords/print_page.robot index 3b873516d..a78cd6623 100644 --- a/atest/acceptance/keywords/print_page.robot +++ b/atest/acceptance/keywords/print_page.robot @@ -11,11 +11,12 @@ Print Page As PDF Without Print Options Verify Index Increments With Multiple Prints [Setup] Remove Files ${OUTPUTDIR}/selenium-page-*.pdf - ${file_1} = Print Page As PDF + ${file_1} = Print Page As PDF background=${True} scale=${2} Should Be Equal ${file_1} ${OUTPUTDIR}${/}selenium-page-1.pdf - ${file_2} = Print Page As PDF + ${file_2} = Print Page As PDF orientation=landscape Should Be Equal ${file_2} ${OUTPUTDIR}${/}selenium-page-2.pdf - ${file_3} = Print Page As PDF + Go To https://robotframework.org/foundation/ + ${file_3} = Print Page As PDF shrink_to_fit=${True} page_height=${35.56} page_width=${21.59} Should Be Equal ${file_3} ${OUTPUTDIR}${/}selenium-page-3.pdf Print With Full Options