forked from pool/python-djvulibre
Matej Cepl
bd23d775f0
OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-djvulibre?expand=0&rev=45
5603 lines
176 KiB
Diff
5603 lines
176 KiB
Diff
From 2e01d632aeddfff1ca71fa8c90c4e441781236cc Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= <mcepl@cepl.eu>
|
|
Date: Mon, 26 Jul 2021 09:24:10 +0200
|
|
Subject: [PATCH] Switch to src/ project layout to simplify testing.
|
|
|
|
---
|
|
{djvu => src}/__init__.py | 0
|
|
{djvu => src}/common.pxi | 0
|
|
{djvu => src}/const.py | 0
|
|
{djvu => src}/decode.pxd | 0
|
|
{djvu => src}/decode.pyx | 0
|
|
{djvu => src}/dllpath.py | 0
|
|
{djvu => src}/sexpr.pxd | 0
|
|
{djvu => src}/sexpr.pyx | 0
|
|
MANIFEST.in | 2
|
|
djvu/__init__.py | 19
|
|
djvu/common.pxi | 142 --
|
|
djvu/const.py | 263 ----
|
|
djvu/decode.pxd | 355 -----
|
|
djvu/decode.pyx | 3425 -------------------------------------------------------
|
|
djvu/dllpath.py | 78 -
|
|
djvu/sexpr.pxd | 32
|
|
djvu/sexpr.pyx | 1091 -----------------
|
|
setup.cfg | 5
|
|
setup.py | 39
|
|
11 files changed, 23 insertions(+), 5428 deletions(-)
|
|
rename {djvu => src}/__init__.py (100%)
|
|
rename {djvu => src}/common.pxi (100%)
|
|
rename {djvu => src}/const.py (100%)
|
|
rename {djvu => src}/decode.pxd (100%)
|
|
rename {djvu => src}/decode.pyx (100%)
|
|
rename {djvu => src}/dllpath.py (100%)
|
|
rename {djvu => src}/sexpr.pxd (100%)
|
|
rename {djvu => src}/sexpr.pyx (100%)
|
|
|
|
--- a/MANIFEST.in
|
|
+++ b/MANIFEST.in
|
|
@@ -13,7 +13,7 @@ include pyproject.toml
|
|
|
|
include examples/*
|
|
|
|
-recursive-include djvu *.py *.pxi *.pxd *.pyx
|
|
+recursive-include src/djvu *.py *.pxi *.pxd *.pyx
|
|
|
|
recursive-include tests *.py Makefile *.tex *.djvu
|
|
|
|
--- a/djvu/__init__.py
|
|
+++ /dev/null
|
|
@@ -1,19 +0,0 @@
|
|
-# encoding=UTF-8
|
|
-
|
|
-# Copyright © 2015-2021 Jakub Wilk <jwilk@jwilk.net>
|
|
-#
|
|
-# This file is part of python-djvulibre.
|
|
-#
|
|
-# python-djvulibre is free software; you can redistribute it and/or modify it
|
|
-# under the terms of the GNU General Public License version 2 as published by
|
|
-# the Free Software Foundation.
|
|
-#
|
|
-# python-djvulibre is distributed in the hope that it will be useful, but
|
|
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
-# more details.
|
|
-
|
|
-type(b'') # Python >= 2.6 is required
|
|
-type(u'') # Python 2.X or >= 3.3 is required
|
|
-
|
|
-# vim:ts=4 sts=4 sts=4 sw=4 et
|
|
--- a/djvu/common.pxi
|
|
+++ /dev/null
|
|
@@ -1,142 +0,0 @@
|
|
-# Copyright © 2008-2018 Jakub Wilk <jwilk@jwilk.net>
|
|
-#
|
|
-# This file is part of python-djvulibre.
|
|
-#
|
|
-# python-djvulibre is free software; you can redistribute it and/or modify it
|
|
-# under the terms of the GNU General Public License version 2 as published by
|
|
-# the Free Software Foundation.
|
|
-#
|
|
-# python-djvulibre is distributed in the hope that it will be useful, but
|
|
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
-# more details.
|
|
-
|
|
-include 'config.pxi'
|
|
-
|
|
-# C library:
|
|
-
|
|
-from libc.stdlib cimport free
|
|
-from libc.string cimport strlen
|
|
-
|
|
-# Python memory handling:
|
|
-
|
|
-from cpython.mem cimport PyMem_Malloc as py_malloc
|
|
-from cpython.mem cimport PyMem_Free as py_free
|
|
-
|
|
-# Python numbers:
|
|
-
|
|
-from cpython cimport (
|
|
- PyInt_Check as is_short_int,
|
|
- PyLong_Check as is_long_int,
|
|
-)
|
|
-cdef int is_int(object o):
|
|
- return is_short_int(o) or is_long_int(o)
|
|
-
|
|
-from cpython cimport (
|
|
- PyNumber_Check as is_number,
|
|
- PyFloat_Check as is_float,
|
|
-)
|
|
-
|
|
-IF PY3K:
|
|
- from cpython cimport PyNumber_Long as int
|
|
-ELSE:
|
|
- from cpython cimport PyNumber_Int as int
|
|
- from cpython cimport PyNumber_Long as long
|
|
-
|
|
-# Python strings:
|
|
-
|
|
-from cpython cimport (
|
|
- PyUnicode_Check as is_unicode,
|
|
- PyString_Check as is_string,
|
|
- PyBytes_Check as is_bytes,
|
|
-)
|
|
-
|
|
-from cpython cimport (
|
|
- PyUnicode_AsUTF8String as encode_utf8,
|
|
- PyUnicode_DecodeUTF8 as decode_utf8_ex,
|
|
- PyBytes_AsStringAndSize as bytes_to_charp,
|
|
- PyBytes_FromStringAndSize as charp_to_bytes,
|
|
-)
|
|
-IF PY3K:
|
|
- cdef extern from 'Python.h':
|
|
- object charp_to_string 'PyUnicode_FromString'(char *v)
|
|
-ELSE:
|
|
- from cpython cimport PyString_FromString as charp_to_string
|
|
-
|
|
-cdef object decode_utf8(const char *s):
|
|
- return decode_utf8_ex(s, strlen(s), NULL)
|
|
-
|
|
-cdef extern from 'Python.h':
|
|
- int buffer_to_writable_memory 'PyObject_AsWriteBuffer'(object, void **, Py_ssize_t *)
|
|
-
|
|
-# Python booleans:
|
|
-
|
|
-from cpython cimport PyBool_FromLong as bool
|
|
-
|
|
-# Python pointer->integer conversion:
|
|
-
|
|
-from cpython cimport PyLong_FromVoidPtr as voidp_to_int
|
|
-
|
|
-# Python files:
|
|
-
|
|
-from libc.stdio cimport FILE
|
|
-
|
|
-# Python lists:
|
|
-
|
|
-from cpython cimport PyList_Append as list_append
|
|
-
|
|
-# Python rich comparison:
|
|
-
|
|
-from cpython cimport PyObject_RichCompare as richcmp
|
|
-
|
|
-# Python slices:
|
|
-
|
|
-cdef extern from 'Python.h':
|
|
- int is_slice 'PySlice_Check'(object)
|
|
-
|
|
-# Python threads:
|
|
-
|
|
-from cpython cimport (
|
|
- PyThread_type_lock as Lock,
|
|
- PyThread_allocate_lock as allocate_lock,
|
|
- PyThread_free_lock as free_lock,
|
|
- PyThread_acquire_lock as acquire_lock,
|
|
- PyThread_release_lock as release_lock,
|
|
- WAIT_LOCK,
|
|
- NOWAIT_LOCK,
|
|
-)
|
|
-
|
|
-# Python type checks:
|
|
-
|
|
-cdef extern from 'object.h':
|
|
- ctypedef struct PyTypeObject:
|
|
- const char *tp_name
|
|
-
|
|
-from cpython cimport PyObject
|
|
-from cpython cimport PyObject_TypeCheck as _typecheck
|
|
-
|
|
-cdef object type(object o):
|
|
- return <object>((<PyObject*>o).ob_type)
|
|
-
|
|
-IF PY3K:
|
|
- cdef object get_type_name(object type):
|
|
- return decode_utf8((<PyTypeObject*>type).tp_name)
|
|
-ELSE:
|
|
- cdef const char* get_type_name(object type):
|
|
- return (<PyTypeObject*>type).tp_name
|
|
-
|
|
-cdef int typecheck(object o, object type):
|
|
- return _typecheck(o, <PyTypeObject*> type)
|
|
-
|
|
-# Python exceptions:
|
|
-
|
|
-cdef void raise_instantiation_error(object cls) except *:
|
|
- raise TypeError('cannot create \'{tp}\' instances'.format(tp=get_type_name(cls)))
|
|
-
|
|
-# Cython before 0.25 didn't support cdef classes deriving from Exception out of
|
|
-# the box: https://github.com/cython/cython/issues/1416
|
|
-cdef extern from 'pyerrors.h':
|
|
- ctypedef class __builtin__.Exception [object PyBaseExceptionObject]:
|
|
- pass
|
|
-
|
|
-# vim:ts=4 sts=4 sw=4 et ft=pyrex
|
|
--- a/djvu/const.py
|
|
+++ /dev/null
|
|
@@ -1,263 +0,0 @@
|
|
-# encoding=UTF-8
|
|
-
|
|
-# Copyright © 2008-2015 Jakub Wilk <jwilk@jwilk.net>
|
|
-#
|
|
-# This file is part of python-djvulibre.
|
|
-#
|
|
-# python-djvulibre is free software; you can redistribute it and/or modify it
|
|
-# under the terms of the GNU General Public License version 2 as published by
|
|
-# the Free Software Foundation.
|
|
-#
|
|
-# python-djvulibre is distributed in the hope that it will be useful, but
|
|
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
-# more details.
|
|
-
|
|
-'''DjVuLibre bindings: various constants.'''
|
|
-
|
|
-import djvu.sexpr
|
|
-
|
|
-EMPTY_LIST = djvu.sexpr.Expression([])
|
|
-EMPTY_OUTLINE = djvu.sexpr.Expression([djvu.sexpr.Symbol('bookmarks')])
|
|
-
|
|
-METADATA_BIBTEX_KEYS = frozenset(djvu.sexpr.Symbol(x) for x in '''
|
|
-address
|
|
-annote
|
|
-author
|
|
-booktitle
|
|
-chapter
|
|
-crossref
|
|
-edition
|
|
-editor
|
|
-howpublished
|
|
-institution
|
|
-journal
|
|
-key
|
|
-month
|
|
-note
|
|
-number
|
|
-organization
|
|
-pages
|
|
-publisher
|
|
-school
|
|
-series
|
|
-title
|
|
-type
|
|
-volume
|
|
-year'''.split())
|
|
-# Retrieved from <https://www.ctan.org/pkg/bibtex>
|
|
-
|
|
-METADATA_PDFINFO_KEYS = frozenset(djvu.sexpr.Symbol(x) for x in '''
|
|
-Author
|
|
-CreationDate
|
|
-Creator
|
|
-Keywords
|
|
-ModDate
|
|
-Producer
|
|
-Subject
|
|
-Title
|
|
-Trapped'''.split())
|
|
-# Retrieved from the PDF specification
|
|
-
|
|
-METADATA_KEYS = METADATA_BIBTEX_KEYS | METADATA_PDFINFO_KEYS
|
|
-
|
|
-class TextZoneType(djvu.sexpr.Symbol):
|
|
-
|
|
- '''
|
|
- A type of a text zone. You can compare text zone types with the < operator.
|
|
-
|
|
- To create objects of this class, use the get_text_zone_type() function.
|
|
- '''
|
|
-
|
|
- __cache = {}
|
|
-
|
|
- @classmethod
|
|
- def from_symbol(cls, symbol):
|
|
- return cls.__cache[symbol]
|
|
-
|
|
- def __new__(cls, value, rank):
|
|
- self = djvu.sexpr.Symbol.__new__(cls, value)
|
|
- TextZoneType.__cache[self] = self
|
|
- return self
|
|
-
|
|
- def __init__(self, value, rank):
|
|
- self.__rank = rank
|
|
-
|
|
- def __lt__(self, other):
|
|
- if not isinstance(other, TextZoneType):
|
|
- raise TypeError('cannot compare text zone type with other object')
|
|
- return self.__rank < other.__rank
|
|
-
|
|
- def __le__(self, other):
|
|
- if not isinstance(other, TextZoneType):
|
|
- raise TypeError('cannot compare text zone type with other object')
|
|
- return self.__rank <= other.__rank
|
|
-
|
|
- def __gt__(self, other):
|
|
- if not isinstance(other, TextZoneType):
|
|
- raise TypeError('cannot compare text zone type with other object')
|
|
- return self.__rank > other.__rank
|
|
-
|
|
- def __ge__(self, other):
|
|
- if not isinstance(other, TextZoneType):
|
|
- raise TypeError('cannot compare text zone type with other object')
|
|
- return self.__rank >= other.__rank
|
|
-
|
|
- def __repr__(self):
|
|
- return '<{mod}.{cls}: {name}>'.format(
|
|
- mod=self.__module__,
|
|
- cls=type(self).__name__,
|
|
- name=self
|
|
- )
|
|
-
|
|
-TEXT_ZONE_PAGE = TextZoneType('page', 7)
|
|
-TEXT_ZONE_COLUMN = TextZoneType('column', 6)
|
|
-TEXT_ZONE_REGION = TextZoneType('region', 5)
|
|
-TEXT_ZONE_PARAGRAPH = TextZoneType('para', 4)
|
|
-TEXT_ZONE_LINE = TextZoneType('line', 3)
|
|
-TEXT_ZONE_WORD = TextZoneType('word', 2)
|
|
-TEXT_ZONE_CHARACTER = TextZoneType('char', 1)
|
|
-
|
|
-def get_text_zone_type(symbol):
|
|
- return TextZoneType.from_symbol(symbol)
|
|
-
|
|
-TEXT_ZONE_SEPARATORS = {
|
|
- TEXT_ZONE_PAGE: '\f', # Form Feed (FF)
|
|
- TEXT_ZONE_COLUMN: '\v', # Vertical tab (VT, LINE TABULATION)
|
|
- TEXT_ZONE_REGION: '\035', # Group Separator (GS, INFORMATION SEPARATOR THREE)
|
|
- TEXT_ZONE_PARAGRAPH: '\037', # Unit Separator (US, INFORMATION SEPARATOR ONE)
|
|
- TEXT_ZONE_LINE: '\n', # Line Feed (LF)
|
|
- TEXT_ZONE_WORD: ' ', # space
|
|
- TEXT_ZONE_CHARACTER: ''
|
|
-}
|
|
-
|
|
-# 8.3.4.2 Maparea (overprinted annotations)
|
|
-ANNOTATION_MAPAREA = djvu.sexpr.Symbol('maparea')
|
|
-
|
|
-# 8.3.4.2 Maparea (overprinted annotations):
|
|
-MAPAREA_SHAPE_RECTANGLE = djvu.sexpr.Symbol('rect')
|
|
-MAPAREA_SHAPE_OVAL = djvu.sexpr.Symbol('oval')
|
|
-MAPAREA_SHAPE_POLYGON = djvu.sexpr.Symbol('poly')
|
|
-MAPAREA_SHAPE_LINE = djvu.sexpr.Symbol('line')
|
|
-MAPAREA_SHAPE_TEXT = djvu.sexpr.Symbol('text')
|
|
-
|
|
-MAPAREA_URI = MAPAREA_URL = djvu.sexpr.Symbol('url')
|
|
-
|
|
-# 8.3.4.2.3.1.1 Border type:
|
|
-MAPAREA_BORDER_NONE = djvu.sexpr.Symbol('none')
|
|
-MAPAREA_BORDER_XOR = djvu.sexpr.Symbol('xor')
|
|
-MAPAREA_BORDER_SOLID_COLOR = djvu.sexpr.Symbol('border')
|
|
-
|
|
-# 8.3.4.2.3.1.1 Border type:
|
|
-MAPAREA_BORDER_SHADOW_IN = djvu.sexpr.Symbol('shadow_in')
|
|
-MAPAREA_BORDER_SHADOW_OUT = djvu.sexpr.Symbol('shadow_out')
|
|
-MAPAREA_BORDER_ETCHED_IN = djvu.sexpr.Symbol('shadow_ein')
|
|
-MAPAREA_BORDER_ETCHED_OUT = djvu.sexpr.Symbol('shadow_eout')
|
|
-MAPAREA_SHADOW_BORDERS = (MAPAREA_BORDER_SHADOW_IN, MAPAREA_BORDER_SHADOW_OUT, MAPAREA_BORDER_ETCHED_IN, MAPAREA_BORDER_ETCHED_OUT)
|
|
-MAPAREA_SHADOW_BORDER_MIN_WIDTH = 1
|
|
-MAPAREA_SHADOW_BORDER_MAX_WIDTH = 32
|
|
-
|
|
-# 8.3.4.2.3.1.2 Border always visible
|
|
-MAPAREA_BORDER_ALWAYS_VISIBLE = djvu.sexpr.Symbol('border_avis')
|
|
-
|
|
-# 8.3.4.2.3.1.3 Highlight color and opacity:
|
|
-MAPAREA_HIGHLIGHT_COLOR = djvu.sexpr.Symbol('hilite')
|
|
-MAPAREA_OPACITY = djvu.sexpr.Symbol('opacity')
|
|
-MAPAREA_OPACITY_MIN = 0
|
|
-MAPAREA_OPACITY_DEFAULT = 50
|
|
-MAPAREA_OPACITY_MAX = 100
|
|
-
|
|
-# 8.3.4.2.3.1.4 Line and Text parameters:
|
|
-MAPAREA_ARROW = djvu.sexpr.Symbol('arrow')
|
|
-MAPAREA_LINE_WIDTH = djvu.sexpr.Symbol('width')
|
|
-MAPAREA_LINE_COLOR = djvu.sexpr.Symbol('lineclr')
|
|
-MAPAREA_LINE_MIN_WIDTH = 1
|
|
-MAPAREA_LINE_COLOR_DEFAULT = '#000000'
|
|
-
|
|
-# 8.3.4.2.3.1.4 Line and Text parameters:
|
|
-MAPAREA_BACKGROUND_COLOR = djvu.sexpr.Symbol('backclr')
|
|
-MAPAREA_TEXT_COLOR = djvu.sexpr.Symbol('textclr')
|
|
-MAPAREA_PUSHPIN = djvu.sexpr.Symbol('pushpin')
|
|
-MAPAREA_TEXT_COLOR_DEFAULT = '#000000'
|
|
-
|
|
-# 8.3.4.1 Initial Document View :
|
|
-ANNOTATION_BACKGROUND = djvu.sexpr.Symbol('background') # 8.3.4.1.1 Background Color
|
|
-ANNOTATION_ZOOM = djvu.sexpr.Symbol('zoom') # 8.3.4.1.2 Initial Zoom
|
|
-ANNOTATION_MODE = djvu.sexpr.Symbol('mode') # 8.3.4.1.3 Initial Display level
|
|
-ANNOTATION_ALIGN = djvu.sexpr.Symbol('align') # 8.3.4.1.4 Alignment
|
|
-
|
|
-# djvuchanges.txt, sections "Metadata Annotations" and "Document Annotations and Metadata":
|
|
-ANNOTATION_METADATA = djvu.sexpr.Symbol('metadata')
|
|
-
|
|
-# 8.3.4.3 Printed headers and footers:
|
|
-ANNOTATION_PRINTED_HEADER = djvu.sexpr.Symbol('phead')
|
|
-ANNOTATION_PRINTED_FOOTER = djvu.sexpr.Symbol('pfoot')
|
|
-PRINTER_HEADER_ALIGN_LEFT = PRINTED_FOOTER_ALIGN_LEFT = djvu.sexpr.Symbol('left')
|
|
-PRINTER_HEADER_ALIGN_CENTER = PRINTED_FOOTER_ALIGN_CENTER = djvu.sexpr.Symbol('center')
|
|
-PRINTER_HEADER_ALIGN_RIGHT = PRINTED_FOOTER_ALIGN_RIGHT = djvu.sexpr.Symbol('right')
|
|
-
|
|
-__all__ = [
|
|
- 'ANNOTATION_ALIGN',
|
|
- 'ANNOTATION_BACKGROUND',
|
|
- 'ANNOTATION_MAPAREA',
|
|
- 'ANNOTATION_METADATA',
|
|
- 'ANNOTATION_MODE',
|
|
- 'ANNOTATION_PRINTED_FOOTER',
|
|
- 'ANNOTATION_PRINTED_HEADER',
|
|
- 'ANNOTATION_ZOOM',
|
|
- 'EMPTY_LIST',
|
|
- 'EMPTY_OUTLINE',
|
|
- 'MAPAREA_ARROW',
|
|
- 'MAPAREA_BACKGROUND_COLOR',
|
|
- 'MAPAREA_BORDER_ALWAYS_VISIBLE',
|
|
- 'MAPAREA_BORDER_ETCHED_IN',
|
|
- 'MAPAREA_BORDER_ETCHED_OUT',
|
|
- 'MAPAREA_BORDER_NONE',
|
|
- 'MAPAREA_BORDER_SHADOW_IN',
|
|
- 'MAPAREA_BORDER_SHADOW_OUT',
|
|
- 'MAPAREA_BORDER_SOLID_COLOR',
|
|
- 'MAPAREA_BORDER_XOR',
|
|
- 'MAPAREA_HIGHLIGHT_COLOR',
|
|
- 'MAPAREA_LINE_COLOR',
|
|
- 'MAPAREA_LINE_COLOR_DEFAULT',
|
|
- 'MAPAREA_LINE_MIN_WIDTH',
|
|
- 'MAPAREA_LINE_WIDTH',
|
|
- 'MAPAREA_OPACITY',
|
|
- 'MAPAREA_OPACITY_DEFAULT',
|
|
- 'MAPAREA_OPACITY_MAX',
|
|
- 'MAPAREA_OPACITY_MIN',
|
|
- 'MAPAREA_PUSHPIN',
|
|
- 'MAPAREA_SHADOW_BORDERS',
|
|
- 'MAPAREA_SHADOW_BORDER_MAX_WIDTH',
|
|
- 'MAPAREA_SHADOW_BORDER_MIN_WIDTH',
|
|
- 'MAPAREA_SHAPE_LINE',
|
|
- 'MAPAREA_SHAPE_OVAL',
|
|
- 'MAPAREA_SHAPE_POLYGON',
|
|
- 'MAPAREA_SHAPE_RECTANGLE',
|
|
- 'MAPAREA_SHAPE_TEXT',
|
|
- 'MAPAREA_TEXT_COLOR',
|
|
- 'MAPAREA_TEXT_COLOR_DEFAULT',
|
|
- 'MAPAREA_URI',
|
|
- 'MAPAREA_URL',
|
|
- 'METADATA_BIBTEX_KEYS',
|
|
- 'METADATA_KEYS',
|
|
- 'METADATA_PDFINFO_KEYS',
|
|
- 'PRINTED_FOOTER_ALIGN_CENTER',
|
|
- 'PRINTED_FOOTER_ALIGN_LEFT',
|
|
- 'PRINTED_FOOTER_ALIGN_RIGHT',
|
|
- 'PRINTER_HEADER_ALIGN_CENTER',
|
|
- 'PRINTER_HEADER_ALIGN_LEFT',
|
|
- 'PRINTER_HEADER_ALIGN_RIGHT',
|
|
- 'TEXT_ZONE_CHARACTER',
|
|
- 'TEXT_ZONE_COLUMN',
|
|
- 'TEXT_ZONE_LINE',
|
|
- 'TEXT_ZONE_PAGE',
|
|
- 'TEXT_ZONE_PARAGRAPH',
|
|
- 'TEXT_ZONE_REGION',
|
|
- 'TEXT_ZONE_SEPARATORS',
|
|
- 'TEXT_ZONE_WORD',
|
|
- 'TextZoneType',
|
|
- 'get_text_zone_type'
|
|
-]
|
|
-
|
|
-# vim:ts=4 sts=4 sw=4 et
|
|
--- a/djvu/decode.pxd
|
|
+++ /dev/null
|
|
@@ -1,355 +0,0 @@
|
|
-# Copyright © 2007-2020 Jakub Wilk <jwilk@jwilk.net>
|
|
-#
|
|
-# This file is part of python-djvulibre.
|
|
-#
|
|
-# python-djvulibre is free software; you can redistribute it and/or modify it
|
|
-# under the terms of the GNU General Public License version 2 as published by
|
|
-# the Free Software Foundation.
|
|
-#
|
|
-# python-djvulibre is distributed in the hope that it will be useful, but
|
|
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
-# more details.
|
|
-
|
|
-#cython: language_level=2
|
|
-
|
|
-cdef extern from 'stdio.h':
|
|
- ctypedef struct FILE
|
|
-
|
|
-from djvu.sexpr cimport cexpr_t, _WrappedCExpr
|
|
-from djvu.sexpr cimport public_c2py as cexpr2py
|
|
-from djvu.sexpr cimport public_py2c as py2cexpr
|
|
-
|
|
-cdef extern from 'libdjvu/ddjvuapi.h':
|
|
- struct ddjvu_context_s
|
|
- union ddjvu_message_s
|
|
- struct ddjvu_job_s
|
|
- struct ddjvu_document_s
|
|
- struct ddjvu_page_s
|
|
- struct ddjvu_format_s
|
|
- struct ddjvu_rect_s
|
|
- struct ddjvu_rectmapper_s
|
|
-
|
|
- ctypedef ddjvu_context_s ddjvu_context_t
|
|
- ctypedef ddjvu_message_s ddjvu_message_t
|
|
- ctypedef ddjvu_job_s ddjvu_job_t
|
|
- ctypedef ddjvu_document_s ddjvu_document_t
|
|
- ctypedef ddjvu_page_s ddjvu_page_t
|
|
- ctypedef ddjvu_format_s ddjvu_format_t
|
|
- ctypedef ddjvu_rect_s ddjvu_rect_t
|
|
- ctypedef ddjvu_rectmapper_s ddjvu_rectmapper_t
|
|
-
|
|
- ctypedef void (*ddjvu_message_callback_t)(ddjvu_context_t* context, void* closure) nogil
|
|
-
|
|
- ctypedef enum ddjvu_status_t:
|
|
- DDJVU_JOB_NOTSTARTED
|
|
- DDJVU_JOB_STARTED
|
|
- DDJVU_JOB_OK
|
|
- DDJVU_JOB_FAILED
|
|
- DDJVU_JOB_STOPPED
|
|
-
|
|
- ctypedef enum ddjvu_message_tag_t:
|
|
- DDJVU_ERROR
|
|
- DDJVU_INFO
|
|
- DDJVU_NEWSTREAM
|
|
- DDJVU_DOCINFO
|
|
- DDJVU_PAGEINFO
|
|
- DDJVU_RELAYOUT
|
|
- DDJVU_REDISPLAY
|
|
- DDJVU_CHUNK
|
|
- DDJVU_THUMBNAIL
|
|
- DDJVU_PROGRESS
|
|
-
|
|
- cdef struct ddjvu_message_any_s:
|
|
- ddjvu_message_tag_t tag
|
|
- ddjvu_context_t* context
|
|
- ddjvu_document_t* document
|
|
- ddjvu_page_t* page
|
|
- ddjvu_job_t* job
|
|
- ctypedef ddjvu_message_any_s ddjvu_message_any_t
|
|
-
|
|
- cdef struct ddjvu_message_error_s:
|
|
- ddjvu_message_any_t any
|
|
- char* message
|
|
- char* function
|
|
- char* filename
|
|
- int lineno
|
|
-
|
|
- cdef struct ddjvu_message_info_s:
|
|
- ddjvu_message_any_t any
|
|
- char* message
|
|
-
|
|
- cdef struct ddjvu_message_newstream_s:
|
|
- ddjvu_message_any_t any
|
|
- int streamid
|
|
- char* name
|
|
- char* url
|
|
-
|
|
- cdef struct ddjvu_message_docinfo_s:
|
|
- ddjvu_message_any_t any
|
|
-
|
|
- ctypedef enum ddjvu_document_type_t:
|
|
- DDJVU_DOCTYPE_UNKNOWN
|
|
- DDJVU_DOCTYPE_SINGLEPAGE
|
|
- DDJVU_DOCTYPE_BUNDLED
|
|
- DDJVU_DOCTYPE_INDIRECT
|
|
- DDJVU_DOCTYPE_OLD_BUNDLED
|
|
- DDJVU_DOCTYPE_OLD_INDEXED
|
|
-
|
|
- cdef struct ddjvu_fileinfo_s:
|
|
- char type
|
|
- int pageno
|
|
- int size
|
|
- char* id
|
|
- char* name
|
|
- char* title
|
|
- ctypedef ddjvu_fileinfo_s ddjvu_fileinfo_t
|
|
-
|
|
- cdef struct ddjvu_pageinfo_s:
|
|
- int width
|
|
- int height
|
|
- int dpi
|
|
- int rotation
|
|
- int version
|
|
- ctypedef ddjvu_pageinfo_s ddjvu_pageinfo_t
|
|
-
|
|
- cdef struct ddjvu_message_pageinfo_s:
|
|
- ddjvu_message_any_t any
|
|
-
|
|
- cdef struct ddjvu_message_relayout_s:
|
|
- ddjvu_message_any_t any
|
|
-
|
|
- cdef struct ddjvu_message_redisplay_s:
|
|
- ddjvu_message_any_t any
|
|
-
|
|
- cdef struct ddjvu_message_chunk_s:
|
|
- ddjvu_message_any_t any
|
|
- char* chunkid
|
|
-
|
|
- ctypedef enum ddjvu_page_type_t:
|
|
- DDJVU_PAGETYPE_UNKNOWN
|
|
- DDJVU_PAGETYPE_BITONAL
|
|
- DDJVU_PAGETYPE_PHOTO
|
|
- DDJVU_PAGETYPE_COMPOUND
|
|
-
|
|
- ctypedef enum ddjvu_page_rotation_t:
|
|
- DDJVU_ROTATE_0
|
|
- DDJVU_ROTATE_90
|
|
- DDJVU_ROTATE_180
|
|
- DDJVU_ROTATE_270
|
|
-
|
|
- ctypedef enum ddjvu_render_mode_t:
|
|
- DDJVU_RENDER_COLOR
|
|
- DDJVU_RENDER_BLACK
|
|
- DDJVU_RENDER_COLORONLY
|
|
- DDJVU_RENDER_MASKONLY
|
|
- DDJVU_RENDER_BACKGROUND
|
|
- DDJVU_RENDER_FOREGROUND
|
|
-
|
|
- cdef struct ddjvu_rect_s:
|
|
- int x, y
|
|
- unsigned int w, h
|
|
-
|
|
- ctypedef enum ddjvu_format_style_t:
|
|
- DDJVU_FORMAT_BGR24
|
|
- DDJVU_FORMAT_RGB24
|
|
- DDJVU_FORMAT_RGBMASK16
|
|
- DDJVU_FORMAT_RGBMASK32
|
|
- DDJVU_FORMAT_GREY8
|
|
- DDJVU_FORMAT_PALETTE8
|
|
- DDJVU_FORMAT_MSBTOLSB
|
|
- DDJVU_FORMAT_LSBTOMSB
|
|
-
|
|
- cdef struct ddjvu_message_thumbnail_s:
|
|
- ddjvu_message_any_t any
|
|
- int pagenum
|
|
-
|
|
- cdef struct ddjvu_message_progress_s:
|
|
- ddjvu_message_any_t any
|
|
- ddjvu_status_t status
|
|
- int percent
|
|
-
|
|
- cdef union ddjvu_message_s:
|
|
- ddjvu_message_any_s m_any
|
|
- ddjvu_message_error_s m_error
|
|
- ddjvu_message_info_s m_info
|
|
- ddjvu_message_newstream_s m_newstream
|
|
- ddjvu_message_docinfo_s m_docinfo
|
|
- ddjvu_message_pageinfo_s m_pageinfo
|
|
- ddjvu_message_chunk_s m_chunk
|
|
- ddjvu_message_relayout_s m_relayout
|
|
- ddjvu_message_redisplay_s m_redisplay
|
|
- ddjvu_message_thumbnail_s m_thumbnail
|
|
- ddjvu_message_progress_s m_progress
|
|
-
|
|
-cdef class Context
|
|
-
|
|
-cdef class Document
|
|
-
|
|
-cdef class DocumentExtension:
|
|
- cdef Document _document
|
|
-
|
|
-cdef class DocumentPages(DocumentExtension):
|
|
- pass
|
|
-
|
|
-cdef class DocumentFiles(DocumentExtension):
|
|
- cdef object _page_map
|
|
-
|
|
-cdef class Document:
|
|
- cdef ddjvu_document_t* ddjvu_document
|
|
- cdef Context _context
|
|
- cdef DocumentPages _pages
|
|
- cdef DocumentFiles _files
|
|
- cdef object _queue
|
|
- cdef object _condition
|
|
- cdef object __weakref__
|
|
- cdef object _init(self, Context context, ddjvu_document_t* ddjvu_document)
|
|
- cdef object _clear(self)
|
|
-
|
|
-cdef class _SexprWrapper:
|
|
- cdef object _document_weakref
|
|
- cdef cexpr_t _cexpr
|
|
-
|
|
-cdef class DocumentOutline(DocumentExtension):
|
|
- cdef _SexprWrapper _sexpr
|
|
- cdef object _update_sexpr(self)
|
|
-
|
|
-cdef class Annotations:
|
|
- cdef _SexprWrapper _sexpr
|
|
- cdef object _update_sexpr(self)
|
|
- cdef Document _document
|
|
-
|
|
-cdef class DocumentAnnotations(Annotations):
|
|
- cdef int _compat
|
|
-
|
|
-cdef class Hyperlinks:
|
|
- cdef object _sexpr
|
|
-
|
|
-cdef class Metadata:
|
|
- cdef Annotations _annotations
|
|
- cdef object _keys
|
|
-
|
|
-cdef class File:
|
|
- cdef int _n
|
|
- cdef int _have_info
|
|
- cdef ddjvu_fileinfo_t ddjvu_fileinfo
|
|
- cdef Document _document
|
|
- cdef object _get_info(self)
|
|
-
|
|
-cdef class Page:
|
|
- cdef Document _document
|
|
- cdef ddjvu_pageinfo_t ddjvu_pageinfo
|
|
- cdef int _have_info
|
|
- cdef int _n
|
|
- cdef object _get_info(self)
|
|
-
|
|
-cdef class PageAnnotations(Annotations):
|
|
- cdef Page _page
|
|
-
|
|
-cdef class PageText:
|
|
- cdef Page _page
|
|
- cdef object _details
|
|
- cdef _SexprWrapper _sexpr
|
|
- cdef object _update_sexpr(self)
|
|
-
|
|
-cdef class Context:
|
|
- cdef ddjvu_context_t* ddjvu_context
|
|
- cdef object _queue
|
|
-
|
|
-cdef class PixelFormat:
|
|
- cdef ddjvu_format_t* ddjvu_format
|
|
- cdef int _bpp
|
|
- cdef int _dither_bpp
|
|
- cdef int _row_order
|
|
- cdef int _y_direction
|
|
- cdef double _gamma
|
|
-
|
|
-cdef class PixelFormatRgb(PixelFormat):
|
|
- cdef int _rgb
|
|
-
|
|
-cdef class PixelFormatRgbMask(PixelFormat):
|
|
- cdef unsigned int _params[4]
|
|
-
|
|
-cdef class PixelFormatGrey(PixelFormat):
|
|
- pass
|
|
-
|
|
-cdef class PixelFormatPalette(PixelFormat):
|
|
- cdef unsigned int _palette[216]
|
|
-
|
|
-cdef class PixelFormatPackedBits(PixelFormat):
|
|
- cdef int _little_endian
|
|
- pass
|
|
-
|
|
-cdef class Job:
|
|
- cdef Context _context
|
|
- cdef ddjvu_job_t* ddjvu_job
|
|
- cdef object _queue
|
|
- cdef object _condition
|
|
- cdef object _init(self, Context context, ddjvu_job_t *ddjvu_job)
|
|
- cdef object _clear(self)
|
|
- cdef object __weakref__
|
|
-
|
|
-cdef class PageJob(Job):
|
|
- pass
|
|
-
|
|
-cdef class SaveJob(Job):
|
|
- cdef object _file
|
|
-
|
|
-cdef class DocumentDecodingJob(Job):
|
|
- cdef object _document
|
|
- cdef object _init_ddj(self, Document document)
|
|
-
|
|
-cdef class AffineTransform:
|
|
- cdef ddjvu_rectmapper_t* ddjvu_rectmapper
|
|
-
|
|
-cdef class Message:
|
|
- cdef ddjvu_message_t* ddjvu_message
|
|
- cdef Context _context
|
|
- cdef Document _document
|
|
- cdef PageJob _page_job
|
|
- cdef Job _job
|
|
- cdef object _init(self)
|
|
-
|
|
-cdef class ErrorMessage(Message):
|
|
- cdef object _message
|
|
- cdef object _location
|
|
-
|
|
-cdef class InfoMessage(Message):
|
|
- cdef object _message
|
|
-
|
|
-cdef class Stream:
|
|
- cdef int _streamid
|
|
- cdef int _open
|
|
- cdef Document _document
|
|
-
|
|
-cdef class NewStreamMessage(Message):
|
|
- cdef object _name
|
|
- cdef object _uri
|
|
- cdef Stream _stream
|
|
-
|
|
-cdef class DocInfoMessage(Message):
|
|
- pass
|
|
-
|
|
-cdef class PageInfoMessage(Message):
|
|
- pass
|
|
-
|
|
-cdef class ChunkMessage(Message):
|
|
- pass
|
|
-
|
|
-cdef class RelayoutMessage(ChunkMessage):
|
|
- pass
|
|
-
|
|
-cdef class RedisplayMessage(ChunkMessage):
|
|
- pass
|
|
-
|
|
-cdef class ThumbnailMessage(Message):
|
|
- cdef int _page_no
|
|
-
|
|
-cdef class ProgressMessage(Message):
|
|
- cdef int _percent
|
|
- cdef ddjvu_status_t _status
|
|
-
|
|
-cdef class Thumbnail:
|
|
- cdef Page _page
|
|
-
|
|
-# vim:ts=4 sts=4 sw=4 et ft=pyrex
|
|
--- a/djvu/decode.pyx
|
|
+++ /dev/null
|
|
@@ -1,3425 +0,0 @@
|
|
-# Copyright © 2007-2021 Jakub Wilk <jwilk@jwilk.net>
|
|
-#
|
|
-# This file is part of python-djvulibre.
|
|
-#
|
|
-# python-djvulibre is free software; you can redistribute it and/or modify it
|
|
-# under the terms of the GNU General Public License version 2 as published by
|
|
-# the Free Software Foundation.
|
|
-#
|
|
-# python-djvulibre is distributed in the hope that it will be useful, but
|
|
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
-# more details.
|
|
-
|
|
-#cython: autotestdict=False
|
|
-#cython: language_level=2
|
|
-
|
|
-'''
|
|
-DjVuLibre bindings: module for efficiently decoding and displaying DjVu documents
|
|
-
|
|
-Summary
|
|
--------
|
|
-The DDJVU API provides for efficiently decoding and displaying DjVu documents.
|
|
-It provides for displaying images without waiting for the complete DjVu data.
|
|
-Images can be displayed as soon as sufficient data is available. A higher
|
|
-quality image might later be displayed when further data is available. The DjVu
|
|
-library achieves this using a complicated scheme involving multiple threads.
|
|
-The DDJVU API hides this complexity with a familiar event model.
|
|
-'''
|
|
-
|
|
-include 'common.pxi'
|
|
-
|
|
-cdef object weakref
|
|
-import weakref
|
|
-
|
|
-cdef object thread
|
|
-IF PY3K:
|
|
- import _thread as thread
|
|
-ELSE:
|
|
- import thread
|
|
-
|
|
-cdef object Queue, Empty
|
|
-IF PY3K:
|
|
- from queue import Queue, Empty
|
|
-ELSE:
|
|
- from Queue import Queue, Empty
|
|
-
|
|
-cdef object Condition
|
|
-from threading import Condition
|
|
-
|
|
-cdef object imap, izip
|
|
-IF PY3K:
|
|
- imap = map
|
|
- izip = zip
|
|
-ELSE:
|
|
- from itertools import imap, izip
|
|
-
|
|
-cdef object sys, devnull, format_exc
|
|
-import sys
|
|
-from os import devnull
|
|
-from traceback import format_exc
|
|
-
|
|
-IF PY3K:
|
|
- cdef object memoryview
|
|
- from builtins import memoryview
|
|
-
|
|
-cdef object StringIO
|
|
-IF PY3K:
|
|
- from io import StringIO
|
|
-ELSE:
|
|
- from cStringIO import StringIO
|
|
-
|
|
-cdef object Symbol, SymbolExpression, InvalidExpression
|
|
-from djvu.sexpr import Symbol, SymbolExpression, InvalidExpression
|
|
-
|
|
-cdef object the_sentinel
|
|
-the_sentinel = object()
|
|
-
|
|
-cdef object _context_loft, _document_loft, _document_weak_loft, _job_loft, _job_weak_loft
|
|
-cdef Lock loft_lock
|
|
-_context_loft = {}
|
|
-_document_loft = set()
|
|
-_document_weak_loft = weakref.WeakValueDictionary()
|
|
-_job_loft = set()
|
|
-_job_weak_loft = weakref.WeakValueDictionary()
|
|
-loft_lock = allocate_lock()
|
|
-
|
|
-cdef extern from 'libdjvu/ddjvuapi.h':
|
|
- ddjvu_context_t* ddjvu_context_create(const char *program_name) nogil
|
|
- void ddjvu_context_release(ddjvu_context_t* context) nogil
|
|
-
|
|
- void ddjvu_cache_set_size(ddjvu_context_t* context, unsigned long cachesize) nogil
|
|
- unsigned long ddjvu_cache_get_size(ddjvu_context_t* context) nogil
|
|
- void ddjvu_cache_clear(ddjvu_context_t* context) nogil
|
|
-
|
|
- ddjvu_message_t* ddjvu_message_peek(ddjvu_context_t* context) nogil
|
|
- ddjvu_message_t* ddjvu_message_wait(ddjvu_context_t* context) nogil
|
|
- void ddjvu_message_pop(ddjvu_context_t* context) nogil
|
|
-
|
|
- void ddjvu_message_set_callback(ddjvu_context_t* context, ddjvu_message_callback_t callback, void* closure) nogil
|
|
-
|
|
- ddjvu_status_t ddjvu_job_status(ddjvu_job_t* job) nogil
|
|
- int ddjvu_job_done(ddjvu_job_t* job) nogil
|
|
- int ddjvu_job_error(ddjvu_job_t* job) nogil
|
|
- void ddjvu_job_stop(ddjvu_job_t* job) nogil
|
|
- void ddjvu_job_set_user_data(ddjvu_job_t* job, void* userdata) nogil
|
|
- void* ddjvu_job_get_user_data(ddjvu_job_t* job) nogil
|
|
- void ddjvu_job_release(ddjvu_job_t* job) nogil
|
|
-
|
|
- ddjvu_document_t* ddjvu_document_create(ddjvu_context_t *context, const char *url, int cache) nogil
|
|
- ddjvu_document_t* ddjvu_document_create_by_filename(ddjvu_context_t *context, const char *filename, int cache) nogil
|
|
- ddjvu_job_t* ddjvu_document_job(ddjvu_document_t* document) nogil
|
|
- void ddjvu_document_release(ddjvu_document_t* document) nogil
|
|
-
|
|
- void ddjvu_document_set_user_data(ddjvu_document_t* document, void* userdata) nogil
|
|
- void* ddjvu_document_get_user_data(ddjvu_document_t* document) nogil
|
|
-
|
|
- ddjvu_status_t ddjvu_document_decoding_status(ddjvu_document_t* document) nogil
|
|
- int ddjvu_document_decoding_done(ddjvu_document_t* document) nogil
|
|
- int ddjvu_document_decoding_error(ddjvu_document_t* document) nogil
|
|
-
|
|
- void ddjvu_stream_write(ddjvu_document_t* document, int streamid, const char *data, unsigned long datalen) nogil
|
|
- void ddjvu_stream_close(ddjvu_document_t* document, int streamid, int stop) nogil
|
|
-
|
|
- ddjvu_document_type_t ddjvu_document_get_type(ddjvu_document_t* document) nogil
|
|
- int ddjvu_document_get_pagenum(ddjvu_document_t* document) nogil
|
|
- int ddjvu_document_get_filenum(ddjvu_document_t* document) nogil
|
|
-
|
|
- ddjvu_status_t ddjvu_document_get_fileinfo(ddjvu_document_t* document, int fileno, ddjvu_fileinfo_t* info) nogil
|
|
- int ddjvu_document_check_pagedata(ddjvu_document_t* document, int pageno) nogil
|
|
-
|
|
- ddjvu_status_t ddjvu_document_get_pageinfo(ddjvu_document_t* document, int pageno, ddjvu_pageinfo_t* info) nogil
|
|
- ddjvu_status_t ddjvu_document_get_pageinfo_imp(ddjvu_document_t* document, int pageno, ddjvu_pageinfo_t* info, unsigned int infosz) nogil
|
|
- char* ddjvu_document_get_pagedump(ddjvu_document_t* document, int pageno) nogil
|
|
- char* ddjvu_document_get_filedump(ddjvu_document_t* document, int fileno) nogil
|
|
-
|
|
- ddjvu_page_t* ddjvu_page_create_by_pageno(ddjvu_document_t* document, int pageno) nogil
|
|
- ddjvu_job_t* ddjvu_page_job(ddjvu_page_t* page) nogil
|
|
-
|
|
- void ddjvu_page_release(ddjvu_page_t* page) nogil
|
|
- void ddjvu_page_set_user_data(ddjvu_page_t* page, void* userdata) nogil
|
|
- void* ddjvu_page_get_user_data(ddjvu_page_t* page) nogil
|
|
-
|
|
- ddjvu_status_t ddjvu_page_decoding_status(ddjvu_page_t* page) nogil
|
|
- int ddjvu_page_decoding_done(ddjvu_page_t* page) nogil
|
|
- int ddjvu_page_decoding_error(ddjvu_page_t* page) nogil
|
|
-
|
|
- int ddjvu_page_get_width(ddjvu_page_t* page) nogil
|
|
- int ddjvu_page_get_height(ddjvu_page_t* page) nogil
|
|
- int ddjvu_page_get_resolution(ddjvu_page_t* page) nogil
|
|
- double ddjvu_page_get_gamma(ddjvu_page_t* page) nogil
|
|
- int ddjvu_page_get_version(ddjvu_page_t* page) nogil
|
|
- int ddjvu_code_get_version() nogil
|
|
-
|
|
- ddjvu_page_type_t ddjvu_page_get_type(ddjvu_page_t* page) nogil
|
|
-
|
|
- void ddjvu_page_set_rotation(ddjvu_page_t* page, ddjvu_page_rotation_t rot) nogil
|
|
- ddjvu_page_rotation_t ddjvu_page_get_rotation(ddjvu_page_t* page) nogil
|
|
- ddjvu_page_rotation_t ddjvu_page_get_initial_rotation(ddjvu_page_t* page) nogil
|
|
-
|
|
- int ddjvu_page_render(ddjvu_page_t *page, const ddjvu_render_mode_t mode, const ddjvu_rect_t *pagerect, const ddjvu_rect_t *renderrect, const ddjvu_format_t *pixelformat, unsigned long rowsize, char *imagebuffer) nogil
|
|
-
|
|
- ddjvu_rectmapper_t* ddjvu_rectmapper_create(ddjvu_rect_t* input, ddjvu_rect_t* output) nogil
|
|
- void ddjvu_rectmapper_modify(ddjvu_rectmapper_t* mapper, int rotation, int mirrorx, int mirrory) nogil
|
|
- void ddjvu_rectmapper_release(ddjvu_rectmapper_t* mapper) nogil
|
|
- void ddjvu_map_point(ddjvu_rectmapper_t* mapper, int* x, int* y) nogil
|
|
- void ddjvu_map_rect(ddjvu_rectmapper_t* mapper, ddjvu_rect_t* rect) nogil
|
|
- void ddjvu_unmap_point(ddjvu_rectmapper_t* mapper, int* x, int* y) nogil
|
|
- void ddjvu_unmap_rect(ddjvu_rectmapper_t* mapper, ddjvu_rect_t* rect) nogil
|
|
-
|
|
- ddjvu_format_t* ddjvu_format_create(ddjvu_format_style_t style, int nargs, unsigned int* args) nogil
|
|
- void ddjvu_format_set_row_order(ddjvu_format_t* format, int top_to_bottom) nogil
|
|
- void ddjvu_format_set_y_direction(ddjvu_format_t* format, int top_to_bottom) nogil
|
|
- void ddjvu_format_set_ditherbits(ddjvu_format_t* format, int bits) nogil
|
|
- void ddjvu_format_set_gamma(ddjvu_format_t* format, double gamma) nogil
|
|
- void ddjvu_format_release(ddjvu_format_t* format) nogil
|
|
-
|
|
- ddjvu_status_t ddjvu_thumbnail_status(ddjvu_document_t* document, int pagenum, int start) nogil
|
|
-
|
|
- int ddjvu_thumbnail_render(ddjvu_document_t *document, int pagenum, int *wptr, int *hptr, const ddjvu_format_t *pixelformat, unsigned long rowsize, char *imagebuffer) nogil
|
|
-
|
|
- ddjvu_job_t* ddjvu_document_print(ddjvu_document_t* document, FILE* output, int optc, const char * const *optv) nogil
|
|
- ddjvu_job_t* ddjvu_document_save(ddjvu_document_t* document, FILE* output, int optc, const char * const *optv) nogil
|
|
-
|
|
- void ddjvu_miniexp_release(ddjvu_document_t* document, cexpr_t expr) nogil
|
|
-
|
|
- cexpr_t ddjvu_document_get_outline(ddjvu_document_t* document) nogil
|
|
- cexpr_t ddjvu_document_get_anno(ddjvu_document_t* document, int compat) nogil
|
|
- cexpr_t ddjvu_document_get_pagetext(ddjvu_document_t* document, int pageno, const char *maxdetail) nogil
|
|
- cexpr_t ddjvu_document_get_pageanno(ddjvu_document_t* document, int pageno) nogil
|
|
- const char * ddjvu_anno_get_bgcolor(cexpr_t annotations) nogil
|
|
- const char * ddjvu_anno_get_zoom(cexpr_t annotations) nogil
|
|
- const char * ddjvu_anno_get_mode(cexpr_t annotations) nogil
|
|
- const char * ddjvu_anno_get_horizalign(cexpr_t annotations) nogil
|
|
- const char * ddjvu_anno_get_vertalign(cexpr_t annotations) nogil
|
|
- cexpr_t* ddjvu_anno_get_hyperlinks(cexpr_t annotations) nogil
|
|
- cexpr_t* ddjvu_anno_get_metadata_keys(cexpr_t annotations) nogil
|
|
- const char * ddjvu_anno_get_metadata(cexpr_t annotations, cexpr_t key) nogil
|
|
-
|
|
-# Python files:
|
|
-
|
|
-IF PY3K:
|
|
- from cpython cimport (
|
|
- PyErr_SetFromErrno as posix_error,
|
|
- PyObject_AsFileDescriptor as file_to_fd,
|
|
- )
|
|
- cdef int is_file(object o):
|
|
- return not is_number(o) and file_to_fd(o) != -1
|
|
-ELSE:
|
|
- cdef extern from 'Python.h':
|
|
- FILE* file_to_cfile 'PyFile_AsFile'(object)
|
|
- int is_file 'PyFile_Check'(object)
|
|
-IF WINDOWS:
|
|
- cdef extern from 'io.h' nogil:
|
|
- int dup(int)
|
|
-ELSE:
|
|
- from posix.unistd cimport dup
|
|
-from libc.stdio cimport fclose
|
|
-from libc.stdio cimport fdopen
|
|
-
|
|
-IF HAVE_LANGINFO_H:
|
|
- cdef extern from 'langinfo.h':
|
|
- ctypedef enum nl_item:
|
|
- CODESET
|
|
- char *nl_langinfo(nl_item item)
|
|
-
|
|
-DDJVU_VERSION = ddjvu_code_get_version()
|
|
-
|
|
-FILE_TYPE_PAGE = 'P'
|
|
-FILE_TYPE_THUMBNAILS = 'T'
|
|
-FILE_TYPE_INCLUDE = 'I'
|
|
-
|
|
-DOCUMENT_TYPE_UNKNOWN = DDJVU_DOCTYPE_UNKNOWN
|
|
-DOCUMENT_TYPE_SINGLE_PAGE = DDJVU_DOCTYPE_SINGLEPAGE
|
|
-DOCUMENT_TYPE_BUNDLED = DDJVU_DOCTYPE_BUNDLED
|
|
-DOCUMENT_TYPE_INDIRECT = DDJVU_DOCTYPE_INDIRECT
|
|
-DOCUMENT_TYPE_OLD_BUNDLED = DDJVU_DOCTYPE_OLD_BUNDLED
|
|
-DOCUMENT_TYPE_OLD_INDEXED = DDJVU_DOCTYPE_OLD_INDEXED
|
|
-
|
|
-cdef object check_sentinel(self, kwargs):
|
|
- if kwargs.get('sentinel') is not the_sentinel:
|
|
- raise_instantiation_error(type(self))
|
|
-
|
|
-cdef object write_unraisable_exception(object cause):
|
|
- try:
|
|
- message = format_exc()
|
|
- except AttributeError:
|
|
- # This mostly happens during interpreter cleanup.
|
|
- # It's worthless to try to recover.
|
|
- raise SystemExit
|
|
- sys.stderr.write(
|
|
- 'Unhandled exception in thread started by {obj!r}\n{msg}\n'.format(obj=cause, msg=message)
|
|
- )
|
|
-
|
|
-cdef class _FileWrapper:
|
|
-
|
|
- cdef object _file
|
|
- cdef FILE *cfile
|
|
-
|
|
- def __cinit__(self, object file, object mode):
|
|
- self._file = file
|
|
- self.cfile = NULL
|
|
- if not is_file(file):
|
|
- raise TypeError('file must be a real file object')
|
|
- IF PY3K:
|
|
- fd = file_to_fd(file)
|
|
- if fd == -1:
|
|
- posix_error(OSError)
|
|
- fd = dup(fd)
|
|
- if fd == -1:
|
|
- posix_error(OSError)
|
|
- self.cfile = fdopen(fd, mode)
|
|
- if self.cfile == NULL:
|
|
- posix_error(OSError)
|
|
- ELSE:
|
|
- self.cfile = file_to_cfile(file)
|
|
-
|
|
- cdef object close(self):
|
|
- IF PY3K:
|
|
- cdef int rc
|
|
- if self.cfile == NULL:
|
|
- return
|
|
- rc = fclose(self.cfile)
|
|
- self.cfile = NULL
|
|
- if rc != 0:
|
|
- posix_error(OSError)
|
|
- ELSE:
|
|
- if self._file is not None:
|
|
- self._file.flush()
|
|
- self._file = None
|
|
- self.cfile = NULL
|
|
-
|
|
- IF PY3K:
|
|
- def __dealloc__(self):
|
|
- cdef int rc
|
|
- if self.cfile == NULL:
|
|
- return
|
|
- rc = fclose(self.cfile)
|
|
- # XXX It's too late to handle errors.
|
|
-
|
|
-class NotAvailable(Exception):
|
|
- '''
|
|
- A resource not (yet) available.
|
|
- '''
|
|
-
|
|
-cdef object _NotAvailable_
|
|
-_NotAvailable_ = NotAvailable
|
|
-
|
|
-cdef class DocumentExtension:
|
|
-
|
|
- property document:
|
|
-
|
|
- '''
|
|
- Return the concerned Document.
|
|
- '''
|
|
-
|
|
- def __get__(self):
|
|
- return self._document
|
|
-
|
|
-cdef class DocumentPages(DocumentExtension):
|
|
-
|
|
- '''
|
|
- Pages of a document.
|
|
-
|
|
- Use document.pages to obtain instances of this class.
|
|
-
|
|
- Page indexing is zero-based, i.e. pages[0] stands for the very first page.
|
|
-
|
|
- len(pages) might return 1 when called before receiving a DocInfoMessage.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, Document document not None, **kwargs):
|
|
- check_sentinel(self, kwargs)
|
|
- self._document = document
|
|
-
|
|
- def __len__(self):
|
|
- return ddjvu_document_get_pagenum(self._document.ddjvu_document)
|
|
-
|
|
- def __getitem__(self, key):
|
|
- if is_int(key):
|
|
- if key < 0 or key >= len(self):
|
|
- raise IndexError('page number out of range')
|
|
- return Page(self.document, key)
|
|
- else:
|
|
- raise TypeError('page numbers must be integers')
|
|
-
|
|
-cdef class Page:
|
|
-
|
|
- '''
|
|
- Page of a document.
|
|
-
|
|
- Use document.pages[N] to obtain instances of this class.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, Document document not None, int n):
|
|
- self._document = document
|
|
- self._have_info = 0
|
|
- self._n = n
|
|
-
|
|
- property document:
|
|
- '''
|
|
- Return the Document which includes the page.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._document
|
|
-
|
|
- property file:
|
|
- '''
|
|
- Return a File associated with the page.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._document.files[self]
|
|
-
|
|
- property n:
|
|
- '''
|
|
- Return the page number.
|
|
-
|
|
- Page indexing is zero-based, i.e. 0 stands for the very first page.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._n
|
|
-
|
|
- property thumbnail:
|
|
- '''
|
|
- Return a Thumbnail for the page.
|
|
- '''
|
|
- def __get__(self):
|
|
- return Thumbnail(self)
|
|
-
|
|
- cdef object _get_info(self):
|
|
- cdef ddjvu_status_t status
|
|
- if self._have_info:
|
|
- return
|
|
- status = ddjvu_document_get_pageinfo(self._document.ddjvu_document, self._n, &self.ddjvu_pageinfo)
|
|
- ex = JobException_from_c(status)
|
|
- if ex is JobOK:
|
|
- return
|
|
- elif ex is JobStarted:
|
|
- raise _NotAvailable_
|
|
- else:
|
|
- raise ex
|
|
-
|
|
- def get_info(self, wait=1):
|
|
- '''
|
|
- P.get_info(wait=True) -> None
|
|
-
|
|
- Attempt to obtain information about the page without decoding the page.
|
|
-
|
|
- If wait is true, wait until the information is available.
|
|
-
|
|
- If the information is not available, raise NotAvailable exception.
|
|
- Then, start fetching the page data, which causes emission of
|
|
- PageInfoMessage messages with empty .page_job.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- '''
|
|
- cdef ddjvu_status_t status
|
|
- if self._have_info:
|
|
- return
|
|
- if not wait:
|
|
- return self._get_info()
|
|
- while True:
|
|
- self._document._condition.acquire()
|
|
- try:
|
|
- status = ddjvu_document_get_pageinfo(self._document.ddjvu_document, self._n, &self.ddjvu_pageinfo)
|
|
- ex = JobException_from_c(status)
|
|
- if ex is JobOK:
|
|
- self._have_info = 1
|
|
- return
|
|
- elif ex is JobStarted:
|
|
- self._document._condition.wait()
|
|
- else:
|
|
- raise ex
|
|
- finally:
|
|
- self._document._condition.release()
|
|
-
|
|
- property width:
|
|
- '''
|
|
- Return the page width, in pixels.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- See Page.get_info() for details.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._get_info()
|
|
- return self.ddjvu_pageinfo.width
|
|
-
|
|
- property height:
|
|
- '''
|
|
- Return the page height, in pixels.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- See Page.get_info() for details.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._get_info()
|
|
- return self.ddjvu_pageinfo.height
|
|
-
|
|
- property size:
|
|
- '''
|
|
- page.size == (page.width, page.height)
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- See Page.get_info() for details.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._get_info()
|
|
- return self.ddjvu_pageinfo.width, self.ddjvu_pageinfo.height
|
|
-
|
|
- property dpi:
|
|
- '''
|
|
- Return the page resolution, in pixels per inch.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- See Page.get_info() for details.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._get_info()
|
|
- return self.ddjvu_pageinfo.dpi
|
|
-
|
|
- property rotation:
|
|
- '''
|
|
- Return the initial page rotation, in degrees.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- See Page.get_info() for details.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._get_info()
|
|
- return self.ddjvu_pageinfo.rotation * 90
|
|
-
|
|
- property version:
|
|
- '''
|
|
- Return the page version.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- See Page.get_info() for details.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._get_info()
|
|
- return self.ddjvu_pageinfo.version
|
|
-
|
|
- property dump:
|
|
- '''
|
|
- Return a text describing the contents of the page using the same format
|
|
- as the djvudump command.
|
|
-
|
|
- If the information is not available, raise NotAvailable exception.
|
|
- Then PageInfoMessage messages with empty page_job may be emitted.
|
|
-
|
|
- Possible exceptions: NotAvailable.
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef char* s
|
|
- s = ddjvu_document_get_pagedump(self._document.ddjvu_document, self._n)
|
|
- if s == NULL:
|
|
- raise _NotAvailable_
|
|
- try:
|
|
- return decode_utf8(s)
|
|
- finally:
|
|
- free(s)
|
|
-
|
|
- def decode(self, wait=1):
|
|
- '''
|
|
- P.decode(wait=True) -> a PageJob
|
|
-
|
|
- Initiate data transfer and decoding threads for the page.
|
|
-
|
|
- If wait is true, wait until the job is done.
|
|
-
|
|
- Possible exceptions:
|
|
-
|
|
- - NotAvailable (if called before receiving the DocInfoMessage).
|
|
- - JobFailed (if document decoding failed).
|
|
- '''
|
|
- cdef PageJob job
|
|
- cdef ddjvu_job_t* ddjvu_job
|
|
- with nogil:
|
|
- acquire_lock(loft_lock, WAIT_LOCK)
|
|
- try:
|
|
- ddjvu_job = <ddjvu_job_t*> ddjvu_page_create_by_pageno(self._document.ddjvu_document, self._n)
|
|
- if ddjvu_job == NULL:
|
|
- raise _NotAvailable_
|
|
- if ddjvu_document_decoding_error(self._document.ddjvu_document):
|
|
- raise JobException_from_c(ddjvu_document_decoding_status(self._document.ddjvu_document))
|
|
- job = PageJob(sentinel = the_sentinel)
|
|
- job._init(self._document._context, ddjvu_job)
|
|
- finally:
|
|
- release_lock(loft_lock)
|
|
- if wait:
|
|
- job.wait()
|
|
- return job
|
|
-
|
|
- property annotations:
|
|
- '''
|
|
- Return PageAnnotations for the page.
|
|
- '''
|
|
- def __get__(self):
|
|
- return PageAnnotations(self)
|
|
-
|
|
- property text:
|
|
- '''
|
|
- Return PageText for the page.
|
|
- '''
|
|
- def __get__(self):
|
|
- return PageText(self)
|
|
-
|
|
- def __repr__(self):
|
|
- return '{tp}({doc!r}, {n})'.format(
|
|
- tp=get_type_name(Page),
|
|
- doc=self._document,
|
|
- n=self._n,
|
|
- )
|
|
-
|
|
-cdef class Thumbnail:
|
|
-
|
|
- '''
|
|
- Thumbnail for a page.
|
|
-
|
|
- Use page.thumbnail to obtain instances of this class.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, Page page not None):
|
|
- self._page = page
|
|
-
|
|
- property page:
|
|
- '''
|
|
- Return the page.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._page
|
|
-
|
|
- property status:
|
|
- '''
|
|
- Determine whether the thumbnail is available. Return a JobException
|
|
- subclass indicating the current job status.
|
|
- '''
|
|
- def __get__(self):
|
|
- return JobException_from_c(ddjvu_thumbnail_status(self._page._document.ddjvu_document, self._page._n, 0))
|
|
-
|
|
- def calculate(self):
|
|
- '''
|
|
- T.calculate() -> a JobException
|
|
-
|
|
- Determine whether the thumbnail is available. If it's not, initiate the
|
|
- thumbnail calculating job. Regardless of its success, the completion of
|
|
- the job is signalled by a subsequent ThumbnailMessage.
|
|
-
|
|
- Return a JobException subclass indicating the current job status.
|
|
- '''
|
|
- return JobException_from_c(ddjvu_thumbnail_status(self._page._document.ddjvu_document, self._page._n, 1))
|
|
-
|
|
- def render(self, size, PixelFormat pixel_format not None, long row_alignment=1, dry_run=0, buffer=None):
|
|
- '''
|
|
- T.render((w0, h0), pixel_format, row_alignment=1, dry_run=False, buffer=None) -> ((w1, h1, row_size), data)
|
|
-
|
|
- Render the thumbnail:
|
|
-
|
|
- * not larger than w0 x h0 pixels;
|
|
- * using the pixel_format pixel format;
|
|
- * with each row starting at row_alignment bytes boundary;
|
|
- * into the provided buffer or to a newly created string.
|
|
-
|
|
- Raise NotAvailable when no thumbnail is available.
|
|
- Otherwise, return a ((w1, h1, row_size), data) tuple:
|
|
-
|
|
- * w1 and h1 are actual thumbnail dimensions in pixels
|
|
- (w1 <= w0 and h1 <= h0);
|
|
- * row_size is length of each image row, in bytes;
|
|
- * data is None if dry_run is true; otherwise is contains the
|
|
- actual image data.
|
|
- '''
|
|
- cdef int iw, ih
|
|
- cdef long w, h, row_size
|
|
- cdef void* memory
|
|
- if row_alignment <= 0:
|
|
- raise ValueError('row_alignment must be a positive integer')
|
|
- w, h = size
|
|
- if w <= 0 or h <= 0:
|
|
- raise ValueError('size width/height must a positive integer')
|
|
- iw, ih = w, h
|
|
- if iw != w or ih != h:
|
|
- raise OverflowError('size width/height is too large')
|
|
- row_size = calculate_row_size(w, row_alignment, pixel_format._bpp)
|
|
- if dry_run:
|
|
- result = None
|
|
- memory = NULL
|
|
- else:
|
|
- (result, memview) = allocate_image_memory(row_size, h, buffer, &memory)
|
|
- if ddjvu_thumbnail_render(self._page._document.ddjvu_document, self._page._n, &iw, &ih, pixel_format.ddjvu_format, row_size, <char*> memory):
|
|
- return (iw, ih, row_size), result
|
|
- else:
|
|
- raise _NotAvailable_
|
|
-
|
|
- def __repr__(self):
|
|
- return '{tp}({page!r})'.format(
|
|
- tp=get_type_name(Thumbnail),
|
|
- page=self._page,
|
|
- )
|
|
-
|
|
-cdef class DocumentFiles(DocumentExtension):
|
|
-
|
|
- '''
|
|
- Component files of a document.
|
|
-
|
|
- Use document.files to obtain instances of this class.
|
|
-
|
|
- File indexing is zero-based, i.e. files[0] stands for the very first file.
|
|
-
|
|
- len(files) might raise NotAvailable when called before receiving
|
|
- a DocInfoMessage.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, Document document not None, **kwargs):
|
|
- check_sentinel(self, kwargs)
|
|
- self._page_map = None
|
|
- self._document = document
|
|
-
|
|
- def __len__(self):
|
|
- cdef int result
|
|
- result = ddjvu_document_get_filenum(self._document.ddjvu_document)
|
|
- if result is None:
|
|
- raise _NotAvailable_
|
|
- return result
|
|
-
|
|
- def __getitem__(self, key):
|
|
- cdef int i
|
|
- if is_int(key):
|
|
- if key < 0 or key >= len(self):
|
|
- raise IndexError('file number out of range')
|
|
- return File(self._document, key, sentinel = the_sentinel)
|
|
- elif typecheck(key, Page):
|
|
- if (<Page>key)._document is not self._document:
|
|
- raise KeyError(key)
|
|
- if self._page_map is None:
|
|
- self._page_map = {}
|
|
- for i in range(len(self)):
|
|
- file = File(self._document, i, sentinel = the_sentinel)
|
|
- n_page = file.n_page
|
|
- if n_page is not None:
|
|
- self._page_map[n_page] = file
|
|
- try:
|
|
- return self._page_map[(<Page>key)._n]
|
|
- except KeyError:
|
|
- raise KeyError(key)
|
|
- else:
|
|
- raise TypeError('DocumentFiles indices must be integers or Page instances')
|
|
-
|
|
-
|
|
-cdef class File:
|
|
-
|
|
- '''
|
|
- Component file of a document.
|
|
-
|
|
- Use document.files[N] to obtain instances of this class.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, Document document not None, int n, **kwargs):
|
|
- check_sentinel(self, kwargs)
|
|
- self._document = document
|
|
- self._have_info = 0
|
|
- self._n = n
|
|
-
|
|
- property document:
|
|
- '''Return the Document which includes the component file.'''
|
|
- def __get__(self):
|
|
- return self._document
|
|
-
|
|
- property n:
|
|
- '''
|
|
- Return the component file number.
|
|
-
|
|
- File indexing is zero-based, i.e. 0 stands for the very first file.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._n
|
|
-
|
|
- cdef object _get_info(self):
|
|
- cdef ddjvu_status_t status
|
|
- if self._have_info:
|
|
- return
|
|
- status = ddjvu_document_get_fileinfo(self._document.ddjvu_document, self._n, &self.ddjvu_fileinfo)
|
|
- ex = JobException_from_c(status)
|
|
- if ex is JobOK:
|
|
- return
|
|
- elif ex is JobStarted:
|
|
- raise _NotAvailable_
|
|
- else:
|
|
- raise ex
|
|
-
|
|
- def get_info(self, wait=1):
|
|
- '''
|
|
- F.get_info(wait=True) -> None
|
|
-
|
|
- Attempt to obtain information about the component file.
|
|
-
|
|
- If wait is true, wait until the information is available.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- '''
|
|
- cdef ddjvu_status_t status
|
|
- if self._have_info:
|
|
- return
|
|
- if not wait:
|
|
- return self._get_info()
|
|
- while True:
|
|
- self._document._condition.acquire()
|
|
- try:
|
|
- status = ddjvu_document_get_fileinfo(self._document.ddjvu_document, self._n, &self.ddjvu_fileinfo)
|
|
- ex = JobException_from_c(status)
|
|
- if ex is JobOK:
|
|
- self._have_info = 1
|
|
- return
|
|
- elif ex is JobStarted:
|
|
- self._document._condition.wait()
|
|
- else:
|
|
- raise ex
|
|
- finally:
|
|
- self._document._condition.release()
|
|
-
|
|
- property type:
|
|
- '''
|
|
- Return the type of the compound file:
|
|
-
|
|
- * FILE_TYPE_PAGE,
|
|
- * FILE_TYPE_THUMBNAILS,
|
|
- * FILE_TYPE_INCLUDE.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef char buffer[2]
|
|
- self._get_info()
|
|
- buffer[0] = self.ddjvu_fileinfo.type
|
|
- buffer[1] = '\0'
|
|
- return charp_to_string(buffer)
|
|
-
|
|
- property n_page:
|
|
- '''
|
|
- Return the page number, or None when not applicable.
|
|
-
|
|
- Page indexing is zero-based, i.e. 0 stands for the very first page.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._get_info()
|
|
- if self.ddjvu_fileinfo.pageno < 0:
|
|
- return
|
|
- else:
|
|
- return self.ddjvu_fileinfo.pageno
|
|
-
|
|
- property page:
|
|
- '''
|
|
- Return the page, or None when not applicable.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._get_info()
|
|
- if self.ddjvu_fileinfo.pageno < 0:
|
|
- return
|
|
- else:
|
|
- return self._document.pages[self.ddjvu_fileinfo.pageno]
|
|
-
|
|
- property size:
|
|
- '''
|
|
- Return the compound file size, or None when unknown.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._get_info()
|
|
- if self.ddjvu_fileinfo.size < 0:
|
|
- return
|
|
- else:
|
|
- return self.ddjvu_fileinfo.size
|
|
-
|
|
- property id:
|
|
- '''
|
|
- Return the compound file identifier, or None.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._get_info()
|
|
- cdef char* result
|
|
- result = <char*> self.ddjvu_fileinfo.id
|
|
- if result == NULL:
|
|
- return
|
|
- else:
|
|
- return decode_utf8(result)
|
|
-
|
|
- property name:
|
|
- '''
|
|
- Return the compound file name, or None.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._get_info()
|
|
- cdef char* result
|
|
- result = <char*> self.ddjvu_fileinfo.name
|
|
- if result == NULL:
|
|
- return
|
|
- else:
|
|
- return decode_utf8(result)
|
|
-
|
|
- property title:
|
|
- '''
|
|
- Return the compound file title, or None.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._get_info()
|
|
- cdef char* result
|
|
- result = <char*> self.ddjvu_fileinfo.title
|
|
- if result == NULL:
|
|
- return
|
|
- else:
|
|
- return decode_utf8(result)
|
|
-
|
|
-
|
|
- property dump:
|
|
- '''
|
|
- Return a text describing the contents of the file using the same format
|
|
- as the djvudump command.
|
|
-
|
|
- If the information is not available, raise NotAvailable exception.
|
|
- Then, PageInfoMessage messages with empty page_job may be emitted.
|
|
-
|
|
- Possible exceptions: NotAvailable.
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef char* s
|
|
- s = ddjvu_document_get_filedump(self._document.ddjvu_document, self._n)
|
|
- if s == NULL:
|
|
- raise _NotAvailable_
|
|
- try:
|
|
- return decode_utf8(s)
|
|
- finally:
|
|
- free(s)
|
|
-
|
|
-cdef object pages_to_opt(object pages, int sort_uniq):
|
|
- if sort_uniq:
|
|
- pages = sorted(frozenset(pages))
|
|
- else:
|
|
- pages = list(pages)
|
|
- for i in range(len(pages)):
|
|
- if not is_int(pages[i]):
|
|
- raise TypeError('page numbers must be integers')
|
|
- if pages[i] < 0:
|
|
- raise ValueError('page number out of range')
|
|
- pages[i] = pages[i] + 1
|
|
- result = '--pages=' + (','.join(imap(str, pages)))
|
|
- if is_unicode(result):
|
|
- result = encode_utf8(result)
|
|
- return result
|
|
-
|
|
-PRINT_ORIENTATION_AUTO = None
|
|
-PRINT_ORIENTATION_LANDSCAPE = 'landscape'
|
|
-PRINT_ORIENTATION_PORTRAIT = 'portrait'
|
|
-
|
|
-cdef object PRINT_RENDER_MODE_MAP
|
|
-PRINT_RENDER_MODE_MAP = {
|
|
- DDJVU_RENDER_COLOR: None,
|
|
- DDJVU_RENDER_BLACK: 'bw',
|
|
- DDJVU_RENDER_FOREGROUND: 'fore',
|
|
- DDJVU_RENDER_BACKGROUND: 'back'
|
|
-}
|
|
-
|
|
-PRINT_BOOKLET_NO = None
|
|
-PRINT_BOOKLET_YES = 'yes'
|
|
-PRINT_BOOKLET_RECTO = 'recto'
|
|
-PRINT_BOOKLET_VERSO = 'verso'
|
|
-
|
|
-cdef object PRINT_BOOKLET_OPTIONS
|
|
-PRINT_BOOKLET_OPTIONS = (PRINT_BOOKLET_NO, PRINT_BOOKLET_YES, PRINT_BOOKLET_RECTO, PRINT_BOOKLET_VERSO)
|
|
-
|
|
-cdef class SaveJob(Job):
|
|
-
|
|
- '''
|
|
- Document saving job.
|
|
-
|
|
- Use document.save(...) to obtain instances of this class.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, **kwargs):
|
|
- self._file = None
|
|
-
|
|
- def wait(self):
|
|
- Job.wait(self)
|
|
- # Ensure that the underlying file is flushed.
|
|
- # FIXME: In Python 3, the file might be never flushed if you don't use wait()!
|
|
- if self._file is not None:
|
|
- (<_FileWrapper> self._file).close()
|
|
- self._file = None
|
|
-
|
|
-cdef class DocumentDecodingJob(Job):
|
|
-
|
|
- '''
|
|
- Document decoding job.
|
|
-
|
|
- Use document.decoding_job to obtain instances of this class.
|
|
- '''
|
|
-
|
|
- cdef object _init_ddj(self, Document document):
|
|
- self._context = document._context
|
|
- self._document = document
|
|
- self._condition = document._condition
|
|
- self._queue = document._queue
|
|
- self.ddjvu_job = <ddjvu_job_t*> document.ddjvu_document
|
|
-
|
|
- def __dealloc__(self):
|
|
- self.ddjvu_job = NULL # Don't allow Job.__dealloc__ to release the job.
|
|
-
|
|
- def __repr__(self):
|
|
- return '<{tp} for {doc!r}>'.format(
|
|
- tp=get_type_name(DocumentDecodingJob),
|
|
- doc=self._document,
|
|
- )
|
|
-
|
|
-cdef class Document:
|
|
-
|
|
- '''
|
|
- DjVu document.
|
|
-
|
|
- Use context.new_document(...) to obtain instances of this class.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, **kwargs):
|
|
- self.ddjvu_document = NULL
|
|
- check_sentinel(self, kwargs)
|
|
- self._pages = DocumentPages(self, sentinel = the_sentinel)
|
|
- self._files = DocumentFiles(self, sentinel = the_sentinel)
|
|
- self._context = None
|
|
- self._queue = Queue()
|
|
- self._condition = Condition()
|
|
-
|
|
- cdef object _init(self, Context context, ddjvu_document_t *ddjvu_document):
|
|
- # Assumption: loft_lock is already acquired.
|
|
- assert (context is not None) and ddjvu_document != NULL
|
|
- self.ddjvu_document = ddjvu_document
|
|
- self._context = context
|
|
- _document_loft.add(self)
|
|
- _document_weak_loft[voidp_to_int(ddjvu_document)] = self
|
|
-
|
|
- cdef object _clear(self):
|
|
- with nogil:
|
|
- acquire_lock(loft_lock, WAIT_LOCK)
|
|
- try:
|
|
- _document_loft.discard(self)
|
|
- finally:
|
|
- release_lock(loft_lock)
|
|
-
|
|
- property decoding_status:
|
|
- '''
|
|
- Return a JobException subclass indicating the decoding job status.
|
|
- '''
|
|
- def __get__(self):
|
|
- return JobException_from_c(ddjvu_document_decoding_status(self.ddjvu_document))
|
|
-
|
|
- property decoding_error:
|
|
- '''
|
|
- Indicate whether the decoding job failed.
|
|
- '''
|
|
- def __get__(self):
|
|
- return bool(ddjvu_document_decoding_error(self.ddjvu_document))
|
|
-
|
|
- property decoding_done:
|
|
- '''
|
|
- Indicate whether the decoding job is done.
|
|
- '''
|
|
- def __get__(self):
|
|
- return bool(ddjvu_document_decoding_done(self.ddjvu_document))
|
|
-
|
|
- property decoding_job:
|
|
- '''
|
|
- Return the DocumentDecodingJob.
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef DocumentDecodingJob job
|
|
- job = DocumentDecodingJob(sentinel = the_sentinel)
|
|
- job._init_ddj(self)
|
|
- return job
|
|
-
|
|
- property type:
|
|
- '''
|
|
- Return the type of the document.
|
|
-
|
|
- The following values are possible:
|
|
- * DOCUMENT_TYPE_UNKNOWN;
|
|
- * DOCUMENT_TYPE_SINGLE_PAGE: single-page document;
|
|
- * DOCUMENT_TYPE_BUNDLED: bundled multi-page document;
|
|
- * DOCUMENT_TYPE_INDIRECT: indirect multi-page document;
|
|
- * (obsolete) DOCUMENT_TYPE_OLD_BUNDLED,
|
|
- * (obsolete) DOCUMENT_TYPE_OLD_INDEXED.
|
|
-
|
|
- Before receiving the DocInfoMessage, DOCUMENT_TYPE_UNKNOWN may be returned.
|
|
- '''
|
|
- def __get__(self):
|
|
- return ddjvu_document_get_type(self.ddjvu_document)
|
|
-
|
|
- property pages:
|
|
- '''
|
|
- Return the DocumentPages.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._pages
|
|
-
|
|
- property files:
|
|
- '''
|
|
- Return the DocumentPages.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._files
|
|
-
|
|
- property outline:
|
|
- '''
|
|
- Return the DocumentOutline.
|
|
- '''
|
|
- def __get__(self):
|
|
- return DocumentOutline(self)
|
|
-
|
|
- property annotations:
|
|
- '''
|
|
- Return the DocumentAnnotations.
|
|
- '''
|
|
- def __get__(self):
|
|
- return DocumentAnnotations(self)
|
|
-
|
|
- def __dealloc__(self):
|
|
- if self.ddjvu_document == NULL:
|
|
- return
|
|
- ddjvu_document_release(self.ddjvu_document)
|
|
-
|
|
- def save(self, file=None, indirect=None, pages=None, wait=1):
|
|
- '''
|
|
- D.save(file=None, indirect=None, pages=<all-pages>, wait=True) -> a SaveJob
|
|
-
|
|
- Save the document as:
|
|
-
|
|
- * a bundled DjVu file or;
|
|
- * an indirect DjVu document with index file name indirect.
|
|
-
|
|
- pages argument specifies a subset of saved pages.
|
|
-
|
|
- If wait is true, wait until the job is done.
|
|
- '''
|
|
- cdef const char * optv[2]
|
|
- cdef int optc
|
|
- cdef SaveJob job
|
|
- optc = 0
|
|
- cdef FILE* output
|
|
- cdef Py_ssize_t i
|
|
- cdef _FileWrapper file_wrapper
|
|
- if indirect is None:
|
|
- file_wrapper = _FileWrapper(file, <char*> "wb")
|
|
- output = file_wrapper.cfile
|
|
- else:
|
|
- if file is not None:
|
|
- raise TypeError('file must be None if indirect is specified')
|
|
- if not is_string(indirect):
|
|
- raise TypeError('indirect must be a string')
|
|
- file_wrapper = None
|
|
- output = NULL
|
|
- s1 = '--indirect=' + indirect
|
|
- if is_unicode(s1):
|
|
- s1 = encode_utf8(s1)
|
|
- optv[optc] = s1
|
|
- optc = optc + 1
|
|
- if pages is not None:
|
|
- s2 = pages_to_opt(pages, 1)
|
|
- optv[optc] = s2
|
|
- optc = optc + 1
|
|
- with nogil:
|
|
- acquire_lock(loft_lock, WAIT_LOCK)
|
|
- try:
|
|
- job = SaveJob(sentinel = the_sentinel)
|
|
- job._init(self._context, ddjvu_document_save(self.ddjvu_document, output, optc, optv))
|
|
- job._file = file_wrapper
|
|
- finally:
|
|
- release_lock(loft_lock)
|
|
- if wait:
|
|
- job.wait()
|
|
- return job
|
|
-
|
|
- def export_ps(self, file, pages=None, eps=0, level=None, orientation=PRINT_ORIENTATION_AUTO, mode=DDJVU_RENDER_COLOR, zoom=None, color=1, srgb=1, gamma=None, copies=1, frame=0, crop_marks=0, text=0, booklet=PRINT_BOOKLET_NO, booklet_max=0, booklet_align=0, booklet_fold=(18, 200), wait=1):
|
|
- '''
|
|
- D.export_ps(file, pages=<all-pages>, ..., wait=True) -> a Job
|
|
-
|
|
- Convert the document into PostScript.
|
|
-
|
|
- pages argument specifies a subset of saved pages.
|
|
-
|
|
- If wait is true, wait until the job is done.
|
|
-
|
|
- Additional options
|
|
- ------------------
|
|
-
|
|
- eps
|
|
- Produce an *Encapsulated* PostScript file. Encapsulated PostScript
|
|
- files are suitable for embedding images into other documents.
|
|
- Encapsulated PostScript file can only contain a single page.
|
|
- Setting this option overrides the options copies, orientation,
|
|
- zoom, crop_marks, and booklet.
|
|
- level
|
|
- Selects the language level of the generated PostScript. Valid
|
|
- language levels are 1, 2, and 3. Level 3 produces the most compact
|
|
- and fast printing PostScript files. Some of these files however
|
|
- require a very modern printer. Level 2 is the default value. The
|
|
- generated PostScript files are almost as compact and work with all
|
|
- but the oldest PostScript printers. Level 1 can be used as a last
|
|
- resort option.
|
|
- orientation
|
|
- Specifies the pages orientation:
|
|
- PRINT_ORIENTATION_AUTO
|
|
- automatic
|
|
- PRINT_ORIENTATION_PORTRAIT
|
|
- portrait
|
|
- PRINT_ORIENTATION_LANDSCAPE
|
|
- landscape
|
|
- mode
|
|
- Specifies how pages should be decoded:
|
|
- RENDER_COLOR
|
|
- render all the layers of the DjVu documents
|
|
- RENDER_BLACK
|
|
- render only the foreground layer mask
|
|
- RENDER_FOREGROUND
|
|
- render only the foreground layer
|
|
- RENDER_BACKGROUND
|
|
- render only the background layer
|
|
- zoom
|
|
- Specifies a zoom factor. The default zoom factor scales the image to
|
|
- fit the page.
|
|
- color
|
|
- Specifies whether to generate a color or a gray scale PostScript
|
|
- file. A gray scale PostScript files are smaller and marginally more
|
|
- portable.
|
|
- srgb
|
|
- The default value, True, generates a PostScript file using device
|
|
- independent colors in compliance with the sRGB specification.
|
|
- Modern printers then produce colors that match the original as well
|
|
- as possible. Specifying a false value generates a PostScript file
|
|
- using device dependent colors. This is sometimes useful with older
|
|
- printers. You can then use the gamma option to tune the output
|
|
- colors.
|
|
- gamma
|
|
- Specifies a gamma correction factor for the device dependent
|
|
- PostScript colors. Argument must be in range 0.3 to 5.0. Gamma
|
|
- correction normally pertains to cathodic screens only. It gets
|
|
- meaningful for printers because several models interpret device
|
|
- dependent RGB colors by emulating the color response of a cathodic
|
|
- tube.
|
|
- copies
|
|
- Specifies the number of copies to print.
|
|
- frame,
|
|
- If true, generate a thin gray border representing the boundaries of
|
|
- the document pages.
|
|
- crop_marks
|
|
- If true, generate crop marks indicating where pages should be cut.
|
|
- text
|
|
- Generate hidden text. This option is deprecated. See also the
|
|
- warning below.
|
|
- booklet
|
|
- * PRINT_BOOKLET_NO
|
|
- Disable booklet mode. This is the default.
|
|
- * PRINT_BOOKLET_YES:
|
|
- Enable recto/verse booklet mode.
|
|
- * PRINT_BOOKLET_RECTO
|
|
- Enable recto booklet mode.
|
|
- * PRINT_BOOKLET_VERSO
|
|
- Enable verso booklet mode.
|
|
- booklet_max
|
|
- Specifies the maximal number of pages per booklet. A single printout
|
|
- might then be composed of several booklets. The argument is rounded
|
|
- up to the next multiple of 4. Specifying 0 sets no maximal number
|
|
- of pages and ensures that the printout will produce
|
|
- a single booklet. This is the default.
|
|
- booklet_align
|
|
- Specifies a positive or negative offset applied to the verso of
|
|
- each sheet. The argument is expressed in points[1]_. This is useful
|
|
- with certain printers to ensure that both recto and verso are
|
|
- properly aligned. The default value is 0.
|
|
- booklet_fold (= (base, increment))
|
|
- Specifies the extra margin left between both pages on a single
|
|
- sheet. The base value is expressed in points[1]_. This margin is
|
|
- incremented for each outer sheet by value expressed in millipoints.
|
|
- The default value is (18, 200).
|
|
-
|
|
- .. [1] 1 pt = 1/72 in = 0.3528 mm
|
|
- '''
|
|
- cdef FILE* output
|
|
- cdef SaveJob job
|
|
- cdef _FileWrapper file_wrapper
|
|
- options = []
|
|
- file_wrapper = _FileWrapper(file, <char*> "wb")
|
|
- output = file_wrapper.cfile
|
|
- if pages is not None:
|
|
- list_append(options, pages_to_opt(pages, 0))
|
|
- if eps:
|
|
- list_append(options, '--format=eps')
|
|
- if level is not None:
|
|
- if not is_int(level):
|
|
- raise TypeError('level must be an integer')
|
|
- list_append(options, '--level={0}'.format(level))
|
|
- if orientation is not None:
|
|
- if not is_string(orientation):
|
|
- raise TypeError('orientation must be a string or none')
|
|
- list_append(options, '--orientation=' + orientation)
|
|
- if not is_int(mode):
|
|
- raise TypeError('mode must be an integer')
|
|
- try:
|
|
- mode = PRINT_RENDER_MODE_MAP[mode]
|
|
- if mode is not None:
|
|
- list_append(options, '--mode=' + mode)
|
|
- except KeyError:
|
|
- raise ValueError('mode must be equal to RENDER_COLOR, or RENDER_BLACK, or RENDER_FOREGROUND, or RENDER_BACKGROUND')
|
|
- if zoom is not None:
|
|
- if not is_int(zoom):
|
|
- raise TypeError('zoom must be an integer or none')
|
|
- list_append(options, '--zoom={0}'.format(zoom))
|
|
- if not color:
|
|
- list_append(options, '--color=no')
|
|
- if not srgb:
|
|
- list_append(options, '--srgb=no')
|
|
- if gamma is not None:
|
|
- if not is_int(gamma) and not is_float(gamma):
|
|
- raise TypeError('gamma must be a number or none')
|
|
- list_append(options, '--gamma={0:.16f}'.format(gamma))
|
|
- if not is_int(copies):
|
|
- raise TypeError('copies must be an integer')
|
|
- if copies != 1:
|
|
- list_append(options, '--options={0}'.format(copies))
|
|
- if frame:
|
|
- list_append(options, '--frame')
|
|
- if crop_marks:
|
|
- list_append(options, '--cropmarks')
|
|
- if text:
|
|
- list_append(options, '--text')
|
|
- if booklet is not None:
|
|
- if not is_string(booklet):
|
|
- raise TypeError('booklet must be a string or none')
|
|
- if options not in PRINT_BOOKLET_OPTIONS:
|
|
- raise ValueError('booklet must be equal to PRINT_BOOKLET_NO, or PRINT_BOOKLET_YES, or PRINT_BOOKLET_VERSO, or PRINT_BOOKLET_RECTO')
|
|
- list_append(options, '--booklet=' + booklet)
|
|
- if not is_int(booklet_max):
|
|
- raise TypeError('booklet_max must be an integer')
|
|
- if booklet_max:
|
|
- list_append(options, '--bookletmax={0}'.format(booklet_max))
|
|
- if not is_int(booklet_align):
|
|
- raise TypeError('booklet_align must be an integer')
|
|
- if booklet_align:
|
|
- list_append(options, '--bookletalign={0}'.format(booklet_align))
|
|
- if is_int(booklet_fold):
|
|
- list_append(options, '--bookletfold={0}'.format(booklet_fold))
|
|
- else:
|
|
- try:
|
|
- fold_base, fold_incr = booklet_fold
|
|
- if not is_int(fold_base) or not is_int(fold_incr):
|
|
- raise TypeError
|
|
- except TypeError:
|
|
- raise TypeError('booklet_fold must a be an integer or a pair of integers')
|
|
- list_append(options, '--bookletfold={0}+{1}'.format(fold_base, fold_incr))
|
|
- cdef const char **optv
|
|
- cdef int optc
|
|
- cdef size_t buffer_size
|
|
- buffer_size = len(options) * sizeof (char*)
|
|
- optv = <const char**> py_malloc(buffer_size)
|
|
- if optv == NULL:
|
|
- raise MemoryError('Unable to allocate {0} bytes for print options'.format(buffer_size))
|
|
- try:
|
|
- for optc in range(len(options)):
|
|
- option = options[optc]
|
|
- if is_unicode(option):
|
|
- options[optc] = option = encode_utf8(option)
|
|
- optv[optc] = option
|
|
- with nogil:
|
|
- acquire_lock(loft_lock, WAIT_LOCK)
|
|
- try:
|
|
- job = SaveJob(sentinel = the_sentinel)
|
|
- job._init(
|
|
- self._context,
|
|
- ddjvu_document_print(self.ddjvu_document, output, len(options), optv)
|
|
- )
|
|
- job._file = file_wrapper
|
|
- finally:
|
|
- release_lock(loft_lock)
|
|
- finally:
|
|
- py_free(optv)
|
|
- if wait:
|
|
- job.wait()
|
|
- return job
|
|
-
|
|
- property message_queue:
|
|
- '''
|
|
- Return the internal message queue.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._queue
|
|
-
|
|
- def get_message(self, wait=1):
|
|
- '''
|
|
- D.get_message(wait=True) -> a Message or None
|
|
-
|
|
- Get message from the internal document queue.
|
|
- Return None if wait is false and no message is available.
|
|
- '''
|
|
- try:
|
|
- return self._queue.get(wait)
|
|
- except Empty:
|
|
- return
|
|
-
|
|
- def __iter__(self):
|
|
- return self
|
|
-
|
|
- def __next__(self):
|
|
- return self.get_message()
|
|
-
|
|
-cdef Document Document_from_c(ddjvu_document_t* ddjvu_document):
|
|
- cdef Document result
|
|
- if ddjvu_document == NULL:
|
|
- result = None
|
|
- else:
|
|
- with nogil:
|
|
- acquire_lock(loft_lock, WAIT_LOCK)
|
|
- try:
|
|
- result = _document_weak_loft.get(voidp_to_int(ddjvu_document))
|
|
- finally:
|
|
- release_lock(loft_lock)
|
|
- return result
|
|
-
|
|
-
|
|
-class FileUri(str):
|
|
- '''
|
|
- See the Document.new_document() method.
|
|
- '''
|
|
-
|
|
-FileURI = FileUri
|
|
-
|
|
-cdef object Context_message_distributor
|
|
-def _Context_message_distributor(Context self not None, **kwargs):
|
|
- cdef Message message
|
|
- cdef Document document
|
|
- cdef Job job
|
|
- cdef PageJob page_job
|
|
- cdef ddjvu_message_t* ddjvu_message
|
|
-
|
|
- check_sentinel(self, kwargs)
|
|
- while True:
|
|
- with nogil:
|
|
- ddjvu_message = ddjvu_message_wait(self.ddjvu_context)
|
|
- try:
|
|
- try:
|
|
- message = Message_from_c(ddjvu_message)
|
|
- finally:
|
|
- ddjvu_message_pop(self.ddjvu_context)
|
|
- if message is None:
|
|
- raise SystemError
|
|
- self.handle_message(message)
|
|
- # XXX Order of branches below is *crucial*. Do not change.
|
|
- if message._job is not None:
|
|
- job = message._job
|
|
- job._condition.acquire()
|
|
- try:
|
|
- job._condition.notifyAll()
|
|
- finally:
|
|
- job._condition.release()
|
|
- if job.is_done:
|
|
- job._clear()
|
|
- elif message._page_job is not None:
|
|
- raise SystemError # should not happen
|
|
- elif message._document is not None:
|
|
- document = message._document
|
|
- document._condition.acquire()
|
|
- try:
|
|
- document._condition.notifyAll()
|
|
- finally:
|
|
- document._condition.release()
|
|
- if document.decoding_done:
|
|
- document._clear()
|
|
- except KeyboardInterrupt:
|
|
- return
|
|
- except SystemExit:
|
|
- return
|
|
- except Exception:
|
|
- write_unraisable_exception(self)
|
|
-Context_message_distributor = _Context_message_distributor
|
|
-del _Context_message_distributor
|
|
-
|
|
-cdef class Context:
|
|
-
|
|
- def __cinit__(self, argv0=None):
|
|
- if argv0 is None:
|
|
- argv0 = sys.argv[0]
|
|
- if is_unicode(argv0):
|
|
- argv0 = encode_utf8(argv0)
|
|
- with nogil:
|
|
- acquire_lock(loft_lock, WAIT_LOCK)
|
|
- try:
|
|
- self.ddjvu_context = ddjvu_context_create(argv0)
|
|
- if self.ddjvu_context == NULL:
|
|
- raise MemoryError('Unable to create DjVu context')
|
|
- _context_loft[voidp_to_int(self.ddjvu_context)] = self
|
|
- finally:
|
|
- release_lock(loft_lock)
|
|
- self._queue = Queue()
|
|
- thread.start_new_thread(Context_message_distributor, (self,), {'sentinel': the_sentinel})
|
|
-
|
|
- property cache_size:
|
|
-
|
|
- def __set__(self, value):
|
|
- if 0 < value < (1 << 31):
|
|
- ddjvu_cache_set_size(self.ddjvu_context, value)
|
|
- else:
|
|
- raise ValueError('0 < cache_size < (2 ** 31) must be satisfied')
|
|
-
|
|
- def __get__(self):
|
|
- return ddjvu_cache_get_size(self.ddjvu_context)
|
|
-
|
|
- def handle_message(self, Message message not None):
|
|
- '''
|
|
- C.handle_message(message) -> None
|
|
-
|
|
- This method is called, in a separate thread, for every received
|
|
- message, *before* any blocking method finishes.
|
|
-
|
|
- By default do something roughly equivalent to::
|
|
-
|
|
- if message.job is not None:
|
|
- message.job.message_queue.put(message)
|
|
- elif message.document is not None:
|
|
- message.document.message_queue.put(message)
|
|
- else:
|
|
- message.context.message_queue.put(message)
|
|
-
|
|
- You may want to override this method to change this behaviour.
|
|
-
|
|
- All exceptions raised by this method will be ignored.
|
|
- '''
|
|
-
|
|
- # XXX Order of branches below is *crucial*. Do not change.
|
|
- if message._job is not None:
|
|
- message._job._queue.put(message)
|
|
- elif message._page_job is not None:
|
|
- raise SystemError # should not happen
|
|
- elif message._document is not None:
|
|
- message._document._queue.put(message)
|
|
- else:
|
|
- message._context._queue.put(message)
|
|
-
|
|
- property message_queue:
|
|
- '''
|
|
- Return the internal message queue.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._queue
|
|
-
|
|
- def get_message(self, wait=1):
|
|
- '''
|
|
- C.get_message(wait=True) -> a Message or None
|
|
-
|
|
- Get message from the internal context queue.
|
|
- Return None if wait is false and no message is available.
|
|
- '''
|
|
- try:
|
|
- return self._queue.get(wait)
|
|
- except Empty:
|
|
- return
|
|
-
|
|
- def new_document(self, uri, cache=1):
|
|
- '''
|
|
- C.new_document(uri, cache=True) -> a Document
|
|
-
|
|
- Creates a decoder for a DjVu document and starts decoding. This
|
|
- method returns immediately. The decoding job then generates messages to
|
|
- request the raw data and to indicate the state of the decoding process.
|
|
-
|
|
- uri specifies an optional URI for the document. The URI follows the
|
|
- usual syntax (protocol://machine/path). It should not end with
|
|
- a slash. It only serves two purposes:
|
|
-
|
|
- - The URI is used as a key for the cache of decoded pages.
|
|
- - The URI is used to document NewStreamMessage messages.
|
|
-
|
|
- Setting argument cache to a true value indicates that decoded pages
|
|
- should be cached when possible.
|
|
-
|
|
- It is important to understand that the URI is not used to access the
|
|
- data. The document generates NewStreamMessage messages to indicate
|
|
- which data is needed. The caller must then provide the raw data using
|
|
- a NewStreamMessage.stream object.
|
|
-
|
|
- To open a local file, provide a FileUri instance as a URI.
|
|
-
|
|
- Localized characters in uri should be in URI-encoded.
|
|
-
|
|
- Possible exceptions: JobFailed.
|
|
- '''
|
|
- cdef Document document
|
|
- cdef ddjvu_document_t* ddjvu_document
|
|
- with nogil:
|
|
- acquire_lock(loft_lock, WAIT_LOCK)
|
|
- try:
|
|
- if typecheck(uri, FileUri):
|
|
- IF PY3K:
|
|
- uri = encode_utf8(uri)
|
|
- ddjvu_document = ddjvu_document_create_by_filename(self.ddjvu_context, uri, cache)
|
|
- else:
|
|
- IF PY3K:
|
|
- uri = encode_utf8(uri)
|
|
- ddjvu_document = ddjvu_document_create(self.ddjvu_context, uri, cache)
|
|
- if ddjvu_document == NULL:
|
|
- raise JobFailed
|
|
- document = Document(sentinel = the_sentinel)
|
|
- document._init(self, ddjvu_document)
|
|
- finally:
|
|
- release_lock(loft_lock)
|
|
- return document
|
|
-
|
|
- def __iter__(self):
|
|
- return self
|
|
-
|
|
- def __next__(self):
|
|
- return self.get_message()
|
|
-
|
|
- def clear_cache(self):
|
|
- '''
|
|
- C.clear_cache() -> None
|
|
- '''
|
|
- ddjvu_cache_clear(self.ddjvu_context)
|
|
-
|
|
- def __dealloc__(self):
|
|
- ddjvu_context_release(self.ddjvu_context)
|
|
-
|
|
-cdef Context Context_from_c(ddjvu_context_t* ddjvu_context):
|
|
- cdef Context result
|
|
- if ddjvu_context == NULL:
|
|
- result = None
|
|
- else:
|
|
- with nogil:
|
|
- acquire_lock(loft_lock, WAIT_LOCK)
|
|
- try:
|
|
- try:
|
|
- result = _context_loft[voidp_to_int(ddjvu_context)]
|
|
- except KeyError:
|
|
- raise SystemError
|
|
- finally:
|
|
- release_lock(loft_lock)
|
|
- return result
|
|
-
|
|
-RENDER_COLOR = DDJVU_RENDER_COLOR
|
|
-RENDER_BLACK = DDJVU_RENDER_BLACK
|
|
-RENDER_COLOR_ONLY = DDJVU_RENDER_COLORONLY
|
|
-RENDER_MASK_ONLY = DDJVU_RENDER_MASKONLY
|
|
-RENDER_BACKGROUND = DDJVU_RENDER_BACKGROUND
|
|
-RENDER_FOREGROUND = DDJVU_RENDER_FOREGROUND
|
|
-
|
|
-PAGE_TYPE_UNKNOWN = DDJVU_PAGETYPE_UNKNOWN
|
|
-PAGE_TYPE_BITONAL = DDJVU_PAGETYPE_BITONAL
|
|
-PAGE_TYPE_PHOTO = DDJVU_PAGETYPE_PHOTO
|
|
-PAGE_TYPE_COMPOUND = DDJVU_PAGETYPE_COMPOUND
|
|
-
|
|
-cdef class PixelFormat:
|
|
-
|
|
- '''
|
|
- Abstract pixel format.
|
|
-
|
|
- Don't use this class directly, use one of its subclasses.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, *args, **kwargs):
|
|
- self._row_order = 0
|
|
- self._y_direction = 0
|
|
- self._dither_bpp = 32
|
|
- self._gamma = 2.2
|
|
- self.ddjvu_format = NULL
|
|
- for cls in (PixelFormatRgb, PixelFormatRgbMask, PixelFormatGrey, PixelFormatPalette, PixelFormatPackedBits):
|
|
- if typecheck(self, cls):
|
|
- return
|
|
- raise_instantiation_error(type(self))
|
|
-
|
|
- property rows_top_to_bottom:
|
|
- '''
|
|
- Flag indicating whether the rows in the pixel buffer are stored
|
|
- starting from the top or the bottom of the image.
|
|
-
|
|
- Default ordering starts from the bottom of the image. This is the
|
|
- opposite of the X11 convention.
|
|
- '''
|
|
-
|
|
- def __get__(self):
|
|
- return bool(self._row_order)
|
|
-
|
|
- def __set__(self, value):
|
|
- ddjvu_format_set_row_order(self.ddjvu_format, not not value)
|
|
-
|
|
- property y_top_to_bottom:
|
|
- '''
|
|
- Flag indicating whether the *y* coordinates in the drawing area are
|
|
- oriented from bottom to top, or from top to bottom.
|
|
-
|
|
- The default is bottom to top, similar to PostScript. This is the
|
|
- opposite of the X11 convention.
|
|
- '''
|
|
-
|
|
- def __get__(self):
|
|
- return bool(self._row_order)
|
|
-
|
|
- def __set__(self, value):
|
|
- ddjvu_format_set_y_direction(self.ddjvu_format, not not value)
|
|
-
|
|
- property bpp:
|
|
- '''
|
|
- Return the depth of the image, in bits per pixel.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._bpp
|
|
-
|
|
- property dither_bpp:
|
|
- '''
|
|
- The final depth of the image on the screen. This is used to decide
|
|
- which dithering algorithm should be used.
|
|
-
|
|
- The default is usually appropriate.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._dither_bpp
|
|
-
|
|
- def __set__(self, int value):
|
|
- if (0 < value < 64):
|
|
- ddjvu_format_set_ditherbits(self.ddjvu_format, value)
|
|
- self._dither_bpp = value
|
|
- else:
|
|
- raise ValueError('0 < value < 64 must be satisfied')
|
|
-
|
|
- property gamma:
|
|
- '''
|
|
- Gamma of the display for which the pixels are intended. This will be
|
|
- combined with the gamma stored in DjVu documents in order to compute
|
|
- a suitable color correction.
|
|
-
|
|
- The default value is 2.2.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._gamma
|
|
-
|
|
- def __set__(self, double value):
|
|
- if (0.5 <= value <= 5.0):
|
|
- ddjvu_format_set_gamma(self.ddjvu_format, value)
|
|
- else:
|
|
- raise ValueError('0.5 <= value <= 5.0 must be satisfied')
|
|
-
|
|
- def __dealloc__(self):
|
|
- if self.ddjvu_format != NULL:
|
|
- ddjvu_format_release(self.ddjvu_format)
|
|
-
|
|
- def __repr__(self):
|
|
- return '{tp}()'.format(tp=get_type_name(type(self)))
|
|
-
|
|
-cdef class PixelFormatRgb(PixelFormat):
|
|
-
|
|
- '''
|
|
- PixelFormatRgb([byteorder='RGB']) -> a pixel format
|
|
-
|
|
- 24-bit pixel format, with:
|
|
-
|
|
- - RGB (byteorder == 'RGB') or
|
|
- - BGR (byteorder == 'BGR')
|
|
-
|
|
- byte order.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, byte_order='RGB', unsigned int bpp=24):
|
|
- cdef ddjvu_format_style_t _format
|
|
- if byte_order == 'RGB':
|
|
- self._rgb = 1
|
|
- _format = DDJVU_FORMAT_RGB24
|
|
- elif byte_order == 'BGR':
|
|
- self._rgb = 0
|
|
- _format = DDJVU_FORMAT_BGR24
|
|
- else:
|
|
- raise ValueError("byte_order must be equal to 'RGB' or 'BGR'")
|
|
- if bpp != 24:
|
|
- raise ValueError('bpp must be equal to 24')
|
|
- self._bpp = 24
|
|
- self.ddjvu_format = ddjvu_format_create(_format, 0, NULL)
|
|
-
|
|
- property byte_order:
|
|
- '''
|
|
- Return the byte order:
|
|
- - 'RGB' or
|
|
- - 'BGR'.
|
|
- '''
|
|
- def __get__(self):
|
|
- if self._rgb:
|
|
- return 'RGB'
|
|
- else:
|
|
- return 'BGR'
|
|
-
|
|
- def __repr__(self):
|
|
- return '{tp}(byte_order = {bo!r}, bpp = {bpp})'.format(
|
|
- tp=get_type_name(PixelFormatRgb),
|
|
- bo=self.byte_order,
|
|
- bpp=self.bpp,
|
|
- )
|
|
-
|
|
-cdef class PixelFormatRgbMask(PixelFormat):
|
|
-
|
|
- '''
|
|
- PixelFormatRgbMask(red_mask, green_mask, blue_mask[, xor_value], bpp=16) -> a pixel format
|
|
- PixelFormatRgbMask(red_mask, green_mask, blue_mask[, xor_value], bpp=32) -> a pixel format
|
|
-
|
|
- red_mask, green_mask and blue_mask are bit masks for color components
|
|
- for each pixel. The resulting color is then xored with the xor_value.
|
|
-
|
|
- For example, PixelFormatRgbMask(0xF800, 0x07E0, 0x001F, bpp=16) is a
|
|
- highcolor format with:
|
|
-
|
|
- - 5 (most significant) bits for red,
|
|
- - 6 bits for green,
|
|
- - 5 (least significant) bits for blue.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, unsigned int red_mask, unsigned int green_mask, unsigned int blue_mask, unsigned int xor_value = 0, unsigned int bpp = 16):
|
|
- cdef ddjvu_format_style_t _format
|
|
- if bpp == 16:
|
|
- _format = DDJVU_FORMAT_RGBMASK16
|
|
- red_mask = red_mask & 0xFFFF
|
|
- blue_mask = blue_mask & 0xFFFF
|
|
- green_mask = green_mask & 0xFFFF
|
|
- xor_value = xor_value & 0xFFFF
|
|
- elif bpp == 32:
|
|
- _format = DDJVU_FORMAT_RGBMASK32
|
|
- red_mask = red_mask & 0xFFFFFFFF
|
|
- blue_mask = blue_mask & 0xFFFFFFFF
|
|
- green_mask = green_mask & 0xFFFFFFFF
|
|
- xor_value = xor_value & 0xFFFFFFFF
|
|
- else:
|
|
- raise ValueError('bpp must be equal to 16 or 32')
|
|
- self._bpp = self._dither_bpp = bpp
|
|
- (self._params[0], self._params[1], self._params[2], self._params[3]) = (red_mask, green_mask, blue_mask, xor_value)
|
|
- self.ddjvu_format = ddjvu_format_create(_format, 4, self._params)
|
|
-
|
|
- def __repr__(self):
|
|
- return '{tp}(red_mask = 0x{r:0{w}x}, green_mask = 0x{g:0{w}x}, blue_mask = 0x{b:0{w}x}, xor_value = 0x{x:0{w}x}, bpp = {bpp})'.format(
|
|
- tp=get_type_name(PixelFormatRgbMask),
|
|
- r=self._params[0],
|
|
- g=self._params[1],
|
|
- b=self._params[2],
|
|
- x=self._params[3],
|
|
- w=self.bpp//4,
|
|
- bpp=self.bpp,
|
|
- )
|
|
-
|
|
-cdef class PixelFormatGrey(PixelFormat):
|
|
-
|
|
- '''
|
|
- PixelFormatGrey() -> a pixel format
|
|
-
|
|
- 8-bit, grey pixel format.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, unsigned int bpp = 8):
|
|
- cdef unsigned int params[4]
|
|
- if bpp != 8:
|
|
- raise ValueError('bpp must be equal to 8')
|
|
- self._bpp = self._dither_bpp = bpp
|
|
- self.ddjvu_format = ddjvu_format_create(DDJVU_FORMAT_GREY8, 0, NULL)
|
|
-
|
|
- def __repr__(self):
|
|
- return '{tp}(bpp = {bpp!r})'.format(
|
|
- tp=get_type_name(PixelFormatGrey),
|
|
- bpp=self.bpp,
|
|
- )
|
|
-
|
|
-cdef class PixelFormatPalette(PixelFormat):
|
|
-
|
|
- '''
|
|
- PixelFormatPalette(palette) -> a pixel format
|
|
-
|
|
- Palette pixel format.
|
|
-
|
|
- palette must be a dictionary which contains 216 (6 * 6 * 6) entries of
|
|
- a web color cube, such that:
|
|
-
|
|
- - for each key (r, g, b): r in range(0, 6), g in range(0, 6) etc.;
|
|
- - for each value v: v in range(0, 0x100).
|
|
- '''
|
|
-
|
|
- def __cinit__(self, palette, unsigned int bpp = 8):
|
|
- cdef int i, j, k, n
|
|
- for i in range(6):
|
|
- for j in range(6):
|
|
- for k in range(6):
|
|
- n = palette[(i, j, k)]
|
|
- if not 0 <= n < 0x100:
|
|
- raise ValueError('palette entries must be in range(0, 0x100)')
|
|
- self._palette[i*6*6 + j*6 + k] = n
|
|
- if bpp != 8:
|
|
- raise ValueError('bpp must be equal to 8')
|
|
- self._bpp = self._dither_bpp = bpp
|
|
- self.ddjvu_format = ddjvu_format_create(DDJVU_FORMAT_PALETTE8, 216, self._palette)
|
|
-
|
|
- def __repr__(self):
|
|
- cdef int i, j, k
|
|
- io = StringIO()
|
|
- io.write(get_type_name(PixelFormatPalette) + '({')
|
|
- for i in range(6):
|
|
- for j in range(6):
|
|
- for k in range(6):
|
|
- io.write('({i}, {j}, {k}): 0x{v:02x}'.format(i=i, j=j, k=k, v=self._palette[i * 6 * 6 + j * 6 + k]))
|
|
- if not (i == j == k == 5):
|
|
- io.write(', ')
|
|
- io.write('}}, bpp = {bpp})'.format(bpp=self.bpp))
|
|
- return io.getvalue()
|
|
-
|
|
-cdef class PixelFormatPackedBits(PixelFormat):
|
|
-
|
|
- '''
|
|
- PixelFormatPackedBits(endianness) -> a pixel format
|
|
-
|
|
- Bitonal, 1 bit per pixel format with:
|
|
-
|
|
- - most significant bits on the left (endianness=='>') or
|
|
- - least significant bits on the left (endianness=='<').
|
|
- '''
|
|
-
|
|
- def __cinit__(self, endianness):
|
|
- cdef ddjvu_format_style_t _format
|
|
- if endianness == '<':
|
|
- self._little_endian = 1
|
|
- _format = DDJVU_FORMAT_LSBTOMSB
|
|
- elif endianness == '>':
|
|
- self._little_endian = 0
|
|
- _format = DDJVU_FORMAT_MSBTOLSB
|
|
- else:
|
|
- raise ValueError("endianness must be equal to '<' or '>'")
|
|
- self._bpp = 1
|
|
- self._dither_bpp = 1
|
|
- self.ddjvu_format = ddjvu_format_create(_format, 0, NULL)
|
|
-
|
|
- property endianness:
|
|
- '''
|
|
- The endianness:
|
|
- - '<' (most significant bits on the left) or
|
|
- - '>' (least significant bits on the left).
|
|
- '''
|
|
- def __get__(self):
|
|
- if self._little_endian:
|
|
- return '<'
|
|
- else:
|
|
- return '>'
|
|
-
|
|
- def __repr__(self):
|
|
- return '{tp}({end!r})'.format(
|
|
- tp=get_type_name(PixelFormatPackedBits),
|
|
- end=self.endianness,
|
|
- )
|
|
-
|
|
-cdef object calculate_row_size(long width, long row_alignment, int bpp):
|
|
- cdef long result
|
|
- cdef object row_size
|
|
- if bpp == 1:
|
|
- row_size = (width >> 3) + ((width & 7) != 0)
|
|
- elif bpp & 7 == 0:
|
|
- row_size = width
|
|
- row_size = row_size * (bpp >> 3)
|
|
- else:
|
|
- raise SystemError
|
|
- result = ((row_size + (row_alignment - 1)) // row_alignment) * row_alignment
|
|
- return result
|
|
-
|
|
-cdef object allocate_image_memory(long width, long height, object buffer, void **memory):
|
|
- cdef char[::1] memview = None
|
|
- cdef Py_ssize_t c_requested_size
|
|
- cdef Py_ssize_t c_memory_size
|
|
- py_requested_size = int(width) * int(height)
|
|
- try:
|
|
- c_requested_size = py_requested_size
|
|
- except OverflowError:
|
|
- raise MemoryError('Unable to allocate {0} bytes for an image memory'.format(py_requested_size))
|
|
- if buffer is None:
|
|
- result = charp_to_bytes(NULL, c_requested_size)
|
|
- memory[0] = <char*> result
|
|
- else:
|
|
- result = buffer
|
|
- IF PY3K:
|
|
- memview = memoryview(buffer).cast('c')
|
|
- if len(memview) < c_requested_size:
|
|
- raise ValueError('Image buffer is too small ({0} > {1})'.format(c_requested_size, len(memview)))
|
|
- memory[0] = &memview[0]
|
|
- ELSE:
|
|
- buffer_to_writable_memory(buffer, memory, &c_memory_size)
|
|
- if c_memory_size < c_requested_size:
|
|
- raise ValueError('Image buffer is too small ({0} > {1})'.format(c_requested_size, c_memory_size))
|
|
- return (result, memview)
|
|
-
|
|
-
|
|
-cdef class PageJob(Job):
|
|
-
|
|
- '''
|
|
- A page decoding job.
|
|
-
|
|
- Use page.decode(...) to obtain instances of this class.
|
|
- '''
|
|
-
|
|
- cdef object _init(self, Context context, ddjvu_job_t *ddjvu_job):
|
|
- Job._init(self, context, ddjvu_job)
|
|
-
|
|
- property width:
|
|
- '''
|
|
- Return the page width in pixels.
|
|
-
|
|
- Possible exceptions: NotAvailable (before receiving a
|
|
- PageInfoMessage).
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef int width
|
|
- width = ddjvu_page_get_width(<ddjvu_page_t*> self.ddjvu_job)
|
|
- if width == 0:
|
|
- raise _NotAvailable_
|
|
- else:
|
|
- return width
|
|
-
|
|
- property height:
|
|
- '''
|
|
- Return the page height in pixels.
|
|
-
|
|
- Possible exceptions: NotAvailable (before receiving
|
|
- a PageInfoMessage).
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef int height
|
|
- height = ddjvu_page_get_height(<ddjvu_page_t*> self.ddjvu_job)
|
|
- if height == 0:
|
|
- raise _NotAvailable_
|
|
- else:
|
|
- return height
|
|
-
|
|
- property size:
|
|
- '''
|
|
- page_job.size == (page_job.width, page_job.height)
|
|
-
|
|
- Possible exceptions: NotAvailable (before receiving
|
|
- a PageInfoMessage).
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef int width
|
|
- cdef int height
|
|
- width = ddjvu_page_get_width(<ddjvu_page_t*> self.ddjvu_job)
|
|
- height = ddjvu_page_get_height(<ddjvu_page_t*> self.ddjvu_job)
|
|
- if width == 0 or height == 0:
|
|
- raise _NotAvailable_
|
|
- else:
|
|
- return width, height
|
|
-
|
|
- property dpi:
|
|
- '''
|
|
- Return the page resolution in pixels per inch.
|
|
-
|
|
- Possible exceptions: NotAvailable (before receiving
|
|
- a PageInfoMessage).
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef int dpi
|
|
- dpi = ddjvu_page_get_resolution(<ddjvu_page_t*> self.ddjvu_job)
|
|
- if dpi == 0:
|
|
- raise _NotAvailable_
|
|
- else:
|
|
- return dpi
|
|
-
|
|
- property gamma:
|
|
- '''
|
|
- Return the gamma of the display for which this page was designed.
|
|
-
|
|
- Possible exceptions: NotAvailable (before receiving
|
|
- a PageInfoMessage).
|
|
- '''
|
|
- def __get__(self):
|
|
- return ddjvu_page_get_gamma(<ddjvu_page_t*> self.ddjvu_job)
|
|
-
|
|
- property version:
|
|
- '''
|
|
- Return the version of the DjVu file format.
|
|
-
|
|
- Possible exceptions: NotAvailable (before receiving
|
|
- a PageInfoMessage).
|
|
- '''
|
|
- def __get__(self):
|
|
- return ddjvu_page_get_version(<ddjvu_page_t*> self.ddjvu_job)
|
|
-
|
|
- property type:
|
|
- '''
|
|
- Return the type of the page data. Possible values are:
|
|
-
|
|
- * PAGE_TYPE_UNKNOWN,
|
|
- * PAGE_TYPE_BITONAL,
|
|
- * PAGE_TYPE_PHOTO,
|
|
- * PAGE_TYPE_COMPOUND.
|
|
-
|
|
- Possible exceptions: NotAvailable (before receiving
|
|
- a PageInfoMessage).
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef ddjvu_page_type_t type
|
|
- cdef int is_done
|
|
- is_done = self.is_done
|
|
- type = ddjvu_page_get_type(<ddjvu_page_t*> self.ddjvu_job)
|
|
- if <int> type == <int> DDJVU_PAGETYPE_UNKNOWN and not is_done:
|
|
- # XXX An unavoidable race condition
|
|
- raise _NotAvailable_
|
|
- return type
|
|
-
|
|
- property initial_rotation:
|
|
- '''
|
|
- Return the counter-clockwise page rotation angle (in degrees)
|
|
- specified by the orientation flags in the DjVu file.
|
|
-
|
|
- Brain damage warning
|
|
- --------------------
|
|
- This is useful because maparea coordinates in the annotation chunks
|
|
- are expressed relative to the rotated coordinates whereas text
|
|
- coordinates in the hidden text data are expressed relative to the
|
|
- unrotated coordinates.
|
|
- '''
|
|
- def __get__(self):
|
|
- return 90 * <int> ddjvu_page_get_initial_rotation(<ddjvu_page_t*> self.ddjvu_job)
|
|
-
|
|
- property rotation:
|
|
- '''
|
|
- Return the counter-clockwise rotation angle (in degrees) for the page.
|
|
- The rotation is automatically taken into account by render(...)
|
|
- method and width and height properties.
|
|
- '''
|
|
- def __get__(self):
|
|
- return 90 * <int> ddjvu_page_get_rotation(<ddjvu_page_t*> self.ddjvu_job)
|
|
-
|
|
- def __set__(self, int value):
|
|
- cdef ddjvu_page_rotation_t rotation
|
|
- if value == 0:
|
|
- rotation = DDJVU_ROTATE_0
|
|
- elif value == 90:
|
|
- rotation = DDJVU_ROTATE_90
|
|
- elif value == 180:
|
|
- rotation = DDJVU_ROTATE_180
|
|
- elif value == 270:
|
|
- rotation = DDJVU_ROTATE_180
|
|
- else:
|
|
- raise ValueError('rotation must be equal to 0, 90, 180, or 270')
|
|
- ddjvu_page_set_rotation(<ddjvu_page_t*> self.ddjvu_job, rotation)
|
|
-
|
|
- def __del__(self):
|
|
- ddjvu_page_set_rotation(<ddjvu_page_t*> self.ddjvu_job, ddjvu_page_get_initial_rotation(<ddjvu_page_t*> self.ddjvu_job))
|
|
-
|
|
- def render(self, ddjvu_render_mode_t mode, page_rect, render_rect, PixelFormat pixel_format not None, long row_alignment=1, buffer=None):
|
|
- '''
|
|
- J.render(mode, page_rect, render_rect, pixel_format, row_alignment=1, buffer=None) -> data
|
|
-
|
|
- Render a segment of a page with arbitrary scale. mode indicates
|
|
- which image layers should be rendered:
|
|
-
|
|
- RENDER_COLOR
|
|
- color page or stencil
|
|
- RENDER_BLACK
|
|
- stencil or color page
|
|
- RENDER_COLOR_ONLY
|
|
- color page or fail
|
|
- RENDER_MASK_ONLY
|
|
- stencil or fail
|
|
- RENDER_BACKGROUND
|
|
- color background layer
|
|
- RENDER_FOREGROUND
|
|
- color foreground layer
|
|
-
|
|
- Conceptually this method renders the full page into a rectangle
|
|
- page_rect and copies the pixels specified by rectangle
|
|
- render_rect into a buffer. The actual code is much more efficient
|
|
- than that.
|
|
-
|
|
- pixel_format specifies the expected pixel format. Each row will start
|
|
- at row_alignment bytes boundary.
|
|
-
|
|
- Data will be saved to the provided buffer or to a newly created string.
|
|
-
|
|
- This method makes a best effort to compute an image that reflects the
|
|
- most recently decoded data.
|
|
-
|
|
- Possible exceptions: NotAvailable (to indicate that no image could be
|
|
- computed at this point.)
|
|
- '''
|
|
- cdef ddjvu_rect_t c_page_rect
|
|
- cdef ddjvu_rect_t c_render_rect
|
|
- cdef Py_ssize_t buffer_size
|
|
- cdef long row_size
|
|
- cdef int bpp
|
|
- cdef long x, y, w, h
|
|
- cdef void *memory
|
|
- if row_alignment <= 0:
|
|
- raise ValueError('row_alignment must be a positive integer')
|
|
- x, y, w, h = page_rect
|
|
- if w <= 0 or h <= 0:
|
|
- raise ValueError('page_rect width/height must be a positive integer')
|
|
- c_page_rect.x, c_page_rect.y, c_page_rect.w, c_page_rect.h = x, y, w, h
|
|
- if c_page_rect.x != x or c_page_rect.y != y or c_page_rect.w != w or c_page_rect.h != h:
|
|
- raise OverflowError('page_rect coordinates are too large')
|
|
- x, y, w, h = render_rect
|
|
- if w <= 0 or h <= 0:
|
|
- raise ValueError('render_rect width/height must be a positive integer')
|
|
- c_render_rect.x, c_render_rect.y, c_render_rect.w, c_render_rect.h = x, y, w, h
|
|
- if c_render_rect.x != x or c_render_rect.y != y or c_render_rect.w != w or c_render_rect.h != h:
|
|
- raise OverflowError('render_rect coordinates are too large')
|
|
- if (
|
|
- c_page_rect.x > c_render_rect.x or
|
|
- c_page_rect.y > c_render_rect.y or
|
|
- c_page_rect.x + c_page_rect.w < c_render_rect.x + c_render_rect.w or
|
|
- c_page_rect.y + c_page_rect.h < c_render_rect.y + c_render_rect.h
|
|
- ):
|
|
- raise ValueError('render_rect must be inside page_rect')
|
|
- row_size = calculate_row_size(c_render_rect.w, row_alignment, pixel_format._bpp)
|
|
- (result, memview) = allocate_image_memory(row_size, c_render_rect.h, buffer, &memory)
|
|
- if ddjvu_page_render(<ddjvu_page_t*> self.ddjvu_job, mode, &c_page_rect, &c_render_rect, pixel_format.ddjvu_format, row_size, <char*> memory) == 0:
|
|
- raise _NotAvailable_
|
|
- return result
|
|
-
|
|
- def __dealloc__(self):
|
|
- if self.ddjvu_job == NULL:
|
|
- return
|
|
- ddjvu_page_release(<ddjvu_page_t*> self.ddjvu_job)
|
|
- self.ddjvu_job = NULL
|
|
-
|
|
-cdef PageJob PageJob_from_c(ddjvu_page_t* ddjvu_page):
|
|
- cdef PageJob job
|
|
- job = Job_from_c(<ddjvu_job_t*> ddjvu_page)
|
|
- return job
|
|
-
|
|
-cdef class Job:
|
|
-
|
|
- '''
|
|
- A job.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, **kwargs):
|
|
- check_sentinel(self, kwargs)
|
|
- self._context = None
|
|
- self.ddjvu_job = NULL
|
|
- self._condition = Condition()
|
|
- self._queue = Queue()
|
|
-
|
|
- cdef object _init(self, Context context, ddjvu_job_t *ddjvu_job):
|
|
- # Assumption: loft_lock is already acquired.
|
|
- assert (context is not None) and ddjvu_job != NULL
|
|
- self._context = context
|
|
- self.ddjvu_job = ddjvu_job
|
|
- _job_loft.add(self)
|
|
- _job_weak_loft[voidp_to_int(ddjvu_job)] = self
|
|
-
|
|
- cdef object _clear(self):
|
|
- with nogil:
|
|
- acquire_lock(loft_lock, WAIT_LOCK)
|
|
- try:
|
|
- _job_loft.discard(self)
|
|
- finally:
|
|
- release_lock(loft_lock)
|
|
-
|
|
- property status:
|
|
- '''
|
|
- Return a JobException subclass indicating the job status.
|
|
- '''
|
|
- def __get__(self):
|
|
- return JobException_from_c(ddjvu_job_status(self.ddjvu_job))
|
|
-
|
|
- property is_error:
|
|
- '''
|
|
- Indicate whether the job failed.
|
|
- '''
|
|
- def __get__(self):
|
|
- return bool(ddjvu_job_error(self.ddjvu_job))
|
|
-
|
|
- property is_done:
|
|
- '''
|
|
- Indicate whether the decoding job is done.
|
|
- '''
|
|
- def __get__(self):
|
|
- return bool(ddjvu_job_done(self.ddjvu_job))
|
|
-
|
|
- def wait(self):
|
|
- '''
|
|
- J.wait() -> None
|
|
-
|
|
- Wait until the job is done.
|
|
- '''
|
|
- while True:
|
|
- self._condition.acquire()
|
|
- try:
|
|
- if ddjvu_job_done(self.ddjvu_job):
|
|
- break
|
|
- self._condition.wait()
|
|
- finally:
|
|
- self._condition.release()
|
|
-
|
|
- def stop(self):
|
|
- '''
|
|
- J.stop() -> None
|
|
-
|
|
- Attempt to cancel the job.
|
|
-
|
|
- This is a best effort method. There no guarantee that the job will
|
|
- actually stop.
|
|
- '''
|
|
- ddjvu_job_stop(self.ddjvu_job)
|
|
-
|
|
- property message_queue:
|
|
- '''
|
|
- Return the internal message queue.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._queue
|
|
-
|
|
- def get_message(self, wait=1):
|
|
- '''
|
|
- J.get_message(wait=True) -> a Message or None
|
|
-
|
|
- Get message from the internal job queue.
|
|
- Return None if wait is false and no message is available.
|
|
- '''
|
|
- try:
|
|
- return self._queue.get(wait)
|
|
- except Empty:
|
|
- return
|
|
-
|
|
- def __iter__(self):
|
|
- return self
|
|
-
|
|
- def __next__(self):
|
|
- return self.get_message()
|
|
-
|
|
- def __dealloc__(self):
|
|
- if self.ddjvu_job == NULL:
|
|
- return
|
|
- ddjvu_job_release(self.ddjvu_job)
|
|
- self.ddjvu_job = NULL
|
|
-
|
|
-cdef Job Job_from_c(ddjvu_job_t* ddjvu_job):
|
|
- cdef Job result
|
|
- if ddjvu_job == NULL:
|
|
- result = None
|
|
- else:
|
|
- with nogil:
|
|
- acquire_lock(loft_lock, WAIT_LOCK)
|
|
- try:
|
|
- result = _job_weak_loft.get(voidp_to_int(ddjvu_job))
|
|
- finally:
|
|
- release_lock(loft_lock)
|
|
- return result
|
|
-
|
|
-cdef class AffineTransform:
|
|
-
|
|
- '''
|
|
- AffineTransform((x0, y0, w0, h0), (x1, y1, w1, h1))
|
|
- -> an affine coordinate transformation
|
|
-
|
|
- The object represents an affine coordinate transformation that maps points
|
|
- from rectangle (x0, y0, w0, h0) to rectangle (x1, y1, w1, h1).
|
|
- '''
|
|
-
|
|
- def __cinit__(self, input, output):
|
|
- cdef ddjvu_rect_t c_input
|
|
- cdef ddjvu_rect_t c_output
|
|
- self.ddjvu_rectmapper = NULL
|
|
- (c_input.x, c_input.y, c_input.w, c_input.h) = input
|
|
- (c_output.x, c_output.y, c_output.w, c_output.h) = output
|
|
- self.ddjvu_rectmapper = ddjvu_rectmapper_create(&c_input, &c_output)
|
|
-
|
|
- def rotate(self, int n):
|
|
- '''
|
|
- A.rotate(n) -> None
|
|
-
|
|
- Rotate the output rectangle counter-clockwise by n degrees.
|
|
- '''
|
|
- if n % 90:
|
|
- raise ValueError('n must a multiple of 90')
|
|
- else:
|
|
- ddjvu_rectmapper_modify(self.ddjvu_rectmapper, n // 90, 0, 0)
|
|
-
|
|
- def __call__(self, value):
|
|
- cdef ddjvu_rect_t rect
|
|
- IF PY3K:
|
|
- next = iter(value).__next__
|
|
- ELSE:
|
|
- next = iter(value).next
|
|
- try:
|
|
- rect.x = next()
|
|
- rect.y = next()
|
|
- except StopIteration:
|
|
- raise ValueError('value must be a pair or a 4-tuple')
|
|
- try:
|
|
- rect.w = next()
|
|
- except StopIteration:
|
|
- ddjvu_map_point(self.ddjvu_rectmapper, &rect.x, &rect.y)
|
|
- return (rect.x, rect.y)
|
|
- try:
|
|
- rect.h = next()
|
|
- except StopIteration:
|
|
- raise ValueError('value must be a pair or a 4-tuple')
|
|
- try:
|
|
- next()
|
|
- except StopIteration:
|
|
- pass
|
|
- else:
|
|
- raise ValueError('value must be a pair or a 4-tuple')
|
|
- ddjvu_map_rect(self.ddjvu_rectmapper, &rect)
|
|
- return (rect.x, rect.y, int(rect.w), int(rect.h))
|
|
-
|
|
- def apply(self, value):
|
|
- '''
|
|
- A.apply((x0, y0)) -> (x1, y1)
|
|
- A.apply((x0, y0, w0, h0)) -> (x1, y1, w1, h1)
|
|
-
|
|
- Apply the coordinate transform to a point or a rectangle.
|
|
- '''
|
|
- return self(value)
|
|
-
|
|
- def inverse(self, value):
|
|
- '''
|
|
- A.inverse((x0, y0)) -> (x1, y1)
|
|
- A.inverse((x0, y0, w0, h0)) -> (x1, y1, w1, h1)
|
|
-
|
|
- Apply the inverse coordinate transform to a point or a rectangle.
|
|
- '''
|
|
- cdef ddjvu_rect_t rect
|
|
- IF PY3K:
|
|
- next = iter(value).__next__
|
|
- ELSE:
|
|
- next = iter(value).next
|
|
- try:
|
|
- rect.x = next()
|
|
- rect.y = next()
|
|
- except StopIteration:
|
|
- raise ValueError('value must be a pair or a 4-tuple')
|
|
- try:
|
|
- rect.w = next()
|
|
- except StopIteration:
|
|
- ddjvu_unmap_point(self.ddjvu_rectmapper, &rect.x, &rect.y)
|
|
- return (rect.x, rect.y)
|
|
- try:
|
|
- rect.h = next()
|
|
- except StopIteration:
|
|
- raise ValueError('value must be a pair or a 4-tuple')
|
|
- try:
|
|
- next()
|
|
- except StopIteration:
|
|
- pass
|
|
- else:
|
|
- raise ValueError('value must be a pair or a 4-tuple')
|
|
- ddjvu_unmap_rect(self.ddjvu_rectmapper, &rect)
|
|
- return (rect.x, rect.y, int(rect.w), int(rect.h))
|
|
-
|
|
- def mirror_x(self):
|
|
- '''
|
|
- A.mirror_x()
|
|
-
|
|
- Reverse the X coordinates of the output rectangle.
|
|
- '''
|
|
- ddjvu_rectmapper_modify(self.ddjvu_rectmapper, 0, 1, 0)
|
|
-
|
|
- def mirror_y(self):
|
|
- '''
|
|
- A.mirror_y()
|
|
-
|
|
- Reverse the Y coordinates of the output rectangle.
|
|
- '''
|
|
- ddjvu_rectmapper_modify(self.ddjvu_rectmapper, 0, 0, 1)
|
|
-
|
|
- def __dealloc__(self):
|
|
- if self.ddjvu_rectmapper != NULL:
|
|
- ddjvu_rectmapper_release(self.ddjvu_rectmapper)
|
|
-
|
|
-cdef class Message:
|
|
- '''
|
|
- An abstract message.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, **kwargs):
|
|
- check_sentinel(self, kwargs)
|
|
- self.ddjvu_message = NULL
|
|
-
|
|
- cdef object _init(self):
|
|
- if self.ddjvu_message == NULL:
|
|
- raise SystemError
|
|
- self._context = Context_from_c(self.ddjvu_message.m_any.context)
|
|
- self._document = Document_from_c(self.ddjvu_message.m_any.document)
|
|
- self._page_job = PageJob_from_c(self.ddjvu_message.m_any.page)
|
|
- self._job = Job_from_c(self.ddjvu_message.m_any.job)
|
|
-
|
|
- property context:
|
|
- '''
|
|
- Return the concerned Context.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._context
|
|
-
|
|
- property document:
|
|
- '''
|
|
- Return the concerned Document or None.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._document
|
|
-
|
|
- property page_job:
|
|
- '''
|
|
- Return the concerned PageJob or None.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._page_job
|
|
-
|
|
- property job:
|
|
- '''
|
|
- Return the concerned Job or None.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._job
|
|
-
|
|
-cdef class ErrorMessage(Message):
|
|
- '''
|
|
- An ErrorMessage is generated whenever the decoder or the DDJVU API
|
|
- encounters an error condition. All errors are reported as error messages
|
|
- because they can occur asynchronously.
|
|
- '''
|
|
-
|
|
- cdef object _init(self):
|
|
- Message._init(self)
|
|
- IF HAVE_LANGINFO_H:
|
|
- locale_encoding = charp_to_string(nl_langinfo(CODESET))
|
|
- ELSE:
|
|
- # Presumably a Windows system.
|
|
- import locale
|
|
- locale_encoding = locale.getpreferredencoding()
|
|
- if self.ddjvu_message.m_error.message != NULL:
|
|
- # Things can go awry if user calls setlocale() between the time the
|
|
- # message was created and the time it was received. Let's hope it
|
|
- # never happens, but don't throw an exception if it did anyway.
|
|
- self._message = self.ddjvu_message.m_error.message.decode(locale_encoding, 'replace')
|
|
- else:
|
|
- self._message = None
|
|
- if self.ddjvu_message.m_error.function != NULL:
|
|
- # Should be ASCII-only, so don't care about encoding.
|
|
- function = charp_to_string(self.ddjvu_message.m_error.function)
|
|
- else:
|
|
- function = None
|
|
- if self.ddjvu_message.m_error.filename != NULL:
|
|
- # Should be ASCII-only, so don't care about encoding.
|
|
- filename = charp_to_string(self.ddjvu_message.m_error.filename)
|
|
- else:
|
|
- filename = None
|
|
- self._location = (function, filename, self.ddjvu_message.m_error.lineno)
|
|
-
|
|
- property message:
|
|
- '''
|
|
- Return the actual error message, as text.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._message
|
|
-
|
|
- property location:
|
|
- '''
|
|
- Return a (function, filename, line_no) tuple indicating where the
|
|
- error was detected.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._location
|
|
-
|
|
- IF PY3K:
|
|
- def __str__(self):
|
|
- return self.message
|
|
- ELSE:
|
|
- def __str__(self):
|
|
- IF HAVE_LANGINFO_H:
|
|
- locale_encoding = charp_to_string(nl_langinfo(CODESET))
|
|
- ELSE:
|
|
- # Presumably a Windows system.
|
|
- import locale
|
|
- locale_encoding = locale.getpreferredencoding()
|
|
- return self.message.encode(locale_encoding, 'replace')
|
|
- def __unicode__(self):
|
|
- return self.message
|
|
-
|
|
- def __repr__(self):
|
|
- return '<{tp}: {msg!r} at {loc!r}>'.format(
|
|
- tp=get_type_name(ErrorMessage),
|
|
- msg=self.message,
|
|
- loc=self.location,
|
|
- )
|
|
-
|
|
-cdef class InfoMessage(Message):
|
|
- '''
|
|
- An InfoMessage provides informational text indicating the progress of the
|
|
- decoding process. This might be displayed in the browser status bar.
|
|
- '''
|
|
-
|
|
- cdef object _init(self):
|
|
- Message._init(self)
|
|
- self._message = charp_to_string(self.ddjvu_message.m_error.message)
|
|
-
|
|
- property message:
|
|
- '''
|
|
- Return the actual information message, as text.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._message
|
|
-
|
|
-cdef class Stream:
|
|
- '''
|
|
- Data stream.
|
|
-
|
|
- Use new_stream_message.stream to obtain instances of this class.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, Document document not None, int streamid, **kwargs):
|
|
- check_sentinel(self, kwargs)
|
|
- self._streamid = streamid
|
|
- self._document = document
|
|
- self._open = 1
|
|
-
|
|
- def close(self):
|
|
- '''
|
|
- S.close() -> None
|
|
-
|
|
- Indicate that no more data will be provided on the particular stream.
|
|
- '''
|
|
- ddjvu_stream_close(self._document.ddjvu_document, self._streamid, 0)
|
|
- self._open = 0
|
|
-
|
|
- def abort(self):
|
|
- '''
|
|
- S.abort() -> None
|
|
-
|
|
- Indicate that no more data will be provided on the particular stream,
|
|
- because the user has interrupted the data transfer (for instance by
|
|
- pressing the stop button of a browser) and that the decoding threads
|
|
- should be stopped as soon as feasible.
|
|
- '''
|
|
- ddjvu_stream_close(self._document.ddjvu_document, self._streamid, 1)
|
|
- self._open = 0
|
|
-
|
|
- def flush(self):
|
|
- '''
|
|
- S.flush() -> None
|
|
-
|
|
- Do nothing. (This method is provided solely to implement Python's
|
|
- file-like interface.)
|
|
- '''
|
|
-
|
|
- def read(self, size=None):
|
|
- '''
|
|
- S.read([size])
|
|
-
|
|
- Raise IOError. (This method is provided solely to implement Python's
|
|
- file-like interface.)
|
|
- '''
|
|
- raise IOError('write-only data stream')
|
|
-
|
|
- def write(self, data):
|
|
- '''
|
|
- S.write(data) -> None
|
|
-
|
|
- Provide raw data to the DjVu decoder.
|
|
-
|
|
- This method should be called as soon as the data is available, for
|
|
- instance when receiving DjVu data from a network connection.
|
|
- '''
|
|
- cdef char* raw_data
|
|
- cdef Py_ssize_t length
|
|
- if self._open:
|
|
- bytes_to_charp(data, &raw_data, &length)
|
|
- ddjvu_stream_write(self._document.ddjvu_document, self._streamid, raw_data, length)
|
|
- else:
|
|
- raise IOError('I/O operation on closed file')
|
|
-
|
|
- def __dealloc__(self):
|
|
- if <object>self._document is None:
|
|
- return
|
|
- if self._open:
|
|
- ddjvu_stream_close(self._document.ddjvu_document, self._streamid, 1)
|
|
-
|
|
-cdef class NewStreamMessage(Message):
|
|
-
|
|
- '''
|
|
- A NewStreamMessage is generated whenever the decoder needs to access raw
|
|
- DjVu data. The caller must then provide the requested data using the
|
|
- .stream file-like object.
|
|
-
|
|
- In the case of indirect documents, a single decoder might simultaneously
|
|
- request several streams of data.
|
|
-
|
|
- '''
|
|
-
|
|
- cdef object _init(self):
|
|
- Message._init(self)
|
|
- self._stream = Stream(self.document, self.ddjvu_message.m_newstream.streamid, sentinel = the_sentinel)
|
|
- self._name = charp_to_string(self.ddjvu_message.m_newstream.name)
|
|
- self._uri = charp_to_string(self.ddjvu_message.m_newstream.url)
|
|
-
|
|
- property stream:
|
|
- '''
|
|
- Return the concerned Stream.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._stream
|
|
-
|
|
- property name:
|
|
- '''
|
|
- The first NewStreamMessage message always has .name set to None.
|
|
- It indicates that the decoder needs to access the data in the main DjVu
|
|
- file.
|
|
-
|
|
- Further NewStreamMessage messages are generated to access the
|
|
- auxiliary files of indirect or indexed DjVu documents. .name then
|
|
- provides the base name of the auxiliary file.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._name
|
|
-
|
|
- property uri:
|
|
- '''
|
|
- Return the requested URI.
|
|
-
|
|
- URI is set according to the uri argument provided to function
|
|
- Context.new_document(). The first NewMessageStream message always
|
|
- contain the URI passed to Context.new_document(). Subsequent
|
|
- NewMessageStream messages contain the URI of the auxiliary files for
|
|
- indirect or indexed DjVu documents.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._uri
|
|
-
|
|
-cdef class DocInfoMessage(Message):
|
|
- '''
|
|
- A DocInfoMessage indicates that basic information about the document has
|
|
- been obtained and decoded. Not much can be done before this happens.
|
|
-
|
|
- Check Document.decoding_status to determine whether the operation was
|
|
- successful.
|
|
- '''
|
|
-
|
|
-cdef class PageInfoMessage(Message):
|
|
- '''
|
|
- The page decoding process generates a PageInfoMessage:
|
|
-
|
|
- - when basic page information is available and before any RelayoutMessage
|
|
- or RedisplayMessage,
|
|
- - when the page decoding thread terminates.
|
|
-
|
|
- You can distinguish both cases using PageJob.status.
|
|
-
|
|
- A PageInfoMessage may be also generated as a consequence of reading
|
|
- Page.get_info() or Page.dump.
|
|
- '''
|
|
-
|
|
-cdef class ChunkMessage(Message):
|
|
- '''
|
|
- A ChunkMessage indicates that an additional chunk of DjVu data has been
|
|
- decoded.
|
|
- '''
|
|
-
|
|
-cdef class RelayoutMessage(ChunkMessage):
|
|
- '''
|
|
- A RelayoutMessage is generated when a DjVu viewer should recompute the
|
|
- layout of the page viewer because the page size and resolution information
|
|
- has been updated.
|
|
- '''
|
|
-
|
|
-cdef class RedisplayMessage(ChunkMessage):
|
|
- '''
|
|
- A RedisplayMessage is generated when a DjVu viewer should call
|
|
- PageJob.render() and redisplay the page. This happens, for instance, when
|
|
- newly decoded DjVu data provides a better image.
|
|
- '''
|
|
-
|
|
-cdef class ThumbnailMessage(Message):
|
|
- '''
|
|
- A ThumbnailMessage is sent when additional thumbnails are available.
|
|
- '''
|
|
-
|
|
- cdef object _init(self):
|
|
- Message._init(self)
|
|
- self._page_no = self.ddjvu_message.m_thumbnail.pagenum
|
|
-
|
|
- property thumbnail:
|
|
- '''
|
|
- Return the Thumbnail.
|
|
-
|
|
- Raise NotAvailable if the Document has been garbage-collected.
|
|
- '''
|
|
- def __get__(self):
|
|
- if self._document is None:
|
|
- raise _NotAvailable_
|
|
- return self._document.pages[self._page_no].thumbnail
|
|
-
|
|
-cdef class ProgressMessage(Message):
|
|
- '''
|
|
- A ProgressMessage is generated to indicate progress towards the
|
|
- completion of a print or save job.
|
|
- '''
|
|
-
|
|
- cdef object _init(self):
|
|
- Message._init(self)
|
|
- self._percent = self.ddjvu_message.m_progress.percent
|
|
- self._status = self.ddjvu_message.m_progress.status
|
|
-
|
|
- property percent:
|
|
- '''
|
|
- Return the percent of the job done.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._percent
|
|
-
|
|
- property status:
|
|
- '''
|
|
- Return a JobException subclass indicating the current job status.
|
|
- '''
|
|
- def __get__(self):
|
|
- return JobException_from_c(self._status)
|
|
-
|
|
-cdef object MESSAGE_MAP
|
|
-MESSAGE_MAP = {
|
|
- DDJVU_ERROR: ErrorMessage,
|
|
- DDJVU_INFO: InfoMessage,
|
|
- DDJVU_NEWSTREAM: NewStreamMessage,
|
|
- DDJVU_DOCINFO: DocInfoMessage,
|
|
- DDJVU_PAGEINFO: PageInfoMessage,
|
|
- DDJVU_RELAYOUT: RelayoutMessage,
|
|
- DDJVU_REDISPLAY: RedisplayMessage,
|
|
- DDJVU_CHUNK: ChunkMessage,
|
|
- DDJVU_THUMBNAIL: ThumbnailMessage,
|
|
- DDJVU_PROGRESS: ProgressMessage
|
|
-}
|
|
-
|
|
-cdef Message Message_from_c(ddjvu_message_t* ddjvu_message):
|
|
- cdef Message message
|
|
- if ddjvu_message == NULL:
|
|
- return
|
|
- try:
|
|
- klass = MESSAGE_MAP[ddjvu_message.m_any.tag]
|
|
- except KeyError:
|
|
- raise SystemError
|
|
- message = klass(sentinel = the_sentinel)
|
|
- message.ddjvu_message = ddjvu_message
|
|
- message._init()
|
|
- return message
|
|
-
|
|
-cdef object JOB_EXCEPTION_MAP
|
|
-cdef object JOB_FAILED_SYMBOL, JOB_STOPPED_SYMBOL
|
|
-
|
|
-JOB_FAILED_SYMBOL = Symbol('failed')
|
|
-JOB_STOPPED_SYMBOL = Symbol('stopped')
|
|
-
|
|
-cdef object JobException_from_sexpr(object sexpr):
|
|
- if typecheck(sexpr, SymbolExpression):
|
|
- if sexpr.value is JOB_FAILED_SYMBOL:
|
|
- return JobFailed
|
|
- elif sexpr.value is JOB_STOPPED_SYMBOL:
|
|
- return JobStopped
|
|
-
|
|
-cdef JobException_from_c(ddjvu_status_t code):
|
|
- try:
|
|
- return JOB_EXCEPTION_MAP[code]
|
|
- except KeyError:
|
|
- raise SystemError
|
|
-
|
|
-class JobException(Exception):
|
|
- '''
|
|
- Status of a job. Possibly, but not necessarily, exceptional.
|
|
- '''
|
|
-
|
|
-class JobNotDone(JobException):
|
|
- '''
|
|
- Operation is not yet done.
|
|
- '''
|
|
-
|
|
-class JobNotStarted(JobNotDone):
|
|
- '''
|
|
- Operation was not even started.
|
|
- '''
|
|
-
|
|
-class JobStarted(JobNotDone):
|
|
- '''
|
|
- Operation is in progress.
|
|
- '''
|
|
-
|
|
-class JobDone(JobException):
|
|
- '''
|
|
- Operation finished.
|
|
- '''
|
|
-
|
|
-class JobOK(JobDone):
|
|
- '''
|
|
- Operation finished successfully.
|
|
- '''
|
|
-
|
|
-class JobFailed(JobDone):
|
|
- '''
|
|
- Operation failed because of an error.
|
|
- '''
|
|
-
|
|
-class JobStopped(JobFailed):
|
|
- '''
|
|
- Operation was interrupted by user.
|
|
- '''
|
|
-
|
|
-JOB_EXCEPTION_MAP = {
|
|
- DDJVU_JOB_NOTSTARTED: JobNotStarted,
|
|
- DDJVU_JOB_STARTED: JobStarted,
|
|
- DDJVU_JOB_OK: JobOK,
|
|
- DDJVU_JOB_FAILED: JobFailed,
|
|
- DDJVU_JOB_STOPPED: JobStopped
|
|
-}
|
|
-
|
|
-cdef class _SexprWrapper:
|
|
-
|
|
- def __cinit__(self, document, **kwargs):
|
|
- check_sentinel(self, kwargs)
|
|
- self._document_weakref = weakref.ref(document)
|
|
-
|
|
- def __call__(self):
|
|
- return cexpr2py(self._cexpr)
|
|
-
|
|
- def __dealloc__(self):
|
|
- cdef Document document
|
|
- if self._cexpr == NULL:
|
|
- return
|
|
- document = self._document_weakref()
|
|
- if document is None:
|
|
- return
|
|
- ddjvu_miniexp_release(document.ddjvu_document, self._cexpr)
|
|
-
|
|
-cdef _SexprWrapper wrap_sexpr(Document document, cexpr_t cexpr):
|
|
- cdef _SexprWrapper result
|
|
- result = _SexprWrapper(document, sentinel = the_sentinel)
|
|
- result._cexpr = cexpr
|
|
- return result
|
|
-
|
|
-cdef class DocumentOutline(DocumentExtension):
|
|
- '''
|
|
- DocumentOutline(document) -> a document outline
|
|
- '''
|
|
-
|
|
- def __cinit__(self, Document document not None):
|
|
- self._document = document
|
|
- self._sexpr = None
|
|
-
|
|
- cdef object _update_sexpr(self):
|
|
- if self._sexpr is not None:
|
|
- return
|
|
- self._sexpr = wrap_sexpr(
|
|
- self._document,
|
|
- ddjvu_document_get_outline(self._document.ddjvu_document)
|
|
- )
|
|
-
|
|
- def wait(self):
|
|
- '''
|
|
- O.wait() -> None
|
|
-
|
|
- Wait until the associated S-expression is available.
|
|
- '''
|
|
- while True:
|
|
- self._document._condition.acquire()
|
|
- try:
|
|
- try:
|
|
- self.sexpr
|
|
- return
|
|
- except NotAvailable:
|
|
- self._document._condition.wait()
|
|
- finally:
|
|
- self._document._condition.release()
|
|
-
|
|
- property sexpr:
|
|
- '''
|
|
- Return the associated S-expression. See "Outline/Bookmark syntax" in
|
|
- the djvused manual page.
|
|
-
|
|
- If the S-expression is not available, raise NotAvailable exception.
|
|
- Then, PageInfoMessage messages with empty page_job may be emitted.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._update_sexpr()
|
|
- try:
|
|
- sexpr = self._sexpr()
|
|
- exception = JobException_from_sexpr(sexpr)
|
|
- if exception is not None:
|
|
- raise exception
|
|
- return sexpr
|
|
- except InvalidExpression:
|
|
- self._sexpr = None
|
|
- raise _NotAvailable_
|
|
-
|
|
- def __repr__(self):
|
|
- return '{tp}({doc!r})'.format(
|
|
- tp=get_type_name(DocumentOutline),
|
|
- doc=self._document,
|
|
- )
|
|
-
|
|
-cdef class Annotations:
|
|
- '''
|
|
- Document or page annotation.
|
|
-
|
|
- Don't use this class directly, use one of its subclasses.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, *args, **kwargs):
|
|
- if typecheck(self, DocumentAnnotations):
|
|
- return
|
|
- if typecheck(self, PageAnnotations):
|
|
- return
|
|
- raise_instantiation_error(type(self))
|
|
-
|
|
- cdef object _update_sexpr(self):
|
|
- raise NotImplementedError
|
|
-
|
|
- def wait(self):
|
|
- '''
|
|
- A.wait() -> None
|
|
-
|
|
- Wait until the associated S-expression is available.
|
|
- '''
|
|
- while True:
|
|
- self._document._condition.acquire()
|
|
- try:
|
|
- try:
|
|
- self.sexpr
|
|
- return
|
|
- except NotAvailable:
|
|
- self._document._condition.wait()
|
|
- finally:
|
|
- self._document._condition.release()
|
|
-
|
|
- property sexpr:
|
|
- '''
|
|
- Return the associated S-expression. See "Annotation syntax" in the
|
|
- djvused manual page.
|
|
-
|
|
- If the S-expression is not available, raise NotAvailable exception.
|
|
- Then, PageInfoMessage messages with empty page_job may be emitted.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._update_sexpr()
|
|
- try:
|
|
- sexpr = self._sexpr()
|
|
- exception = JobException_from_sexpr(sexpr)
|
|
- if exception is not None:
|
|
- raise exception
|
|
- return sexpr
|
|
- except InvalidExpression:
|
|
- self._sexpr = None
|
|
- raise _NotAvailable_
|
|
-
|
|
- property background_color:
|
|
- '''
|
|
- Parse the annotations and extract the desired background color as
|
|
- a color string '(#FFFFFF)'. See '(background ...)' in the
|
|
- djvused manual page.
|
|
-
|
|
- Return None if this information is not specified.
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef const char *result
|
|
- result = ddjvu_anno_get_bgcolor(self._sexpr._cexpr)
|
|
- if result == NULL:
|
|
- return
|
|
- return result
|
|
-
|
|
- property zoom:
|
|
- '''
|
|
- Parse the annotations and extract the desired zoom factor. See
|
|
- '(zoom ...)' in the djvused manual page.
|
|
-
|
|
- Return None if this information is not specified.
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef const char *result
|
|
- result = ddjvu_anno_get_zoom(self._sexpr._cexpr)
|
|
- if result == NULL:
|
|
- return
|
|
- return result
|
|
-
|
|
- property mode:
|
|
- '''
|
|
- Parse the annotations and extract the desired display mode. See
|
|
- '(mode ...)' in the djvused manual page.
|
|
-
|
|
- Return zero if this information is not specified.
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef const char *result
|
|
- result = ddjvu_anno_get_mode(self._sexpr._cexpr)
|
|
- if result == NULL:
|
|
- return
|
|
- return result
|
|
-
|
|
- property horizontal_align:
|
|
- '''
|
|
- Parse the annotations and extract how the page image should be aligned
|
|
- horizontally. See '(align ...)' in the djvused manual page.
|
|
-
|
|
- Return None if this information is not specified.
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef const char *result
|
|
- result = ddjvu_anno_get_horizalign(self._sexpr._cexpr)
|
|
- if result == NULL:
|
|
- return
|
|
- return result
|
|
-
|
|
- property vertical_align:
|
|
- '''
|
|
- Parse the annotations and extract how the page image should be aligned
|
|
- vertically. See '(align ...)' in the djvused manual page.
|
|
-
|
|
- Return None if this information is not specified.
|
|
- '''
|
|
- def __get__(self):
|
|
- cdef const char *result
|
|
- result = ddjvu_anno_get_vertalign(self._sexpr._cexpr)
|
|
- if result == NULL:
|
|
- return
|
|
- return result
|
|
-
|
|
- property hyperlinks:
|
|
- '''
|
|
- Return an associated Hyperlinks object.
|
|
- '''
|
|
- def __get__(self):
|
|
- return Hyperlinks(self)
|
|
-
|
|
- property metadata:
|
|
- '''
|
|
- Return an associated Metadata object.
|
|
- '''
|
|
- def __get__(self):
|
|
- return Metadata(self)
|
|
-
|
|
-cdef class DocumentAnnotations(Annotations):
|
|
- '''
|
|
- DocumentAnnotations(document[, shared=True]) -> document-wide annotations
|
|
-
|
|
- If shared is true and no document-wide annotations are available, shared
|
|
- annotations are considered document-wide.
|
|
-
|
|
- See also "Document annotations and metadata" in the djvuchanges.txt file.
|
|
-
|
|
- '''
|
|
-
|
|
- def __cinit__(self, Document document not None, shared=1):
|
|
- self._document = document
|
|
- self._compat = shared
|
|
- self._sexpr = None
|
|
-
|
|
- cdef object _update_sexpr(self):
|
|
- if self._sexpr is not None:
|
|
- return
|
|
- self._sexpr = wrap_sexpr(
|
|
- self._document,
|
|
- ddjvu_document_get_anno(self._document.ddjvu_document, self._compat)
|
|
- )
|
|
-
|
|
- property document:
|
|
- '''
|
|
- Return the concerned Document.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._document
|
|
-
|
|
-cdef class PageAnnotations(Annotations):
|
|
-
|
|
- '''
|
|
- PageAnnotation(page) -> page annotations
|
|
- '''
|
|
-
|
|
- def __cinit__(self, Page page not None):
|
|
- self._document = page._document
|
|
- self._page = page
|
|
- self._sexpr = None
|
|
-
|
|
- cdef object _update_sexpr(self):
|
|
- if self._sexpr is not None:
|
|
- return
|
|
- self._sexpr = wrap_sexpr(
|
|
- self._page._document,
|
|
- ddjvu_document_get_pageanno(self._page._document.ddjvu_document, self._page._n)
|
|
- )
|
|
-
|
|
- property page:
|
|
- '''
|
|
- Return the concerned page.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._page
|
|
-
|
|
-TEXT_DETAILS_PAGE = Symbol('page')
|
|
-TEXT_DETAILS_COLUMN = Symbol('column')
|
|
-TEXT_DETAILS_REGION = Symbol('region')
|
|
-TEXT_DETAILS_PARAGRAPH = Symbol('para')
|
|
-TEXT_DETAILS_LINE = Symbol('line')
|
|
-TEXT_DETAILS_WORD = Symbol('word')
|
|
-TEXT_DETAILS_CHARACTER = Symbol('char')
|
|
-TEXT_DETAILS_ALL = None
|
|
-
|
|
-cdef object TEXT_DETAILS
|
|
-TEXT_DETAILS = {
|
|
- TEXT_DETAILS_PAGE: 7,
|
|
- TEXT_DETAILS_COLUMN: 6,
|
|
- TEXT_DETAILS_REGION: 5,
|
|
- TEXT_DETAILS_PARAGRAPH: 4,
|
|
- TEXT_DETAILS_LINE: 3,
|
|
- TEXT_DETAILS_WORD: 2,
|
|
- TEXT_DETAILS_CHARACTER: 1,
|
|
-}
|
|
-
|
|
-def cmp_text_zone(zonetype1, zonetype2):
|
|
- '''
|
|
- cmp_text_zone(zonetype1, zonetype2) -> integer
|
|
-
|
|
- Return:
|
|
-
|
|
- - negative if zonetype1 is more concrete than zonetype2;
|
|
- - zero if zonetype1 == zonetype2;
|
|
- - positive if zonetype1 is more general than zonetype2.
|
|
-
|
|
- Possible zone types:
|
|
-
|
|
- - TEXT_ZONE_PAGE,
|
|
- - TEXT_ZONE_COLUMN,
|
|
- - TEXT_ZONE_REGION,
|
|
- - TEXT_ZONE_PARAGRAPH,
|
|
- - TEXT_ZONE_LINE,
|
|
- - TEXT_ZONE_WORD,
|
|
- - TEXT_ZONE_CHARACTER.
|
|
- '''
|
|
- if not typecheck(zonetype1, Symbol) or not typecheck(zonetype2, Symbol):
|
|
- raise TypeError('zonetype must be a symbol')
|
|
- try:
|
|
- n1 = TEXT_DETAILS[zonetype1]
|
|
- n2 = TEXT_DETAILS[zonetype2]
|
|
- except KeyError:
|
|
- raise ValueError('zonetype must be equal to TEXT_ZONE_PAGE, or TEXT_ZONE_COLUMN, or TEXT_ZONE_REGION, or TEXT_ZONE_PARAGRAPH, or TEXT_ZONE_LINE, or TEXT_ZONE_WORD, or TEXT_ZONE_CHARACTER')
|
|
- if n1 < n2:
|
|
- return -1
|
|
- elif n1 > n2:
|
|
- return 1
|
|
- else:
|
|
- return 0
|
|
-
|
|
-cdef class PageText:
|
|
- '''
|
|
- PageText(page, details=TEXT_DETAILS_ALL) -> wrapper around page text
|
|
-
|
|
- details controls the level of details in the returned S-expression:
|
|
-
|
|
- - TEXT_DETAILS_PAGE,
|
|
- - TEXT_DETAILS_COLUMN,
|
|
- - TEXT_DETAILS_REGION,
|
|
- - TEXT_DETAILS_PARAGRAPH,
|
|
- - TEXT_DETAILS_LINE,
|
|
- - TEXT_DETAILS_WORD,
|
|
- - TEXT_DETAILS_CHARACTER,
|
|
- - TEXT_DETAILS_ALL.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, Page page not None, details=TEXT_DETAILS_ALL):
|
|
- if details is None:
|
|
- self._details = charp_to_bytes('', 0)
|
|
- elif not typecheck(details, Symbol):
|
|
- raise TypeError('details must be a symbol or none')
|
|
- elif details not in TEXT_DETAILS:
|
|
- raise ValueError('details must be equal to TEXT_DETAILS_PAGE, or TEXT_DETAILS_COLUMN, or TEXT_DETAILS_REGION, or TEXT_DETAILS_PARAGRAPH, or TEXT_DETAILS_LINE, or TEXT_DETAILS_WORD, or TEXT_DETAILS_CHARACTER or TEXT_DETAILS_ALL')
|
|
- else:
|
|
- self._details = details.bytes
|
|
- self._page = page
|
|
- self._sexpr = None
|
|
-
|
|
- cdef object _update_sexpr(self):
|
|
- if self._sexpr is None:
|
|
- self._sexpr = wrap_sexpr(
|
|
- self._page._document,
|
|
- ddjvu_document_get_pagetext(self._page._document.ddjvu_document, self._page._n, self._details)
|
|
- )
|
|
-
|
|
- def wait(self):
|
|
- '''
|
|
- PT.wait() -> None
|
|
-
|
|
- Wait until the associated S-expression is available.
|
|
- '''
|
|
- while True:
|
|
- self._page._document._condition.acquire()
|
|
- try:
|
|
- try:
|
|
- self.sexpr
|
|
- return
|
|
- except NotAvailable:
|
|
- self._page._document._condition.wait()
|
|
- finally:
|
|
- self._page._document._condition.release()
|
|
-
|
|
- property page:
|
|
- '''
|
|
- Return the concerned page.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._page
|
|
-
|
|
- property sexpr:
|
|
- '''
|
|
- Return the associated S-expression. See "Hidden text syntax" in the
|
|
- djvused manual page.
|
|
-
|
|
- If the S-expression is not available, raise NotAvailable exception.
|
|
- Then, PageInfoMessage messages with empty page_job may be emitted.
|
|
-
|
|
- Possible exceptions: NotAvailable, JobFailed.
|
|
- '''
|
|
- def __get__(self):
|
|
- self._update_sexpr()
|
|
- try:
|
|
- sexpr = self._sexpr()
|
|
- exception = JobException_from_sexpr(sexpr)
|
|
- if exception is not None:
|
|
- raise exception
|
|
- return sexpr
|
|
- except InvalidExpression:
|
|
- self._sexpr = None
|
|
- raise _NotAvailable_
|
|
-
|
|
-cdef class Hyperlinks:
|
|
- '''
|
|
- Hyperlinks(annotations) -> sequence of hyperlinks
|
|
-
|
|
- Parse the annotations and return a sequence of '(maparea ...)'
|
|
- S-expressions.
|
|
-
|
|
- See also '(maparea ...)' in the djvused manual page.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, Annotations annotations not None):
|
|
- cdef cexpr_t* all
|
|
- cdef cexpr_t* current
|
|
- all = ddjvu_anno_get_hyperlinks(annotations._sexpr._cexpr)
|
|
- if all == NULL:
|
|
- raise MemoryError
|
|
- try:
|
|
- current = all
|
|
- self._sexpr = []
|
|
- while current[0]:
|
|
- list_append(self._sexpr, wrap_sexpr(annotations._document, current[0]))
|
|
- current = current + 1
|
|
- finally:
|
|
- free(all)
|
|
-
|
|
- def __len__(self):
|
|
- return len(self._sexpr)
|
|
-
|
|
- def __getitem__(self, Py_ssize_t n):
|
|
- return self._sexpr[n]()
|
|
-
|
|
-cdef class Metadata:
|
|
- '''
|
|
- Metadata(annotations) -> mapping of metadata
|
|
-
|
|
- Parse the annotations and return a mapping of metadata.
|
|
-
|
|
- See also '(metadata ...)' in the djvused manual page.
|
|
- '''
|
|
-
|
|
- def __cinit__(self, Annotations annotations not None):
|
|
- cdef cexpr_t* all
|
|
- cdef cexpr_t* current
|
|
- self._annotations = annotations
|
|
- all = ddjvu_anno_get_metadata_keys(annotations._sexpr._cexpr)
|
|
- if all == NULL:
|
|
- raise MemoryError
|
|
- try:
|
|
- current = all
|
|
- keys = []
|
|
- while current[0]:
|
|
- list_append(keys, unicode(wrap_sexpr(annotations._document, current[0])().value))
|
|
- current = current + 1
|
|
- self._keys = frozenset(keys)
|
|
- finally:
|
|
- free(all)
|
|
-
|
|
- def __len__(self):
|
|
- return len(self._keys)
|
|
-
|
|
- def __getitem__(self, key):
|
|
- cdef _WrappedCExpr cexpr_key
|
|
- cdef const char *s
|
|
- cexpr_key = py2cexpr(Symbol(key))
|
|
- s = ddjvu_anno_get_metadata(self._annotations._sexpr._cexpr, cexpr_key.cexpr())
|
|
- if s == NULL:
|
|
- raise KeyError(key)
|
|
- return decode_utf8(s)
|
|
-
|
|
- def keys(self):
|
|
- '''
|
|
- M.keys() -> sequence of M's keys
|
|
- '''
|
|
- return self._keys
|
|
-
|
|
- IF not PY3K:
|
|
- def iterkeys(self):
|
|
- '''
|
|
- M.iterkeys() -> an iterator over the keys of M
|
|
- '''
|
|
- return iter(self)
|
|
-
|
|
- def __iter__(self):
|
|
- return iter(self._keys)
|
|
-
|
|
- def values(self):
|
|
- '''
|
|
- M.values() -> list of M's values
|
|
- '''
|
|
- return map(self.__getitem__, self._keys)
|
|
-
|
|
- IF not PY3K:
|
|
- def itervalues(self):
|
|
- '''
|
|
- M.itervalues() -> an iterator over values of M
|
|
- '''
|
|
- return imap(self.__getitem__, self._keys)
|
|
-
|
|
- def items(self):
|
|
- '''
|
|
- M.items() -> list of M's (key, value) pairs, as 2-tuples
|
|
- '''
|
|
- return zip(self._keys, imap(self.__getitem__, self._keys))
|
|
-
|
|
- IF not PY3K:
|
|
- def iteritems(self):
|
|
- '''
|
|
- M.iteritems() -> an iterator over the (key, value) items of M
|
|
- '''
|
|
- return izip(self._keys, imap(self.__getitem__, self._keys))
|
|
-
|
|
- IF not PY3K:
|
|
- def has_key(self, k):
|
|
- '''
|
|
- M.has_key(k) -> True if D has a key k, else False
|
|
- '''
|
|
- return k in self
|
|
-
|
|
- def __contains__(self, k):
|
|
- return k in self._keys
|
|
-
|
|
-__author__ = 'Jakub Wilk <jwilk@jwilk.net>'
|
|
-IF PY3K:
|
|
- __version__ = decode_utf8(PYTHON_DJVULIBRE_VERSION)
|
|
-ELSE:
|
|
- __version__ = str(PYTHON_DJVULIBRE_VERSION)
|
|
-
|
|
-# vim:ts=4 sts=4 sw=4 et ft=pyrex
|
|
--- a/djvu/dllpath.py
|
|
+++ /dev/null
|
|
@@ -1,78 +0,0 @@
|
|
-# encoding=UTF-8
|
|
-
|
|
-# Copyright © 2011-2017 Jakub Wilk <jwilk@jwilk.net>
|
|
-#
|
|
-# This file is part of python-djvulibre.
|
|
-#
|
|
-# python-djvulibre is free software; you can redistribute it and/or modify it
|
|
-# under the terms of the GNU General Public License version 2 as published by
|
|
-# the Free Software Foundation.
|
|
-#
|
|
-# python-djvulibre is distributed in the hope that it will be useful, but
|
|
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
-# more details.
|
|
-
|
|
-'''
|
|
-ease finding DjVuLibre DLLs in non-standard locations
|
|
-'''
|
|
-
|
|
-import ctypes
|
|
-import os
|
|
-
|
|
-if os.name != 'nt':
|
|
- raise ImportError('This module is for Windows only')
|
|
-
|
|
-try:
|
|
- # Python 3.X
|
|
- import winreg
|
|
- unicode = str
|
|
-except ImportError:
|
|
- # Python 2.X
|
|
- import _winreg as winreg
|
|
-
|
|
-def _get(key, subkey):
|
|
- unicode = type(b''.decode())
|
|
- with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as registry:
|
|
- with winreg.OpenKey(registry, key) as regkey:
|
|
- value, tp = winreg.QueryValueEx(regkey, subkey)
|
|
- del tp
|
|
- if not isinstance(value, unicode):
|
|
- raise TypeError
|
|
- return value
|
|
-
|
|
-_djvulibre_key = r'Software\Microsoft\Windows\CurrentVersion\Uninstall\DjVuLibre+DjView'
|
|
-
|
|
-def guess_dll_path():
|
|
- try:
|
|
- path = _get(_djvulibre_key, 'UninstallString')
|
|
- except (TypeError, WindowsError):
|
|
- return
|
|
- path = os.path.dirname(path)
|
|
- if os.path.isfile(os.path.join(path, 'libdjvulibre.dll')):
|
|
- return path
|
|
-
|
|
-def _guess_dll_version():
|
|
- try:
|
|
- version = _get(_djvulibre_key, 'DisplayVersion')
|
|
- except (TypeError, WindowsError):
|
|
- return
|
|
- return version.split('+')[0]
|
|
-
|
|
-def set_dll_search_path(path=None):
|
|
- unicode = type(b''.decode())
|
|
- if path is None:
|
|
- path = guess_dll_path()
|
|
- if path is None:
|
|
- return
|
|
- if not isinstance(path, unicode):
|
|
- raise TypeError
|
|
- ctypes.windll.kernel32.SetDllDirectoryW(path)
|
|
- return path
|
|
-
|
|
-__all__ = [
|
|
- 'guess_dll_path',
|
|
- 'set_dll_search_path'
|
|
-]
|
|
-
|
|
-# vim:ts=4 sts=4 sw=4 et
|
|
--- a/djvu/sexpr.pxd
|
|
+++ /dev/null
|
|
@@ -1,32 +0,0 @@
|
|
-# Copyright © 2007-2018 Jakub Wilk <jwilk@jwilk.net>
|
|
-#
|
|
-# This file is part of python-djvulibre.
|
|
-#
|
|
-# python-djvulibre is free software; you can redistribute it and/or modify it
|
|
-# under the terms of the GNU General Public License version 2 as published by
|
|
-# the Free Software Foundation.
|
|
-#
|
|
-# python-djvulibre is distributed in the hope that it will be useful, but
|
|
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
-# more details.
|
|
-
|
|
-#cython: language_level=2
|
|
-
|
|
-cdef extern from 'libdjvu/miniexp.h':
|
|
- struct cexpr_s 'miniexp_s'
|
|
- ctypedef cexpr_s* cexpr_t 'miniexp_t'
|
|
-
|
|
- cdef extern struct cvar_s 'minivar_s'
|
|
- ctypedef cvar_s cvar_t 'minivar_t'
|
|
-
|
|
-cdef class _WrappedCExpr:
|
|
- cdef cvar_t* cvar
|
|
- cdef cexpr_t cexpr(self)
|
|
- cdef object print_into(self, object, object, bint)
|
|
- cdef object as_string(self, object, bint)
|
|
-
|
|
-cdef object public_c2py(cexpr_t)
|
|
-cdef _WrappedCExpr public_py2c(object)
|
|
-
|
|
-# vim:ts=4 sts=4 sw=4 et ft=pyrex
|
|
--- a/djvu/sexpr.pyx
|
|
+++ /dev/null
|
|
@@ -1,1091 +0,0 @@
|
|
-# Copyright © 2007-2019 Jakub Wilk <jwilk@jwilk.net>
|
|
-#
|
|
-# This file is part of python-djvulibre.
|
|
-#
|
|
-# python-djvulibre is free software; you can redistribute it and/or modify it
|
|
-# under the terms of the GNU General Public License version 2 as published by
|
|
-# the Free Software Foundation.
|
|
-#
|
|
-# python-djvulibre is distributed in the hope that it will be useful, but
|
|
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
-# more details.
|
|
-
|
|
-#cython: autotestdict=False
|
|
-#cython: language_level=2
|
|
-
|
|
-'''
|
|
-DjVuLibre bindings: module for handling Lisp S-expressions
|
|
-'''
|
|
-
|
|
-cimport cython
|
|
-
|
|
-include 'common.pxi'
|
|
-
|
|
-cdef extern from 'libdjvu/miniexp.h':
|
|
- int cexpr_is_int 'miniexp_numberp'(cexpr_t sexp) nogil
|
|
- int cexpr_to_int 'miniexp_to_int'(cexpr_t sexp) nogil
|
|
- cexpr_t int_to_cexpr 'miniexp_number'(int n) nogil
|
|
-
|
|
- int cexpr_is_symbol 'miniexp_symbolp'(cexpr_t sexp) nogil
|
|
- char* cexpr_to_symbol 'miniexp_to_name'(cexpr_t sexp) nogil
|
|
- cexpr_t symbol_to_cexpr 'miniexp_symbol'(char* name) nogil
|
|
-
|
|
- cexpr_t cexpr_nil 'miniexp_nil'
|
|
- cexpr_t cexpr_dummy 'miniexp_dummy'
|
|
- int cexpr_is_list 'miniexp_listp'(cexpr_t exp) nogil
|
|
- int cexpr_is_nonempty_list 'miniexp_consp'(cexpr_t exp) nogil
|
|
- int cexpr_length 'miniexp_length'(cexpr_t exp) nogil
|
|
- cexpr_t cexpr_head 'miniexp_car'(cexpr_t exp) nogil
|
|
- cexpr_t cexpr_tail 'miniexp_cdr'(cexpr_t exp) nogil
|
|
- cexpr_t cexpr_nth 'miniexp_nth'(int n, cexpr_t exp) nogil
|
|
- cexpr_t pair_to_cexpr 'miniexp_cons'(cexpr_t head, cexpr_t tail) nogil
|
|
- cexpr_t cexpr_replace_head 'miniexp_rplaca'(cexpr_t exp, cexpr_t new_head) nogil
|
|
- cexpr_t cexpr_replace_tail 'miniexp_rplacd'(cexpr_t exp, cexpr_t new_tail) nogil
|
|
- cexpr_t cexpr_reverse_list 'miniexp_reverse'(cexpr_t exp) nogil
|
|
-
|
|
- int cexpr_is_str 'miniexp_stringp'(cexpr_t cexpr) nogil
|
|
- const char * cexpr_to_str 'miniexp_to_str'(cexpr_t cexpr) nogil
|
|
- cexpr_t str_to_cexpr 'miniexp_string'(const char *s) nogil
|
|
- cexpr_t cexpr_substr 'miniexp_substring'(const char *s, int n) nogil
|
|
- cexpr_t cexpr_concat 'miniexp_concat'(cexpr_t cexpr_list) nogil
|
|
-
|
|
- cexpr_t gc_lock 'minilisp_acquire_gc_lock'(cexpr_t cexpr) nogil
|
|
- cexpr_t gc_unlock 'minilisp_release_gc_lock'(cexpr_t cexpr) nogil
|
|
-
|
|
- cvar_t* cvar_new 'minivar_alloc'() nogil
|
|
- void cvar_free 'minivar_free'(cvar_t* v) nogil
|
|
- cexpr_t* cvar_ptr 'minivar_pointer'(cvar_t* v) nogil
|
|
-
|
|
- IF HAVE_MINIEXP_IO_T:
|
|
- ctypedef cexpr_io_s cexpr_io_t 'miniexp_io_t'
|
|
- struct cexpr_io_s 'miniexp_io_s':
|
|
- int (*puts 'fputs')(cexpr_io_t*, char*)
|
|
- int (*getc 'fgetc')(cexpr_io_t*)
|
|
- int (*ungetc)(cexpr_io_t*, int)
|
|
- void *data[4]
|
|
- int *p_flags
|
|
- void cexpr_io_init 'miniexp_io_init'(cexpr_io_t *cio)
|
|
- enum:
|
|
- cexpr_io_print7bits 'miniexp_io_print7bits'
|
|
- cexpr_t cexpr_read 'miniexp_read_r'(cexpr_io_t *cio)
|
|
- cexpr_t cexpr_print 'miniexp_prin_r'(cexpr_io_t *cio, cexpr_t cexpr)
|
|
- cexpr_t cexpr_printw 'miniexp_pprin_r'(cexpr_io_t *cio, cexpr_t cexpr, int width)
|
|
- ELSE:
|
|
- int io_7bit 'minilisp_print_7bits'
|
|
- int (*io_puts 'minilisp_puts')(char *s)
|
|
- int (*io_getc 'minilisp_getc')()
|
|
- int (*io_ungetc 'minilisp_ungetc')(int c)
|
|
- cexpr_t cexpr_read 'miniexp_read'()
|
|
- cexpr_t cexpr_print 'miniexp_prin'(cexpr_t cexpr)
|
|
- cexpr_t cexpr_printw 'miniexp_pprin'(cexpr_t cexpr, int width)
|
|
-
|
|
-cdef extern from 'stdio.h':
|
|
- int EOF
|
|
-
|
|
-cdef object sys
|
|
-import sys
|
|
-
|
|
-cdef object format_exc
|
|
-from traceback import format_exc
|
|
-
|
|
-cdef object StringIO
|
|
-IF PY3K:
|
|
- from io import StringIO
|
|
-ELSE:
|
|
- from cStringIO import StringIO
|
|
-
|
|
-cdef object BytesIO
|
|
-from io import BytesIO
|
|
-
|
|
-cdef object weakref
|
|
-import weakref
|
|
-
|
|
-cdef object symbol_dict
|
|
-symbol_dict = weakref.WeakValueDictionary()
|
|
-
|
|
-cdef object codecs
|
|
-import codecs
|
|
-
|
|
-IF not HAVE_MINIEXP_IO_T:
|
|
- cdef Lock _myio_lock
|
|
- _myio_lock = allocate_lock()
|
|
-
|
|
-cdef class _ExpressionIO:
|
|
- IF HAVE_MINIEXP_IO_T:
|
|
- cdef cexpr_io_t cio
|
|
- cdef int flags
|
|
- ELSE:
|
|
- cdef int (*backup_io_puts)(const char *s)
|
|
- cdef int (*backup_io_getc)()
|
|
- cdef int (*backup_io_ungetc)(int c)
|
|
- cdef int backup_io_7bit
|
|
- cdef object stdin 'stdin_fp'
|
|
- cdef object stdout 'stdout_fp'
|
|
- cdef int stdout_binary
|
|
- cdef object buffer
|
|
- cdef object exc
|
|
-
|
|
- _reentrant = HAVE_MINIEXP_IO_T
|
|
-
|
|
- def __init__(self, object stdin=None, object stdout=None, int escape_unicode=True):
|
|
- IF not HAVE_MINIEXP_IO_T:
|
|
- global io_7bit, io_puts, io_getc, io_ungetc
|
|
- global _myio
|
|
- with nogil:
|
|
- acquire_lock(_myio_lock, WAIT_LOCK)
|
|
- self.backup_io_7bit = io_7bit
|
|
- self.backup_io_puts = io_puts
|
|
- self.backup_io_getc = io_getc
|
|
- self.backup_io_ungetc = io_ungetc
|
|
- self.stdin = stdin
|
|
- self.stdout = stdout
|
|
- IF PY3K:
|
|
- self.stdout_binary = not hasattr(stdout, 'encoding')
|
|
- ELSE:
|
|
- # In Python 2, sys.stdout has the encoding attribute,
|
|
- # even though it accepts byte strings.
|
|
- # Let's only make a special-case for codecs.
|
|
- self.stdout_binary = not isinstance(stdout, codecs.StreamReaderWriter)
|
|
- self.buffer = []
|
|
- self.exc = None
|
|
- IF HAVE_MINIEXP_IO_T:
|
|
- cexpr_io_init(&self.cio)
|
|
- self.cio.data[0] = <void*>self
|
|
- self.cio.getc = _myio_getc
|
|
- self.cio.ungetc = _myio_ungetc
|
|
- self.cio.puts = _myio_puts
|
|
- if escape_unicode:
|
|
- self.flags = cexpr_io_print7bits
|
|
- else:
|
|
- self.flags = 0
|
|
- self.cio.p_flags = &self.flags
|
|
- ELSE:
|
|
- io_getc = _myio_getc
|
|
- io_ungetc = _myio_ungetc
|
|
- io_puts = _myio_puts
|
|
- io_7bit = escape_unicode
|
|
- _myio = self
|
|
-
|
|
- @cython.final
|
|
- cdef close(self):
|
|
- IF not HAVE_MINIEXP_IO_T:
|
|
- global io_7bit, io_puts, io_getc, io_ungetc
|
|
- global _myio
|
|
- _myio = None
|
|
- self.stdin = None
|
|
- self.stdout = None
|
|
- self.buffer = None
|
|
- IF not HAVE_MINIEXP_IO_T:
|
|
- io_7bit = self.backup_io_7bit
|
|
- io_puts = self.backup_io_puts
|
|
- io_getc = self.backup_io_getc
|
|
- io_ungetc = self.backup_io_ungetc
|
|
- try:
|
|
- if self.exc is not None:
|
|
- raise self.exc[0], self.exc[1], self.exc[2]
|
|
- finally:
|
|
- IF not HAVE_MINIEXP_IO_T:
|
|
- release_lock(_myio_lock)
|
|
- self.exc = None
|
|
-
|
|
- IF HAVE_MINIEXP_IO_T:
|
|
-
|
|
- @cython.final
|
|
- cdef cexpr_t read(self):
|
|
- return cexpr_read(&self.cio)
|
|
-
|
|
- @cython.final
|
|
- cdef cexpr_t print_(self, cexpr_t cexpr):
|
|
- return cexpr_print(&self.cio, cexpr)
|
|
-
|
|
- @cython.final
|
|
- cdef cexpr_t printw(self, cexpr_t cexpr, int width):
|
|
- return cexpr_printw(&self.cio, cexpr, width)
|
|
-
|
|
- ELSE:
|
|
-
|
|
- @cython.final
|
|
- cdef cexpr_t read(self):
|
|
- return cexpr_read()
|
|
-
|
|
- @cython.final
|
|
- cdef cexpr_t print_(self, cexpr_t cexpr):
|
|
- return cexpr_print(cexpr)
|
|
-
|
|
- @cython.final
|
|
- cdef cexpr_t printw(self, cexpr_t cexpr, int width):
|
|
- return cexpr_printw(cexpr, width)
|
|
-
|
|
-IF HAVE_MINIEXP_IO_T:
|
|
-
|
|
- cdef int _myio_puts(cexpr_io_t* cio, const char *s):
|
|
- cdef _ExpressionIO io
|
|
- xio = <_ExpressionIO> cio.data[0]
|
|
- try:
|
|
- if xio.stdout_binary:
|
|
- xio.stdout.write(s)
|
|
- else:
|
|
- xio.stdout.write(decode_utf8(s))
|
|
- except:
|
|
- xio.exc = sys.exc_info()
|
|
- return EOF
|
|
-
|
|
- cdef int _myio_getc(cexpr_io_t* cio):
|
|
- cdef _ExpressionIO xio
|
|
- cdef int result
|
|
- xio = <_ExpressionIO> cio.data[0]
|
|
- if xio.buffer:
|
|
- return xio.buffer.pop()
|
|
- try:
|
|
- s = xio.stdin.read(1)
|
|
- if not s:
|
|
- return EOF
|
|
- if is_unicode(s):
|
|
- s = encode_utf8(s)
|
|
- IF PY3K:
|
|
- xio.buffer += reversed(s)
|
|
- ELSE:
|
|
- xio.buffer += map(ord, reversed(s))
|
|
- return xio.buffer.pop()
|
|
- except:
|
|
- xio.exc = sys.exc_info()
|
|
- return EOF
|
|
-
|
|
- cdef int _myio_ungetc(cexpr_io_t* cio, int c):
|
|
- cdef _ExpressionIO io
|
|
- xio = <_ExpressionIO> cio.data[0]
|
|
- list_append(xio.buffer, c)
|
|
-
|
|
-ELSE:
|
|
-
|
|
- cdef _ExpressionIO _myio
|
|
-
|
|
- cdef int _myio_puts(const char *s):
|
|
- try:
|
|
- if _myio.stdout_binary:
|
|
- _myio.stdout.write(s)
|
|
- else:
|
|
- _myio.stdout.write(decode_utf8(s))
|
|
- except:
|
|
- _myio.exc = sys.exc_info()
|
|
- return EOF
|
|
-
|
|
- cdef int _myio_getc():
|
|
- cdef int result
|
|
- if _myio.buffer:
|
|
- return _myio.buffer.pop()
|
|
- try:
|
|
- s = _myio.stdin.read(1)
|
|
- if not s:
|
|
- return EOF
|
|
- if is_unicode(s):
|
|
- s = encode_utf8(s)
|
|
- IF PY3K:
|
|
- _myio.buffer += reversed(s)
|
|
- ELSE:
|
|
- _myio.buffer += map(ord, reversed(s))
|
|
- return _myio.buffer.pop()
|
|
- except:
|
|
- _myio.exc = sys.exc_info()
|
|
- return EOF
|
|
-
|
|
- cdef int _myio_ungetc(int c):
|
|
- list_append(_myio.buffer, c)
|
|
-
|
|
-cdef object the_sentinel
|
|
-the_sentinel = object()
|
|
-
|
|
-cdef class _WrappedCExpr:
|
|
-
|
|
- def __cinit__(self, object sentinel):
|
|
- if sentinel is not the_sentinel:
|
|
- raise_instantiation_error(type(self))
|
|
- self.cvar = cvar_new()
|
|
-
|
|
- cdef cexpr_t cexpr(self):
|
|
- return cvar_ptr(self.cvar)[0]
|
|
-
|
|
- cdef object print_into(self, object stdout, object width, bint escape_unicode):
|
|
- cdef cexpr_t cexpr
|
|
- cdef _ExpressionIO xio
|
|
- if width is None:
|
|
- pass
|
|
- elif not is_int(width):
|
|
- raise TypeError('width must be an integer')
|
|
- elif width <= 0:
|
|
- raise ValueError('width <= 0')
|
|
- cexpr = self.cexpr()
|
|
- xio = _ExpressionIO(stdout=stdout, escape_unicode=escape_unicode)
|
|
- try:
|
|
- if width is None:
|
|
- xio.print_(cexpr)
|
|
- else:
|
|
- xio.printw(cexpr, width)
|
|
- finally:
|
|
- xio.close()
|
|
-
|
|
- cdef object as_string(self, object width, bint escape_unicode):
|
|
- stdout = StringIO()
|
|
- try:
|
|
- self.print_into(stdout, width, escape_unicode)
|
|
- return stdout.getvalue()
|
|
- finally:
|
|
- stdout.close()
|
|
-
|
|
- def __dealloc__(self):
|
|
- cvar_free(self.cvar)
|
|
-
|
|
-cdef _WrappedCExpr wexpr(cexpr_t cexpr):
|
|
- cdef _WrappedCExpr wexpr
|
|
- wexpr = _WrappedCExpr(sentinel = the_sentinel)
|
|
- cvar_ptr(wexpr.cvar)[0] = cexpr
|
|
- return wexpr
|
|
-
|
|
-cdef class _MissingCExpr(_WrappedCExpr):
|
|
-
|
|
- cdef object print_into(self, object stdout, object width, bint escape_unicode):
|
|
- raise NotImplementedError
|
|
-
|
|
- cdef object as_string(self, object width, bint escape_unicode):
|
|
- raise NotImplementedError
|
|
-
|
|
-cdef _MissingCExpr wexpr_missing():
|
|
- return _MissingCExpr(the_sentinel)
|
|
-
|
|
-
|
|
-cdef class BaseSymbol:
|
|
-
|
|
- cdef object __weakref__
|
|
- cdef object _bytes
|
|
-
|
|
- def __cinit__(self, bytes):
|
|
- cdef char *cbytes
|
|
- cbytes = bytes
|
|
- self._bytes = cbytes
|
|
-
|
|
- def __repr__(self):
|
|
- IF PY3K:
|
|
- try:
|
|
- string = self._bytes.decode('UTF-8')
|
|
- except UnicodeDecodeError:
|
|
- string = self._bytes
|
|
- ELSE:
|
|
- string = self._bytes
|
|
- return '{tp}({s!r})'.format(tp=get_type_name(_Symbol_), s=string)
|
|
-
|
|
- def __richcmp__(self, object other, int op):
|
|
- cdef BaseSymbol _self, _other
|
|
- if not typecheck(self, BaseSymbol) or not typecheck(other, BaseSymbol):
|
|
- return NotImplemented
|
|
- _self = self
|
|
- _other = other
|
|
- return richcmp(_self._bytes, _other._bytes, op)
|
|
-
|
|
- def __hash__(self):
|
|
- return hash(self._bytes)
|
|
-
|
|
- property bytes:
|
|
- def __get__(self):
|
|
- return self._bytes
|
|
-
|
|
- IF not PY3K:
|
|
- def __str__(self):
|
|
- return self._bytes
|
|
-
|
|
- IF PY3K:
|
|
- def __str__(self):
|
|
- return self._bytes.decode('UTF-8')
|
|
- ELSE:
|
|
- def __unicode__(self):
|
|
- return self._bytes.decode('UTF-8')
|
|
-
|
|
- def __reduce__(self):
|
|
- return (Symbol, (self._bytes,))
|
|
-
|
|
-class Symbol(BaseSymbol):
|
|
-
|
|
- @staticmethod
|
|
- def __new__(cls, name):
|
|
- '''
|
|
- Symbol(name) -> a symbol
|
|
- '''
|
|
- self = None
|
|
- if is_unicode(name):
|
|
- name = encode_utf8(name)
|
|
- try:
|
|
- if cls is _Symbol_:
|
|
- self = symbol_dict[name]
|
|
- except KeyError:
|
|
- pass
|
|
- if self is None:
|
|
- if not is_bytes(name):
|
|
- name = str(name)
|
|
- IF PY3K:
|
|
- name = encode_utf8(name)
|
|
- self = BaseSymbol.__new__(cls, name)
|
|
- if cls is _Symbol_:
|
|
- symbol_dict[name] = self
|
|
- return self
|
|
-
|
|
-cdef object _Symbol_
|
|
-_Symbol_ = Symbol
|
|
-
|
|
-def _expression_from_string(s):
|
|
- '''
|
|
- Expression.from_string(s) -> an expression
|
|
-
|
|
- Read an expression from a string.
|
|
- '''
|
|
- if is_unicode(s):
|
|
- s = encode_utf8(s)
|
|
- stdin = BytesIO(s)
|
|
- try:
|
|
- return _Expression_.from_stream(stdin)
|
|
- finally:
|
|
- stdin.close()
|
|
-
|
|
-class Expression(BaseExpression):
|
|
-
|
|
- '''
|
|
- Notes about the textual representation of S-expressions
|
|
- -------------------------------------------------------
|
|
-
|
|
- Special characters are:
|
|
-
|
|
- * the parenthesis '(' and ')',
|
|
- * the double quote '"',
|
|
- * the vertical bar '|'.
|
|
-
|
|
- Symbols are represented by their name. Vertical bars | can be used to
|
|
- delimit names that contain blanks, special characters, non printable
|
|
- characters, non-ASCII characters, or can be confused as a number.
|
|
-
|
|
- Numbers follow the syntax specified by the C function strtol() with
|
|
- base=0.
|
|
-
|
|
- Strings are delimited by double quotes. All C string escapes are
|
|
- recognized. Non-printable ASCII characters must be escaped.
|
|
-
|
|
- List are represented by an open parenthesis '(' followed by the space
|
|
- separated list elements, followed by a closing parenthesis ')'.
|
|
-
|
|
- When the cdr of the last pair is non zero, the closed parenthesis is
|
|
- preceded by a space, a dot '.', a space, and the textual representation
|
|
- of the cdr. (This is only partially supported by Python bindings.)
|
|
-
|
|
- '''
|
|
-
|
|
- @staticmethod
|
|
- def __new__(cls, value):
|
|
- '''
|
|
- Expression(value) -> an expression
|
|
- '''
|
|
- if typecheck(value, _Expression_) and (not typecheck(value, ListExpression) or not value):
|
|
- return value
|
|
- if is_int(value):
|
|
- return IntExpression(value)
|
|
- elif typecheck(value, _Symbol_):
|
|
- return SymbolExpression(value)
|
|
- elif is_unicode(value):
|
|
- return StringExpression(encode_utf8(value))
|
|
- elif is_bytes(value):
|
|
- if PY3K:
|
|
- return StringExpression(bytes(value))
|
|
- else:
|
|
- return StringExpression(str(value))
|
|
- else:
|
|
- return ListExpression(iter(value))
|
|
-
|
|
- @staticmethod
|
|
- def from_stream(stdin):
|
|
- '''
|
|
- Expression.from_stream(stream) -> an expression
|
|
-
|
|
- Read an expression from a stream.
|
|
- '''
|
|
- cdef _ExpressionIO xio
|
|
- try:
|
|
- xio = _ExpressionIO(stdin=stdin)
|
|
- try:
|
|
- return _c2py(xio.read())
|
|
- except InvalidExpression:
|
|
- raise ExpressionSyntaxError
|
|
- finally:
|
|
- xio.close()
|
|
-
|
|
- from_string = staticmethod(_expression_from_string)
|
|
-
|
|
-cdef object _Expression_
|
|
-_Expression_ = Expression
|
|
-
|
|
-cdef object BaseExpression_richcmp(object left, object right, int op):
|
|
- if not typecheck(left, BaseExpression):
|
|
- return NotImplemented
|
|
- elif not typecheck(right, BaseExpression):
|
|
- return NotImplemented
|
|
- return richcmp(left.value, right.value, op)
|
|
-
|
|
-cdef class BaseExpression:
|
|
- '''
|
|
- Don't use this class directly. Use the Expression class instead.
|
|
- '''
|
|
-
|
|
- cdef _WrappedCExpr wexpr
|
|
-
|
|
- def __cinit__(self, *args, **kwargs):
|
|
- self.wexpr = wexpr_missing()
|
|
-
|
|
- def print_into(self, stdout, width=None, escape_unicode=True):
|
|
- '''
|
|
- expr.print_into(file, width=None, escape_unicode=True) -> None
|
|
-
|
|
- Print the expression into the file.
|
|
- '''
|
|
- self.wexpr.print_into(stdout, width, escape_unicode)
|
|
-
|
|
- def as_string(self, width=None, escape_unicode=True):
|
|
- '''
|
|
- expr.as_string(width=None, escape_unicode=True) -> a string
|
|
-
|
|
- Return a string representation of the expression.
|
|
- '''
|
|
- return self.wexpr.as_string(width, escape_unicode)
|
|
-
|
|
- def __str__(self):
|
|
- return self.as_string()
|
|
-
|
|
- IF not PY3K:
|
|
- def __unicode__(self):
|
|
- return self.as_string().decode('UTF-8')
|
|
-
|
|
- property value:
|
|
- '''
|
|
- The "pythonic" value of the expression.
|
|
- Lisp lists as mapped to Python tuples.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._get_value()
|
|
-
|
|
- property lvalue:
|
|
- '''
|
|
- The "pythonic" value of the expression.
|
|
- Lisp lists as mapped to Python lists.
|
|
- '''
|
|
- def __get__(self):
|
|
- return self._get_lvalue()
|
|
-
|
|
- def _get_value(self):
|
|
- return self._get_lvalue()
|
|
-
|
|
- def _get_lvalue(self):
|
|
- raise NotImplementedError
|
|
-
|
|
- def __richcmp__(self, other, int op):
|
|
- return BaseExpression_richcmp(self, other, op)
|
|
-
|
|
- def __repr__(self):
|
|
- return '{tp}({expr!r})'.format(tp=get_type_name(_Expression_), expr=self.lvalue)
|
|
-
|
|
- def __copy__(self):
|
|
- # Most of S-expressions are immutable.
|
|
- # Mutable S-expressions should override this method.
|
|
- return self
|
|
-
|
|
- def __deepcopy__(self, memo):
|
|
- # Most of S-expressions are immutable.
|
|
- # Mutable S-expressions should override this method.
|
|
- return self
|
|
-
|
|
- def __reduce__(self):
|
|
- return (_expression_from_string, (self.as_string(),))
|
|
-
|
|
-class IntExpression(_Expression_):
|
|
-
|
|
- '''
|
|
- IntExpression can represent any integer in range(-2 ** 29, 2 ** 29).
|
|
-
|
|
- To create objects of this class, use the Expression class constructor.
|
|
- '''
|
|
-
|
|
- @staticmethod
|
|
- def __new__(cls, value):
|
|
- '''
|
|
- IntExpression(n) -> an integer expression
|
|
- '''
|
|
- cdef BaseExpression self
|
|
- self = BaseExpression.__new__(cls)
|
|
- if typecheck(value, _WrappedCExpr):
|
|
- self.wexpr = value
|
|
- elif is_int(value):
|
|
- if -1 << 29 <= value < 1 << 29:
|
|
- self.wexpr = wexpr(int_to_cexpr(value))
|
|
- else:
|
|
- raise ValueError('value not in range(-2 ** 29, 2 ** 29)')
|
|
- else:
|
|
- raise TypeError('value must be an integer')
|
|
- return self
|
|
-
|
|
- IF PY3K:
|
|
- def __bool__(self):
|
|
- return bool(self.value)
|
|
- ELSE:
|
|
- def __nonzero__(self):
|
|
- return bool(self.value)
|
|
-
|
|
- def __int__(self):
|
|
- return self.value
|
|
-
|
|
- def __long__(self):
|
|
- return long(self.value)
|
|
-
|
|
- def __float__(self):
|
|
- return 0.0 + self.value
|
|
-
|
|
- def _get_lvalue(BaseExpression self not None):
|
|
- return cexpr_to_int(self.wexpr.cexpr())
|
|
-
|
|
- def __richcmp__(self, other, int op):
|
|
- return BaseExpression_richcmp(self, other, op)
|
|
-
|
|
- def __hash__(self):
|
|
- return hash(self.value)
|
|
-
|
|
-class SymbolExpression(_Expression_):
|
|
- '''
|
|
- To create objects of this class, use the Expression class constructor.
|
|
- '''
|
|
-
|
|
- @staticmethod
|
|
- def __new__(cls, value):
|
|
- '''
|
|
- SymbolExpression(Symbol(s)) -> a symbol expression
|
|
- '''
|
|
- cdef BaseExpression self
|
|
- cdef BaseSymbol symbol
|
|
- self = BaseExpression.__new__(cls)
|
|
- if typecheck(value, _WrappedCExpr):
|
|
- self.wexpr = value
|
|
- elif typecheck(value, _Symbol_):
|
|
- symbol = value
|
|
- self.wexpr = wexpr(symbol_to_cexpr(symbol._bytes))
|
|
- else:
|
|
- raise TypeError('value must be a Symbol')
|
|
- return self
|
|
-
|
|
- def _get_lvalue(BaseExpression self not None):
|
|
- return _Symbol_(cexpr_to_symbol(self.wexpr.cexpr()))
|
|
-
|
|
- def __richcmp__(self, other, int op):
|
|
- return BaseExpression_richcmp(self, other, op)
|
|
-
|
|
- def __hash__(self):
|
|
- return hash(self.value)
|
|
-
|
|
-class StringExpression(_Expression_):
|
|
- '''
|
|
- To create objects of this class, use the Expression class constructor.
|
|
- '''
|
|
-
|
|
- @staticmethod
|
|
- def __new__(cls, value):
|
|
- '''
|
|
- SymbolExpression(s) -> a string expression
|
|
- '''
|
|
- cdef BaseExpression self
|
|
- self = BaseExpression.__new__(cls)
|
|
- if typecheck(value, _WrappedCExpr):
|
|
- self.wexpr = value
|
|
- elif is_bytes(value):
|
|
- gc_lock(NULL) # protect from collecting a just-created object
|
|
- try:
|
|
- self.wexpr = wexpr(str_to_cexpr(value))
|
|
- finally:
|
|
- gc_unlock(NULL)
|
|
- else:
|
|
- raise TypeError('value must be a byte string')
|
|
- return self
|
|
-
|
|
- @property
|
|
- def bytes(BaseExpression self not None):
|
|
- return cexpr_to_str(self.wexpr.cexpr())
|
|
-
|
|
- def _get_lvalue(BaseExpression self not None):
|
|
- cdef const char *bytes
|
|
- bytes = cexpr_to_str(self.wexpr.cexpr())
|
|
- IF PY3K:
|
|
- return decode_utf8(bytes)
|
|
- ELSE:
|
|
- return bytes
|
|
-
|
|
- IF PY3K:
|
|
- def __repr__(BaseExpression self not None):
|
|
- cdef const char *bytes
|
|
- bytes = cexpr_to_str(self.wexpr.cexpr())
|
|
- try:
|
|
- string = decode_utf8(bytes)
|
|
- except UnicodeDecodeError:
|
|
- string = bytes
|
|
- return '{tp}({s!r})'.format(tp=get_type_name(_Expression_), s=string)
|
|
-
|
|
- def __richcmp__(self, other, int op):
|
|
- return BaseExpression_richcmp(self, other, op)
|
|
-
|
|
- def __hash__(self):
|
|
- return hash(self.value)
|
|
-
|
|
-class InvalidExpression(ValueError):
|
|
- pass
|
|
-
|
|
-class ExpressionSyntaxError(Exception):
|
|
- '''
|
|
- Invalid expression syntax.
|
|
- '''
|
|
- pass
|
|
-
|
|
-cdef _WrappedCExpr public_py2c(object o):
|
|
- cdef BaseExpression pyexpr
|
|
- pyexpr = _Expression_(o)
|
|
- if pyexpr is None:
|
|
- raise TypeError
|
|
- return pyexpr.wexpr
|
|
-
|
|
-cdef object public_c2py(cexpr_t cexpr):
|
|
- return _c2py(cexpr)
|
|
-
|
|
-cdef BaseExpression _c2py(cexpr_t cexpr):
|
|
- if cexpr == cexpr_dummy:
|
|
- raise InvalidExpression
|
|
- _wexpr = wexpr(cexpr)
|
|
- if cexpr_is_int(cexpr):
|
|
- result = IntExpression(_wexpr)
|
|
- elif cexpr_is_symbol(cexpr):
|
|
- result = SymbolExpression(_wexpr)
|
|
- elif cexpr_is_list(cexpr):
|
|
- result = ListExpression(_wexpr)
|
|
- elif cexpr_is_str(cexpr):
|
|
- result = StringExpression(_wexpr)
|
|
- else:
|
|
- raise InvalidExpression
|
|
- return result
|
|
-
|
|
-cdef _WrappedCExpr _build_list_cexpr(object items):
|
|
- cdef cexpr_t cexpr
|
|
- cdef BaseExpression citem
|
|
- gc_lock(NULL) # protect from collecting a just-created object
|
|
- try:
|
|
- cexpr = cexpr_nil
|
|
- for item in items:
|
|
- if typecheck(item, BaseExpression):
|
|
- citem = item
|
|
- else:
|
|
- citem = _Expression_(item)
|
|
- if citem is None:
|
|
- raise TypeError
|
|
- cexpr = pair_to_cexpr(citem.wexpr.cexpr(), cexpr)
|
|
- cexpr = cexpr_reverse_list(cexpr)
|
|
- return wexpr(cexpr)
|
|
- finally:
|
|
- gc_unlock(NULL)
|
|
-
|
|
-
|
|
-class ListExpression(_Expression_):
|
|
- '''
|
|
- To create objects of this class, use the Expression class constructor.
|
|
- '''
|
|
-
|
|
- @staticmethod
|
|
- def __new__(cls, items):
|
|
- '''
|
|
- ListExpression(iterable) -> a list expression
|
|
- '''
|
|
- cdef BaseExpression self
|
|
- self = BaseExpression.__new__(cls)
|
|
- if typecheck(items, _WrappedCExpr):
|
|
- self.wexpr = items
|
|
- else:
|
|
- self.wexpr = _build_list_cexpr(items)
|
|
- return self
|
|
-
|
|
- IF PY3K:
|
|
- def __bool__(BaseExpression self not None):
|
|
- return self.wexpr.cexpr() != cexpr_nil
|
|
- ELSE:
|
|
- def __nonzero__(BaseExpression self not None):
|
|
- return self.wexpr.cexpr() != cexpr_nil
|
|
-
|
|
- def __len__(BaseExpression self not None):
|
|
- cdef cexpr_t cexpr
|
|
- cdef int n
|
|
- cexpr = self.wexpr.cexpr()
|
|
- n = 0
|
|
- while cexpr != cexpr_nil:
|
|
- cexpr = cexpr_tail(cexpr)
|
|
- n = n + 1
|
|
- return n
|
|
-
|
|
- def __getitem__(BaseExpression self not None, key):
|
|
- cdef cexpr_t cexpr
|
|
- cdef int n
|
|
- cexpr = self.wexpr.cexpr()
|
|
- if is_int(key):
|
|
- n = key
|
|
- if n < 0:
|
|
- n = n + len(self)
|
|
- if n < 0:
|
|
- raise IndexError('list index of out range')
|
|
- while True:
|
|
- if cexpr == cexpr_nil:
|
|
- raise IndexError('list index of out range')
|
|
- if n > 0:
|
|
- n = n - 1
|
|
- cexpr = cexpr_tail(cexpr)
|
|
- else:
|
|
- cexpr = cexpr_head(cexpr)
|
|
- break
|
|
- elif is_slice(key):
|
|
- if (is_int(key.start) or key.start is None) and key.stop is None and key.step is None:
|
|
- n = key.start or 0
|
|
- if n < 0:
|
|
- n = n + len(self)
|
|
- while n > 0 and cexpr != cexpr_nil:
|
|
- cexpr = cexpr_tail(cexpr)
|
|
- n = n - 1
|
|
- else:
|
|
- raise NotImplementedError('only [n:] slices are supported')
|
|
- else:
|
|
- raise TypeError('key must be an integer or a slice')
|
|
- return _c2py(cexpr)
|
|
-
|
|
- def __setitem__(BaseExpression self not None, key, value):
|
|
- cdef cexpr_t cexpr
|
|
- cdef cexpr_t prev_cexpr
|
|
- cdef cexpr_t new_cexpr
|
|
- cdef int n
|
|
- cdef BaseExpression pyexpr
|
|
- cexpr = self.wexpr.cexpr()
|
|
- pyexpr = _Expression_(value)
|
|
- new_cexpr = pyexpr.wexpr.cexpr()
|
|
- if is_int(key):
|
|
- n = key
|
|
- if n < 0:
|
|
- n = n + len(self)
|
|
- if n < 0:
|
|
- raise IndexError('list index of out range')
|
|
- while True:
|
|
- if cexpr == cexpr_nil:
|
|
- raise IndexError('list index of out range')
|
|
- if n > 0:
|
|
- n = n - 1
|
|
- cexpr = cexpr_tail(cexpr)
|
|
- else:
|
|
- cexpr_replace_head(cexpr, new_cexpr)
|
|
- break
|
|
- elif is_slice(key):
|
|
- if not cexpr_is_list(new_cexpr):
|
|
- raise TypeError('can only assign a list expression')
|
|
- if (is_int(key.start) or key.start is None) and key.stop is None and key.step is None:
|
|
- n = key.start or 0
|
|
- if n < 0:
|
|
- n = n + len(self)
|
|
- prev_cexpr = cexpr_nil
|
|
- while n > 0 and cexpr != cexpr_nil:
|
|
- prev_cexpr = cexpr
|
|
- cexpr = cexpr_tail(cexpr)
|
|
- n = n - 1
|
|
- if prev_cexpr == cexpr_nil:
|
|
- self.wexpr = wexpr(new_cexpr)
|
|
- else:
|
|
- cexpr_replace_tail(prev_cexpr, new_cexpr)
|
|
- else:
|
|
- raise NotImplementedError('only [n:] slices are supported')
|
|
- else:
|
|
- raise TypeError('key must be an integer or a slice')
|
|
-
|
|
- def __delitem__(BaseExpression self not None, key):
|
|
- if is_int(key):
|
|
- self.pop(key)
|
|
- elif is_slice(key):
|
|
- self[key] = ()
|
|
- else:
|
|
- raise TypeError('key must be an integer or a slice')
|
|
-
|
|
- def extend(self, iterable):
|
|
- iter(iterable)
|
|
- self[len(self):] = iterable
|
|
-
|
|
- def __iadd__(self, iterable):
|
|
- iter(iterable)
|
|
- self[len(self):] = iterable
|
|
- return self
|
|
-
|
|
- def insert(BaseExpression self not None, long index, item):
|
|
- cdef cexpr_t cexpr, new_cexpr
|
|
- cdef BaseExpression citem
|
|
- cexpr = self.wexpr.cexpr()
|
|
- if index < 0:
|
|
- index += len(self)
|
|
- if index < 0:
|
|
- index = 0
|
|
- if typecheck(item, BaseExpression):
|
|
- citem = item
|
|
- else:
|
|
- citem = _Expression_(item)
|
|
- if citem is None:
|
|
- raise TypeError
|
|
- if index == 0 or cexpr == cexpr_nil:
|
|
- gc_lock(NULL) # protect from collecting a just-created object
|
|
- try:
|
|
- new_cexpr = pair_to_cexpr(citem.wexpr.cexpr(), cexpr)
|
|
- self.wexpr = wexpr(new_cexpr)
|
|
- finally:
|
|
- gc_unlock(NULL)
|
|
- return
|
|
- while True:
|
|
- assert cexpr != cexpr_nil
|
|
- if index > 1 and cexpr_tail(cexpr) != cexpr_nil:
|
|
- index = index - 1
|
|
- cexpr = cexpr_tail(cexpr)
|
|
- else:
|
|
- gc_lock(NULL) # protect from collecting a just-created object
|
|
- try:
|
|
- new_cexpr = pair_to_cexpr(citem.wexpr.cexpr(), cexpr_tail(cexpr))
|
|
- cexpr_replace_tail(cexpr, new_cexpr)
|
|
- finally:
|
|
- gc_unlock(NULL)
|
|
- break
|
|
-
|
|
- def append(BaseExpression self not None, item):
|
|
- return self.insert(len(self), item)
|
|
-
|
|
- def reverse(BaseExpression self not None):
|
|
- cdef cexpr_t cexpr, new_cexpr
|
|
- gc_lock(NULL) # protect from collecting a just-created object
|
|
- try:
|
|
- new_cexpr = cexpr_reverse_list(self.wexpr.cexpr())
|
|
- self.wexpr = wexpr(new_cexpr)
|
|
- finally:
|
|
- gc_unlock(NULL)
|
|
-
|
|
- def pop(BaseExpression self not None, long index=-1):
|
|
- cdef cexpr_t cexpr, citem
|
|
- cexpr = self.wexpr.cexpr()
|
|
- if cexpr == cexpr_nil:
|
|
- raise IndexError('pop from empty list')
|
|
- if index < 0:
|
|
- index += len(self)
|
|
- if index < 0:
|
|
- raise IndexError('pop index of out range')
|
|
- if index == 0:
|
|
- result = _c2py(cexpr_head(cexpr))
|
|
- self.wexpr = wexpr(cexpr_tail(cexpr))
|
|
- return result
|
|
- while cexpr_tail(cexpr) != cexpr_nil:
|
|
- if index > 1:
|
|
- index = index - 1
|
|
- cexpr = cexpr_tail(cexpr)
|
|
- else:
|
|
- result = _c2py(cexpr_head(cexpr_tail(cexpr)))
|
|
- cexpr_replace_tail(cexpr, cexpr_tail(cexpr_tail(cexpr)))
|
|
- return result
|
|
- raise IndexError('pop index of out range')
|
|
-
|
|
- def remove(BaseExpression self not None, item):
|
|
- cdef cexpr_t cexpr
|
|
- cdef BaseExpression citem
|
|
- cexpr = self.wexpr.cexpr()
|
|
- if cexpr == cexpr_nil:
|
|
- raise IndexError('item not in list')
|
|
- if _c2py(cexpr_head(cexpr)) == item:
|
|
- self.wexpr = wexpr(cexpr_tail(cexpr))
|
|
- return
|
|
- while True:
|
|
- assert cexpr != cexpr_nil
|
|
- if cexpr_tail(cexpr) == cexpr_nil:
|
|
- raise IndexError('item not in list')
|
|
- if _c2py(cexpr_head(cexpr_tail(cexpr))) == item:
|
|
- cexpr_replace_tail(cexpr, cexpr_tail(cexpr_tail(cexpr)))
|
|
- return
|
|
- cexpr = cexpr_tail(cexpr)
|
|
-
|
|
- def index(self, value):
|
|
- # TODO: optimize
|
|
- for i, v in enumerate(self):
|
|
- if v == value:
|
|
- return i
|
|
- raise ValueError('value not in list')
|
|
-
|
|
- def count(self, value):
|
|
- # TODO: optimize
|
|
- cdef long counter = 0
|
|
- for v in self:
|
|
- if v == value:
|
|
- counter += 1
|
|
- return counter
|
|
-
|
|
- def __iter__(self):
|
|
- return _ListExpressionIterator(self)
|
|
-
|
|
- __hash__ = None
|
|
-
|
|
- def _get_value(BaseExpression self not None):
|
|
- cdef cexpr_t current
|
|
- current = self.wexpr.cexpr()
|
|
- result = []
|
|
- while current != cexpr_nil:
|
|
- list_append(result, _c2py(cexpr_head(current))._get_value())
|
|
- current = cexpr_tail(current)
|
|
- return tuple(result)
|
|
-
|
|
- def _get_lvalue(BaseExpression self not None):
|
|
- cdef cexpr_t current
|
|
- current = self.wexpr.cexpr()
|
|
- result = []
|
|
- while current != cexpr_nil:
|
|
- list_append(result, _c2py(cexpr_head(current))._get_lvalue())
|
|
- current = cexpr_tail(current)
|
|
- return result
|
|
-
|
|
- def __copy__(self):
|
|
- return _Expression_(self)
|
|
-
|
|
- def __deepcopy__(self, memo):
|
|
- return _Expression_(self._get_value())
|
|
-
|
|
-if sys.version_info >= (3, 3):
|
|
- import collections.abc as collections_abc
|
|
-else:
|
|
- import collections as collections_abc
|
|
-collections_abc.MutableSequence.register(ListExpression)
|
|
-del collections_abc
|
|
-
|
|
-cdef class _ListExpressionIterator:
|
|
-
|
|
- cdef BaseExpression expression
|
|
- cdef cexpr_t cexpr
|
|
-
|
|
- def __cinit__(self, BaseExpression expression not None):
|
|
- self.expression = expression
|
|
- self.cexpr = expression.wexpr.cexpr()
|
|
-
|
|
- def __next__(self):
|
|
- cdef cexpr_t cexpr
|
|
- cexpr = self.cexpr
|
|
- if cexpr == cexpr_nil:
|
|
- raise StopIteration
|
|
- self.cexpr = cexpr_tail(cexpr)
|
|
- cexpr = cexpr_head(cexpr)
|
|
- return _c2py(cexpr)
|
|
-
|
|
- def __iter__(self):
|
|
- return self
|
|
-
|
|
-
|
|
-__all__ = ('Symbol', 'Expression', 'IntExpression', 'SymbolExpression', 'StringExpression', 'ListExpression', 'InvalidExpression', 'ExpressionSyntaxError')
|
|
-__author__ = 'Jakub Wilk <jwilk@jwilk.net>'
|
|
-IF PY3K:
|
|
- __version__ = decode_utf8(PYTHON_DJVULIBRE_VERSION)
|
|
-ELSE:
|
|
- __version__ = str(PYTHON_DJVULIBRE_VERSION)
|
|
-
|
|
-# vim:ts=4 sts=4 sw=4 et ft=pyrex
|
|
--- a/setup.cfg
|
|
+++ b/setup.cfg
|
|
@@ -1,3 +1,8 @@
|
|
+[options]
|
|
+package_dir =
|
|
+ = src
|
|
+packages = find:
|
|
+
|
|
[pycodestyle]
|
|
filename = *.py,*.pyx,*.px[di]
|
|
ignore = E12,E131,E2,E3,E4,E501,E722,W504
|
|
--- a/setup.py
|
|
+++ b/setup.py
|
|
@@ -26,23 +26,18 @@ import re
|
|
import subprocess as ipc
|
|
import sys
|
|
|
|
-need_setuptools = False
|
|
if os.name == 'nt':
|
|
import djvu.dllpath
|
|
- need_setuptools = True
|
|
|
|
-if need_setuptools:
|
|
- import setuptools.extension
|
|
- del setuptools.extension
|
|
- del setuptools
|
|
-
|
|
-import distutils.core
|
|
-import distutils.ccompiler
|
|
-import distutils.command.build_ext
|
|
-import distutils.command.sdist
|
|
+import setuptools
|
|
+
|
|
+import setuptools.command.build_ext
|
|
+import setuptools.command.sdist
|
|
import distutils.dep_util
|
|
import distutils.dir_util
|
|
import distutils.version
|
|
+import distutils.log
|
|
+import distutils.errors
|
|
|
|
try:
|
|
import sphinx.setup_command as sphinx_setup_command
|
|
@@ -65,7 +60,7 @@ type(b'') # Python >= 2.6 is required
|
|
type(u'') # Python 2.X or >= 3.3 is required
|
|
|
|
def ext_modules():
|
|
- for pyx_file in glob.iglob(os.path.join('djvu', '*.pyx')):
|
|
+ for pyx_file in glob.iglob(os.path.join('src', 'djvu', '*.pyx')):
|
|
module, _ = os.path.splitext(os.path.basename(pyx_file))
|
|
yield module
|
|
ext_modules = list(ext_modules())
|
|
@@ -170,7 +165,7 @@ else:
|
|
# Work-around for <https://bugs.python.org/issue969718>:
|
|
os.environ.pop('CFLAGS', None)
|
|
|
|
-class build_ext(distutils.command.build_ext.build_ext):
|
|
+class build_ext(setuptools.command.build_ext.build_ext):
|
|
|
|
def run(self):
|
|
djvulibre_version = get_djvulibre_version()
|
|
@@ -199,7 +194,7 @@ class build_ext(distutils.command.build_
|
|
if '\n'.join(new_config).strip() != old_config.strip():
|
|
distutils.log.info('creating {conf!r}'.format(conf=self.config_path))
|
|
distutils.file_util.write_file(self.config_path, new_config)
|
|
- distutils.command.build_ext.build_ext.run(self)
|
|
+ setuptools.command.build_ext.build_ext.run(self)
|
|
|
|
def build_extensions(self):
|
|
self.check_extensions_list(self.extensions)
|
|
@@ -244,7 +239,7 @@ if sphinx_setup_command:
|
|
# the extension modules. Prepend the directory that build_ext would
|
|
# use instead.
|
|
build_ext = self.get_finalized_command('build_ext')
|
|
- sys.path[:0] = [build_ext.build_lib]
|
|
+ sys.path[:0] = build_ext.build_lib
|
|
for ext in ext_modules:
|
|
__import__('djvu.' + ext)
|
|
del sys.path[0]
|
|
@@ -252,7 +247,7 @@ if sphinx_setup_command:
|
|
else:
|
|
build_sphinx = None
|
|
|
|
-class sdist(distutils.command.sdist.sdist):
|
|
+class sdist(setuptools.command.sdist.sdist):
|
|
|
|
def maybe_move_file(self, base_dir, src, dst):
|
|
src = os.path.join(base_dir, src)
|
|
@@ -261,7 +256,7 @@ class sdist(distutils.command.sdist.sdis
|
|
self.move_file(src, dst)
|
|
|
|
def make_release_tree(self, base_dir, files):
|
|
- distutils.command.sdist.sdist.make_release_tree(self, base_dir, files)
|
|
+ setuptools.command.sdist.sdist.make_release_tree(self, base_dir, files)
|
|
self.maybe_move_file(base_dir, 'COPYING', 'doc/COPYING')
|
|
|
|
classifiers = '''
|
|
@@ -294,10 +289,10 @@ meta = dict(
|
|
setup_params = dict(
|
|
packages=['djvu'],
|
|
ext_modules=[
|
|
- distutils.command.build_ext.Extension(
|
|
+ setuptools.Extension(
|
|
'djvu.{mod}'.format(mod=name),
|
|
- ['djvu/{mod}.pyx'.format(mod=name)],
|
|
- depends=(['djvu/common.pxi'] + glob.glob('djvu/*.pxd')),
|
|
+ ['src/djvu/{mod}.pyx'.format(mod=name)],
|
|
+ depends=(['src/djvu/common.pxi'] + glob.glob('djvu/*.pxd')),
|
|
)
|
|
for name in ext_modules
|
|
],
|
|
@@ -315,13 +310,13 @@ if __name__ == '__main__':
|
|
if (cython_version < req_cython_version) and egg_info_for_pip:
|
|
# This shouldn't happen with pip >= 10, thanks to PEP-518 support.
|
|
# For older versions, we use this hack to trick it into installing Cython:
|
|
- distutils.core.setup(
|
|
+ setuptools.setup(
|
|
install_requires=['Cython>={ver}'.format(ver=req_cython_version)],
|
|
# Conceptually, “setup_requires” would make more sense than
|
|
# “install_requires”, but the former is not supported by pip.
|
|
**meta
|
|
)
|
|
else:
|
|
- distutils.core.setup(**setup_params)
|
|
+ setuptools.setup(**setup_params)
|
|
|
|
# vim:ts=4 sts=4 sw=4 et
|