utils.py 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. # SPDX-FileCopyrightText: 2024 geisserml <geisserml@gmail.com>
  2. # SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
  3. import ctypes
  4. import pypdfium2.raw as pdfium_c
  5. def color_tohex(color, rev_byteorder):
  6. if len(color) != 4:
  7. raise ValueError("Color must consist of exactly 4 values.")
  8. if not all(0 <= c <= 255 for c in color):
  9. raise ValueError("Color value exceeds boundaries.")
  10. # different color interpretation with FPDF_REVERSE_BYTE_ORDER might be a bug? at least it's not documented.
  11. r, g, b, a = color
  12. channels = (a, b, g, r) if rev_byteorder else (a, r, g, b)
  13. c_color = 0
  14. shift = 24
  15. for c in channels:
  16. c_color |= c << shift
  17. shift -= 8
  18. return c_color
  19. def set_callback(struct, fname, callback):
  20. setattr(struct, fname, type( getattr(struct, fname) )(callback))
  21. def is_buffer(buf, spec="r"):
  22. methods = []
  23. assert set(spec).issubset( set("rw") )
  24. if "r" in spec:
  25. methods += ["seek", "tell", "read", "readinto"]
  26. if "w" in spec:
  27. methods += ["write"]
  28. return all(callable(getattr(buf, a, None)) for a in methods)
  29. class _buffer_reader:
  30. def __init__(self, buffer):
  31. self.buffer = buffer
  32. def __call__(self, _, position, p_buf, size):
  33. c_buf = ctypes.cast(p_buf, ctypes.POINTER(ctypes.c_char * size))
  34. self.buffer.seek(position)
  35. self.buffer.readinto(c_buf.contents)
  36. return 1
  37. class _buffer_writer:
  38. def __init__(self, buffer):
  39. self.buffer = buffer
  40. def __call__(self, _, data, size):
  41. block = ctypes.cast(data, ctypes.POINTER(ctypes.c_ubyte * size))
  42. self.buffer.write(block.contents)
  43. return 1
  44. def get_bufreader(buffer):
  45. file_len = buffer.seek(0, 2)
  46. buffer.seek(0)
  47. reader = pdfium_c.FPDF_FILEACCESS()
  48. reader.m_FileLen = file_len
  49. set_callback(reader, "m_GetBlock", _buffer_reader(buffer))
  50. reader.m_Param = None
  51. to_hold = (reader.m_GetBlock, )
  52. return reader, to_hold
  53. def get_bufwriter(buffer):
  54. writer = pdfium_c.FPDF_FILEWRITE(version=1)
  55. set_callback(writer, "WriteBlock", _buffer_writer(buffer))
  56. return writer
  57. def pages_c_array(pages):
  58. if not pages:
  59. return None, 0
  60. count = len(pages)
  61. c_array = (pdfium_c.FPDF_PAGE * count)(*[p.raw for p in pages])
  62. return c_array, count