Skip to content

Commit 3ea3041

Browse files
authored
Merge pull request #1851 from emanlove/print-page-as-pdf-1824
Added `Print Page As PDF` Keyword
2 parents 1a12a4d + e9061cc commit 3ea3041

File tree

3 files changed

+127
-2
lines changed

3 files changed

+127
-2
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
*** Settings ***
2+
Documentation Suite description
3+
Suite Setup Go To Page "non_ascii.html"
4+
Resource ../resource.robot
5+
6+
Test Setup Remove Files ${OUTPUTDIR}/selenium-page-*.pdf
7+
8+
*** Test Cases ***
9+
Print Page As PDF Without Print Options
10+
Print Page As PDF
11+
12+
Verify Index Increments With Multiple Prints
13+
[Setup] Remove Files ${OUTPUTDIR}/selenium-page-*.pdf
14+
${file_1} = Print Page As PDF background=${True} scale=${2}
15+
Should Be Equal ${file_1} ${OUTPUTDIR}${/}selenium-page-1.pdf
16+
${file_2} = Print Page As PDF orientation=landscape
17+
Should Be Equal ${file_2} ${OUTPUTDIR}${/}selenium-page-2.pdf
18+
Go To https://robotframework.org/foundation/
19+
${file_3} = Print Page As PDF shrink_to_fit=${True} page_height=${35.56} page_width=${21.59}
20+
Should Be Equal ${file_3} ${OUTPUTDIR}${/}selenium-page-3.pdf
21+
22+
Print With Full Options
23+
Print Page As PDF page_ranges=['1'] background=${False} shrink_to_fit=${False} orientation=portrait
24+
... margin_top=${0.5} margin_left=${1.5} margin_bottom=${0.5} margin_right=${1.5}
25+
... page_height=${35.56} page_width=${21.59}

src/SeleniumLibrary/keywords/screenshot.py

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,20 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616
import os
17-
from typing import Union
17+
from typing import Optional, Union
18+
from base64 import b64decode
1819

1920
from robot.utils import get_link_path
2021
from selenium.webdriver.remote.webelement import WebElement
22+
from selenium.webdriver.common.print_page_options import PrintOptions, Orientation
2123

2224
from SeleniumLibrary.base import LibraryComponent, keyword
2325
from SeleniumLibrary.utils.path_formatter import _format_path
2426

2527
DEFAULT_FILENAME_PAGE = "selenium-screenshot-{index}.png"
2628
DEFAULT_FILENAME_ELEMENT = "selenium-element-screenshot-{index}.png"
2729
EMBED = "EMBED"
30+
DEFAULT_FILENAME_PDF = "selenium-page-{index}.pdf"
2831

2932

3033
class ScreenshotKeywords(LibraryComponent):
@@ -235,3 +238,100 @@ def _embed_to_log_as_file(self, path, width):
235238
f'<a href="{src}"><img src="{src}" width="{width}px"></a>',
236239
html=True,
237240
)
241+
242+
@keyword
243+
def print_page_as_pdf(self,
244+
filename: str = DEFAULT_FILENAME_PDF,
245+
background: Optional[bool] = None,
246+
margin_bottom: Optional[float] = None,
247+
margin_left: Optional[float] = None,
248+
margin_right: Optional[float] = None,
249+
margin_top: Optional[float] = None,
250+
orientation: Optional[Orientation] = None,
251+
page_height: Optional[float] = None,
252+
page_ranges: Optional[list] = None,
253+
page_width: Optional[float] = None,
254+
scale: Optional[float] = None,
255+
shrink_to_fit: Optional[bool] = None,
256+
# path_to_file=None,
257+
):
258+
""" Print the current page as a PDF
259+
260+
``page_ranges`` defaults to `['-']` or "all" pages. ``page_ranges`` takes a list of
261+
strings indicating the ranges.
262+
263+
The page size defaults to 21.59 for ``page_width`` and 27.94 for ``page_height``.
264+
This is the equivalent size of US-Letter. The assumed units on these parameters
265+
is centimeters.
266+
267+
The default margin for top, left, bottom, right is `1`. The assumed units on
268+
these parameters is centimeters.
269+
270+
The default ``orientation`` is `portrait`. ``orientation`` can be either `portrait`
271+
or `landscape`.
272+
273+
The default ``scale`` is `1`. ``scale`` must be greater than or equal to `0.1` and
274+
less than or equal to `2`.
275+
276+
``background`` and ``scale_to_fit`` can be either `${True}` or `${False}`..
277+
278+
If all print options are None then a pdf will fail to print silently.
279+
"""
280+
281+
if page_ranges is None:
282+
page_ranges = ['-']
283+
284+
print_options = PrintOptions()
285+
if background is not None:
286+
print_options.background = background
287+
if margin_bottom is not None:
288+
print_options.margin_bottom = margin_bottom
289+
if margin_left is not None:
290+
print_options.margin_left = margin_left
291+
if margin_right is not None:
292+
print_options.margin_right = margin_right
293+
if margin_top is not None:
294+
print_options.margin_top = margin_top
295+
if orientation is not None:
296+
print_options.orientation = orientation
297+
if page_height is not None:
298+
print_options.page_height = page_height
299+
if page_ranges is not None:
300+
print_options.page_ranges = page_ranges
301+
if page_width is not None:
302+
print_options.page_width = page_width
303+
if scale is not None:
304+
print_options.scale = scale
305+
if shrink_to_fit is not None:
306+
print_options.shrink_to_fit = shrink_to_fit
307+
308+
if not self.drivers.current:
309+
self.info("Cannot print page to pdf because no browser is open.")
310+
return
311+
return self._print_page_as_pdf_to_file(filename, print_options)
312+
313+
def _print_page_as_pdf_to_file(self, filename, options):
314+
path = self._get_pdf_path(filename)
315+
self._create_directory(path)
316+
pdfdata = self.driver.print_page(options)
317+
if not pdfdata:
318+
raise RuntimeError(f"Failed to print page.")
319+
self._save_pdf_to_file(pdfdata, path)
320+
return path
321+
322+
def _save_pdf_to_file(self, pdfbase64, path):
323+
pdfdata = b64decode(pdfbase64)
324+
with open(path, mode='wb') as pdf:
325+
pdf.write(pdfdata)
326+
327+
def _get_pdf_path(self, filename):
328+
directory = self.log_dir
329+
filename = filename.replace("/", os.sep)
330+
index = 0
331+
while True:
332+
index += 1
333+
formatted = _format_path(filename, index)
334+
path = os.path.join(directory, formatted)
335+
# filename didn't contain {index} or unique path was found
336+
if formatted == filename or not os.path.exists(path):
337+
return path

utest/test/api/test_plugins.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def setUpClass(cls):
2222
def test_no_libraries(self):
2323
for item in [None, "None", ""]:
2424
sl = SeleniumLibrary(plugins=item)
25-
self.assertEqual(len(sl.get_keyword_names()), 181)
25+
self.assertEqual(len(sl.get_keyword_names()), 182)
2626

2727
def test_parse_library(self):
2828
plugin = "path.to.MyLibrary"

0 commit comments

Comments
 (0)