|
14 | 14 | # See the License for the specific language governing permissions and |
15 | 15 | # limitations under the License. |
16 | 16 | import os |
17 | | -from typing import Union |
| 17 | +from typing import Optional, Union |
| 18 | +from base64 import b64decode |
18 | 19 |
|
19 | 20 | from robot.utils import get_link_path |
20 | 21 | from selenium.webdriver.remote.webelement import WebElement |
| 22 | +from selenium.webdriver.common.print_page_options import PrintOptions, Orientation |
21 | 23 |
|
22 | 24 | from SeleniumLibrary.base import LibraryComponent, keyword |
23 | 25 | from SeleniumLibrary.utils.path_formatter import _format_path |
24 | 26 |
|
25 | 27 | DEFAULT_FILENAME_PAGE = "selenium-screenshot-{index}.png" |
26 | 28 | DEFAULT_FILENAME_ELEMENT = "selenium-element-screenshot-{index}.png" |
27 | 29 | EMBED = "EMBED" |
| 30 | +DEFAULT_FILENAME_PDF = "selenium-page-{index}.pdf" |
28 | 31 |
|
29 | 32 |
|
30 | 33 | class ScreenshotKeywords(LibraryComponent): |
@@ -235,3 +238,100 @@ def _embed_to_log_as_file(self, path, width): |
235 | 238 | f'<a href="{src}"><img src="{src}" width="{width}px"></a>', |
236 | 239 | html=True, |
237 | 240 | ) |
| 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 |
0 commit comments