You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/fitz/fitz.py

5370 lines
165 KiB

# This file was automatically generated by SWIG (http://www.swig.org).
# Version 4.0.1
#
# Do not make changes to this file unless you know what you are doing--modify
# the SWIG interface file instead.
from __future__ import division, print_function
from sys import version_info as _swig_python_version_info
if _swig_python_version_info < (2, 7, 0):
raise RuntimeError("Python 2.7 or later required")
# Import the low-level C/C++ module
if __package__ or "." in __name__:
from . import _fitz
else:
import _fitz
try:
import builtins as __builtin__
except ImportError:
import __builtin__
def _swig_repr(self):
try:
strthis = "proxy of " + self.this.__repr__()
except __builtin__.Exception:
strthis = ""
return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
def _swig_setattr_nondynamic_instance_variable(set):
def set_instance_attr(self, name, value):
if name == "thisown":
self.this.own(value)
elif name == "this":
set(self, name, value)
elif hasattr(self, name) and isinstance(getattr(type(self), name), property):
set(self, name, value)
else:
raise AttributeError("You cannot add instance attributes to %s" % self)
return set_instance_attr
def _swig_setattr_nondynamic_class_variable(set):
def set_class_attr(cls, name, value):
if hasattr(cls, name) and not isinstance(getattr(cls, name), property):
set(cls, name, value)
else:
raise AttributeError("You cannot add class attributes to %s" % cls)
return set_class_attr
def _swig_add_metaclass(metaclass):
"""Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass"""
def wrapper(cls):
return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy())
return wrapper
class _SwigNonDynamicMeta(type):
"""Meta class to enforce nondynamic attributes (no new attributes) for a class"""
__setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__)
import os
import weakref
import io
from binascii import hexlify
import math
fitz_py2 = str is bytes # if true, this is Python 2
VersionFitz = "1.16.0"
VersionBind = "1.16.2"
VersionDate = "2019-09-12 17:43:20"
version = (VersionBind, VersionFitz, "20190912174320")
EPSILON = _fitz.EPSILON
PDF_ANNOT_TEXT = _fitz.PDF_ANNOT_TEXT
PDF_ANNOT_LINK = _fitz.PDF_ANNOT_LINK
PDF_ANNOT_FREE_TEXT = _fitz.PDF_ANNOT_FREE_TEXT
PDF_ANNOT_LINE = _fitz.PDF_ANNOT_LINE
PDF_ANNOT_SQUARE = _fitz.PDF_ANNOT_SQUARE
PDF_ANNOT_CIRCLE = _fitz.PDF_ANNOT_CIRCLE
PDF_ANNOT_POLYGON = _fitz.PDF_ANNOT_POLYGON
PDF_ANNOT_POLYLINE = _fitz.PDF_ANNOT_POLYLINE
PDF_ANNOT_HIGHLIGHT = _fitz.PDF_ANNOT_HIGHLIGHT
PDF_ANNOT_UNDERLINE = _fitz.PDF_ANNOT_UNDERLINE
PDF_ANNOT_SQUIGGLY = _fitz.PDF_ANNOT_SQUIGGLY
PDF_ANNOT_STRIKEOUT = _fitz.PDF_ANNOT_STRIKEOUT
PDF_ANNOT_REDACT = _fitz.PDF_ANNOT_REDACT
PDF_ANNOT_STAMP = _fitz.PDF_ANNOT_STAMP
PDF_ANNOT_CARET = _fitz.PDF_ANNOT_CARET
PDF_ANNOT_INK = _fitz.PDF_ANNOT_INK
PDF_ANNOT_POPUP = _fitz.PDF_ANNOT_POPUP
PDF_ANNOT_FILEATTACHMENT = _fitz.PDF_ANNOT_FILEATTACHMENT
PDF_ANNOT_SOUND = _fitz.PDF_ANNOT_SOUND
PDF_ANNOT_MOVIE = _fitz.PDF_ANNOT_MOVIE
PDF_ANNOT_WIDGET = _fitz.PDF_ANNOT_WIDGET
PDF_ANNOT_SCREEN = _fitz.PDF_ANNOT_SCREEN
PDF_ANNOT_PRINTERMARK = _fitz.PDF_ANNOT_PRINTERMARK
PDF_ANNOT_TRAPNET = _fitz.PDF_ANNOT_TRAPNET
PDF_ANNOT_WATERMARK = _fitz.PDF_ANNOT_WATERMARK
PDF_ANNOT_3D = _fitz.PDF_ANNOT_3D
PDF_ANNOT_UNKNOWN = _fitz.PDF_ANNOT_UNKNOWN
ANNOT_TEXT = _fitz.ANNOT_TEXT
ANNOT_LINK = _fitz.ANNOT_LINK
ANNOT_FREETEXT = _fitz.ANNOT_FREETEXT
ANNOT_LINE = _fitz.ANNOT_LINE
ANNOT_SQUARE = _fitz.ANNOT_SQUARE
ANNOT_CIRCLE = _fitz.ANNOT_CIRCLE
ANNOT_POLYGON = _fitz.ANNOT_POLYGON
ANNOT_POLYLINE = _fitz.ANNOT_POLYLINE
ANNOT_HIGHLIGHT = _fitz.ANNOT_HIGHLIGHT
ANNOT_UNDERLINE = _fitz.ANNOT_UNDERLINE
ANNOT_SQUIGGLY = _fitz.ANNOT_SQUIGGLY
ANNOT_STRIKEOUT = _fitz.ANNOT_STRIKEOUT
ANNOT_STAMP = _fitz.ANNOT_STAMP
ANNOT_CARET = _fitz.ANNOT_CARET
ANNOT_INK = _fitz.ANNOT_INK
ANNOT_POPUP = _fitz.ANNOT_POPUP
ANNOT_FILEATTACHMENT = _fitz.ANNOT_FILEATTACHMENT
ANNOT_SOUND = _fitz.ANNOT_SOUND
ANNOT_MOVIE = _fitz.ANNOT_MOVIE
ANNOT_WIDGET = _fitz.ANNOT_WIDGET
ANNOT_SCREEN = _fitz.ANNOT_SCREEN
ANNOT_PRINTERMARK = _fitz.ANNOT_PRINTERMARK
ANNOT_TRAPNET = _fitz.ANNOT_TRAPNET
ANNOT_WATERMARK = _fitz.ANNOT_WATERMARK
ANNOT_3D = _fitz.ANNOT_3D
PDF_ANNOT_IS_Invisible = _fitz.PDF_ANNOT_IS_Invisible
PDF_ANNOT_IS_Hidden = _fitz.PDF_ANNOT_IS_Hidden
PDF_ANNOT_IS_Print = _fitz.PDF_ANNOT_IS_Print
PDF_ANNOT_IS_NoZoom = _fitz.PDF_ANNOT_IS_NoZoom
PDF_ANNOT_IS_NoRotate = _fitz.PDF_ANNOT_IS_NoRotate
PDF_ANNOT_IS_NoView = _fitz.PDF_ANNOT_IS_NoView
PDF_ANNOT_IS_ReadOnly = _fitz.PDF_ANNOT_IS_ReadOnly
PDF_ANNOT_IS_Locked = _fitz.PDF_ANNOT_IS_Locked
PDF_ANNOT_IS_ToggleNoView = _fitz.PDF_ANNOT_IS_ToggleNoView
PDF_ANNOT_IS_LockedContents = _fitz.PDF_ANNOT_IS_LockedContents
ANNOT_XF_Invisible = _fitz.ANNOT_XF_Invisible
ANNOT_XF_Hidden = _fitz.ANNOT_XF_Hidden
ANNOT_XF_Print = _fitz.ANNOT_XF_Print
ANNOT_XF_NoZoom = _fitz.ANNOT_XF_NoZoom
ANNOT_XF_NoRotate = _fitz.ANNOT_XF_NoRotate
ANNOT_XF_NoView = _fitz.ANNOT_XF_NoView
ANNOT_XF_ReadOnly = _fitz.ANNOT_XF_ReadOnly
ANNOT_XF_Locked = _fitz.ANNOT_XF_Locked
ANNOT_XF_ToggleNoView = _fitz.ANNOT_XF_ToggleNoView
ANNOT_XF_LockedContents = _fitz.ANNOT_XF_LockedContents
PDF_ANNOT_LE_NONE = _fitz.PDF_ANNOT_LE_NONE
PDF_ANNOT_LE_SQUARE = _fitz.PDF_ANNOT_LE_SQUARE
PDF_ANNOT_LE_CIRCLE = _fitz.PDF_ANNOT_LE_CIRCLE
PDF_ANNOT_LE_DIAMOND = _fitz.PDF_ANNOT_LE_DIAMOND
PDF_ANNOT_LE_OPEN_ARROW = _fitz.PDF_ANNOT_LE_OPEN_ARROW
PDF_ANNOT_LE_CLOSED_ARROW = _fitz.PDF_ANNOT_LE_CLOSED_ARROW
PDF_ANNOT_LE_BUTT = _fitz.PDF_ANNOT_LE_BUTT
PDF_ANNOT_LE_R_OPEN_ARROW = _fitz.PDF_ANNOT_LE_R_OPEN_ARROW
PDF_ANNOT_LE_R_CLOSED_ARROW = _fitz.PDF_ANNOT_LE_R_CLOSED_ARROW
PDF_ANNOT_LE_SLASH = _fitz.PDF_ANNOT_LE_SLASH
ANNOT_LE_None = _fitz.ANNOT_LE_None
ANNOT_LE_Square = _fitz.ANNOT_LE_Square
ANNOT_LE_Circle = _fitz.ANNOT_LE_Circle
ANNOT_LE_Diamond = _fitz.ANNOT_LE_Diamond
ANNOT_LE_OpenArrow = _fitz.ANNOT_LE_OpenArrow
ANNOT_LE_ClosedArrow = _fitz.ANNOT_LE_ClosedArrow
ANNOT_LE_Butt = _fitz.ANNOT_LE_Butt
ANNOT_LE_ROpenArrow = _fitz.ANNOT_LE_ROpenArrow
ANNOT_LE_RClosedArrow = _fitz.ANNOT_LE_RClosedArrow
ANNOT_LE_Slash = _fitz.ANNOT_LE_Slash
PDF_WIDGET_TYPE_UNKNOWN = _fitz.PDF_WIDGET_TYPE_UNKNOWN
PDF_WIDGET_TYPE_BUTTON = _fitz.PDF_WIDGET_TYPE_BUTTON
PDF_WIDGET_TYPE_CHECKBOX = _fitz.PDF_WIDGET_TYPE_CHECKBOX
PDF_WIDGET_TYPE_COMBOBOX = _fitz.PDF_WIDGET_TYPE_COMBOBOX
PDF_WIDGET_TYPE_LISTBOX = _fitz.PDF_WIDGET_TYPE_LISTBOX
PDF_WIDGET_TYPE_RADIOBUTTON = _fitz.PDF_WIDGET_TYPE_RADIOBUTTON
PDF_WIDGET_TYPE_SIGNATURE = _fitz.PDF_WIDGET_TYPE_SIGNATURE
PDF_WIDGET_TYPE_TEXT = _fitz.PDF_WIDGET_TYPE_TEXT
ANNOT_WG_NOT_WIDGET = _fitz.ANNOT_WG_NOT_WIDGET
ANNOT_WG_PUSHBUTTON = _fitz.ANNOT_WG_PUSHBUTTON
ANNOT_WG_CHECKBOX = _fitz.ANNOT_WG_CHECKBOX
ANNOT_WG_RADIOBUTTON = _fitz.ANNOT_WG_RADIOBUTTON
ANNOT_WG_TEXT = _fitz.ANNOT_WG_TEXT
ANNOT_WG_LISTBOX = _fitz.ANNOT_WG_LISTBOX
ANNOT_WG_COMBOBOX = _fitz.ANNOT_WG_COMBOBOX
ANNOT_WG_SIGNATURE = _fitz.ANNOT_WG_SIGNATURE
PDF_WIDGET_TX_FORMAT_NONE = _fitz.PDF_WIDGET_TX_FORMAT_NONE
PDF_WIDGET_TX_FORMAT_NUMBER = _fitz.PDF_WIDGET_TX_FORMAT_NUMBER
PDF_WIDGET_TX_FORMAT_SPECIAL = _fitz.PDF_WIDGET_TX_FORMAT_SPECIAL
PDF_WIDGET_TX_FORMAT_DATE = _fitz.PDF_WIDGET_TX_FORMAT_DATE
PDF_WIDGET_TX_FORMAT_TIME = _fitz.PDF_WIDGET_TX_FORMAT_TIME
ANNOT_WG_TEXT_UNRESTRAINED = _fitz.ANNOT_WG_TEXT_UNRESTRAINED
ANNOT_WG_TEXT_NUMBER = _fitz.ANNOT_WG_TEXT_NUMBER
ANNOT_WG_TEXT_SPECIAL = _fitz.ANNOT_WG_TEXT_SPECIAL
ANNOT_WG_TEXT_DATE = _fitz.ANNOT_WG_TEXT_DATE
ANNOT_WG_TEXT_TIME = _fitz.ANNOT_WG_TEXT_TIME
PDF_FIELD_IS_READ_ONLY = _fitz.PDF_FIELD_IS_READ_ONLY
PDF_FIELD_IS_REQUIRED = _fitz.PDF_FIELD_IS_REQUIRED
PDF_FIELD_IS_NO_EXPORT = _fitz.PDF_FIELD_IS_NO_EXPORT
WIDGET_Ff_ReadOnly = _fitz.WIDGET_Ff_ReadOnly
WIDGET_Ff_Required = _fitz.WIDGET_Ff_Required
WIDGET_Ff_NoExport = _fitz.WIDGET_Ff_NoExport
PDF_TX_FIELD_IS_MULTILINE = _fitz.PDF_TX_FIELD_IS_MULTILINE
PDF_TX_FIELD_IS_PASSWORD = _fitz.PDF_TX_FIELD_IS_PASSWORD
PDF_TX_FIELD_IS_FILE_SELECT = _fitz.PDF_TX_FIELD_IS_FILE_SELECT
PDF_TX_FIELD_IS_DO_NOT_SPELL_CHECK = _fitz.PDF_TX_FIELD_IS_DO_NOT_SPELL_CHECK
PDF_TX_FIELD_IS_DO_NOT_SCROLL = _fitz.PDF_TX_FIELD_IS_DO_NOT_SCROLL
PDF_TX_FIELD_IS_COMB = _fitz.PDF_TX_FIELD_IS_COMB
PDF_TX_FIELD_IS_RICH_TEXT = _fitz.PDF_TX_FIELD_IS_RICH_TEXT
WIDGET_Ff_Multiline = _fitz.WIDGET_Ff_Multiline
WIDGET_Ff_Password = _fitz.WIDGET_Ff_Password
WIDGET_Ff_FileSelect = _fitz.WIDGET_Ff_FileSelect
WIDGET_Ff_DoNotSpellCheck = _fitz.WIDGET_Ff_DoNotSpellCheck
WIDGET_Ff_DoNotScroll = _fitz.WIDGET_Ff_DoNotScroll
WIDGET_Ff_Comb = _fitz.WIDGET_Ff_Comb
WIDGET_Ff_RichText = _fitz.WIDGET_Ff_RichText
PDF_BTN_FIELD_IS_NO_TOGGLE_TO_OFF = _fitz.PDF_BTN_FIELD_IS_NO_TOGGLE_TO_OFF
PDF_BTN_FIELD_IS_RADIO = _fitz.PDF_BTN_FIELD_IS_RADIO
PDF_BTN_FIELD_IS_PUSHBUTTON = _fitz.PDF_BTN_FIELD_IS_PUSHBUTTON
PDF_BTN_FIELD_IS_RADIOS_IN_UNISON = _fitz.PDF_BTN_FIELD_IS_RADIOS_IN_UNISON
WIDGET_Ff_NoToggleToOff = _fitz.WIDGET_Ff_NoToggleToOff
WIDGET_Ff_Radio = _fitz.WIDGET_Ff_Radio
WIDGET_Ff_Pushbutton = _fitz.WIDGET_Ff_Pushbutton
WIDGET_Ff_RadioInUnison = _fitz.WIDGET_Ff_RadioInUnison
PDF_CH_FIELD_IS_COMBO = _fitz.PDF_CH_FIELD_IS_COMBO
PDF_CH_FIELD_IS_EDIT = _fitz.PDF_CH_FIELD_IS_EDIT
PDF_CH_FIELD_IS_SORT = _fitz.PDF_CH_FIELD_IS_SORT
PDF_CH_FIELD_IS_MULTI_SELECT = _fitz.PDF_CH_FIELD_IS_MULTI_SELECT
PDF_CH_FIELD_IS_DO_NOT_SPELL_CHECK = _fitz.PDF_CH_FIELD_IS_DO_NOT_SPELL_CHECK
PDF_CH_FIELD_IS_COMMIT_ON_SEL_CHANGE = _fitz.PDF_CH_FIELD_IS_COMMIT_ON_SEL_CHANGE
WIDGET_Ff_Combo = _fitz.WIDGET_Ff_Combo
WIDGET_Ff_Edit = _fitz.WIDGET_Ff_Edit
WIDGET_Ff_Sort = _fitz.WIDGET_Ff_Sort
WIDGET_Ff_MultiSelect = _fitz.WIDGET_Ff_MultiSelect
WIDGET_Ff_CommitOnSelCHange = _fitz.WIDGET_Ff_CommitOnSelCHange
CS_RGB = _fitz.CS_RGB
CS_GRAY = _fitz.CS_GRAY
CS_CMYK = _fitz.CS_CMYK
PDF_ENCRYPT_KEEP = _fitz.PDF_ENCRYPT_KEEP
PDF_ENCRYPT_NONE = _fitz.PDF_ENCRYPT_NONE
PDF_ENCRYPT_RC4_40 = _fitz.PDF_ENCRYPT_RC4_40
PDF_ENCRYPT_RC4_128 = _fitz.PDF_ENCRYPT_RC4_128
PDF_ENCRYPT_AES_128 = _fitz.PDF_ENCRYPT_AES_128
PDF_ENCRYPT_AES_256 = _fitz.PDF_ENCRYPT_AES_256
PDF_ENCRYPT_UNKNOWN = _fitz.PDF_ENCRYPT_UNKNOWN
PDF_PERM_PRINT = _fitz.PDF_PERM_PRINT
PDF_PERM_MODIFY = _fitz.PDF_PERM_MODIFY
PDF_PERM_COPY = _fitz.PDF_PERM_COPY
PDF_PERM_ANNOTATE = _fitz.PDF_PERM_ANNOTATE
PDF_PERM_FORM = _fitz.PDF_PERM_FORM
PDF_PERM_ACCESSIBILITY = _fitz.PDF_PERM_ACCESSIBILITY
PDF_PERM_ASSEMBLE = _fitz.PDF_PERM_ASSEMBLE
PDF_PERM_PRINT_HQ = _fitz.PDF_PERM_PRINT_HQ
class Matrix(object):
"""Matrix() - all zeros\nMatrix(a, b, c, d, e, f)\nMatrix(zoom-x, zoom-y) - zoom\nMatrix(shear-x, shear-y, 1) - shear\nMatrix(degree) - rotate\nMatrix(Matrix) - new copy\nMatrix(sequence) - from 'sequence'"""
def __init__(self, *args):
if not args:
self.a = self.b = self.c = self.d = self.e = self.f = 0.0
return None
if len(args) > 6:
raise ValueError("bad sequ. length")
if len(args) == 6: # 6 numbers
self.a = float(args[0])
self.b = float(args[1])
self.c = float(args[2])
self.d = float(args[3])
self.e = float(args[4])
self.f = float(args[5])
return None
if len(args) == 1: # either an angle or a sequ
if hasattr(args[0], "__float__"):
theta = math.radians(args[0])
c = math.cos(theta)
s = math.sin(theta)
self.a = self.d = c
self.b = s
self.c = -s
self.e = self.f = 0.0
return None
else:
self.a = float(args[0][0])
self.b = float(args[0][1])
self.c = float(args[0][2])
self.d = float(args[0][3])
self.e = float(args[0][4])
self.f = float(args[0][5])
return None
if len(args) == 2 or len(args) == 3 and args[2] == 0:
self.a, self.b, self.c, self.d, self.e, self.f = float(args[0]), \
0.0, 0.0, float(args[1]), 0.0, 0.0
return None
if len(args) == 3 and args[2] == 1:
self.a, self.b, self.c, self.d, self.e, self.f = 1.0, \
float(args[1]), float(args[0]), 1.0, 0.0, 0.0
return None
raise ValueError("illegal Matrix constructor")
def invert(self, src=None):
"""Calculate the inverted matrix. Return 0 if successful and replace
current one. Else return 1 and do nothing.
"""
if src is None:
dst = TOOLS._invert_matrix(self)
else:
dst = TOOLS._invert_matrix(src)
if dst[0] == 1:
return 1
self.a, self.b, self.c, self.d, self.e, self.f = dst[1]
return 0
def preTranslate(self, tx, ty):
"""Calculate pre translation and replace current matrix."""
tx = float(tx)
ty = float(ty)
self.e += tx * self.a + ty * self.c
self.f += tx * self.b + ty * self.d
return self
def preScale(self, sx, sy):
"""Calculate pre scaling and replace current matrix."""
sx = float(sx)
sy = float(sy)
self.a *= sx
self.b *= sx
self.c *= sy
self.d *= sy
return self
def preShear(self, h, v):
"""Calculate pre shearing and replace current matrix."""
h = float(h)
v = float(v)
a, b = self.a, self.b
self.a += v * self.c
self.b += v * self.d
self.c += h * a
self.d += h * b
return self
def preRotate(self, theta):
"""Calculate pre rotation and replace current matrix."""
theta = float(theta)
while theta < 0: theta += 360
while theta >= 360: theta -= 360
if abs(0 - theta) < EPSILON:
pass
elif abs(90.0 - theta) < EPSILON:
a = self.a
b = self.b
self.a = self.c
self.b = self.d
self.c = -a
self.d = -b
elif abs(180.0 - theta) < EPSILON:
self.a = -self.a
self.b = -self.b
self.c = -self.c
self.d = -self.d
elif abs(270.0 - theta) < EPSILON:
a = self.a
b = self.b
self.a = -self.c
self.b = -self.d
self.c = a
self.d = b
else:
rad = math.radians(theta)
s = math.sin(rad)
c = math.cos(rad)
a = self.a
b = self.b
self.a = c * a + s * self.c
self.b = c * b + s * self.d
self.c =-s * a + c * self.c
self.d =-s * b + c * self.d
return self
def concat(self, one, two):
"""Multiply two matrices and replace current one."""
if not len(one) == len(two) == 6:
raise ValueError("bad sequ. length")
self.a, self.b, self.c, self.d, self.e, self.f = TOOLS._concat_matrix(one, two)
return self
def __getitem__(self, i):
return (self.a, self.b, self.c, self.d, self.e, self.f)[i]
def __setitem__(self, i, v):
v = float(v)
if i == 0: self.a = v
elif i == 1: self.b = v
elif i == 2: self.c = v
elif i == 3: self.d = v
elif i == 4: self.e = v
elif i == 5: self.f = v
else:
raise IndexError("index out of range")
return
def __len__(self):
return 6
def __repr__(self):
return "Matrix" + str(tuple(self))
def __invert__(self):
m1 = Matrix()
m1.invert(self)
return m1
__inv__ = __invert__
def __mul__(self, m):
if hasattr(m, "__float__"):
return Matrix(self.a * m, self.b * m, self.c * m,
self.d * m, self.e * m, self.f * m)
m1 = Matrix(1,1)
return m1.concat(self, m)
def __truediv__(self, m):
if hasattr(m, "__float__"):
return Matrix(self.a * 1./m, self.b * 1./m, self.c * 1./m,
self.d * 1./m, self.e * 1./m, self.f * 1./m)
m1 = TOOLS._invert_matrix(m)[1]
if not m1:
raise ZeroDivisionError("matrix not invertible")
m2 = Matrix(1,1)
return m2.concat(self, m1)
__div__ = __truediv__
def __add__(self, m):
if hasattr(m, "__float__"):
return Matrix(self.a + m, self.b + m, self.c + m,
self.d + m, self.e + m, self.f + m)
if len(m) != 6:
raise ValueError("bad sequ. length")
return Matrix(self.a + m[0], self.b + m[1], self.c + m[2],
self.d + m[3], self.e + m[4], self.f + m[5])
def __sub__(self, m):
if hasattr(m, "__float__"):
return Matrix(self.a - m, self.b - m, self.c - m,
self.d - m, self.e - m, self.f - m)
if len(m) != 6:
raise ValueError("bad sequ. length")
return Matrix(self.a - m[0], self.b - m[1], self.c - m[2],
self.d - m[3], self.e - m[4], self.f - m[5])
def __pos__(self):
return Matrix(self)
def __neg__(self):
return Matrix(-self.a, -self.b, -self.c, -self.d, -self.e, -self.f)
def __bool__(self):
return not (max(self) == min(self) == 0)
def __nonzero__(self):
return not (max(self) == min(self) == 0)
def __eq__(self, m):
if not hasattr(m, "__len__"):
return False
return len(m) == 6 and bool(self - m) is False
def __abs__(self):
return math.sqrt(sum([c*c for c in self]))
norm = __abs__
@property
def isRectilinear(self):
return (abs(self.b) < EPSILON and abs(self.c) < EPSILON) or \
(abs(self.a) < EPSILON and abs(self.d) < EPSILON);
class IdentityMatrix(Matrix):
"""Identity matrix [1, 0, 0, 1, 0, 0]"""
def __init__(self):
Matrix.__init__(self, 1.0, 1.0)
def __setattr__(self, name, value):
if name in "ad":
self.__dict__[name] = 1.0
elif name in "bcef":
self.__dict__[name] = 0.0
else:
self.__dict__[name] = value
def checkargs(*args):
raise NotImplementedError("Identity is readonly")
preRotate = checkargs
preShear = checkargs
preScale = checkargs
preTranslate = checkargs
concat = checkargs
invert = checkargs
def __repr__(self):
return "IdentityMatrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)"
def __hash__(self):
return hash((1,0,0,1,0,0))
Identity = IdentityMatrix()
class Point(object):
"""Point() - all zeros\nPoint(x, y)\nPoint(Point) - new copy\nPoint(sequence) - from 'sequence'"""
def __init__(self, *args):
if not args:
self.x = 0.0
self.y = 0.0
return None
if len(args) > 2:
raise ValueError("bad sequ. length")
if len(args) == 2:
self.x = float(args[0])
self.y = float(args[1])
return None
if len(args) == 1:
l = args[0]
if hasattr(l, "__getitem__") is False:
raise ValueError("bad Point constructor")
if len(l) != 2:
raise ValueError("bad sequ. length")
self.x = float(l[0])
self.y = float(l[1])
return None
raise ValueError("bad Point constructor")
def transform(self, m):
"""Replace point by its transformation with matrix-like m."""
if len(m) != 6:
raise ValueError("bad sequ. length")
self.x, self.y = TOOLS._transform_point(self, m)
return self
@property
def unit(self):
"""Return unit vector of a point."""
s = self.x * self.x + self.y * self.y
if s < EPSILON:
return Point(0,0)
s = math.sqrt(s)
return Point(self.x / s, self.y / s)
@property
def abs_unit(self):
"""Return unit vector of a point with positive coordinates."""
s = self.x * self.x + self.y * self.y
if s < EPSILON:
return Point(0,0)
s = math.sqrt(s)
return Point(abs(self.x) / s, abs(self.y) / s)
def distance_to(self, *args):
"""Return the distance to a rectangle or another point."""
if not len(args) > 0:
raise ValueError("at least one parameter must be given")
x = args[0]
if len(x) == 2:
x = Point(x)
elif len(x) == 4:
x = Rect(x)
else:
raise ValueError("arg1 must be point-like or rect-like")
if len(args) > 1:
unit = args[1]
else:
unit = "px"
u = {"px": (1.,1.), "in": (1.,72.), "cm": (2.54, 72.),
"mm": (25.4, 72.)}
f = u[unit][0] / u[unit][1]
if type(x) is Point:
return abs(self - x) * f
# from here on, x is a rectangle
# as a safeguard, make a finite copy of it
r = Rect(x.top_left, x.top_left)
r = r | x.bottom_right
if self in r:
return 0.0
if self.x > r.x1:
if self.y >= r.y1:
return self.distance_to(r.bottom_right, unit)
elif self.y <= r.y0:
return self.distance_to(r.top_right, unit)
else:
return (self.x - r.x1) * f
elif r.x0 <= self.x <= r.x1:
if self.y >= r.y1:
return (self.y - r.y1) * f
else:
return (r.y0 - self.y) * f
else:
if self.y >= r.y1:
return self.distance_to(r.bottom_left, unit)
elif self.y <= r.y0:
return self.distance_to(r.top_left, unit)
else:
return (r.x0 - self.x) * f
def __getitem__(self, i):
return (self.x, self.y)[i]
def __len__(self):
return 2
def __setitem__(self, i, v):
v = float(v)
if i == 0: self.x = v
elif i == 1: self.y = v
else:
raise IndexError("index out of range")
return None
def __repr__(self):
return "Point" + str(tuple(self))
def __pos__(self):
return Point(self)
def __neg__(self):
return Point(-self.x, -self.y)
def __bool__(self):
return not (max(self) == min(self) == 0)
def __nonzero__(self):
return not (max(self) == min(self) == 0)
def __eq__(self, p):
if not hasattr(p, "__len__"):
return False
return len(p) == 2 and bool(self - p) is False
def __abs__(self):
return math.sqrt(self.x * self.x + self.y * self.y)
norm = __abs__
def __add__(self, p):
if hasattr(p, "__float__"):
return Point(self.x + p, self.y + p)
if len(p) != 2:
raise ValueError("bad sequ. length")
return Point(self.x + p[0], self.y + p[1])
def __sub__(self, p):
if hasattr(p, "__float__"):
return Point(self.x - p, self.y - p)
if len(p) != 2:
raise ValueError("bad sequ. length")
return Point(self.x - p[0], self.y - p[1])
def __mul__(self, m):
if hasattr(m, "__float__"):
return Point(self.x * m, self.y * m)
p = Point(self)
return p.transform(m)
def __truediv__(self, m):
if hasattr(m, "__float__"):
return Point(self.x * 1./m, self.y * 1./m)
m1 = TOOLS._invert_matrix(m)[1]
if not m1:
raise ZeroDivisionError("matrix not invertible")
p = Point(self)
return p.transform(m1)
__div__ = __truediv__
def __hash__(self):
return hash(tuple(self))
class Rect(object):
"""Rect() - all zeros\nRect(x0, y0, x1, y1)\nRect(top-left, x1, y1)\nRect(x0, y0, bottom-right)\nRect(top-left, bottom-right)\nRect(Rect or IRect) - new copy\nRect(sequence) - from 'sequence'"""
def __init__(self, *args):
if not args:
self.x0 = self.y0 = self.x1 = self.y1 = 0.0
return None
if len(args) > 4:
raise ValueError("bad sequ. length")
if len(args) == 4:
self.x0 = float(args[0])
self.y0 = float(args[1])
self.x1 = float(args[2])
self.y1 = float(args[3])
return None
if len(args) == 1:
l = args[0]
if hasattr(l, "__getitem__") is False:
raise ValueError("bad Rect constructor")
if len(l) != 4:
raise ValueError("bad sequ. length")
self.x0 = float(l[0])
self.y0 = float(l[1])
self.x1 = float(l[2])
self.y1 = float(l[3])
return None
if len(args) == 2: # 2 Points provided
self.x0 = float(args[0][0])
self.y0 = float(args[0][1])
self.x1 = float(args[1][0])
self.y1 = float(args[1][1])
return None
if len(args) == 3: # 2 floats and 1 Point provided
a0 = args[0]
a1 = args[1]
a2 = args[2]
if hasattr(a0, "__float__"): # (float, float, Point) provided
self.x0 = float(a0)
self.y0 = float(a1)
self.x1 = float(a2[0])
self.y1 = float(a2[1])
return None
self.x0 = float(a0[0]) # (Point, float, float) provided
self.y0 = float(a0[1])
self.x1 = float(a1)
self.y1 = float(a2)
return None
raise ValueError("bad Rect constructor")
def normalize(self):
"""Replace rectangle with its finite version."""
if self.x1 < self.x0:
self.x0, self.x1 = self.x1, self.x0
if self.y1 < self.y0:
self.y0, self.y1 = self.y1, self.y0
return self
@property
def isEmpty(self):
"""Check if rectangle area is empty."""
return self.x0 == self.x1 or self.y0 == self.y1
@property
def isInfinite(self):
"""Check if rectangle is infinite."""
return self.x0 > self.x1 or self.y0 > self.y1
@property
def top_left(self):
return Point(self.x0, self.y0)
@property
def top_right(self):
return Point(self.x1, self.y0)
@property
def bottom_left(self):
return Point(self.x0, self.y1)
@property
def bottom_right(self):
return Point(self.x1, self.y1)
tl = top_left
tr = top_right
bl = bottom_left
br = bottom_right
@property
def quad(self):
return Quad(self.tl, self.tr, self.bl, self.br)
def round(self):
return IRect(min(self.x0, self.x1), min(self.y0, self.y1),
max(self.x0, self.x1), max(self.y0, self.y1))
irect = property(round)
width = property(lambda self: abs(self.x1 - self.x0))
height = property(lambda self: abs(self.y1 - self.y0))
def includePoint(self, p):
"""Extend rectangle to include point p."""
if not len(p) == 2:
raise ValueError("bad sequ. length")
self.x0, self.y0, self.x1, self.y1 = TOOLS._include_point_in_rect(self, p)
return self
def includeRect(self, r):
"""Extend rectangle to include rectangle r."""
if not len(r) == 4:
raise ValueError("bad sequ. length")
self.x0, self.y0, self.x1, self.y1 = TOOLS._union_rect(self, r)
return self
def intersect(self, r):
"""Restrict self to common area with rectangle r."""
if not len(r) == 4:
raise ValueError("bad sequ. length")
self.x0, self.y0, self.x1, self.y1 = TOOLS._intersect_rect(self, r)
return self
def transform(self, m):
"""Replace rectangle with its transformation by matrix m."""
if not len(m) == 6:
raise ValueError("bad sequ. length")
self.x0, self.y0, self.x1, self.y1 = TOOLS._transform_rect(self, m)
return self
def __getitem__(self, i):
return (self.x0, self.y0, self.x1, self.y1)[i]
def __len__(self):
return 4
def __setitem__(self, i, v):
v = float(v)
if i == 0: self.x0 = v
elif i == 1: self.y0 = v
elif i == 2: self.x1 = v
elif i == 3: self.y1 = v
else:
raise IndexError("index out of range")
return None
def __repr__(self):
return "Rect" + str(tuple(self))
def __pos__(self):
return Rect(self)
def __neg__(self):
return Rect(-self.x0, -self.y0, -self.x1, -self.y1)
def __bool__(self):
return not (max(self) == min(self) == 0)
def __nonzero__(self):
return not (max(self) == min(self) == 0)
def __eq__(self, p):
if not hasattr(p, "__len__"):
return False
return len(p) == 4 and bool(self - p) is False
def __abs__(self):
if self.isEmpty or self.isInfinite:
return 0.0
return (self.x1 - self.x0) * (self.y1 - self.y0)
def norm(self):
return math.sqrt(sum([c*c for c in self]))
def __add__(self, p):
if hasattr(p, "__float__"):
r = Rect(self.x0 + p, self.y0 + p, self.x1 + p, self.y1 + p)
else:
if len(p) != 4:
raise ValueError("bad sequ. length")
r = Rect(self.x0 + p[0], self.y0 + p[1], self.x1 + p[2], self.y1 + p[3])
return r
def __sub__(self, p):
if hasattr(p, "__float__"):
return Rect(self.x0 - p, self.y0 - p, self.x1 - p, self.y1 - p)
if len(p) != 4:
raise ValueError("bad sequ. length")
return Rect(self.x0 - p[0], self.y0 - p[1], self.x1 - p[2], self.y1 - p[3])
def __mul__(self, m):
if hasattr(m, "__float__"):
return Rect(self.x0 * m, self.y0 * m, self.x1 * m, self.y1 * m)
r = Rect(self)
r = r.transform(m)
return r
def __truediv__(self, m):
if hasattr(m, "__float__"):
return Rect(self.x0 * 1./m, self.y0 * 1./m, self.x1 * 1./m, self.y1 * 1./m)
im = TOOLS._invert_matrix(m)[1]
if not im:
raise ZeroDivisionError("matrix not invertible")
r = Rect(self)
r = r.transform(im)
return r
__div__ = __truediv__
def __contains__(self, x):
if hasattr(x, "__float__"):
return x in tuple(self)
l = len(x)
r = Rect(self).normalize()
if l == 4:
if r.isEmpty: return False
xr = Rect(x).normalize()
if xr.isEmpty: return True
if r.x0 <= xr.x0 and r.y0 <= xr.y0 and \
r.x1 >= xr.x1 and r.y1 >= xr.y1:
return True
return False
if l == 2:
if r.x0 <= x[0] <= r.x1 and \
r.y0 <= x[1] <= r.y1:
return True
return False
return False
def __or__(self, x):
if not hasattr(x, "__len__"):
raise ValueError("bad operand 2")
r = Rect(self)
if len(x) == 2:
return r.includePoint(x)
if len(x) == 4:
return r.includeRect(x)
raise ValueError("bad operand 2")
def __and__(self, x):
if not hasattr(x, "__len__"):
raise ValueError("bad operand 2")
r1 = Rect(x)
r = Rect(self)
return r.intersect(r1)
def intersects(self, x):
"""Check if intersection with rectangle x is not empty."""
r1 = Rect(x)
if self.isEmpty or self.isInfinite or r1.isEmpty or r1.isInfinite:
return False
r = Rect(self)
if r.intersect(r1).isEmpty:
return False
return True
def __hash__(self):
return hash(tuple(self))
class IRect(Rect):
"""IRect() - all zeros\nIRect(x0, y0, x1, y1)\nIRect(Rect or IRect) - new copy\nIRect(sequence) - from 'sequence'"""
def __init__(self, *args):
Rect.__init__(self, *args)
self.x0 = math.floor(self.x0 + 0.001)
self.y0 = math.floor(self.y0 + 0.001)
self.x1 = math.ceil(self.x1 - 0.001)
self.y1 = math.ceil(self.y1 - 0.001)
return None
@property
def round(self):
pass
irect = round
@property
def rect(self):
return Rect(self)
def __repr__(self):
return "IRect" + str(tuple(self))
def includePoint(self, p):
"""Extend rectangle to include point p."""
return Rect.includePoint(self, p).round()
def includeRect(self, r):
"""Extend rectangle to include rectangle r."""
return Rect.includeRect(self, r).round()
def intersect(self, r):
"""Restrict rectangle to intersection with rectangle r."""
return Rect.intersect(self, r).round()
def __setitem__(self, i, v):
v = int(v)
if i == 0: self.x0 = v
elif i == 1: self.y0 = v
elif i == 2: self.x1 = v
elif i == 3: self.y1 = v
else:
raise IndexError("index out of range")
return None
def __pos__(self):
return IRect(self)
def __neg__(self):
return IRect(-self.x0, -self.y0, -self.x1, -self.y1)
def __add__(self, p):
return Rect.__add__(self, p).round()
def __sub__(self, p):
return Rect.__sub__(self, p).round()
def transform(self, m):
return Rect.transform(self, m).round()
def __mul__(self, m):
return Rect.__mul__(self, m).round()
def __truediv__(self, m):
return Rect.__truediv__(self, m).round()
def __or__(self, x):
return Rect.__or__(self, x).round()
def __and__(self, x):
return Rect.__and__(self, x).round()
class Quad(object):
"""Quad() - all zero points\nQuad(ul, ur, ll, lr)\nQuad(quad) - new copy\nQuad(sequence) - from 'sequence'"""
def __init__(self, *args):
if not args:
self.ul = self.ur = self.ll = self.lr = Point()
return None
if len(args) > 4:
raise ValueError("bad sequ. length")
if len(args) == 4:
self.ul = Point(args[0])
self.ur = Point(args[1])
self.ll = Point(args[2])
self.lr = Point(args[3])
return None
if len(args) == 1:
l = args[0]
if hasattr(l, "__getitem__") is False:
raise ValueError("bad Quad constructor")
if len(l) != 4:
raise ValueError("bad sequ. length")
self.ul = Point(l[0])
self.ur = Point(l[1])
self.ll = Point(l[2])
self.lr = Point(l[3])
return None
raise ValueError("bad Quad constructor")
@property
def isRectangular(self):
"""Check if quad is rectangular.
Notes:
Some rotation matrix can thus transform it into a rectangle.
"""
a = TOOLS._angle_between(self.ul, self.ur, self.lr)
if abs(a.y - 1) > EPSILON:
return False
a = TOOLS._angle_between(self.ur, self.lr, self.ll)
if abs(a.y - 1) > EPSILON:
return False
a = TOOLS._angle_between(self.lr, self.ll, self.ul)
if abs(a.y - 1) > EPSILON:
return False
return True
@property
def isConvex(self):
"""Check if quad is convex.
Notes:
Every line connecting any two points of the quad will be inside
the quad. This is equivalent to that two sides meeting in a corner
always enclose an angle of no more than 180 degrees.
Equivalently, the sine of this angle cannot be negative.
Returns:
True or False.
"""
a = TOOLS._angle_between(self.ul, self.ur, self.lr)
if a.y < 0:
return False
a = TOOLS._angle_between(self.ur, self.lr, self.ll)
if a.y < 0:
return False
a = TOOLS._angle_between(self.lr, self.ll, self.ul)
if a.y < 0:
return False
return True
@property
def isEmpty(self):
"""Check if quad is empty retangle. If rectangular, we are done (not empty).
But all 4 points may still be on one line. We check this out here.
In that case all 3 lines connecting corners to ul will have same angle
with the x-axis.
"""
if self.isRectangular:
return False
ul = Point()
ur = (self.ur - self.ul).abs_unit
lr = (self.lr - self.ul).abs_unit
ll = (self.ll - self.ul).abs_unit
if max(ur.y, lr.y, ll.y) - min(ur.y, lr.y, ll.y) < EPSILON:
return True
return False
width = property(lambda self: max(abs(self.ul - self.ur), abs(self.ll - self.lr)))
height = property(lambda self: max(abs(self.ul - self.ll), abs(self.ur - self.lr)))
@property
def rect(self):
r = Rect()
r.x0 = min(self.ul.x, self.ur.x, self.lr.x, self.ll.x)
r.y0 = min(self.ul.y, self.ur.y, self.lr.y, self.ll.y)
r.x1 = max(self.ul.x, self.ur.x, self.lr.x, self.ll.x)
r.y1 = max(self.ul.y, self.ur.y, self.lr.y, self.ll.y)
return r
def __getitem__(self, i):
return (self.ul, self.ur, self.ll, self.lr)[i]
def __len__(self):
return 4
def __setitem__(self, i, v):
if i == 0: self.ul = Point(v)
elif i == 1: self.ur = Point(v)
elif i == 2: self.ll = Point(v)
elif i == 3: self.lr = Point(v)
else:
raise IndexError("index out of range")
return None
def __repr__(self):
return "Quad" + str(tuple(self))
def __pos__(self):
return Quad(self)
def __neg__(self):
return Quad(-self.ul, -self.ur, -self.ll, -self.lr)
def __bool__(self):
return not self.isEmpty
def __nonzero__(self):
return not self.isEmpty
def __eq__(self, p):
if not hasattr(p, "__len__"):
return False
return len(p) == 4 and self.ul == p[0] and self.ur == p[1] and \
self.ll == p[3] and self.lr == p[3]
def __abs__(self):
if self.isEmpty:
return 0.0
return abs(self.ul - self.ur) * abs(self.ul - self.ll)
def transform(self, m):
"""Replace quad by its transformation with matrix m."""
if len(m) != 6:
raise ValueError("bad sequ. length")
self.ul *= m
self.ur *= m
self.ll *= m
self.lr *= m
return self
def __mul__(self, m):
r = Quad(self)
r = r.transform(m)
return r
def __truediv__(self, m):
if hasattr(m, "__float__"):
im = 1. / m
else:
im = TOOLS._invert_matrix(m)[1]
if not im:
raise ZeroDivisionError("matrix not invertible")
r = Quad(self)
r = r.transform(im)
return r
__div__ = __truediv__
def __hash__(self):
return hash(tuple(self))
#------------------------------------------------------------------------------
# Class describing a PDF form field ("widget")
#------------------------------------------------------------------------------
class Widget(object):
def __init__(self):
self.border_color = None
self.border_style = "S"
self.border_width = 0
self.border_dashes = None
self.choice_values = None # choice fields only
self.field_name = None # field name
self.field_label = None # field label
self.field_value = None
self.field_flags = None
self.field_display = 0
self.fill_color = None
self.button_caption = None # button caption
self.rect = None # annot value
self.is_signed = None # True / False if signature
self.text_color = (0, 0, 0)
self.text_font = "Helv"
self.text_fontsize = 0
self.text_maxlen = 0 # text fields only
self.text_format = 0 # text fields only
self._text_da = "" # /DA = default apparance
self.field_type = 0 # valid range 1 through 7
self.field_type_string = None # field type as string
self._text_da = "" # /DA = default apparance
self.xref = 0 # annot value
def _validate(self):
"""Validate the class entries.
"""
if (self.rect.isInfinite
or self.rect.isEmpty
):
raise ValueError("bad rect")
if not self.field_name:
raise ValueError("field name missing")
if self.field_label == "Unnamed":
self.field_label = None
CheckColor(self.border_color)
CheckColor(self.fill_color)
if not self.text_color:
self.text_color = (0, 0, 0)
CheckColor(self.text_color)
if not self.border_width:
self.border_width = 0
if not self.text_fontsize:
self.text_fontsize = 0
self.border_style = self.border_style.upper()[0:1]
self._checker() # any field_type specific checks
def _adjust_font(self):
"""Ensure text_font is from our list and correctly spelled.
"""
if not self.text_font:
self.text_font = "Helv"
return
valid_fonts = ("Cour", "TiRo", "Helv", "ZaDb")
for f in valid_fonts:
if self.text_font.lower() == f.lower():
self.text_font = f
return
self.text_font = "Helv"
return
def _parse_da(self):
"""Extract font name, size and color from default appearance string (/DA object).
Equivalent to 'pdf_parse_default_appearance' function in MuPDF's 'pdf-annot.c'.
"""
if not self._text_da:
return
font = "Helv"
fsize = 0
col = (0, 0, 0)
dat = self._text_da.split() # split on any whitespace
for i, item in enumerate(dat):
if item == "Tf":
font = dat[i - 2][1:]
fsize = float(dat[i - 1])
dat[i] = dat[i-1] = dat[i-2] = ""
continue
if item == "g": # unicolor text
col = [(float(dat[i - 1]))]
dat[i] = dat[i-1] = ""
continue
if item == "rg": # RGB colored text
col = [float(f) for f in dat[i - 3:i]]
dat[i] = dat[i-1] = dat[i-2] = dat[i-3] = ""
continue
self.text_font = font
self.text_fontsize = fsize
self.text_color = col
self._text_da = ""
return
def _checker(self):
"""Any widget type checks.
"""
if self.field_type not in range(1, 8):
raise ValueError("bad field type")
def update(self):
"""Reflect Python object in the PDF.
"""
self._validate()
doc = self.parent.parent
self._adjust_font() # ensure valid text_font name
# now create the /DA string
self._text_da = ""
if len(self.text_color) == 3:
fmt = "{:g} {:g} {:g} rg /{f:s} {s:g} Tf" + self._text_da
elif len(self.text_color) == 1:
fmt = "{:g} g /{f:s} {s:g} Tf" + self._text_da
elif len(self.text_color) == 4:
fmt = "{:g} {:g} {:g} {:g} k /{f:s} {s:g} Tf" + self._text_da
self._text_da = fmt.format(*self.text_color, f=self.text_font,
s=self.text_fontsize)
# finally update the widget
TOOLS._save_widget(self._annot, self)
self._text_da = ""
def __repr__(self):
return "'%s' widget on %s" % (self.field_type_string, str(self.parent))
def __del__(self):
self._annot.__del__()
@property
def next(self):
return self._annot.next
#------------------------------------------------------------------------------
# link kinds and link flags
#------------------------------------------------------------------------------
LINK_NONE = 0
LINK_GOTO = 1
LINK_URI = 2
LINK_LAUNCH = 3
LINK_NAMED = 4
LINK_GOTOR = 5
LINK_FLAG_L_VALID = 1
LINK_FLAG_T_VALID = 2
LINK_FLAG_R_VALID = 4
LINK_FLAG_B_VALID = 8
LINK_FLAG_FIT_H = 16
LINK_FLAG_FIT_V = 32
LINK_FLAG_R_IS_ZOOM = 64
#------------------------------------------------------------------------------
# Text handling flags
#------------------------------------------------------------------------------
TEXT_ALIGN_LEFT = 0
TEXT_ALIGN_CENTER = 1
TEXT_ALIGN_RIGHT = 2
TEXT_ALIGN_JUSTIFY = 3
TEXT_OUTPUT_TEXT = 0
TEXT_OUTPUT_HTML = 1
TEXT_OUTPUT_JSON = 2
TEXT_OUTPUT_XML = 3
TEXT_OUTPUT_XHTML = 4
TEXT_PRESERVE_LIGATURES = 1
TEXT_PRESERVE_WHITESPACE = 2
TEXT_PRESERVE_IMAGES = 4
TEXT_INHIBIT_SPACES = 8
#------------------------------------------------------------------------------
# Simple text encoding options
#------------------------------------------------------------------------------
TEXT_ENCODING_LATIN = 0
TEXT_ENCODING_GREEK = 1
TEXT_ENCODING_CYRILLIC = 2
#------------------------------------------------------------------------------
# Stamp annotation icon numbers
#------------------------------------------------------------------------------
STAMP_Approved = 0
STAMP_AsIs = 1
STAMP_Confidential = 2
STAMP_Departmental = 3
STAMP_Experimental = 4
STAMP_Expired = 5
STAMP_Final = 6
STAMP_ForComment = 7
STAMP_ForPublicRelease = 8
STAMP_NotApproved = 9
STAMP_NotForPublicRelease = 10
STAMP_Sold = 11
STAMP_TopSecret = 12
STAMP_Draft = 13
#------------------------------------------------------------------------------
# Base 14 font names and dictionary
#------------------------------------------------------------------------------
Base14_fontnames = ("Courier", "Courier-Oblique", "Courier-Bold",
"Courier-BoldOblique", "Helvetica", "Helvetica-Oblique",
"Helvetica-Bold", "Helvetica-BoldOblique",
"Times-Roman", "Times-Italic", "Times-Bold",
"Times-BoldItalic", "Symbol", "ZapfDingbats")
Base14_fontdict = {}
for f in Base14_fontnames:
Base14_fontdict[f.lower()] = f
Base14_fontdict["helv"] = "Helvetica"
Base14_fontdict["heit"] = "Helvetica-Oblique"
Base14_fontdict["hebo"] = "Helvetica-Bold"
Base14_fontdict["hebi"] = "Helvetica-BoldOblique"
Base14_fontdict["cour"] = "Courier"
Base14_fontdict["coit"] = "Courier-Oblique"
Base14_fontdict["cobo"] = "Courier-Bold"
Base14_fontdict["cobi"] = "Courier-BoldOblique"
Base14_fontdict["tiro"] = "Times-Roman"
Base14_fontdict["tibo"] = "Times-Bold"
Base14_fontdict["tiit"] = "Times-Italic"
Base14_fontdict["tibi"] = "Times-BoldItalic"
Base14_fontdict["symb"] = "Symbol"
Base14_fontdict["zadb"] = "ZapfDingbats"
def _toc_remove_page(toc, first, last):
""" Remove all ToC entries pointing to certain pages.
Args:
toc: old table of contents generated with getToC(False).
first: (int) number of first page to remove.
last: (int) number of last page to remove.
Returns:
Modified table of contents, which should be used by PDF
document method setToC.
"""
toc2 = [] # intermediate new toc
count = last - first + 1 # number of pages to remove
# step 1: remove numbers from toc
for t in toc:
if first <= t[2] <= last: # skip entries between first and last
continue
if t[2] < first: # keep smaller page numbers
toc2.append(t)
continue
# larger page numbers
t[2] -= count # decrease page number
d = t[3]
if d["kind"] == LINK_GOTO:
d["page"] -= count
t[3] = d
toc2.append(t)
toc3 = [] # final new toc
old_lvl = 0
# step 2: deal with hierarchy lvl gaps > 1
for t in toc2:
while t[0] - old_lvl > 1: # lvl gap too large
old_lvl += 1 # increase previous lvl
toc3.append([old_lvl] + t[1:]) # insert a filler item
old_lvl = t[0]
toc3.append(t)
return toc3
def getTextlength(text, fontname="helv", fontsize=11, encoding=0):
"""Calculate length of a string for a given built-in font.
Args:
fontname: name of the font.
fontsize: size of font in points.
encoding: encoding to use (0=Latin, 1=Greek, 2=Cyrillic).
Returns:
(float) length of text.
"""
fontname = fontname.lower()
basename = Base14_fontdict.get(fontname, None)
glyphs = None
if basename == "Symbol":
glyphs = symbol_glyphs
if basename == "ZapfDingbats":
glyphs = zapf_glyphs
if glyphs is not None:
w = sum([glyphs[ord(c)][1] if ord(c)<256 else glyphs[183][1] for c in text])
return w * fontsize
if fontname in Base14_fontdict.keys():
return TOOLS.measure_string(text, Base14_fontdict[fontname], fontsize, encoding)
if fontname in ["china-t", "china-s",
"china-ts", "china-ss",
"japan", "japan-s",
"korea", "korea-s"]:
return len(text) * fontsize
raise ValueError("Font '%s' is unsupported" % fontname)
#------------------------------------------------------------------------------
# Glyph list for the built-in font 'ZapfDingbats'
#------------------------------------------------------------------------------
zapf_glyphs = (
(183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788),
(183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788),
(183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788),
(183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788),
(183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788),
(183, 0.788), (183, 0.788), (32, 0.278), (33, 0.974), (34, 0.961), (35, 0.974),
(36, 0.98), (37, 0.719), (38, 0.789), (39, 0.79), (40, 0.791), (41, 0.69),
(42, 0.96), (43, 0.939), (44, 0.549), (45, 0.855), (46, 0.911), (47, 0.933),
(48, 0.911), (49, 0.945), (50, 0.974), (51, 0.755), (52, 0.846), (53, 0.762),
(54, 0.761), (55, 0.571), (56, 0.677), (57, 0.763), (58, 0.76), (59, 0.759),
(60, 0.754), (61, 0.494), (62, 0.552), (63, 0.537), (64, 0.577), (65, 0.692),
(66, 0.786), (67, 0.788), (68, 0.788), (69, 0.79), (70, 0.793), (71, 0.794),
(72, 0.816), (73, 0.823), (74, 0.789), (75, 0.841), (76, 0.823), (77, 0.833),
(78, 0.816), (79, 0.831), (80, 0.923), (81, 0.744), (82, 0.723), (83, 0.749),
(84, 0.79), (85, 0.792), (86, 0.695), (87, 0.776), (88, 0.768), (89, 0.792),
(90, 0.759), (91, 0.707), (92, 0.708), (93, 0.682), (94, 0.701), (95, 0.826),
(96, 0.815), (97, 0.789), (98, 0.789), (99, 0.707), (100, 0.687), (101, 0.696),
(102, 0.689), (103, 0.786), (104, 0.787), (105, 0.713), (106, 0.791),
(107, 0.785), (108, 0.791), (109, 0.873), (110, 0.761), (111, 0.762),
(112, 0.762), (113, 0.759), (114, 0.759), (115, 0.892), (116, 0.892),
(117, 0.788), (118, 0.784), (119, 0.438), (120, 0.138), (121, 0.277),
(122, 0.415), (123, 0.392), (124, 0.392), (125, 0.668), (126, 0.668),
(183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788),
(183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788),
(183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788),
(183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788),
(183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788),
(183, 0.788), (183, 0.788), (183, 0.788), (183, 0.788), (161, 0.732), (162, 0.544),
(163, 0.544), (164, 0.91), (165, 0.667), (166, 0.76), (167, 0.76),
(168, 0.776), (169, 0.595), (170, 0.694), (171, 0.626), (172, 0.788),
(173, 0.788), (174, 0.788), (175, 0.788), (176, 0.788), (177, 0.788),
(178, 0.788), (179, 0.788), (180, 0.788), (181, 0.788), (182, 0.788),
(183, 0.788), (184, 0.788), (185, 0.788), (186, 0.788), (187, 0.788),
(188, 0.788), (189, 0.788), (190, 0.788), (191, 0.788), (192, 0.788),
(193, 0.788), (194, 0.788), (195, 0.788), (196, 0.788), (197, 0.788),
(198, 0.788), (199, 0.788), (200, 0.788), (201, 0.788), (202, 0.788),
(203, 0.788), (204, 0.788), (205, 0.788), (206, 0.788), (207, 0.788),
(208, 0.788), (209, 0.788), (210, 0.788), (211, 0.788), (212, 0.894),
(213, 0.838), (214, 1.016), (215, 0.458), (216, 0.748), (217, 0.924),
(218, 0.748), (219, 0.918), (220, 0.927), (221, 0.928), (222, 0.928),
(223, 0.834), (224, 0.873), (225, 0.828), (226, 0.924), (227, 0.924),
(228, 0.917), (229, 0.93), (230, 0.931), (231, 0.463), (232, 0.883),
(233, 0.836), (234, 0.836), (235, 0.867), (236, 0.867), (237, 0.696),
(238, 0.696), (239, 0.874), (183, 0.788), (241, 0.874), (242, 0.76),
(243, 0.946), (244, 0.771), (245, 0.865), (246, 0.771), (247, 0.888),
(248, 0.967), (249, 0.888), (250, 0.831), (251, 0.873), (252, 0.927),
(253, 0.97), (183, 0.788), (183, 0.788)
)
#------------------------------------------------------------------------------
# Glyph list for the built-in font 'Symbol'
#------------------------------------------------------------------------------
symbol_glyphs = (
(183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46),
(183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46),
(183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46),
(183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46),
(183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46),
(183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46),
(183, 0.46), (183, 0.46), (32, 0.25), (33, 0.333), (34, 0.713),
(35, 0.5), (36, 0.549), (37, 0.833), (38, 0.778), (39, 0.439),
(40, 0.333), (41, 0.333), (42, 0.5), (43, 0.549), (44, 0.25), (45, 0.549),
(46, 0.25), (47, 0.278), (48, 0.5), (49, 0.5), (50, 0.5), (51, 0.5),
(52, 0.5), (53, 0.5), (54, 0.5), (55, 0.5), (56, 0.5), (57, 0.5),
(58, 0.278), (59, 0.278), (60, 0.549), (61, 0.549), (62, 0.549),
(63, 0.444), (64, 0.549), (65, 0.722), (66, 0.667), (67, 0.722),
(68, 0.612), (69, 0.611), (70, 0.763), (71, 0.603), (72, 0.722),
(73, 0.333), (74, 0.631), (75, 0.722), (76, 0.686), (77, 0.889),
(78, 0.722), (79, 0.722), (80, 0.768), (81, 0.741), (82, 0.556),
(83, 0.592), (84, 0.611), (85, 0.69), (86, 0.439), (87, 0.768),
(88, 0.645), (89, 0.795), (90, 0.611), (91, 0.333), (92, 0.863),
(93, 0.333), (94, 0.658), (95, 0.5), (96, 0.5), (97, 0.631), (98, 0.549),
(99, 0.549), (100, 0.494), (101, 0.439), (102, 0.521), (103, 0.411),
(104, 0.603), (105, 0.329), (106, 0.603), (107, 0.549), (108, 0.549),
(109, 0.576), (110, 0.521), (111, 0.549), (112, 0.549), (113, 0.521),
(114, 0.549), (115, 0.603), (116, 0.439), (117, 0.576), (118, 0.713),
(119, 0.686), (120, 0.493), (121, 0.686), (122, 0.494), (123, 0.48),
(124, 0.2), (125, 0.48), (126, 0.549), (183, 0.46), (183, 0.46),
(183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46),
(183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46),
(183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46),
(183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46),
(183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46),
(183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46), (183, 0.46),
(183, 0.46), (160, 0.25), (161, 0.62), (162, 0.247), (163, 0.549),
(164, 0.167), (165, 0.713), (166, 0.5), (167, 0.753), (168, 0.753),
(169, 0.753), (170, 0.753), (171, 1.042), (172, 0.713), (173, 0.603),
(174, 0.987), (175, 0.603), (176, 0.4), (177, 0.549), (178, 0.411),
(179, 0.549), (180, 0.549), (181, 0.576), (182, 0.494), (183, 0.46),
(184, 0.549), (185, 0.549), (186, 0.549), (187, 0.549), (188, 1),
(189, 0.603), (190, 1), (191, 0.658), (192, 0.823), (193, 0.686),
(194, 0.795), (195, 0.987), (196, 0.768), (197, 0.768), (198, 0.823),
(199, 0.768), (200, 0.768), (201, 0.713), (202, 0.713), (203, 0.713),
(204, 0.713), (205, 0.713), (206, 0.713), (207, 0.713), (208, 0.768),
(209, 0.713), (210, 0.79), (211, 0.79), (212, 0.89), (213, 0.823),
(214, 0.549), (215, 0.549), (216, 0.713), (217, 0.603), (218, 0.603),
(219, 1.042), (220, 0.987), (221, 0.603), (222, 0.987), (223, 0.603),
(224, 0.494), (225, 0.329), (226, 0.79), (227, 0.79), (228, 0.786),
(229, 0.713), (230, 0.384), (231, 0.384), (232, 0.384), (233, 0.384),
(234, 0.384), (235, 0.384), (236, 0.494), (237, 0.494), (238, 0.494),
(239, 0.494), (183, 0.46), (241, 0.329), (242, 0.274), (243, 0.686),
(244, 0.686), (245, 0.686), (246, 0.384), (247, 0.549), (248, 0.384),
(249, 0.384), (250, 0.384), (251, 0.384), (252, 0.494), (253, 0.494),
(254, 0.494), (183, 0.46)
)
class linkDest(object):
"""link or outline destination details"""
def __init__(self, obj, rlink):
isExt = obj.isExternal
isInt = not isExt
self.dest = ""
self.fileSpec = ""
self.flags = 0
self.isMap = False
self.isUri = False
self.kind = LINK_NONE
self.lt = Point(0, 0)
self.named = ""
self.newWindow = ""
self.page = obj.page
self.rb = Point(0, 0)
self.uri = obj.uri
if rlink and not self.uri.startswith("#"):
self.uri = "#%i,%g,%g" % (rlink[0]+1, rlink[1], rlink[2])
if obj.isExternal:
self.page = -1
self.kind = LINK_URI
if not self.uri:
self.page = -1
self.kind = LINK_NONE
if isInt and self.uri:
if self.uri.startswith("#"):
self.named = ""
self.kind = LINK_GOTO
ftab = self.uri[1:].split(",")
if len(ftab) == 3:
self.page = int(ftab[0]) - 1
self.lt = Point(float(ftab[1]), float(ftab[2]))
self.flags = self.flags | LINK_FLAG_L_VALID | LINK_FLAG_T_VALID
else:
try:
self.page = int(ftab[0]) - 1
except:
self.kind = LINK_NAMED
self.named = self.uri[1:]
else:
self.kind = LINK_NAMED
self.named = self.uri
if obj.isExternal:
if self.uri.startswith(("http://", "https://", "mailto:", "ftp://")):
self.isUri = True
self.kind = LINK_URI
elif self.uri.startswith("file://"):
self.fileSpec = self.uri[7:]
self.isUri = False
self.uri = ""
self.kind = LINK_LAUNCH
ftab = self.fileSpec.split("#")
if len(ftab) == 2:
if ftab[1].startswith("page="):
self.kind = LINK_GOTOR
self.fileSpec = ftab[0]
self.page = int(ftab[1][5:]) - 1
else:
self.isUri = True
self.kind = LINK_LAUNCH
#-------------------------------------------------------------------------------
# "Now" timestamp in PDF Format
#-------------------------------------------------------------------------------
def getPDFnow():
import time
tz = "%s'%s'" % (str(time.timezone // 3600).rjust(2, "0"),
str((time.timezone // 60)%60).rjust(2, "0"))
tstamp = time.strftime("D:%Y%m%d%H%M%S", time.localtime())
if time.timezone > 0:
tstamp += "-" + tz
elif time.timezone < 0:
tstamp = "+" + tz
else:
pass
return tstamp
def getPDFstr(s):
""" Return a PDF string depending on its coding.
Notes:
If only ascii then "(original)" is returned, else if only 8 bit chars
then "(original)" with interspersed octal strings \nnn is returned,
else a string "<FEFF[hexstring]>" is returned, where [hexstring] is the
UTF-16BE encoding of the original.
"""
if not bool(s):
return "()"
def make_utf16be(s):
r = hexlify(bytearray([254, 255]) + bytearray(s, "UTF-16BE"))
t = r if fitz_py2 else r.decode()
return "<" + t + ">" # brackets indicate hex
# following either returns original string with mixed-in
# octal numbers \nnn for chars outside ASCII range, or:
# exits with utf-16be BOM version of the string
r = ""
for c in s:
oc = ord(c)
if oc > 255: # shortcut if beyond code range
return make_utf16be(s)
if oc > 31 and oc < 127:
if c in ("(", ")", "\\"):
r += "\\"
r += c
continue
if oc > 127:
r += "\\" + oct(oc)[-3:]
continue
if oc < 8 or oc > 13 or oc == 11 or c == 127:
r += "\\267" # indicate unsupported char
continue
if oc == 8:
r += "\\b"
elif oc == 9:
r += "\\t"
elif oc == 10:
r += "\\n"
elif oc == 12:
r += "\\f"
elif oc == 13:
r += "\\r"
return "(" + r + ")"
def getTJstr(text, glyphs, simple, ordering):
""" Return a PDF string enclosed in [] brackets, suitable for the PDF TJ
operator.
Notes:
The input string is converted to either 2 or 4 hex digits per character.
Args:
simple: no glyphs: 2-chars, use char codes as the glyph
glyphs: 2-chars, use glyphs instead of char codes (Symbol,
ZapfDingbats)
not simple: ordering < 0: 4-chars, use glyphs not char codes
ordering >=0: a CJK font! 4 chars, use char codes as glyphs
"""
if text.startswith("[<") and text.endswith(">]"): # already done
return text
if not bool(text):
return "[<>]"
if simple:
if glyphs is None: # simple and not Symbol / ZapfDingbats
otxt = "".join([hex(ord(c))[2:].rjust(2, "0") if ord(c)<256 else "b7" for c in text])
else: # Symbol or ZapfDingbats
otxt = "".join([hex(glyphs[ord(c)][0])[2:].rjust(2, "0") if ord(c)<256 else "b7" for c in text])
return "[<" + otxt + ">]"
if ordering < 0: # not a CJK font: use the glyphs
otxt = "".join([hex(glyphs[ord(c)][0])[2:].rjust(4, "0") for c in text])
else: # CJK: use char codes, no glyphs
otxt = "".join([hex(ord(c))[2:].rjust(4, "0") for c in text])
return "[<" + otxt + ">]"
'''
Information taken from the following web sites:
www.din-formate.de
www.din-formate.info/amerikanische-formate.html
www.directtools.de/wissen/normen/iso.htm
'''
paperSizes = { # known paper formats @ 72 dpi
'a0': (2384, 3370),
'a1': (1684, 2384),
'a10': (74, 105),
'a2': (1191, 1684),
'a3': (842, 1191),
'a4': (595, 842),
'a5': (420, 595),
'a6': (298, 420),
'a7': (210, 298),
'a8': (147, 210),
'a9': (105, 147),
'b0': (2835, 4008),
'b1': (2004, 2835),
'b10': (88, 125),
'b2': (1417, 2004),
'b3': (1001, 1417),
'b4': (709, 1001),
'b5': (499, 709),
'b6': (354, 499),
'b7': (249, 354),
'b8': (176, 249),
'b9': (125, 176),
'c0': (2599, 3677),
'c1': (1837, 2599),
'c10': (79, 113),
'c2': (1298, 1837),
'c3': (918, 1298),
'c4': (649, 918),
'c5': (459, 649),
'c6': (323, 459),
'c7': (230, 323),
'c8': (162, 230),
'c9': (113, 162),
'card-4x6': (288, 432),
'card-5x7': (360, 504),
'commercial': (297, 684),
'executive': (522, 756),
'invoice': (396, 612),
'ledger': (792, 1224),
'legal': (612, 1008),
'legal-13': (612, 936),
'letter': (612, 792),
'monarch': (279, 540),
'tabloid-extra': (864, 1296),
}
def PaperSize(s):
"""Return a tuple (width, height) for a given paper format string. 'A4-L' will
return (842, 595), the values for A4 landscape. Suffix '-P' and no suffix
returns portrait."""
size = s.lower()
f = "p"
if size.endswith("-l"):
f = "l"
size = size[:-2]
if size.endswith("-p"):
size = size[:-2]
rc = paperSizes.get(size, (-1, -1))
if f == "p":
return rc
return (rc[1], rc[0])
def PaperRect(s):
"""Return a fitz.Rect for the paper size indicated in string 's'. Must conform to the argument of method 'PaperSize', which will be invoked.
"""
width, height = PaperSize(s)
return Rect(0.0, 0.0, width, height)
def CheckParent(o):
if not hasattr(o, "parent") or o.parent is None:
raise ValueError("orphaned object: parent is None")
def CheckColor(c):
if c is not None:
if (
type(c) not in (list, tuple)
or len(c) not in (1, 3, 4)
or min(c) < 0
or max(c) > 1
):
raise ValueError("need 1, 3 or 4 color components in range 0 to 1")
def ColorCode(c, f):
if c is None:
return ""
if hasattr(c, "__float__"):
c = (c,)
CheckColor(c)
if len(c) == 1:
s = "%g " % c[0]
return s + "G " if f == "c" else s + "g "
if len(c) == 3:
s = "%g %g %g " % tuple(c)
return s + "RG " if f == "c" else s + "rg "
s = "%g %g %g %g " % tuple(c)
return s + "K " if f == "c" else s + "k "
def JM_TUPLE(o):
return tuple(map(lambda x: round(x, 8), o))
def CheckMorph(o):
if not bool(o): return False
if not (type(o) in (list, tuple) and len(o) == 2):
raise ValueError("morph must be a sequence of length 2")
if not (len(o[0]) == 2 and len(o[1]) == 6):
raise ValueError("invalid morph parm 0")
if not o[1][4] == o[1][5] == 0:
raise ValueError("invalid morph parm 1")
return True
def CheckFont(page, fontname):
"""Return an entry in the page's font list if reference name matches.
"""
for f in page.getFontList():
if f[4] == fontname:
return f
if f[3].lower() == fontname.lower():
return f
def CheckFontInfo(doc, xref):
"""Return a font info if present in the document.
"""
for f in doc.FontInfos:
if xref == f[0]:
return f
def UpdateFontInfo(doc, info):
xref = info[0]
found = False
for i, fi in enumerate(doc.FontInfos):
if fi[0] == xref:
found = True
break
if found:
doc.FontInfos[i] = info
else:
doc.FontInfos.append(info)
def DUMMY(*args, **kw):
return
def planishLine(p1, p2):
"""Return matrix which flattens out the line from p1 to p2.
Args:
p1, p2: point_like
Returns:
Matrix which maps p1 to Point(0,0) and p2 to a point on the x axis at
the same distance to Point(0,0). Will always combine a rotation and a
transformation.
"""
p1 = Point(p1)
p2 = Point(p2)
return TOOLS._hor_matrix(p1, p2)
def ImageProperties(img):
""" Return basic properties of an image.
Args:
img: bytes, bytearray, io.BytesIO object or an opened image file.
Returns:
A dictionary with keys width, height, colorspace.n, bpc, type, ext and size,
where 'type' is the MuPDF image type (0 to 14) and 'ext' the suitable
file extension.
"""
if type(img) is io.BytesIO:
stream = img.getvalue()
elif hasattr(img, "read"):
stream = img.read()
elif type(img) in (bytes, bytearray):
stream = img
else:
raise ValueError("bad argument 'img'")
return TOOLS.image_profile(stream)
def ConversionHeader(i, filename = "unknown"):
t = i.lower()
html = """<!DOCTYPE html>
<html>
<head>
<style>
body{background-color:gray}
div{position:relative;background-color:white;margin:1em auto}
p{position:absolute;margin:0}
img{position:absolute}
</style>
</head>
<body>\n"""
xml = """<?xml version="1.0"?>
<document name="%s">\n""" % filename
xhtml = """<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style>
body{background-color:gray}
div{background-color:white;margin:1em;padding:1em}
p{white-space:pre-wrap}
</style>
</head>
<body>\n"""
text = ""
json = '{"document": "%s", "pages": [\n' % filename
if t == "html":
r = html
elif t == "json":
r = json
elif t == "xml":
r = xml
elif t == "xhtml":
r = xhtml
else:
r = text
return r
def ConversionTrailer(i):
t = i.lower()
text = ""
json = "]\n}"
html = "</body>\n</html>\n"
xml = "</document>\n"
xhtml = html
if t == "html":
r = html
elif t == "json":
r = json
elif t == "xml":
r = xml
elif t == "xhtml":
r = xhtml
else:
r = text
return r
def _make_textpage_dict(TextPage, raw=False):
""" Return a dictionary representing all text on a page.
Notes:
A number of precautions is taken to keep memory consumption under
control. E.g. when calling utility functions, we provide empty lists
to be filled by them. This ensures that garbage collection on the
Python level knows them, when taking appropriate action.
The utility functions themselves strictly return flat structures (e.g.
no dictionaries, no nested lists) to prevent sub-structures that are
not reachable by gc.
Args:
raw: bool which causes inclusion of a dictionary per each character
else characters are concatenated for each span.
Returns:
dict
"""
page_dict = {"width": TextPage.rect.width, "height": TextPage.rect.height}
blocks = []
num_blocks = TextPage._getBlockList(blocks)
block_list = [0] * num_blocks
for i in range(num_blocks):
block = blocks[i]
block_dict = {"type": block[0], "bbox": block[1:5]}
lines = [] # prepare output for the block details
if block[0] == 1: # handle an image block
rc = TextPage._getImageBlock(i, lines) # read block data
if rc != 0: # any problem?
raise ValueError("could not extract image block %i" % i)
ilist = lines[0] # the tuple we want
block_dict["width"] = ilist[1]
block_dict["height"] = ilist[2]
block_dict["ext"] = ilist[3]
block_dict["image"] = ilist[4]
block_list[i] = block_dict # insert image block to list
del block_dict, ilist
continue # to next block
# process a text block
num_lines = TextPage._getLineList(i, lines) # read the line array
line_list = [0] * num_lines
for j in range(num_lines):
line = lines[j]
line_dict = {"wmode": line[0], "dir": line[1:3], "bbox": line[3:]}
span_list = []
characters = []
num_chars = TextPage._getCharList(i, j, characters)
old_style = ()
span = {}
char_list = []
text = ""
for char in characters: # iterate through the characters
orig_x, orig_y, x0, y0, x1, y1, fsize, flags, font, color, char_text = (
char
)
pos = font.find("+") # remove any garbage from font
if pos > 0:
font = font[pos + 1 :]
style = (fsize, flags, font, color) # font info
# check if the character style has changed
if style != old_style:
# check for first span
if old_style != (): # finish previous span first
if raw:
span["chars"] = char_list # char dictionaries
char_list = [] # reset char dict list
else:
span["text"] = text # accumulated text
text = "" # reset text field
span["bbox"] = span_bbox # put in bbox
span_list.append(span) # output previous span
# init a new span
span = {"size": fsize, "flags": flags, "font": font, "color": color}
old_style = style
span_bbox = (x0, y0, x1, y1)
span_bbox = ( # extend span bbox
min(x0, span_bbox[0]),
min(y0, span_bbox[1]),
max(x1, span_bbox[2]),
max(y1, span_bbox[3]),
)
if raw:
char_dict = {
"origin": (orig_x, orig_y),
"bbox": (x0, y0, x1, y1),
"c": char_text,
}
char_list.append(char_dict)
else:
text += char_text
# all characters in line have been processed now
if max(len(char_list), len(text)) > 0: # chars missing in outut?
if raw:
span["chars"] = char_list
else:
span["text"] = text
span["bbox"] = span_bbox # put in bbox
span_list.append(span)
line_dict["spans"] = span_list # put list of spans in line dict
del span_list
line_list[j] = line_dict # insert line dict to list of lines
del line_dict
block_dict["lines"] = line_list
del line_list
block_list[i] = block_dict
del block_dict
page_dict["blocks"] = block_list
del block_list
return page_dict
class Document(object):
r"""open() - new empty PDF
open('type', stream) - from bytes/bytearray
open(filename, filetype='type') - from file"""
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
__swig_destroy__ = _fitz.delete_Document
def __init__(self, filename=None, stream=None, filetype=None, rect=None, width=0, height=0, fontsize=11):
r"""__init__(self, filename=None, stream=None, filetype=None, rect=None, width=0, height=0, fontsize=11) -> Document"""
if not filename or type(filename) is str:
pass
else:
if fitz_py2: # Python 2
if type(filename) is unicode:
filename = filename.encode("utf8")
else:
filename = str(filename) # should take care of pathlib
if stream:
if not (filename or filetype):
raise ValueError("need filetype for opening a stream")
if type(stream) is bytes:
self.stream = stream
elif type(stream) is bytearray:
self.stream = bytes(stream)
elif type(stream) is io.BytesIO:
self.stream = stream.getvalue()
else:
raise ValueError("bad type: 'stream'")
stream = self.stream
else:
self.stream = None
if filename and not stream:
self.name = filename
else:
self.name = ""
self.isClosed = False
self.isEncrypted = False
self.metadata = None
self.FontInfos = []
self.Graftmaps = {}
self.ShownPages = {}
self._page_refs = weakref.WeakValueDictionary()
_fitz.Document_swiginit(self, _fitz.new_Document(filename, stream, filetype, rect, width, height, fontsize))
if self.thisown:
self._graft_id = TOOLS.gen_id()
if self.needsPass is True:
self.isEncrypted = True
else: # we won't init until doc is decrypted
self.initData()
def close(self):
r"""close(self)"""
if self.isClosed:
raise ValueError("document closed")
if hasattr(self, "_outline") and self._outline:
self._dropOutline(self._outline)
self._outline = None
self._reset_page_refs()
self.metadata = None
self.stream = None
self.isClosed = True
self.FontInfos = []
for gmap in self.Graftmaps:
self.Graftmaps[gmap] = None
self.Graftmaps = {}
self.ShownPages = {}
val = _fitz.Document_close(self)
self.thisown = False
return val
def loadPage(self, number=0):
r"""loadPage(self, number=0) -> Page"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
val = _fitz.Document_loadPage(self, number)
if val:
val.thisown = True
val.parent = weakref.proxy(self)
pageCount = self.pageCount
n = number
while n < 0: n += pageCount
val.number = n
self._page_refs[id(val)] = val
val._annot_refs = weakref.WeakValueDictionary()
return val
def _remove_links_to(self, first, last):
r"""_remove_links_to(self, first, last) -> PyObject *"""
return _fitz.Document__remove_links_to(self, first, last)
def _loadOutline(self):
r"""_loadOutline(self) -> Outline"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__loadOutline(self)
def _dropOutline(self, ol):
r"""_dropOutline(self, ol)"""
return _fitz.Document__dropOutline(self, ol)
def _embeddedFileNames(self, namelist):
r"""_embeddedFileNames(self, namelist) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__embeddedFileNames(self, namelist)
def _embeddedFileDel(self, idx):
r"""_embeddedFileDel(self, idx) -> PyObject *"""
return _fitz.Document__embeddedFileDel(self, idx)
def _embeddedFileInfo(self, idx, infodict):
r"""_embeddedFileInfo(self, idx, infodict) -> PyObject *"""
return _fitz.Document__embeddedFileInfo(self, idx, infodict)
def _embeddedFileUpd(self, idx, buffer=None, filename=None, ufilename=None, desc=None):
r"""_embeddedFileUpd(self, idx, buffer=None, filename=None, ufilename=None, desc=None) -> PyObject *"""
return _fitz.Document__embeddedFileUpd(self, idx, buffer, filename, ufilename, desc)
def _embeddedFileGet(self, idx):
r"""_embeddedFileGet(self, idx) -> PyObject *"""
return _fitz.Document__embeddedFileGet(self, idx)
def _embeddedFileAdd(self, name, buffer, filename=None, ufilename=None, desc=None):
r"""_embeddedFileAdd(self, name, buffer, filename=None, ufilename=None, desc=None) -> PyObject *"""
return _fitz.Document__embeddedFileAdd(self, name, buffer, filename, ufilename, desc)
def embeddedFileNames(self):
""" Return a list of names of EmbeddedFiles.
"""
filenames = []
self._embeddedFileNames(filenames)
return filenames
def _embeddedFileIndex(self, item):
filenames = self.embeddedFileNames()
msg = "'%s' not in EmbeddedFiles array." % str(item)
if item in filenames:
idx = filenames.index(item)
elif item in range(len(filenames)):
idx = item
else:
raise ValueError(msg)
return idx
def embeddedFileCount(self):
""" Return the number of EmbeddedFiles.
"""
return len(self.embeddedFileNames())
def embeddedFileDel(self, item):
""" Delete an entry from EmbeddedFiles.
Notes:
The argument must be name or index of an EmbeddedFiles item.
Physical deletion of associated data will happen on save to a
new file with appropriate garbage option.
Args:
item: (str/int) the name or index of the entry.
Returns:
None
"""
idx = self._embeddedFileIndex(item)
return self._embeddedFileDel(idx)
def embeddedFileInfo(self, item):
""" Return information of an item in the EmbeddedFiles array.
Args:
item: the number or the name of the item.
Returns:
A dictionary of respective information.
"""
idx = self._embeddedFileIndex(item)
infodict = {"name": self.embeddedFileNames()[idx]}
self._embeddedFileInfo(idx, infodict)
return infodict
def embeddedFileGet(self, item):
""" Return the content of an item in the EmbeddedFiles array.
Args:
item: the number or the name of the item.
Returns:
(bytes) The file content.
"""
idx = self._embeddedFileIndex(item)
return self._embeddedFileGet(idx)
def embeddedFileUpd(self, item, buffer=None,
filename=None,
ufilename=None,
desc=None):
""" Make changes to an item in the EmbeddedFiles array.
Notes:
All parameter are optional. If all arguments are omitted, the
method results in a no-op.
Args:
item: the number or the name of the item.
buffer: (binary data) the new file content.
filename: (str) the new file name.
ufilename: (unicode) the new filen ame.
desc: (str) the new description.
"""
idx = self._embeddedFileIndex(item)
return self._embeddedFileUpd(idx, buffer=buffer,
filename=filename,
ufilename=ufilename,
desc=desc)
def embeddedFileAdd(self, name, buffer,
filename=None,
ufilename=None,
desc=None):
""" Add an item to the EmbeddedFiles array.
Args:
name: the name of the new item.
buffer: (binary data) the file content.
filename: (str) the file name.
ufilename: (unicode) the filen ame.
desc: (str) the description.
"""
filenames = self.embeddedFileNames()
msg = "Name '%s' already in EmbeddedFiles array." % str(name)
if name in filenames:
raise ValueError(msg)
if filename is None:
filename = name
if ufilename is None:
ufilename = unicode(filename, "utf8") if str is bytes else filename
if desc is None:
desc = name
return self._embeddedFileAdd(name, buffer=buffer,
filename=filename,
ufilename=ufilename,
desc=desc)
def convertToPDF(self, from_page=0, to_page=-1, rotate=0):
r"""Convert document to PDF selecting page range and optional rotation. Output bytes object."""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document_convertToPDF(self, from_page, to_page, rotate)
@property
def pageCount(self):
r"""pageCount(self) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document_pageCount(self)
def _getMetadata(self, key):
r"""_getMetadata(self, key) -> char *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__getMetadata(self, key)
@property
def needsPass(self):
r"""needsPass(self) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document_needsPass(self)
def resolveLink(self, uri=None):
r"""Calculate internal link destination."""
return _fitz.Document_resolveLink(self, uri)
def layout(self, rect=None, width=0, height=0, fontsize=11):
r"""Re-layout a reflowable document."""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
val = _fitz.Document_layout(self, rect, width, height, fontsize)
self._reset_page_refs()
self.initData()
return val
def makeBookmark(self, pno=0):
r"""Make page bookmark in a reflowable document."""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document_makeBookmark(self, pno)
def findBookmark(self, bookmark):
r"""Find page number after layouting a document."""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document_findBookmark(self, bookmark)
@property
def isReflowable(self):
r"""isReflowable(self) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document_isReflowable(self)
def _deleteObject(self, xref):
r"""Delete an object given its xref."""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__deleteObject(self, xref)
def _getPDFroot(self):
r"""Get XREF number of PDF catalog."""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__getPDFroot(self)
def _getPDFfileid(self):
r"""Return PDF file /ID strings (hexadecimal)."""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__getPDFfileid(self)
@property
def isPDF(self):
r"""isPDF(self) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document_isPDF(self)
@property
def _hasXrefStream(self):
r"""_hasXrefStream(self) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__hasXrefStream(self)
@property
def _hasXrefOldStyle(self):
r"""_hasXrefOldStyle(self) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__hasXrefOldStyle(self)
@property
def isDirty(self):
r"""isDirty(self) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document_isDirty(self)
def can_save_incrementally(self):
r"""Check if can be saved incrementally."""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document_can_save_incrementally(self)
def authenticate(self, password):
r"""Decrypt document with a password."""
if self.isClosed:
raise ValueError("document closed")
val = _fitz.Document_authenticate(self, password)
if val: # the doc is decrypted successfully and we init the outline
self.isEncrypted = False
self.initData()
self.thisown = True
return val
def save(self, filename, garbage=0, clean=0, deflate=0, incremental=0, ascii=0, expand=0, linear=0, pretty=0, encryption=1, permissions=-1, owner_pw=None, user_pw=None):
r"""save(self, filename, garbage=0, clean=0, deflate=0, incremental=0, ascii=0, expand=0, linear=0, pretty=0, encryption=1, permissions=-1, owner_pw=None, user_pw=None) -> PyObject *"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
if type(filename) == str:
pass
elif type(filename) == unicode:
filename = filename.encode('utf8')
else:
raise TypeError("filename must be a string")
if filename == self.name and not incremental:
raise ValueError("save to original must be incremental")
if self.pageCount < 1:
raise ValueError("cannot save with zero pages")
if incremental:
if self.name != filename or self.stream:
raise ValueError("incremental needs original file")
return _fitz.Document_save(self, filename, garbage, clean, deflate, incremental, ascii, expand, linear, pretty, encryption, permissions, owner_pw, user_pw)
def write(self, garbage=0, clean=0, deflate=0, ascii=0, expand=0, linear=0, pretty=0, encryption=1, permissions=-1, owner_pw=None, user_pw=None):
r"""Write document to a bytes object."""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
if self.pageCount < 1:
raise ValueError("cannot write with zero pages")
return _fitz.Document_write(self, garbage, clean, deflate, ascii, expand, linear, pretty, encryption, permissions, owner_pw, user_pw)
def insertPDF(self, docsrc, from_page=-1, to_page=-1, start_at=-1, rotate=-1, links=1, annots=1):
r"""Copy page range ['from', 'to'] of source PDF, starting as page number 'start_at'."""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
if id(self) == id(docsrc):
raise ValueError("source must not equal target PDF")
sa = start_at
if sa < 0:
sa = self.pageCount
val = _fitz.Document_insertPDF(self, docsrc, from_page, to_page, start_at, rotate, links, annots)
self._reset_page_refs()
if links:
self._do_links(docsrc, from_page = from_page, to_page = to_page,
start_at = sa)
return val
def _newPage(self, pno=-1, width=595, height=842):
r"""_newPage(self, pno=-1, width=595, height=842) -> PyObject *"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
val = _fitz.Document__newPage(self, pno, width, height)
self._reset_page_refs()
return val
def select(self, pyliste):
r"""Build sub-pdf with page numbers in 'list'."""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
if not self.isPDF:
raise ValueError("not a PDF")
if not hasattr(pyliste, "__getitem__"):
raise ValueError("sequence required")
if len(pyliste) == 0 or min(pyliste) not in range(len(self)) or max(pyliste) not in range(len(self)):
raise ValueError("sequence items out of range")
val = _fitz.Document_select(self, pyliste)
self._reset_page_refs()
return val
def _deletePage(self, pno):
r"""_deletePage(self, pno) -> PyObject *"""
return _fitz.Document__deletePage(self, pno)
@property
def permissions(self):
r"""Get document permissions."""
if self.isEncrypted:
return 0
return _fitz.Document_permissions(self)
def _getCharWidths(self, xref, bfname, ext, ordering, limit, idx=0):
r"""Return list of glyphs and glyph widths of a font."""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document__getCharWidths(self, xref, bfname, ext, ordering, limit, idx)
def _getPageObjNumber(self, pno):
r"""_getPageObjNumber(self, pno) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__getPageObjNumber(self, pno)
def _getPageInfo(self, pno, what):
r"""Show fonts or images used on a page."""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
val = _fitz.Document__getPageInfo(self, pno, what)
x = []
for v in val:
if v not in x:
x.append(v)
val = x
return val
def extractFont(self, xref=0, info_only=0):
r"""extractFont(self, xref=0, info_only=0) -> PyObject *"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document_extractFont(self, xref, info_only)
def extractImage(self, xref=0):
r"""Extract image which 'xref' is pointing to."""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document_extractImage(self, xref)
def _delToC(self):
r"""_delToC(self) -> PyObject *"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
val = _fitz.Document__delToC(self)
self.initData()
return val
def isStream(self, xref=0):
r"""isStream(self, xref=0) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document_isStream(self, xref)
def getSigFlags(self):
r"""getSigFlags(self) -> int"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document_getSigFlags(self)
@property
def isFormPDF(self):
r"""isFormPDF(self) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document_isFormPDF(self)
@property
def FormFonts(self):
r"""FormFonts(self) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document_FormFonts(self)
def _addFormFont(self, name, font):
r"""_addFormFont(self, name, font) -> PyObject *"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document__addFormFont(self, name, font)
def _getOLRootNumber(self):
r"""_getOLRootNumber(self) -> PyObject *"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document__getOLRootNumber(self)
def _getNewXref(self):
r"""_getNewXref(self) -> PyObject *"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document__getNewXref(self)
def _getXrefLength(self):
r"""_getXrefLength(self) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__getXrefLength(self)
def _getXmlMetadataXref(self):
r"""_getXmlMetadataXref(self) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__getXmlMetadataXref(self)
def _delXmlMetadata(self):
r"""_delXmlMetadata(self) -> PyObject *"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document__delXmlMetadata(self)
def _getXrefString(self, xref, compressed=0, ascii=0):
r"""_getXrefString(self, xref, compressed=0, ascii=0) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__getXrefString(self, xref, compressed, ascii)
def _getTrailerString(self, compressed=0, ascii=0):
r"""_getTrailerString(self, compressed=0, ascii=0) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__getTrailerString(self, compressed, ascii)
def _getXrefStream(self, xref):
r"""_getXrefStream(self, xref) -> PyObject *"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document__getXrefStream(self, xref)
def _updateObject(self, xref, text, page=None):
r"""_updateObject(self, xref, text, page=None) -> PyObject *"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document__updateObject(self, xref, text, page)
def _updateStream(self, xref=0, stream=None, new=0):
r"""_updateStream(self, xref=0, stream=None, new=0) -> PyObject *"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document__updateStream(self, xref, stream, new)
def _setMetadata(self, text):
r"""_setMetadata(self, text) -> PyObject *"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
return _fitz.Document__setMetadata(self, text)
def _make_page_map(self):
r"""_make_page_map(self) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
return _fitz.Document__make_page_map(self)
def fullcopyPage(self, pno, to=-1):
r"""fullcopyPage(self, pno, to=-1) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
val = _fitz.Document_fullcopyPage(self, pno, to)
self._reset_page_refs()
return val
def _move_copy_page(self, pno, nb, before, copy):
r"""_move_copy_page(self, pno, nb, before, copy) -> PyObject *"""
if self.isClosed:
raise ValueError("document closed")
val = _fitz.Document__move_copy_page(self, pno, nb, before, copy)
self._reset_page_refs()
return val
def initData(self):
if self.isEncrypted:
raise ValueError("cannot initData - document still encrypted")
self._outline = self._loadOutline()
self.metadata = dict([(k,self._getMetadata(v)) for k,v in {'format':'format', 'title':'info:Title', 'author':'info:Author','subject':'info:Subject', 'keywords':'info:Keywords','creator':'info:Creator', 'producer':'info:Producer', 'creationDate':'info:CreationDate', 'modDate':'info:ModDate'}.items()])
self.metadata['encryption'] = None if self._getMetadata('encryption')=='None' else self._getMetadata('encryption')
outline = property(lambda self: self._outline)
_getPageXref = _getPageObjNumber
def getPageFontList(self, pno):
"""Retrieve a list of fonts used on a page.
"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
if self.isPDF:
return self._getPageInfo(pno, 1)
return []
def getPageImageList(self, pno):
"""Retrieve a list of images used on a page.
"""
if self.isClosed or self.isEncrypted:
raise ValueError("document closed or encrypted")
if self.isPDF:
return self._getPageInfo(pno, 2)
return []
def copyPage(self, pno, to=-1):
"""Copy a page within a PDF document.
Args:
pno: source page number
to: put before this page, '-1' means after last page.
"""
if self.isClosed:
raise ValueError("document closed")
pageCount = len(self)
if (
pno not in range(pageCount) or
to not in range(-1, pageCount)
):
raise ValueError("bad page number(s)")
before = 1
copy = 1
if to == -1:
to = pageCount - 1
before = 0
return self._move_copy_page(pno, to, before, copy)
def movePage(self, pno, to = -1):
"""Move a page within a PDF document.
Args:
pno: source page number.
to: put before this page, '-1' means after last page.
"""
if self.isClosed:
raise ValueError("document closed")
pageCount = len(self)
if (
pno not in range(pageCount) or
to not in range(-1, pageCount)
):
raise ValueError("bad page number(s)")
before = 1
copy = 0
if to == -1:
to = pageCount - 1
before = 0
return self._move_copy_page(pno, to, before, copy)
def deletePage(self, pno = -1):
""" Delete one page from a PDF.
"""
if not self.isPDF:
raise ValueError("not a PDF")
if self.isClosed:
raise ValueError("document closed")
pageCount = self.pageCount
while pno < 0:
pno += pageCount
if not pno in range(pageCount):
raise ValueError("bad page number(s)")
old_toc = self.getToC(False)
new_toc = _toc_remove_page(old_toc, pno+1, pno+1)
self._remove_links_to(pno, pno)
self._deletePage(pno)
self.setToC(new_toc)
self._reset_page_refs()
def deletePageRange(self, from_page = -1, to_page = -1):
"""Delete pages from a PDF.
"""
if not self.isPDF:
raise ValueError("not a PDF")
if self.isClosed:
raise ValueError("document closed")
pageCount = self.pageCount # page count of document
f = from_page # first page to delete
t = to_page # last page to delete
while f < 0:
f += pageCount
while t < 0:
t += pageCount
if not f <= t < pageCount:
raise ValueError("bad page number(s)")
old_toc = self.getToC(False)
new_toc = _toc_remove_page(old_toc, f+1, t+1)
self._remove_links_to(f, t)
for i in range(t, f - 1, -1): # delete pages, last to first
self._deletePage(i)
self.setToC(new_toc)
self._reset_page_refs()
def saveIncr(self):
""" Save PDF incrementally"""
return self.save(self.name, incremental=True, encryption=PDF_ENCRYPT_KEEP)
def __repr__(self):
m = "closed " if self.isClosed else ""
if self.stream is None:
if self.name == "":
return m + "fitz.Document(<new PDF, doc# %i>)" % self._graft_id
return m + "fitz.Document('%s')" % (self.name,)
return m + "fitz.Document('%s', <memory, doc# %i>)" % (self.name, self._graft_id)
def __getitem__(self, i=0):
if type(i) is not int:
raise ValueError("bad page number(s)")
if i >= len(self):
raise IndexError("bad page number(s)")
return self.loadPage(i)
def __len__(self):
return self.pageCount
def _forget_page(self, page):
"""Remove a page from document page dict."""
pid = id(page)
if pid in self._page_refs:
self._page_refs[pid] = None
def _reset_page_refs(self):
"""Invalidate all pages in document dictionary."""
if self.isClosed:
return
for page in self._page_refs.values():
if page:
page._erase()
page = None
self._page_refs.clear()
def __del__(self):
if hasattr(self, "_reset_page_refs"):
self._reset_page_refs()
if hasattr(self, "Graftmaps"):
for gmap in self.Graftmaps:
self.Graftmaps[gmap] = None
if hasattr(self, "this") and self.thisown:
self.__swig_destroy__(self)
self.thisown = False
self.Graftmaps = {}
self.ShownPages = {}
self.stream = None
self._reset_page_refs = DUMMY
self.__swig_destroy__ = DUMMY
self.isClosed = True
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
# Register Document in _fitz:
_fitz.Document_swigregister(Document)
class Page(object):
r"""Proxy of C fz_page_s struct."""
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
def __init__(self, *args, **kwargs):
raise AttributeError("No constructor defined")
__repr__ = _swig_repr
__swig_destroy__ = _fitz.delete_Page
def bound(self):
r"""bound(self) -> PyObject *"""
CheckParent(self)
val = _fitz.Page_bound(self)
val = Rect(val)
return val
rect = property(bound, doc="page rectangle")
def run(self, dw, m):
r"""run(self, dw, m) -> PyObject *"""
CheckParent(self)
return _fitz.Page_run(self, dw, m)
def getSVGimage(self, matrix=None):
r"""Create an SVG image from the page."""
CheckParent(self)
return _fitz.Page_getSVGimage(self, matrix)
def addCaretAnnot(self, point):
r"""Add 'Caret' annot on the page."""
CheckParent(self)
if not self.parent.isPDF:
raise ValueError("not a PDF")
val = _fitz.Page_addCaretAnnot(self, point)
if not val: return
val.thisown = True
val.parent = weakref.proxy(self)
self._annot_refs[id(val)] = val
return val
def addLineAnnot(self, p1, p2):
r"""Add 'Line' annot for points p1 and p2."""
CheckParent(self)
if not self.parent.isPDF:
raise ValueError("not a PDF")
val = _fitz.Page_addLineAnnot(self, p1, p2)
if not val: return
val.thisown = True
val.parent = weakref.proxy(self)
self._annot_refs[id(val)] = val
return val
def addTextAnnot(self, point, text, icon=None):
r"""Add a 'sticky note' at position 'point'."""
CheckParent(self)
if not self.parent.isPDF:
raise ValueError("not a PDF")
val = _fitz.Page_addTextAnnot(self, point, text, icon)
if not val: return
val.thisown = True
val.parent = weakref.proxy(self)
self._annot_refs[id(val)] = val
return val
def addInkAnnot(self, list):
r"""Add a 'handwriting' as a list of list of point-likes. Each sublist forms an independent stroke."""
CheckParent(self)
if not self.parent.isPDF:
raise ValueError("not a PDF")
val = _fitz.Page_addInkAnnot(self, list)
if not val: return
val.thisown = True
val.parent = weakref.proxy(self)
self._annot_refs[id(val)] = val
return val
def addStampAnnot(self, rect, stamp=0):
r"""Add a 'rubber stamp' in a rectangle."""
CheckParent(self)
if not self.parent.isPDF:
raise ValueError("not a PDF")
val = _fitz.Page_addStampAnnot(self, rect, stamp)
if not val: return
val.thisown = True
val.parent = weakref.proxy(self)
self._annot_refs[id(val)] = val
return val
def addFileAnnot(self, point, buffer, filename, ufilename=None, desc=None, icon=None):
r"""Add a 'FileAttachment' annotation at location 'point'."""
CheckParent(self)
if not self.parent.isPDF:
raise ValueError("not a PDF")
val = _fitz.Page_addFileAnnot(self, point, buffer, filename, ufilename, desc, icon)
if not val: return
val.thisown = True
val.parent = weakref.proxy(self)
self._annot_refs[id(val)] = val
return val
def _add_text_marker(self, quads, annot_type):
r"""Add a text marker annotation."""
CheckParent(self)
if not self.parent.isPDF:
raise ValueError("not a PDF")
if not hasattr(quads, "__getitem__"):
raise ValueError("'quads' must be a sequence")
if len(quads) == 4:
if hasattr(quads[0], "__float__"):
quads = [quads]
elif hasattr(quads[0], "__getitem__") and len(quads[0]) == 2:
quads = [quads]
if type(quads) not in (list, tuple):
raise ValueError("bad argument 'quads'")
for a in quads:
if not hasattr(a, "__getitem__") or len(a) != 4:
raise ValueError("bad items in 'quads'")
val = _fitz.Page__add_text_marker(self, quads, annot_type)
if not val:
return None
val.thisown = True
val.parent = weakref.proxy(self)
self._annot_refs[id(val)] = val
return val
def addStrikeoutAnnot(self, quads):
"""Add a 'StrikeOut' annotation."""
return self._add_text_marker(quads, PDF_ANNOT_STRIKEOUT)
def addUnderlineAnnot(self, quads):
"""Add a 'Underline' annotation."""
return self._add_text_marker(quads, PDF_ANNOT_UNDERLINE)
def addSquigglyAnnot(self, quads):
"""Add a 'Squiggly' annotation."""
return self._add_text_marker(quads, PDF_ANNOT_SQUIGGLY)
def addHighlightAnnot(self, quads):
"""Add a 'Highlight' annotation."""
return self._add_text_marker(quads, PDF_ANNOT_HIGHLIGHT)
def _add_square_or_circle(self, rect, annot_type):
r"""Add a 'Square' or 'Circle' annotation."""
CheckParent(self)
if not self.parent.isPDF:
raise ValueError("not a PDF")
val = _fitz.Page__add_square_or_circle(self, rect, annot_type)
if not val: return
val.thisown = True
val.parent = weakref.proxy(self)
self._annot_refs[id(val)] = val
return val
def addRectAnnot(self, rect):
"""Add a 'Square' annotation."""
return self._add_square_or_circle(rect, PDF_ANNOT_SQUARE)
def addCircleAnnot(self, rect):
"""Add a 'Circle' annotation."""
return self._add_square_or_circle(rect, PDF_ANNOT_CIRCLE)
def _add_multiline(self, points, annot_type):
r"""Add a multiline annotation."""
CheckParent(self)
if not self.parent.isPDF:
raise ValueError("not a PDF")
val = _fitz.Page__add_multiline(self, points, annot_type)
if not val: return
val.thisown = True
val.parent = weakref.proxy(self)
self._annot_refs[id(val)] = val
return val
def addPolylineAnnot(self, points):
"""Add a 'PolyLine' annotation."""
return self._add_multiline(points, PDF_ANNOT_POLYLINE)
def addPolygonAnnot(self, points):
"""Add a 'Polygon' annotation."""
return self._add_multiline(points, PDF_ANNOT_POLYGON)
def addFreetextAnnot(self, rect, text, fontsize=12, fontname=None, text_color=None, fill_color=None, rotate=0):
r"""Add a 'FreeText' annotation in rectangle 'rect'."""
CheckParent(self)
if not self.parent.isPDF:
raise ValueError("not a PDF")
val = _fitz.Page_addFreetextAnnot(self, rect, text, fontsize, fontname, text_color, fill_color, rotate)
if not val: return
val.thisown = True
val.parent = weakref.proxy(self)
self._annot_refs[id(val)] = val
return val
#---------------------------------------------------------------------
# page addWidget
#---------------------------------------------------------------------
def addWidget(self, widget):
"""Add a form field.
"""
CheckParent(self)
doc = self.parent
if not doc.isPDF:
raise ValueError("not a PDF")
widget._validate()
annot = self._addWidget(widget)
if not annot:
return None
annot.thisown = True
annot.parent = weakref.proxy(self) # owning page object
self._annot_refs[id(annot)] = annot
widget.parent = self
widget._annot = annot
widget.update()
return annot
def _addWidget(self, Widget):
r"""_addWidget(self, Widget) -> Annot"""
return _fitz.Page__addWidget(self, Widget)
def getDisplayList(self, annots=1):
r"""getDisplayList(self, annots=1) -> DisplayList"""
CheckParent(self)
return _fitz.Page_getDisplayList(self, annots)
def setCropBox(self, rect):
r"""setCropBox(self, rect) -> PyObject *"""
CheckParent(self)
return _fitz.Page_setCropBox(self, rect)
def loadLinks(self):
r"""loadLinks(self) -> Link"""
CheckParent(self)
val = _fitz.Page_loadLinks(self)
if val:
val.thisown = True
val.parent = weakref.proxy(self) # owning page object
self._annot_refs[id(val)] = val
if self.parent.isPDF:
val.xref = self._getLinkXrefs()[0]
else:
val.xref = 0
return val
firstLink = property(loadLinks, doc="First link on page")
@property
def firstAnnot(self):
r"""Points to first annotation on page"""
CheckParent(self)
val = _fitz.Page_firstAnnot(self)
if val:
val.thisown = True
val.parent = weakref.proxy(self) # owning page object
self._annot_refs[id(val)] = val
return val
@property
def firstWidget(self):
r"""firstWidget(self) -> Annot"""
CheckParent(self)
val = _fitz.Page_firstWidget(self)
if not val:
return None
val.thisown = True
val.parent = weakref.proxy(self) # owning page object
self._annot_refs[id(val)] = val
widget = Widget()
TOOLS._fill_widget(val, widget)
val = widget
return val
def deleteLink(self, linkdict):
r"""Delete link if PDF"""
CheckParent(self)
val = _fitz.Page_deleteLink(self, linkdict)
if linkdict["xref"] == 0: return
try:
linkid = linkdict["id"]
linkobj = self._annot_refs[linkid]
linkobj._erase()
except:
pass
return val
def deleteAnnot(self, fannot):
r"""Delete annot if PDF and return next one"""
CheckParent(self)
val = _fitz.Page_deleteAnnot(self, fannot)
if val:
val.thisown = True
val.parent = weakref.proxy(self) # owning page object
val.parent._annot_refs[id(val)] = val
fannot._erase()
return val
@property
def MediaBoxSize(self):
r"""Retrieve width, height of /MediaBox."""
CheckParent(self)
val = _fitz.Page_MediaBoxSize(self)
val = Point(val)
if not bool(val):
r = self.rect
val = Point(r.width, r.height)
return val
@property
def CropBoxPosition(self):
r"""Retrieve position of /CropBox. Return (0,0) for non-PDF, or no /CropBox."""
CheckParent(self)
val = _fitz.Page_CropBoxPosition(self)
val = Point(val)
return val
@property
def rotation(self):
r"""Retrieve page rotation."""
CheckParent(self)
return _fitz.Page_rotation(self)
def setRotation(self, rot):
r"""Set page rotation to 'rot' degrees."""
CheckParent(self)
return _fitz.Page_setRotation(self, rot)
def _addAnnot_FromString(self, linklist):
r"""_addAnnot_FromString(self, linklist) -> PyObject *"""
CheckParent(self)
return _fitz.Page__addAnnot_FromString(self, linklist)
def _getLinkXrefs(self):
r"""_getLinkXrefs(self) -> PyObject *"""
return _fitz.Page__getLinkXrefs(self)
def _cleanContents(self):
r"""_cleanContents(self) -> PyObject *"""
CheckParent(self)
return _fitz.Page__cleanContents(self)
def _showPDFpage(self, fz_srcpage, overlay=1, matrix=None, xref=0, clip=None, graftmap=None, _imgname=None):
r"""_showPDFpage(self, fz_srcpage, overlay=1, matrix=None, xref=0, clip=None, graftmap=None, _imgname=None) -> PyObject *"""
return _fitz.Page__showPDFpage(self, fz_srcpage, overlay, matrix, xref, clip, graftmap, _imgname)
def _insertImage(self, filename=None, pixmap=None, stream=None, overlay=1, matrix=None, _imgname=None, _imgpointer=None):
r"""_insertImage(self, filename=None, pixmap=None, stream=None, overlay=1, matrix=None, _imgname=None, _imgpointer=None) -> PyObject *"""
return _fitz.Page__insertImage(self, filename, pixmap, stream, overlay, matrix, _imgname, _imgpointer)
def insertFont(self, fontname="helv", fontfile=None, fontbuffer=None,
set_simple=False, wmode=0, encoding=0):
doc = self.parent
if not doc:
raise ValueError("orphaned object: parent is None")
idx = 0
if fontname.startswith("/"):
fontname = fontname[1:]
font = CheckFont(self, fontname)
if font is not None: # font already in font list of page
xref = font[0] # this is the xref
if CheckFontInfo(doc, xref): # also in our document font list?
return xref # yes: we are done
# need to build the doc FontInfo entry - done via getCharWidths
doc.getCharWidths(xref)
return xref
#--------------------------------------------------------------------------
# the font is not present for this page
#--------------------------------------------------------------------------
bfname = Base14_fontdict.get(fontname.lower(), None) # BaseFont if Base-14 font
serif = 0
CJK_number = -1
CJK_list_n = ["china-t", "china-s", "japan", "korea"]
CJK_list_s = ["china-ts", "china-ss", "japan-s", "korea-s"]
try:
CJK_number = CJK_list_n.index(fontname)
serif = 0
except:
pass
if CJK_number < 0:
try:
CJK_number = CJK_list_s.index(fontname)
serif = 1
except:
pass
# install the font for the page
val = self._insertFont(fontname, bfname, fontfile, fontbuffer, set_simple, idx,
wmode, serif, encoding, CJK_number)
if not val: # did not work, error return
return val
xref = val[0] # xref of installed font
if CheckFontInfo(doc, xref): # check again: document already has this font
return xref # we are done
# need to create document font info
doc.getCharWidths(xref)
return xref
def _insertFont(self, fontname, bfname, fontfile, fontbuffer, set_simple, idx, wmode, serif, encoding, ordering):
r"""_insertFont(self, fontname, bfname, fontfile, fontbuffer, set_simple, idx, wmode, serif, encoding, ordering) -> PyObject *"""
return _fitz.Page__insertFont(self, fontname, bfname, fontfile, fontbuffer, set_simple, idx, wmode, serif, encoding, ordering)
def _getTransformation(self):
r"""Return page transformation matrix."""
CheckParent(self)
val = _fitz.Page__getTransformation(self)
val = Matrix(val)
return val
def _getContents(self):
r"""Return list of /Contents objects as xref integers."""
CheckParent(self)
return _fitz.Page__getContents(self)
def _setContents(self, xref=0):
r"""Set the /Contents object in page definition"""
CheckParent(self)
return _fitz.Page__setContents(self, xref)
@property
def _isWrapped(self):
"""Check if /Contents is wrapped in string pair "q" / "Q".
"""
xrefs = self._getContents()
doc = self.parent
if len(xrefs) == 0:
return True
if len(xrefs) == 1:
cont = doc._getXrefStream(xrefs[0]).split()
if len(cont) < 1:
return True
if cont[0] != b"q" or cont[-1] != b"Q":
return False
return True
else:
cont = doc._getXrefStream(xrefs[0]).split()
if len(cont) < 1 or cont[0] != b"q":
return False
cont = doc._getXrefStream(xrefs[-1]).split()
if len(cont) < 1 or cont[-1] != b"Q":
return False
return True
def _wrapContents(self):
TOOLS._insert_contents(self, b"q\n", False)
TOOLS._insert_contents(self, b"\nQ", True)
def __str__(self):
CheckParent(self)
x = self.parent.name
if self.parent.stream is not None:
x = "<memory, doc# %i>" % (self.parent._graft_id,)
if x == "":
x = "<new PDF, doc# %i>" % self.parent._graft_id
return "page %s of %s" % (self.number, x)
def __repr__(self):
CheckParent(self)
x = self.parent.name
if self.parent.stream is not None:
x = "<memory, doc# %i>" % (self.parent._graft_id,)
if x == "":
x = "<new PDF, doc# %i>" % self.parent._graft_id
return "page %s of %s" % (self.number, x)
def _forget_annot(self, annot):
"""Remove an annot from reference dictionary."""
aid = id(annot)
if aid in self._annot_refs:
self._annot_refs[aid] = None
def _reset_annot_refs(self):
"""Invalidate / delete all annots of this page."""
for annot in self._annot_refs.values():
if annot:
annot._erase()
self._annot_refs.clear()
@property
def xref(self):
"""Return PDF XREF number of page."""
CheckParent(self)
return self.parent._getPageXref(self.number)[0]
def _erase(self):
self._reset_annot_refs()
try:
self.parent._forget_page(self)
except:
pass
if getattr(self, "thisown", True):
self.__swig_destroy__(self)
self.parent = None
self.thisown = False
self.number = None
def __del__(self):
self._erase()
def getFontList(self):
CheckParent(self)
return self.parent.getPageFontList(self.number)
def getImageList(self):
CheckParent(self)
return self.parent.getPageImageList(self.number)
@property
def CropBox(self):
x0 = self.CropBoxPosition.x
y0 = self.MediaBoxSize.y - self.CropBoxPosition.y - self.rect.height
x1 = x0 + self.rect.width
y1 = y0 + self.rect.height
return Rect(x0, y0, x1, y1)
@property
def MediaBox(self):
return Rect(0, 0, self.MediaBoxSize)
# Register Page in _fitz:
_fitz.Page_swigregister(Page)
class Pixmap(object):
r"""Pixmap(Colorspace, width, height, samples, alpha)
Pixmap(Colorspace, Irect, alpha)
Pixmap(Colorspace, Pixmap) - converted copy
Pixmap(filename)
Pixmap(Pixmap, alpha) - copy & add / drop alpha
Pixmap(bytearray)
Pixmap(Document, xref) - from a PDF image"""
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
x = property(_fitz.Pixmap_x_get, _fitz.Pixmap_x_set, doc=r"""x""")
y = property(_fitz.Pixmap_y_get, _fitz.Pixmap_y_set, doc=r"""y""")
w = property(_fitz.Pixmap_w_get, _fitz.Pixmap_w_set, doc=r"""w""")
h = property(_fitz.Pixmap_h_get, _fitz.Pixmap_h_set, doc=r"""h""")
n = property(_fitz.Pixmap_n_get, _fitz.Pixmap_n_set, doc=r"""n""")
xres = property(_fitz.Pixmap_xres_get, _fitz.Pixmap_xres_set, doc=r"""xres""")
yres = property(_fitz.Pixmap_yres_get, _fitz.Pixmap_yres_set, doc=r"""yres""")
__swig_destroy__ = _fitz.delete_Pixmap
def __init__(self, *args):
r"""
__init__(self, cs, bbox, alpha=0) -> Pixmap
__init__(self, cs, spix) -> Pixmap
__init__(self, spix, w, h, clip=None) -> Pixmap
__init__(self, spix, alpha=1) -> Pixmap
__init__(self, cs, w, h, samples, alpha=0) -> Pixmap
__init__(self, filename) -> Pixmap
__init__(self, imagedata) -> Pixmap
__init__(self, doc, xref) -> Pixmap
"""
_fitz.Pixmap_swiginit(self, _fitz.new_Pixmap(*args))
def shrink(self, factor):
r"""shrink(self, factor)"""
return _fitz.Pixmap_shrink(self, factor)
def gammaWith(self, gamma):
r"""gammaWith(self, gamma)"""
return _fitz.Pixmap_gammaWith(self, gamma)
def tintWith(self, black, white):
r"""tintWith(self, black, white)"""
if not self.colorspace or self.colorspace.n > 3:
print("warning: colorspace invalid for function")
return
return _fitz.Pixmap_tintWith(self, black, white)
def clearWith(self, *args):
r"""
clearWith(self)
clearWith(self, value)
clearWith(self, value, bbox)
"""
return _fitz.Pixmap_clearWith(self, *args)
def copyPixmap(self, src, bbox):
r"""copyPixmap(self, src, bbox) -> PyObject *"""
return _fitz.Pixmap_copyPixmap(self, src, bbox)
def setAlpha(self, alphavalues=None):
r"""setAlpha(self, alphavalues=None) -> PyObject *"""
return _fitz.Pixmap_setAlpha(self, alphavalues)
def _getImageData(self, format):
r"""_getImageData(self, format) -> PyObject *"""
return _fitz.Pixmap__getImageData(self, format)
def getImageData(self, output="png"):
valid_formats = {"png": 1, "pnm": 2, "pgm": 2, "ppm": 2, "pbm": 2,
"pam": 3, "tga": 4, "tpic": 4,
"psd": 5, "ps": 6}
idx = valid_formats.get(output.lower(), 1)
if self.alpha and idx in (2, 6):
raise ValueError("'%s' cannot have alpha" % output)
if self.colorspace and self.colorspace.n > 3 and idx in (1, 2, 4):
raise ValueError("unsupported colorspace for '%s'" % output)
barray = self._getImageData(idx)
return barray
def getPNGdata(self):
barray = self._getImageData(1)
return barray
def getPNGData(self):
barray = self._getImageData(1)
return barray
def _writeIMG(self, filename, format):
r"""_writeIMG(self, filename, format) -> PyObject *"""
return _fitz.Pixmap__writeIMG(self, filename, format)
def writeImage(self, filename, output=None):
valid_formats = {"png": 1, "pnm": 2, "pgm": 2, "ppm": 2, "pbm": 2,
"pam": 3, "tga": 4, "tpic": 4,
"psd": 5, "ps": 6}
if output is None:
_, ext = os.path.splitext(filename)
output = ext[1:]
idx = valid_formats.get(output.lower(), 1)
if self.alpha and idx in (2, 6):
raise ValueError("'%s' cannot have alpha" % output)
if self.colorspace and self.colorspace.n > 3 and idx in (1, 2, 4):
raise ValueError("unsupported colorspace for '%s'" % output)
return self._writeIMG(filename, idx)
def writePNG(self, filename, savealpha = -1):
return self._writeIMG(filename, 1)
def invertIRect(self, irect=None):
r"""invertIRect(self, irect=None) -> PyObject *"""
return _fitz.Pixmap_invertIRect(self, irect)
def pixel(self, x, y):
r"""Return the pixel at (x,y) as a list. Last item is the alpha if Pixmap.alpha is true."""
return _fitz.Pixmap_pixel(self, x, y)
def setPixel(self, x, y, color):
r"""Set the pixel at (x,y) to the integers in sequence 'color'."""
return _fitz.Pixmap_setPixel(self, x, y, color)
def setRect(self, irect, color):
r"""Set a rectangle to the integers in sequence 'color'."""
return _fitz.Pixmap_setRect(self, irect, color)
@property
def stride(self):
r"""stride(self) -> int"""
return _fitz.Pixmap_stride(self)
@property
def alpha(self):
r"""alpha(self) -> int"""
return _fitz.Pixmap_alpha(self)
@property
def colorspace(self):
r"""colorspace(self) -> Colorspace"""
return _fitz.Pixmap_colorspace(self)
@property
def irect(self):
r"""irect(self) -> PyObject *"""
val = _fitz.Pixmap_irect(self)
val = IRect(val)
return val
@property
def size(self):
r"""size(self) -> int"""
return _fitz.Pixmap_size(self)
@property
def samples(self):
r"""samples(self) -> PyObject *"""
return _fitz.Pixmap_samples(self)
width = w
height = h
def __len__(self):
return self.size
def __repr__(self):
if not type(self) is Pixmap: return
if self.colorspace:
return "fitz.Pixmap(%s, %s, %s)" % (self.colorspace.name, self.irect, self.alpha)
else:
return "fitz.Pixmap(%s, %s, %s)" % ('None', self.irect, self.alpha)
def __del__(self):
if not type(self) is Pixmap: return
self.__swig_destroy__(self)
# Register Pixmap in _fitz:
_fitz.Pixmap_swigregister(Pixmap)
class Colorspace(object):
r"""Proxy of C fz_colorspace_s struct."""
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
__swig_destroy__ = _fitz.delete_Colorspace
def __init__(self, type):
r"""__init__(self, type) -> Colorspace"""
_fitz.Colorspace_swiginit(self, _fitz.new_Colorspace(type))
@property
def n(self):
r"""n(self) -> PyObject *"""
return _fitz.Colorspace_n(self)
def _name(self):
r"""_name(self) -> PyObject *"""
return _fitz.Colorspace__name(self)
@property
def name(self):
if self.n == 1:
return csGRAY._name()
elif self.n == 3:
return csRGB._name()
elif self.n == 4:
return csCMYK._name()
return self._name()
def __repr__(self):
x = ("", "GRAY", "", "RGB", "CMYK")[self.n]
return "fitz.Colorspace(fitz.CS_%s) - %s" % (x, self.name)
# Register Colorspace in _fitz:
_fitz.Colorspace_swigregister(Colorspace)
class Device(object):
r"""Proxy of C DeviceWrapper struct."""
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
def __init__(self, *args):
r"""
__init__(self, pm, clip) -> Device
__init__(self, dl) -> Device
__init__(self, tp, flags=0) -> Device
"""
_fitz.Device_swiginit(self, _fitz.new_Device(*args))
__swig_destroy__ = _fitz.delete_Device
# Register Device in _fitz:
_fitz.Device_swigregister(Device)
class Outline(object):
r"""Proxy of C fz_outline_s struct."""
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
def __init__(self, *args, **kwargs):
raise AttributeError("No constructor defined")
__repr__ = _swig_repr
title = property(_fitz.Outline_title_get, doc=r"""title""")
page = property(_fitz.Outline_page_get, doc=r"""page""")
next = property(_fitz.Outline_next_get, doc=r"""next""")
down = property(_fitz.Outline_down_get, doc=r"""down""")
is_open = property(_fitz.Outline_is_open_get, doc=r"""is_open""")
@property
def uri(self):
r"""uri(self) -> PyObject *"""
val = _fitz.Outline_uri(self)
if val:
nval = "".join([c for c in val if 32 <= ord(c) <= 127])
val = nval
else:
val = ""
return val
@property
def isExternal(self):
r"""isExternal(self) -> PyObject *"""
return _fitz.Outline_isExternal(self)
isOpen = is_open
@property
def dest(self):
'''outline destination details'''
return linkDest(self, None)
__swig_destroy__ = _fitz.delete_Outline
# Register Outline in _fitz:
_fitz.Outline_swigregister(Outline)
class Annot(object):
r"""Proxy of C pdf_annot_s struct."""
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
def __init__(self, *args, **kwargs):
raise AttributeError("No constructor defined")
__repr__ = _swig_repr
__swig_destroy__ = _fitz.delete_Annot
@property
def rect(self):
r"""Rectangle containing the annot"""
CheckParent(self)
val = _fitz.Annot_rect(self)
val = Rect(val)
return val
@property
def xref(self):
r"""xref(self) -> PyObject *"""
return _fitz.Annot_xref(self)
def _getAP(self):
r"""Get contents source of a PDF annot"""
return _fitz.Annot__getAP(self)
def _setAP(self, ap, rect=0):
r"""Update contents source of a PDF annot"""
return _fitz.Annot__setAP(self, ap, rect)
def setName(self, name):
r"""Set the (icon) name"""
CheckParent(self)
return _fitz.Annot_setName(self, name)
def setRect(self, rect):
r"""Set the rectangle"""
CheckParent(self)
return _fitz.Annot_setRect(self, rect)
@property
def vertices(self):
r"""Point coordinates for various annot types"""
CheckParent(self)
return _fitz.Annot_vertices(self)
@property
def colors(self):
r"""dictionary of the annot's colors"""
CheckParent(self)
return _fitz.Annot_colors(self)
def update(self, fontsize=0, fontname=None, text_color=None, border_color=None, fill_color=None, rotate=-1):
r"""Update the appearance of an annotation."""
CheckParent(self)
val = _fitz.Annot_update(self, fontsize, fontname, text_color, border_color, fill_color, rotate)
"""
The following code fixes shortcomings of MuPDF's "pdf_update_annot"
function. Currently these are:
1. Opacity (all annots). MuPDF ignores this proprty. This requires
to add an ExtGState (extended graphics state) object in the
C code as well.
2. Dashing (all annots). MuPDF ignores this proprty.
3. Colors and font size for FreeText annotations.
4. Line end icons also for POLYGON and POLY_LINE annotations.
MuPDF only honors them for LINE annotations.
5. Always perform a "clean" for the annot, because MuPDF does not
enclose their syntax in a string pair "q ... Q", which may cause
Adobe and other readers not to display the annot.
"""
if not val is True: # skip if something went wrong
return val
def color_string(cs, code):
"""Return valid PDF color operator for a given color sequence.
"""
if cs is None or cs == "":
return b""
if hasattr(cs, "__float__") or len(cs) == 1:
app = " g\n" if code == "f" else " G\n"
elif len(cs) == 3:
app = " rg\n" if code == "f" else " RG\n"
elif len(cs) == 4:
app = " k\n" if code == "f" else " K\n"
else:
return b""
if hasattr(cs, "__len__"):
col = " ".join(map(str, cs)) + app
else:
col = "%g" % cs + app
return bytes(col, "utf8") if not fitz_py2 else col
type = self.type[0] # get the annot type
dt = self.border["dashes"] # get the dashes spec
bwidth = self.border["width"] # get border line width
stroke = self.colors["stroke"] # get the stroke color
fill = self.colors["fill"] # get the fill color
rect = None # used if we change the rect here
bfill = color_string(fill, "f")
p_ctm = self.parent._getTransformation() # page transformation matrix
imat = ~p_ctm # inverse page transf. matrix
if dt:
dashes = "[" + " ".join(map(str, dt)) + "] d\n"
dashes = dashes.encode("utf-8")
else:
dashes = None
line_end_le, line_end_ri = 0, 0 # line end codes
if self.lineEnds:
line_end_le, line_end_ri = self.lineEnds
ap = self._getAP() # get the annot operator source
ap_tab = ap.splitlines()[1:-1] # temporary remove of 'q ...Q'
ap = b"\n".join(ap_tab)
ap_updated = False # assume we did nothing
if type == PDF_ANNOT_FREE_TEXT:
CheckColor(border_color)
CheckColor(text_color)
# read and update default appearance as necessary
update_default_appearance = False
tcol, fname, fsize = TOOLS._parse_da(self)
if fname.lower() not in ("helv", "cour", "tiro", "zadb", "symb"):
fname = "Helv"
update_default_appearance = True
if fsize <= 0:
fsize = 12
update_default_appearance = True
if text_color is not None:
tcol = text_color
update_default_appearance = True
if fontname is not None:
fname = fontname
update_default_appearance = True
if fontsize > 0:
fsize = fontsize
update_default_appearance = True
da_str = ""
if len(tcol) == 3:
fmt = "{:g} {:g} {:g} rg /{f:s} {s:g} Tf"
elif len(tcol) == 1:
fmt = "{:g} g /{f:s} {s:g} Tf"
elif len(tcol) == 4:
fmt = "{:g} {:g} {:g} {:g} k /{f:s} {s:g} Tf"
da_str = fmt.format(*tcol, f=fname, s=fsize)
TOOLS._update_da(self, da_str)
if border_color is not None:
for i, item in enumerate(ap_tab):
if not item.endswith(b" w"):
continue
idx = i + 2 # step into wrong border color spec
ap_tab[i + 2] = color_string(border_color, "s")
break
if dashes is not None: # handle dashes
ap_tab.insert(0, dashes)
dashes = None
ap = b"\n".join(ap_tab) # updated AP stream
ap_updated = True
if bfill != "":
if type == PDF_ANNOT_POLYGON:
ap = ap[:-1] + bfill + b"b" # close, fill, and stroke
ap_updated = True
elif type == PDF_ANNOT_POLYLINE:
ap = ap[:-1] + bfill + b"B" # fill and stroke
ap_updated = True
# Dashes not handled by MuPDF, so we do it here.
if dashes is not None:
ap = dashes + ap
# reset dashing - only applies for LINE annots with line ends given
ap = ap.replace(b"\nS\n", b"\nS\n[] d\n", 1)
ap_updated = True
# Opacity not handled by MuPDF, so we do it here. The /ExtGState object
# "Alp0" referenced here has already been added by our C code.
if 0 <= self.opacity < 1:
ap = b"/Alp0 gs\n" + ap
ap_updated = True
#----------------------------------------------------------------------
# the following handles line end symbols for 'Polygon' and 'Polyline
#----------------------------------------------------------------------
if max(line_end_le, line_end_ri) > 0 and type in (PDF_ANNOT_POLYGON, PDF_ANNOT_POLYLINE):
le_funcs = (None, TOOLS._le_square, TOOLS._le_circle,
TOOLS._le_diamond, TOOLS._le_openarrow,
TOOLS._le_closedarrow, TOOLS._le_butt,
TOOLS._le_ropenarrow, TOOLS._le_rclosedarrow,
TOOLS._le_slash)
le_funcs_range = range(1, len(le_funcs))
d = 4 * max(1, self.border["width"])
rect = self.rect + (-d, -d, d, d)
ap_updated = True
points = self.vertices
ap = b"q\n" + ap + b"\nQ\n"
if line_end_le in le_funcs_range:
p1 = Point(points[0]) * imat
p2 = Point(points[1]) * imat
left = le_funcs[line_end_le](self, p1, p2, False)
ap += bytes(left, "utf8") if not fitz_py2 else left
if line_end_ri in le_funcs_range:
p1 = Point(points[-2]) * imat
p2 = Point(points[-1]) * imat
left = le_funcs[line_end_ri](self, p1, p2, True)
ap += bytes(left, "utf8") if not fitz_py2 else left
if ap_updated:
if rect: # rect modified here?
self.setRect(rect)
self._setAP(ap, rect = 1)
else:
self._setAP(ap, rect = 0)
# always perform a clean to wrap stream by "q" / "Q"
self._cleanContents()
return val
def setColors(self, colors):
r"""
setColors(dict)
Changes the 'stroke' and 'fill' colors of an annotation. If provided, values must be lists of up to 4 floats.
"""
CheckParent(self)
return _fitz.Annot_setColors(self, colors)
@property
def lineEnds(self):
r"""lineEnds(self) -> PyObject *"""
CheckParent(self)
return _fitz.Annot_lineEnds(self)
def setLineEnds(self, start, end):
r"""setLineEnds(self, start, end)"""
CheckParent(self)
return _fitz.Annot_setLineEnds(self, start, end)
@property
def type(self):
r"""type(self) -> PyObject *"""
CheckParent(self)
return _fitz.Annot_type(self)
@property
def opacity(self):
r"""opacity(self) -> PyObject *"""
CheckParent(self)
return _fitz.Annot_opacity(self)
def setOpacity(self, opacity):
r"""setOpacity(self, opacity)"""
CheckParent(self)
return _fitz.Annot_setOpacity(self, opacity)
def fileInfo(self):
r"""Retrieve attached file information."""
CheckParent(self)
return _fitz.Annot_fileInfo(self)
def fileGet(self):
r"""Retrieve annotation attached file content."""
CheckParent(self)
return _fitz.Annot_fileGet(self)
def fileUpd(self, buffer=None, filename=None, ufilename=None, desc=None):
r"""Update annotation attached file."""
CheckParent(self)
return _fitz.Annot_fileUpd(self, buffer, filename, ufilename, desc)
@property
def info(self):
r"""info(self) -> PyObject *"""
CheckParent(self)
return _fitz.Annot_info(self)
def setInfo(self, info):
r"""setInfo(self, info) -> PyObject *"""
CheckParent(self)
return _fitz.Annot_setInfo(self, info)
@property
def border(self):
r"""border(self) -> PyObject *"""
CheckParent(self)
return _fitz.Annot_border(self)
def setBorder(self, border):
r"""setBorder(self, border) -> PyObject *"""
CheckParent(self)
return _fitz.Annot_setBorder(self, border)
@property
def flags(self):
r"""flags(self) -> int"""
CheckParent(self)
return _fitz.Annot_flags(self)
def _cleanContents(self):
r"""_cleanContents(self) -> PyObject *"""
CheckParent(self)
return _fitz.Annot__cleanContents(self)
def setFlags(self, flags):
r"""setFlags(self, flags)"""
CheckParent(self)
return _fitz.Annot_setFlags(self, flags)
@property
def next(self):
r"""next(self) -> Annot"""
CheckParent(self)
val = _fitz.Annot_next(self)
if not val:
return None
val.thisown = True
val.parent = self.parent # copy owning page object from previous annot
val.parent._annot_refs[id(val)] = val
if val.type[0] != PDF_ANNOT_WIDGET:
return val
widget = Widget()
TOOLS._fill_widget(val, widget)
val = widget
return val
def getPixmap(self, matrix=None, colorspace=None, alpha=1):
r"""getPixmap(self, matrix=None, colorspace=None, alpha=1) -> Pixmap"""
CheckParent(self)
return _fitz.Annot_getPixmap(self, matrix, colorspace, alpha)
def _erase(self):
try:
self.parent._forget_annot(self)
except:
pass
if getattr(self, "thisown", True):
self.__swig_destroy__(self)
self.parent = None
self.thisown = False
def __str__(self):
CheckParent(self)
return "'%s' annotation on %s" % (self.type[1], str(self.parent))
def __repr__(self):
CheckParent(self)
return "'%s' annotation on %s" % (self.type[1], str(self.parent))
def __del__(self):
self._erase()
# Register Annot in _fitz:
_fitz.Annot_swigregister(Annot)
class Link(object):
r"""Proxy of C fz_link_s struct."""
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
def __init__(self, *args, **kwargs):
raise AttributeError("No constructor defined")
__repr__ = _swig_repr
__swig_destroy__ = _fitz.delete_Link
def _border(self, doc, xref):
r"""_border(self, doc, xref) -> PyObject *"""
return _fitz.Link__border(self, doc, xref)
def _setBorder(self, border, doc, xref):
r"""_setBorder(self, border, doc, xref) -> PyObject *"""
return _fitz.Link__setBorder(self, border, doc, xref)
def _colors(self, doc, xref):
r"""_colors(self, doc, xref) -> PyObject *"""
return _fitz.Link__colors(self, doc, xref)
def _setColors(self, colors, doc, xref):
r"""_setColors(self, colors, doc, xref) -> PyObject *"""
return _fitz.Link__setColors(self, colors, doc, xref)
@property
def border(self):
return self._border(self.parent.parent.this, self.xref)
def setBorder(self, border):
return self._setBorder(border, self.parent.parent.this, self.xref)
@property
def colors(self):
return self._colors(self.parent.parent.this, self.xref)
def setColors(self, colors):
return self._setColors(colors, self.parent.parent.this, self.xref)
@property
def uri(self):
r"""uri(self) -> PyObject *"""
CheckParent(self)
val = _fitz.Link_uri(self)
if not val:
val = ""
else:
nval = "".join([c for c in val if 32 <= ord(c) <= 127])
val = nval
return val
@property
def isExternal(self):
r"""isExternal(self) -> PyObject *"""
CheckParent(self)
return _fitz.Link_isExternal(self)
page = -1
@property
def dest(self):
"""Create link destination details."""
if hasattr(self, "parent") and self.parent is None:
raise ValueError("orphaned object: parent is None")
if self.parent.parent.isClosed or self.parent.parent.isEncrypted:
raise ValueError("document closed or encrypted")
doc = self.parent.parent
if self.isExternal or self.uri.startswith("#"):
uri = None
else:
uri = doc.resolveLink(self.uri)
return linkDest(self, uri)
@property
def rect(self):
r"""rect(self) -> PyObject *"""
CheckParent(self)
val = _fitz.Link_rect(self)
val = Rect(val)
return val
@property
def next(self):
r"""next(self) -> Link"""
CheckParent(self)
val = _fitz.Link_next(self)
if val:
val.thisown = True
val.parent = self.parent # copy owning page from prev link
val.parent._annot_refs[id(val)] = val
if self.xref > 0: # prev link has an xref
link_xrefs = self.parent._getLinkXrefs()
idx = link_xrefs.index(self.xref)
val.xref = link_xrefs[idx + 1]
else:
val.xref = 0
return val
def _erase(self):
try:
self.parent._forget_annot(self)
except:
pass
if getattr(self, "thisown", True):
self.__swig_destroy__(self)
self.parent = None
self.thisown = False
def __str__(self):
CheckParent(self)
return "link on " + str(self.parent)
def __repr__(self):
CheckParent(self)
return "link on " + str(self.parent)
def __del__(self):
self._erase()
# Register Link in _fitz:
_fitz.Link_swigregister(Link)
class DisplayList(object):
r"""Proxy of C fz_display_list_s struct."""
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
__swig_destroy__ = _fitz.delete_DisplayList
def __init__(self, mediabox):
r"""__init__(self, mediabox) -> DisplayList"""
_fitz.DisplayList_swiginit(self, _fitz.new_DisplayList(mediabox))
def run(self, dw, m, area):
r"""run(self, dw, m, area) -> PyObject *"""
return _fitz.DisplayList_run(self, dw, m, area)
@property
def rect(self):
r"""rect(self) -> PyObject *"""
val = _fitz.DisplayList_rect(self)
val = Rect(val)
return val
def getPixmap(self, matrix=None, colorspace=None, alpha=1, clip=None):
r"""getPixmap(self, matrix=None, colorspace=None, alpha=1, clip=None) -> Pixmap"""
return _fitz.DisplayList_getPixmap(self, matrix, colorspace, alpha, clip)
def getTextPage(self, flags=3):
r"""getTextPage(self, flags=3) -> TextPage"""
return _fitz.DisplayList_getTextPage(self, flags)
def __del__(self):
if not type(self) is DisplayList: return
self.__swig_destroy__(self)
# Register DisplayList in _fitz:
_fitz.DisplayList_swigregister(DisplayList)
class TextPage(object):
r"""Proxy of C fz_stext_page_s struct."""
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
def __init__(self, mediabox):
r"""__init__(self, mediabox) -> TextPage"""
_fitz.TextPage_swiginit(self, _fitz.new_TextPage(mediabox))
__swig_destroy__ = _fitz.delete_TextPage
def search(self, needle, hit_max=16, quads=0):
r"""search(self, needle, hit_max=16, quads=0) -> PyObject *"""
val = _fitz.TextPage_search(self, needle, hit_max, quads)
if len(val) == 0:
return val
nval = []
for v in val:
q = Quad(v)
if not quads:
nval.append(q.rect)
else:
nval.append(q)
val = nval
return val
def _getBlockList(self, list):
r"""_getBlockList(self, list) -> PyObject *"""
return _fitz.TextPage__getBlockList(self, list)
def _getImageBlock(self, blockno, list):
r"""_getImageBlock(self, blockno, list) -> PyObject *"""
return _fitz.TextPage__getImageBlock(self, blockno, list)
def _getLineList(self, blockno, list):
r"""_getLineList(self, blockno, list) -> PyObject *"""
return _fitz.TextPage__getLineList(self, blockno, list)
def _getCharList(self, blockno, lineno, list):
r"""_getCharList(self, blockno, lineno, list) -> PyObject *"""
return _fitz.TextPage__getCharList(self, blockno, lineno, list)
def _extractTextBlocks_AsList(self, lines):
r"""_extractTextBlocks_AsList(self, lines) -> PyObject *"""
return _fitz.TextPage__extractTextBlocks_AsList(self, lines)
def _extractTextWords_AsList(self, lines):
r"""_extractTextWords_AsList(self, lines) -> PyObject *"""
return _fitz.TextPage__extractTextWords_AsList(self, lines)
@property
def rect(self):
r"""rect(self) -> PyObject *"""
val = _fitz.TextPage_rect(self)
val = Rect(val)
return val
def _extractText(self, format):
r"""_extractText(self, format) -> PyObject *"""
return _fitz.TextPage__extractText(self, format)
def extractText(self):
return self._extractText(0)
def extractHTML(self):
return self._extractText(1)
def extractJSON(self):
import base64, json
val = _make_textpage_dict(self)
class b64encode(json.JSONEncoder):
def default(self,s):
if not fitz_py2 and type(s) is bytes:
return base64.b64encode(s).decode()
if type(s) is bytearray:
if fitz_py2:
return base64.b64encode(s)
else:
return base64.b64encode(s).decode()
val = json.dumps(val, separators=(",", ":"), cls=b64encode, indent=1)
return val
def extractXML(self):
return self._extractText(3)
def extractXHTML(self):
return self._extractText(4)
def extractDICT(self):
return _make_textpage_dict(self)
def extractRAWDICT(self):
return _make_textpage_dict(self, raw=True)
def __del__(self):
if self.this:
self.__swig_destroy__(self)
self.this = None
# Register TextPage in _fitz:
_fitz.TextPage_swigregister(TextPage)
class Graftmap(object):
r"""Proxy of C pdf_graft_map_s struct."""
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
__swig_destroy__ = _fitz.delete_Graftmap
def __init__(self, doc):
r"""__init__(self, doc) -> Graftmap"""
_fitz.Graftmap_swiginit(self, _fitz.new_Graftmap(doc))
def __del__(self):
self.__swig_destroy__(self)
# Register Graftmap in _fitz:
_fitz.Graftmap_swigregister(Graftmap)
class Tools(object):
r"""Proxy of C Tools struct."""
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
def gen_id(self):
r"""Return a unique positive integer."""
return _fitz.Tools_gen_id(self)
def store_shrink(self, percent):
r"""Free 'percent' of current store size."""
return _fitz.Tools_store_shrink(self, percent)
def image_profile(self, stream, keep_image=0):
r"""Determine dimension and other image data."""
return _fitz.Tools_image_profile(self, stream, keep_image)
@property
def store_size(self):
r"""Current store size."""
return _fitz.Tools_store_size(self)
@property
def store_maxsize(self):
r"""Maximum store size."""
return _fitz.Tools_store_maxsize(self)
@property
def fitz_config(self):
r"""Show configuration data."""
return _fitz.Tools_fitz_config(self)
def glyph_cache_empty(self):
r"""Empty the glyph cache."""
return _fitz.Tools_glyph_cache_empty(self)
def _fill_widget(self, annot, widget):
r"""_fill_widget(self, annot, widget) -> PyObject *"""
val = _fitz.Tools__fill_widget(self, annot, widget)
widget.rect = Rect(annot.rect)
widget.xref = annot.xref
widget.parent = annot.parent
widget._annot = annot # backpointer to annot object
return val
def _save_widget(self, annot, widget):
r"""_save_widget(self, annot, widget) -> PyObject *"""
return _fitz.Tools__save_widget(self, annot, widget)
def _parse_da(self, annot):
r"""_parse_da(self, annot) -> PyObject *"""
val = _fitz.Tools__parse_da(self, annot)
if not val or val == "":
retun ((0,), "", 0)
font = "Helv"
fsize = 12
col = (0, 0, 0)
dat = val.split() # split on any whitespace
for i, item in enumerate(dat):
if item == "Tf":
font = dat[i - 2][1:]
fsize = float(dat[i - 1])
dat[i] = dat[i-1] = dat[i-2] = ""
continue
if item == "g": # unicolor text
col = [(float(dat[i - 1]))]
dat[i] = dat[i-1] = ""
continue
if item == "rg": # RGB colored text
col = [float(f) for f in dat[i - 3:i]]
dat[i] = dat[i-1] = dat[i-2] = dat[i-3] = ""
continue
if item == "k": # CMYK colored text
col = [float(f) for f in dat[i - 4:i]]
dat[i] = dat[i-1] = dat[i-2] = dat[i-3] = dat[i-4] = ""
continue
val = (col, font, fsize)
return val
def _update_da(self, annot, da_str):
r"""_update_da(self, annot, da_str) -> PyObject *"""
return _fitz.Tools__update_da(self, annot, da_str)
def _insert_contents(self, page, newcont, overlay=1):
r"""Make a new /Contents object for a page from bytes, and return its xref."""
return _fitz.Tools__insert_contents(self, page, newcont, overlay)
def mupdf_version(self):
r"""Return compiled MuPDF version."""
return _fitz.Tools_mupdf_version(self)
property
def mupdf_warnings(self):
r"""mupdf_warnings(self) -> PyObject *"""
val = _fitz.Tools_mupdf_warnings(self)
val = "\n".join(val)
return val
def reset_mupdf_warnings(self):
r"""Reset MuPDF warnings."""
return _fitz.Tools_reset_mupdf_warnings(self)
def _transform_rect(self, rect, matrix):
r"""Transform rectangle with matrix."""
return _fitz.Tools__transform_rect(self, rect, matrix)
def _intersect_rect(self, r1, r2):
r"""Intersect two rectangles."""
return _fitz.Tools__intersect_rect(self, r1, r2)
def _include_point_in_rect(self, r, p):
r"""Include point in a rect."""
return _fitz.Tools__include_point_in_rect(self, r, p)
def _transform_point(self, point, matrix):
r"""Transform point with matrix."""
return _fitz.Tools__transform_point(self, point, matrix)
def _union_rect(self, r1, r2):
r"""Replace r1 with smallest rect containing both."""
return _fitz.Tools__union_rect(self, r1, r2)
def _concat_matrix(self, m1, m2):
r"""Concatenate matrices m1, m2."""
return _fitz.Tools__concat_matrix(self, m1, m2)
def _invert_matrix(self, matrix):
r"""Invert a matrix."""
return _fitz.Tools__invert_matrix(self, matrix)
def measure_string(self, text, fontname, fontsize, encoding=0):
r"""Measure length of a string for a Base14 font."""
return _fitz.Tools_measure_string(self, text, fontname, fontsize, encoding)
def _hor_matrix(self, C, P):
"""Make a line horizontal.
Args:
C, P: points defining a line.
Notes:
Given two points C, P calculate matrix that rotates and translates the
vector C -> P such that C is mapped to Point(0, 0), and P to some point
on the x axis maintaining the distance between the points.
If C == P, the null matrix will result.
Returns:
Matrix m such that C * m = (0, 0) and (P * m).y = 0.
"""
S = (P - C).unit # unit vector C -> P
return Matrix(1, 0, 0, 1, -C.x, -C.y) * Matrix(S.x, -S.y, S.y, S.x, 0, 0)
def _angle_between(self, C, P, Q):
"""Compute the angle between two lines.
Args:
C, P, Q: points defining two lines which cross in P.
Notes:
Compute sine and cosine of the angle between two lines crossing in
point P.
Returns:
(cos(alfa), sin(alfa)) of the angle alfa between the lines.
"""
m = self._hor_matrix(P, Q)
return (C * m).unit
def _le_annot_parms(self, annot, p1, p2):
"""Get common parameters for making line end symbols.
"""
w = annot.border["width"] # line width
sc = annot.colors["stroke"] # stroke color
if not sc: sc = (0,0,0)
scol = " ".join(map(str, sc)) + " RG\n"
fc = annot.colors["fill"] # fill color
if not fc: fc = (0,0,0)
fcol = " ".join(map(str, fc)) + " rg\n"
nr = annot.rect
np1 = p1 # point coord relative to annot rect
np2 = p2 # point coord relative to annot rect
m = self._hor_matrix(np1, np2) # matrix makes the line horizontal
im = ~m # inverted matrix
L = np1 * m # converted start (left) point
R = np2 * m # converted end (right) point
if 0 <= annot.opacity < 1:
opacity = "/Alp0 gs\n"
else:
opacity = ""
return m, im, L, R, w, scol, fcol, opacity
def _oval_string(self, p1, p2, p3, p4):
"""Return /AP string defining an oval within a 4-polygon provided as points
"""
def bezier(p, q, r):
f = "%f %f %f %f %f %f c\n"
return f % (p.x, p.y, q.x, q.y, r.x, r.y)
kappa = 0.55228474983 # magic number
ml = p1 + (p4 - p1) * 0.5 # middle points ...
mo = p1 + (p2 - p1) * 0.5 # for each ...
mr = p2 + (p3 - p2) * 0.5 # polygon ...
mu = p4 + (p3 - p4) * 0.5 # side
ol1 = ml + (p1 - ml) * kappa # the 8 bezier
ol2 = mo + (p1 - mo) * kappa # helper points
or1 = mo + (p2 - mo) * kappa
or2 = mr + (p2 - mr) * kappa
ur1 = mr + (p3 - mr) * kappa
ur2 = mu + (p3 - mu) * kappa
ul1 = mu + (p4 - mu) * kappa
ul2 = ml + (p4 - ml) * kappa
# now draw, starting from middle point of left side
ap = "%f %f m\n" % (ml.x, ml.y)
ap += bezier(ol1, ol2, mo)
ap += bezier(or1, or2, mr)
ap += bezier(ur1, ur2, mu)
ap += bezier(ul1, ul2, ml)
return ap
def _le_diamond(self, annot, p1, p2, lr):
"""Make stream commands for diamond line end symbol. "lr" denotes left (False) or right point.
"""
m, im, L, R, w, scol, fcol, opacity = self._le_annot_parms(annot, p1, p2)
shift = 2.5 # 2*shift*width = length of square edge
d = shift * max(1, w)
M = R - (d/2., 0) if lr else L + (d/2., 0)
r = Rect(M, M) + (-d, -d, d, d) # the square
# the square makes line longer by (2*shift - 1)*width
p = (r.tl + (r.bl - r.tl) * 0.5) * im
ap = "q\n%s%f %f m\n" % (opacity, p.x, p.y)
p = (r.tl + (r.tr - r.tl) * 0.5) * im
ap += "%f %f l\n" % (p.x, p.y)
p = (r.tr + (r.br - r.tr) * 0.5) * im
ap += "%f %f l\n" % (p.x, p.y)
p = (r.br + (r.bl - r.br) * 0.5) * im
ap += "%f %f l\n" % (p.x, p.y)
ap += "%g w\n" % w
ap += scol + fcol + "b\nQ\n"
return ap
def _le_square(self, annot, p1, p2, lr):
"""Make stream commands for square line end symbol. "lr" denotes left (False) or right point.
"""
m, im, L, R, w, scol, fcol, opacity = self._le_annot_parms(annot, p1, p2)
shift = 2.5 # 2*shift*width = length of square edge
d = shift * max(1, w)
M = R - (d/2., 0) if lr else L + (d/2., 0)
r = Rect(M, M) + (-d, -d, d, d) # the square
# the square makes line longer by (2*shift - 1)*width
p = r.tl * im
ap = "q\n%s%f %f m\n" % (opacity, p.x, p.y)
p = r.tr * im
ap += "%f %f l\n" % (p.x, p.y)
p = r.br * im
ap += "%f %f l\n" % (p.x, p.y)
p = r.bl * im
ap += "%f %f l\n" % (p.x, p.y)
ap += "%g w\n" % w
ap += scol + fcol + "b\nQ\n"
return ap
def _le_circle(self, annot, p1, p2, lr):
"""Make stream commands for circle line end symbol. "lr" denotes left (False) or right point.
"""
m, im, L, R, w, scol, fcol, opacity = self._le_annot_parms(annot, p1, p2)
shift = 2.5 # 2*shift*width = length of square edge
d = shift * max(1, w)
M = R - (d/2., 0) if lr else L + (d/2., 0)
r = Rect(M, M) + (-d, -d, d, d) # the square
ap = "q\n" + opacity + self._oval_string(r.tl * im, r.tr * im, r.br * im, r.bl * im)
ap += "%g w\n" % w
ap += scol + fcol + "b\nQ\n"
return ap
def _le_butt(self, annot, p1, p2, lr):
"""Make stream commands for butt line end symbol. "lr" denotes left (False) or right point.
"""
m, im, L, R, w, scol, fcol, opacity = self._le_annot_parms(annot, p1, p2)
shift = 3
d = shift * max(1, w)
M = R if lr else L
top = (M + (0, -d/2.)) * im
bot = (M + (0, d/2.)) * im
ap = "\nq\n%s%f %f m\n" % (opacity, top.x, top.y)
ap += "%f %f l\n" % (bot.x, bot.y)
ap += "%g w\n" % w
ap += scol + "s\nQ\n"
return ap
def _le_slash(self, annot, p1, p2, lr):
"""Make stream commands for slash line end symbol. "lr" denotes left (False) or right point.
"""
m, im, L, R, w, scol, fcol, opacity = self._le_annot_parms(annot, p1, p2)
rw = 1.1547 * max(1, w) * 1.0 # makes rect diagonal a 30 deg inclination
M = R if lr else L
r = Rect(M.x - rw, M.y - 2 * w, M.x + rw, M.y + 2 * w)
top = r.tl * im
bot = r.br * im
ap = "\nq\n%s%f %f m\n" % (opacity, top.x, top.y)
ap += "%f %f l\n" % (bot.x, bot.y)
ap += "%g w\n" % w
ap += scol + "s\nQ\n"
return ap
def _le_openarrow(self, annot, p1, p2, lr):
"""Make stream commands for open arrow line end symbol. "lr" denotes left (False) or right point.
"""
m, im, L, R, w, scol, fcol, opacity = self._le_annot_parms(annot, p1, p2)
shift = 2.5
d = shift * max(1, w)
p2 = R + (d/2., 0) if lr else L - (d/2., 0)
p1 = p2 + (-2*d, -d) if lr else p2 + (2*d, -d)
p3 = p2 + (-2*d, d) if lr else p2 + (2*d, d)
p1 *= im
p2 *= im
p3 *= im
ap = "\nq\n%s%f %f m\n" % (opacity, p1.x, p1.y)
ap += "%f %f l\n" % (p2.x, p2.y)
ap += "%f %f l\n" % (p3.x, p3.y)
ap += "%g w\n" % w
ap += scol + "S\nQ\n"
return ap
def _le_closedarrow(self, annot, p1, p2, lr):
"""Make stream commands for closed arrow line end symbol. "lr" denotes left (False) or right point.
"""
m, im, L, R, w, scol, fcol, opacity = self._le_annot_parms(annot, p1, p2)
shift = 2.5
d = shift * max(1, w)
p2 = R + (d/2., 0) if lr else L - (d/2., 0)
p1 = p2 + (-2*d, -d) if lr else p2 + (2*d, -d)
p3 = p2 + (-2*d, d) if lr else p2 + (2*d, d)
p1 *= im
p2 *= im
p3 *= im
ap = "\nq\n%s%f %f m\n" % (opacity, p1.x, p1.y)
ap += "%f %f l\n" % (p2.x, p2.y)
ap += "%f %f l\n" % (p3.x, p3.y)
ap += "%g w\n" % w
ap += scol + fcol + "b\nQ\n"
return ap
def _le_ropenarrow(self, annot, p1, p2, lr):
"""Make stream commands for right open arrow line end symbol. "lr" denotes left (False) or right point.
"""
m, im, L, R, w, scol, fcol, opacity = self._le_annot_parms(annot, p1, p2)
shift = 2.5
d = shift * max(1, w)
p2 = R - (d/3., 0) if lr else L + (d/3., 0)
p1 = p2 + (2*d, -d) if lr else p2 + (-2*d, -d)
p3 = p2 + (2*d, d) if lr else p2 + (-2*d, d)
p1 *= im
p2 *= im
p3 *= im
ap = "\nq\n%s%f %f m\n" % (opacity, p1.x, p1.y)
ap += "%f %f l\n" % (p2.x, p2.y)
ap += "%f %f l\n" % (p3.x, p3.y)
ap += "%g w\n" % w
ap += scol + fcol + "S\nQ\n"
return ap
def _le_rclosedarrow(self, annot, p1, p2, lr):
"""Make stream commands for right closed arrow line end symbol. "lr" denotes left (False) or right point.
"""
m, im, L, R, w, scol, fcol, opacity = self._le_annot_parms(annot, p1, p2)
shift = 2.5
d = shift * max(1, w)
p2 = R - (2*d, 0) if lr else L + (2*d, 0)
p1 = p2 + (2*d, -d) if lr else p2 + (-2*d, -d)
p3 = p2 + (2*d, d) if lr else p2 + (-2*d, d)
p1 *= im
p2 *= im
p3 *= im
ap = "\nq\n%s%f %f m\n" % (opacity, p1.x, p1.y)
ap += "%f %f l\n" % (p2.x, p2.y)
ap += "%f %f l\n" % (p3.x, p3.y)
ap += "%g w\n" % w
ap += scol + fcol + "b\nQ\n"
return ap
def __init__(self):
r"""__init__(self) -> Tools"""
_fitz.Tools_swiginit(self, _fitz.new_Tools())
__swig_destroy__ = _fitz.delete_Tools
# Register Tools in _fitz:
_fitz.Tools_swigregister(Tools)