Initial commit of Roster

This commit is contained in:
Media
2025-06-28 01:12:46 -05:00
commit c23d3a7676
4367 changed files with 890519 additions and 0 deletions
@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: 2024 geisserml <[email protected]>
# SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
from pypdfium2.internal.bases import *
from pypdfium2.internal.consts import *
from pypdfium2.internal.utils import *
@@ -0,0 +1,108 @@
# SPDX-FileCopyrightText: 2024 geisserml <[email protected]>
# SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
__all__ = ("AutoCastable", "AutoCloseable", "DEBUG_AUTOCLOSE", "LIBRARY_AVAILABLE")
import os
import sys
import ctypes
import weakref
import logging
import uuid
logger = logging.getLogger(__name__)
# mutable bools
DEBUG_AUTOCLOSE = ctypes.c_bool(False)
LIBRARY_AVAILABLE = ctypes.c_bool(False) # set to true on library init
STATE_INVALID = -1
STATE_AUTO = 0
STATE_EXPLICIT = 1
STATE_BYPARENT = 2
class AutoCastable:
@property
def _as_parameter_(self):
return self.raw
def _close_template(close_func, raw, obj_repr, state, parent, *args, **kwargs):
if DEBUG_AUTOCLOSE:
desc = {STATE_AUTO: "auto", STATE_EXPLICIT: "explicit", STATE_BYPARENT: "by parent"}[state.value]
# use os.write() rather than print() to avoid "reentrant call" exceptions on shutdown (see https://stackoverflow.com/q/75367828/15547292)
os.write(sys.stderr.fileno(), f"Close ({desc}) {obj_repr}\n".encode())
if not LIBRARY_AVAILABLE:
os.write(sys.stderr.fileno(), f"-> Cannot close object, library is destroyed. This may cause a memory leak!\n".encode())
return
assert (parent is None) or not parent._tree_closed()
close_func(raw, *args, **kwargs)
class AutoCloseable (AutoCastable):
def __init__(self, close_func, *args, obj=None, needs_free=True, **kwargs):
# NOTE proactively prevent accidental double initialization
assert not hasattr(self, "_finalizer")
self._close_func = close_func
self._obj = self if obj is None else obj
self._uuid = uuid.uuid4()
self._ex_args = args
self._ex_kwargs = kwargs
self._autoclose_state = ctypes.c_int8(STATE_AUTO) # mutable int
self._finalizer = None
self._kids = []
if needs_free:
self._attach_finalizer()
def __repr__(self):
return f"<{type(self).__name__} uuid:{str(self._uuid)[:8]}>"
def _attach_finalizer(self):
# NOTE this function captures the value of the `parent` property at finalizer installation time - if it changes, detach the old finalizer and create a new one
assert self._finalizer is None
self._finalizer = weakref.finalize(self._obj, _close_template, self._close_func, self.raw, repr(self), self._autoclose_state, self.parent, *self._ex_args, **self._ex_kwargs)
def _detach_finalizer(self):
self._finalizer.detach()
self._finalizer = None
def _tree_closed(self):
if self.raw is None:
return True
if (self.parent is not None) and self.parent._tree_closed():
return True
return False
def _add_kid(self, k):
self._kids.append( weakref.ref(k) )
def close(self, _by_parent=False):
if not self.raw or not self._finalizer:
return False
for k_ref in self._kids:
k = k_ref()
if k and k.raw:
k.close(_by_parent=True)
self._autoclose_state.value = STATE_BYPARENT if _by_parent else STATE_EXPLICIT
self._finalizer()
self._autoclose_state.value = STATE_INVALID
self.raw = None
self._finalizer = None
self._kids.clear()
return True
@@ -0,0 +1,154 @@
# SPDX-FileCopyrightText: 2024 geisserml <[email protected]>
# SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
import pypdfium2.raw as pdfium_c
from pypdfium2.version import PDFIUM_INFO
class _fallback_dict (dict):
def get(self, key, default_prefix="Unhandled constant"):
return dict.get(self, key, f"{default_prefix} {key}")
#: Convert a rotation value in degrees to a PDFium constant.
RotationToConst = {
0: 0,
90: 1,
180: 2,
270: 3,
}
#: Convert a PDFium rotation constant to a value in degrees. Inversion of :data:`.RotationToConst`.
RotationToDegrees = {v: k for k, v in RotationToConst.items()}
#: Get the number of channels for a PDFium bitmap format. (:attr:`FPDFBitmap_Unknown` is deliberately not handled.)
BitmapTypeToNChannels = {
pdfium_c.FPDFBitmap_Gray: 1,
pdfium_c.FPDFBitmap_BGR: 3,
pdfium_c.FPDFBitmap_BGRA: 4,
pdfium_c.FPDFBitmap_BGRx: 4,
}
#: Convert a PDFium bitmap format to string, assuming BGR byte order. (:attr:`FPDFBitmap_Unknown` is deliberately not handled.)
BitmapTypeToStr = {
pdfium_c.FPDFBitmap_Gray: "L",
pdfium_c.FPDFBitmap_BGR: "BGR",
pdfium_c.FPDFBitmap_BGRA: "BGRA",
pdfium_c.FPDFBitmap_BGRx: "BGRX",
}
#: Convert a PDFium bitmap format to string, assuming RGB byte order. (:attr:`FPDFBitmap_Unknown` is deliberately not handled.)
BitmapTypeToStrReverse = {
pdfium_c.FPDFBitmap_Gray: "L",
pdfium_c.FPDFBitmap_BGR: "RGB",
pdfium_c.FPDFBitmap_BGRA: "RGBA",
pdfium_c.FPDFBitmap_BGRx: "RGBX",
}
#: Convert a string to PDFium bitmap format, assuming BGR byte order. Inversion of :data:`BitmapTypeToStr`.
BitmapStrToConst = {v: k for k, v in BitmapTypeToStr.items()}
#: Convert a string to PDFium bitmap format, assuming RGB byte order. Inversion of :data:`BitmapTypeToStrReverse`.
BitmapStrReverseToConst = {v: k for k, v in BitmapTypeToStrReverse.items()}
#: Convert a PDFium form type (:attr:`FORMTYPE_*`) to string.
FormTypeToStr = _fallback_dict({
pdfium_c.FORMTYPE_NONE: "None",
pdfium_c.FORMTYPE_ACRO_FORM: "AcroForm",
pdfium_c.FORMTYPE_XFA_FULL: "XFA",
pdfium_c.FORMTYPE_XFA_FOREGROUND: "XFAF",
})
#: Convert a PDFium color space constant (:attr:`FPDF_COLORSPACE_*`) to string.
ColorspaceToStr = _fallback_dict({
pdfium_c.FPDF_COLORSPACE_UNKNOWN: "?",
pdfium_c.FPDF_COLORSPACE_DEVICEGRAY: "DeviceGray",
pdfium_c.FPDF_COLORSPACE_DEVICERGB: "DeviceRGB",
pdfium_c.FPDF_COLORSPACE_DEVICECMYK: "DeviceCMYK",
pdfium_c.FPDF_COLORSPACE_CALGRAY: "CalGray",
pdfium_c.FPDF_COLORSPACE_CALRGB: "CalRGB",
pdfium_c.FPDF_COLORSPACE_LAB: "Lab",
pdfium_c.FPDF_COLORSPACE_ICCBASED: "ICCBased",
pdfium_c.FPDF_COLORSPACE_SEPARATION: "Separation",
pdfium_c.FPDF_COLORSPACE_DEVICEN: "DeviceN",
pdfium_c.FPDF_COLORSPACE_INDEXED: "Indexed", # i. e. palettized
pdfium_c.FPDF_COLORSPACE_PATTERN: "Pattern",
})
#: Convert a PDFium view mode constant (:attr:`PDFDEST_VIEW_*`) to string.
ViewmodeToStr = _fallback_dict({
pdfium_c.PDFDEST_VIEW_UNKNOWN_MODE: "?",
pdfium_c.PDFDEST_VIEW_XYZ: "XYZ",
pdfium_c.PDFDEST_VIEW_FIT: "Fit",
pdfium_c.PDFDEST_VIEW_FITH: "FitH",
pdfium_c.PDFDEST_VIEW_FITV: "FitV",
pdfium_c.PDFDEST_VIEW_FITR: "FitR",
pdfium_c.PDFDEST_VIEW_FITB: "FitB",
pdfium_c.PDFDEST_VIEW_FITBH: "FitBH",
pdfium_c.PDFDEST_VIEW_FITBV: "FitBV",
})
#: Convert a PDFium object type constant (:attr:`FPDF_PAGEOBJ_*`) to string.
ObjectTypeToStr = _fallback_dict({
pdfium_c.FPDF_PAGEOBJ_UNKNOWN: "?",
pdfium_c.FPDF_PAGEOBJ_TEXT: "text",
pdfium_c.FPDF_PAGEOBJ_PATH: "path",
pdfium_c.FPDF_PAGEOBJ_IMAGE: "image",
pdfium_c.FPDF_PAGEOBJ_SHADING: "shading",
pdfium_c.FPDF_PAGEOBJ_FORM: "form",
})
#: Convert an object type string to a PDFium constant. Inversion of :data:`.ObjectTypeToStr`.
ObjectTypeToConst = {v: k for k, v in ObjectTypeToStr.items()}
#: Convert a PDFium page mode constant (:attr:`PAGEMODE_*`) to string.
PageModeToStr = _fallback_dict({
pdfium_c.PAGEMODE_UNKNOWN: "?",
pdfium_c.PAGEMODE_USENONE: "None",
pdfium_c.PAGEMODE_USEOUTLINES: "Outline",
pdfium_c.PAGEMODE_USETHUMBS: "Thumbnails",
pdfium_c.PAGEMODE_FULLSCREEN: "Full-screen",
pdfium_c.PAGEMODE_USEOC: "Layers",
pdfium_c.PAGEMODE_USEATTACHMENTS: "Attachments",
})
#: Convert a PDFium error constant (:attr:`FPDF_ERR_*`) to string.
ErrorToStr = _fallback_dict({
pdfium_c.FPDF_ERR_SUCCESS: "Success",
pdfium_c.FPDF_ERR_UNKNOWN: "Unknown error",
pdfium_c.FPDF_ERR_FILE: "File access error",
pdfium_c.FPDF_ERR_FORMAT: "Data format error",
pdfium_c.FPDF_ERR_PASSWORD: "Incorrect password error",
pdfium_c.FPDF_ERR_SECURITY: "Unsupported security scheme error",
pdfium_c.FPDF_ERR_PAGE: "Page not found or content error",
})
# known implication: causes eager evaluation of pdfium version
if "XFA" in PDFIUM_INFO.flags:
#: [V8/XFA builds only] Convert a PDFium XFA error constant (:attr:`FPDF_ERR_XFA*`) to string.
XFAErrorToStr = _fallback_dict({
pdfium_c.FPDF_ERR_XFALOAD: "Load error",
pdfium_c.FPDF_ERR_XFALAYOUT: "Layout error",
})
#: Convert a PDFium unsupported constant (:attr:`FPDF_UNSP_*`) to string.
UnsupportedInfoToStr = _fallback_dict({
pdfium_c.FPDF_UNSP_DOC_XFAFORM: "XFA form",
pdfium_c.FPDF_UNSP_DOC_PORTABLECOLLECTION: "Portable collection",
pdfium_c.FPDF_UNSP_DOC_ATTACHMENT: "Attachment (incomplete support)", # https://crbug.com/pdfium/1945
pdfium_c.FPDF_UNSP_DOC_SECURITY: "Security",
pdfium_c.FPDF_UNSP_DOC_SHAREDREVIEW: "Shared review",
pdfium_c.FPDF_UNSP_DOC_SHAREDFORM_ACROBAT: "Shared form (acrobat)",
pdfium_c.FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM: "Shared form (filesystem)",
pdfium_c.FPDF_UNSP_DOC_SHAREDFORM_EMAIL: "Shared form (email)",
pdfium_c.FPDF_UNSP_ANNOT_3DANNOT: "3D annotation",
pdfium_c.FPDF_UNSP_ANNOT_MOVIE: "Movie annotation",
pdfium_c.FPDF_UNSP_ANNOT_SOUND: "Sound annotation",
pdfium_c.FPDF_UNSP_ANNOT_SCREEN_MEDIA: "Screen media annotation",
pdfium_c.FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA: "Screen rich media annotation",
pdfium_c.FPDF_UNSP_ANNOT_ATTACHMENT: "Attachment annotation",
pdfium_c.FPDF_UNSP_ANNOT_SIG: "Signature annotation",
})
@@ -0,0 +1,91 @@
# SPDX-FileCopyrightText: 2024 geisserml <[email protected]>
# SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
import ctypes
import pypdfium2.raw as pdfium_c
def color_tohex(color, rev_byteorder):
if len(color) != 4:
raise ValueError("Color must consist of exactly 4 values.")
if not all(0 <= c <= 255 for c in color):
raise ValueError("Color value exceeds boundaries.")
# different color interpretation with FPDF_REVERSE_BYTE_ORDER might be a bug? at least it's not documented.
r, g, b, a = color
channels = (a, b, g, r) if rev_byteorder else (a, r, g, b)
c_color = 0
shift = 24
for c in channels:
c_color |= c << shift
shift -= 8
return c_color
def set_callback(struct, fname, callback):
setattr(struct, fname, type( getattr(struct, fname) )(callback))
def is_buffer(buf, spec="r"):
methods = []
assert set(spec).issubset( set("rw") )
if "r" in spec:
methods += ["seek", "tell", "read", "readinto"]
if "w" in spec:
methods += ["write"]
return all(callable(getattr(buf, a, None)) for a in methods)
class _buffer_reader:
def __init__(self, buffer):
self.buffer = buffer
def __call__(self, _, position, p_buf, size):
c_buf = ctypes.cast(p_buf, ctypes.POINTER(ctypes.c_char * size))
self.buffer.seek(position)
self.buffer.readinto(c_buf.contents)
return 1
class _buffer_writer:
def __init__(self, buffer):
self.buffer = buffer
def __call__(self, _, data, size):
block = ctypes.cast(data, ctypes.POINTER(ctypes.c_ubyte * size))
self.buffer.write(block.contents)
return 1
def get_bufreader(buffer):
file_len = buffer.seek(0, 2)
buffer.seek(0)
reader = pdfium_c.FPDF_FILEACCESS()
reader.m_FileLen = file_len
set_callback(reader, "m_GetBlock", _buffer_reader(buffer))
reader.m_Param = None
to_hold = (reader.m_GetBlock, )
return reader, to_hold
def get_bufwriter(buffer):
writer = pdfium_c.FPDF_FILEWRITE(version=1)
set_callback(writer, "WriteBlock", _buffer_writer(buffer))
return writer
def pages_c_array(pages):
if not pages:
return None, 0
count = len(pages)
c_array = (pdfium_c.FPDF_PAGE * count)(*[p.raw for p in pages])
return c_array, count