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.
5370 lines
165 KiB
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)
|
|
|
|
|
|
|