Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Print Page As PDF Keyword #1851

Merged
merged 8 commits into from May 14, 2024
25 changes: 25 additions & 0 deletions atest/acceptance/keywords/print_page.robot
@@ -0,0 +1,25 @@
*** Settings ***
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
emanlove marked this conversation as resolved.
Show resolved Hide resolved
Print Page As PDF

Verify Index Increments With Multiple Prints
[Setup] Remove Files ${OUTPUTDIR}/selenium-page-*.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 orientation=landscape
Should Be Equal ${file_2} ${OUTPUTDIR}${/}selenium-page-2.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
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}
102 changes: 101 additions & 1 deletion src/SeleniumLibrary/keywords/screenshot.py
Expand Up @@ -14,17 +14,20 @@
# 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, Orientation

from SeleniumLibrary.base import LibraryComponent, keyword
from SeleniumLibrary.utils.path_formatter import _format_path

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):
Expand Down Expand Up @@ -235,3 +238,100 @@ def _embed_to_log_as_file(self, path, width):
f'<a href="{src}"><img src="{src}" width="{width}px"></a>',
html=True,
)

@keyword
def print_page_as_pdf(self,
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

``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_fit`` can be either `${True}` or `${False}`..

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
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

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_pdf_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
2 changes: 1 addition & 1 deletion utest/test/api/test_plugins.py
Expand Up @@ -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"
Expand Down