Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #
  2. # Copyright 2025 The InfiniFlow Authors. All Rights Reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. import base64
  17. import ipaddress
  18. import json
  19. import re
  20. import socket
  21. from urllib.parse import urlparse
  22. from selenium import webdriver
  23. from selenium.common.exceptions import TimeoutException
  24. from selenium.webdriver.chrome.options import Options
  25. from selenium.webdriver.chrome.service import Service
  26. from selenium.webdriver.common.by import By
  27. from selenium.webdriver.support.expected_conditions import staleness_of
  28. from selenium.webdriver.support.ui import WebDriverWait
  29. from webdriver_manager.chrome import ChromeDriverManager
  30. def html2pdf(
  31. source: str,
  32. timeout: int = 2,
  33. install_driver: bool = True,
  34. print_options: dict = {},
  35. ):
  36. result = __get_pdf_from_html(source, timeout, install_driver, print_options)
  37. return result
  38. def __send_devtools(driver, cmd, params={}):
  39. resource = "/session/%s/chromium/send_command_and_get_result" % driver.session_id
  40. url = driver.command_executor._url + resource
  41. body = json.dumps({"cmd": cmd, "params": params})
  42. response = driver.command_executor._request("POST", url, body)
  43. if not response:
  44. raise Exception(response.get("value"))
  45. return response.get("value")
  46. def __get_pdf_from_html(path: str, timeout: int, install_driver: bool, print_options: dict):
  47. webdriver_options = Options()
  48. webdriver_prefs = {}
  49. webdriver_options.add_argument("--headless")
  50. webdriver_options.add_argument("--disable-gpu")
  51. webdriver_options.add_argument("--no-sandbox")
  52. webdriver_options.add_argument("--disable-dev-shm-usage")
  53. webdriver_options.experimental_options["prefs"] = webdriver_prefs
  54. webdriver_prefs["profile.default_content_settings"] = {"images": 2}
  55. if install_driver:
  56. service = Service(ChromeDriverManager().install())
  57. driver = webdriver.Chrome(service=service, options=webdriver_options)
  58. else:
  59. driver = webdriver.Chrome(options=webdriver_options)
  60. driver.get(path)
  61. try:
  62. WebDriverWait(driver, timeout).until(staleness_of(driver.find_element(by=By.TAG_NAME, value="html")))
  63. except TimeoutException:
  64. calculated_print_options = {
  65. "landscape": False,
  66. "displayHeaderFooter": False,
  67. "printBackground": True,
  68. "preferCSSPageSize": True,
  69. }
  70. calculated_print_options.update(print_options)
  71. result = __send_devtools(driver, "Page.printToPDF", calculated_print_options)
  72. driver.quit()
  73. return base64.b64decode(result["data"])
  74. def is_private_ip(ip: str) -> bool:
  75. try:
  76. ip_obj = ipaddress.ip_address(ip)
  77. return ip_obj.is_private
  78. except ValueError:
  79. return False
  80. def is_valid_url(url: str) -> bool:
  81. if not re.match(r"(https?)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]", url):
  82. return False
  83. parsed_url = urlparse(url)
  84. hostname = parsed_url.hostname
  85. if not hostname:
  86. return False
  87. try:
  88. ip = socket.gethostbyname(hostname)
  89. if is_private_ip(ip):
  90. return False
  91. except socket.gaierror:
  92. return False
  93. return True
  94. def safe_json_parse(data: str | dict) -> dict:
  95. if isinstance(data, dict):
  96. return data
  97. try:
  98. return json.loads(data) if data else {}
  99. except (json.JSONDecodeError, TypeError):
  100. return {}
  101. def get_float(req: dict, key: str, default: float | int = 10.0) -> float:
  102. try:
  103. parsed = float(req.get(key, default))
  104. return parsed if parsed > 0 else default
  105. except (TypeError, ValueError):
  106. return default