diff --git a/setup/extensions.py b/setup/extensions.py index d6052125e5..2f2e2aa9ba 100644 --- a/setup/extensions.py +++ b/setup/extensions.py @@ -140,6 +140,7 @@ extensions = [ [ 'calibre/utils/podofo/utils.cpp', 'calibre/utils/podofo/doc.cpp', + 'calibre/utils/podofo/outline.cpp', 'calibre/utils/podofo/podofo.cpp', ], headers=[ diff --git a/src/calibre/utils/podofo/__init__.py b/src/calibre/utils/podofo/__init__.py index eb1d22d3e2..3134dcd1ba 100644 --- a/src/calibre/utils/podofo/__init__.py +++ b/src/calibre/utils/podofo/__init__.py @@ -98,7 +98,23 @@ def delete_all_but(path, pages): with open(path, 'wb') as f: f.write(raw) -if __name__ == '__main__': - f = u'/tmp/t.pdf' - delete_all_but(f, [0, 1, -2, -1]) +def test_outline(src): + podofo = get_podofo() + p = podofo.PDFDoc() + with open(src, 'rb') as f: + raw = f.read() + p.load(raw) + total = p.page_count() + root = p.create_outline(u'Table of Contents') + for i in xrange(0, total): + root.create(u'Page %d'%i, i, True) + raw = p.write() + out = '/tmp/outlined.pdf' + with open(out, 'wb') as f: + f.write(raw) + print 'Outlined PDF:', out + +if __name__ == '__main__': + import sys + test_outline(sys.argv[-1]) diff --git a/src/calibre/utils/podofo/doc.cpp b/src/calibre/utils/podofo/doc.cpp index 26951fcdce..fcc7c7253f 100644 --- a/src/calibre/utils/podofo/doc.cpp +++ b/src/calibre/utils/podofo/doc.cpp @@ -194,6 +194,41 @@ PDFDoc_set_box(PDFDoc *self, PyObject *args) { Py_RETURN_NONE; } // }}} +// create_outline() {{{ +static PyObject * +PDFDoc_create_outline(PDFDoc *self, PyObject *args) { + PyObject *p; + PDFOutlineItem *ans; + PdfString *title; + + if (!PyArg_ParseTuple(args, "U", &p)) return NULL; + title = podofo_convert_pystring(p); + if (title == NULL) return NULL; + + ans = PyObject_New(PDFOutlineItem, &PDFOutlineItemType); + if (ans == NULL) goto error; + + try { + PdfOutlines *outlines = self->doc->GetOutlines(); + if (outlines == NULL) {PyErr_NoMemory(); goto error;} + ans->item = outlines->CreateRoot(*title); + if (ans->item == NULL) {PyErr_NoMemory(); goto error;} + ans->doc = self->doc; + } catch(const PdfError & err) { + podofo_set_exception(err); goto error; + } catch (...) { + PyErr_SetString(PyExc_ValueError, "An unknown error occurred while trying to create the outline"); + goto error; + } + + delete title; + return (PyObject*)ans; +error: + Py_XDECREF(ans); delete title; + return NULL; + +} // }}} + // Properties {{{ static PyObject * @@ -430,7 +465,9 @@ static PyMethodDef PDFDoc_methods[] = { {"set_box", (PyCFunction)PDFDoc_set_box, METH_VARARGS, "set_box(page_num, box, left, bottom, width, height) -> Set the PDF bounding box for the page numbered nu, box must be one of: MediaBox, CropBox, TrimBox, BleedBox, ArtBox. The numbers are interpreted as pts." }, - + {"create_outline", (PyCFunction)PDFDoc_create_outline, METH_VARARGS, + "create_outline(title) -> Create an outline, return the root outline item." + }, {NULL} /* Sentinel */ }; diff --git a/src/calibre/utils/podofo/global.h b/src/calibre/utils/podofo/global.h index c7a5696ad6..fa9a141b21 100644 --- a/src/calibre/utils/podofo/global.h +++ b/src/calibre/utils/podofo/global.h @@ -26,7 +26,14 @@ typedef struct { } PDFDoc; +typedef struct { + PyObject_HEAD + PdfMemDocument *doc; + PdfOutlineItem *item; +} PDFOutlineItem; + extern PyTypeObject PDFDocType; +extern PyTypeObject PDFOutlineItemType; extern PyObject *Error; // Utilities diff --git a/src/calibre/utils/podofo/outline.cpp b/src/calibre/utils/podofo/outline.cpp new file mode 100644 index 0000000000..40f5852204 --- /dev/null +++ b/src/calibre/utils/podofo/outline.cpp @@ -0,0 +1,142 @@ +/* + * outline.cpp + * Copyright (C) 2012 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#include "global.h" + +using namespace pdf; + +// Constructor/destructor {{{ +static void +dealloc(PDFOutlineItem* self) +{ + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject * +new_item(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PDFOutlineItem *self; + + self = (PDFOutlineItem *)type->tp_alloc(type, 0); + if (self != NULL) { + self->item = NULL; + } + + return (PyObject *)self; +} +// }}} + +// erase() {{{ +static PyObject * +erase(PDFOutlineItem *self, PyObject *args) { + try { + self->item->Erase(); + } catch(const PdfError & err) { + podofo_set_exception(err); + return NULL; + } + Py_RETURN_NONE; +} // }}} + +static PyObject * +create(PDFOutlineItem *self, PyObject *args) { + PyObject *ptitle, *as_child = NULL; + PDFOutlineItem *ans; + int num; + PdfString *title; + PdfPage *page; + + if (!PyArg_ParseTuple(args, "Ui|O", &ptitle, &num, &as_child)) return NULL; + title = podofo_convert_pystring(ptitle); + if (title == NULL) return NULL; + + ans = PyObject_New(PDFOutlineItem, &PDFOutlineItemType); + if (ans == NULL) goto error; + ans->doc = self->doc; + + try { + page = self->doc->GetPage(num); + if (page == NULL) { PyErr_Format(PyExc_ValueError, "Invalid page number: %d", num); goto error; } + PdfDestination dest(page); + if (as_child != NULL && PyObject_IsTrue(as_child)) { + ans->item = self->item->CreateChild(*title, dest); + } else + ans->item = self->item->CreateNext(*title, dest); + } catch (const PdfError &err) { + podofo_set_exception(err); goto error; + } catch (...) { + PyErr_SetString(PyExc_Exception, "An unknown error occurred while trying to create the outline item"); + goto error; + } + + delete title; + return (PyObject*) ans; +error: + Py_XDECREF(ans); delete title; + return NULL; +} + +static PyMethodDef methods[] = { + + {"create", (PyCFunction)create, METH_VARARGS, + "create(title, pagenum, as_child=False) -> Create a new outline item with title 'title', pointing to page number pagenum. If as_child is True the new item will be a child of this item otherwise it will be a sibling. Returns the newly created item." + }, + + {"erase", (PyCFunction)erase, METH_VARARGS, + "erase() -> Delete this item and all its children, removing it from the outline tree completely." + }, + + {NULL} /* Sentinel */ +}; + + +// Type definition {{{ +PyTypeObject pdf::PDFOutlineItemType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "podofo.PDFOutlineItem", /*tp_name*/ + sizeof(PDFOutlineItem), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "PDF Outline items", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + new_item, /* tp_new */ + +}; +// }}} + + diff --git a/src/calibre/utils/podofo/podofo.cpp b/src/calibre/utils/podofo/podofo.cpp index eefe182cec..250c18b4a8 100644 --- a/src/calibre/utils/podofo/podofo.cpp +++ b/src/calibre/utils/podofo/podofo.cpp @@ -46,6 +46,9 @@ initpodofo(void) if (PyType_Ready(&pdf::PDFDocType) < 0) return; + if (PyType_Ready(&pdf::PDFOutlineItemType) < 0) + return; + pdf::Error = PyErr_NewException((char*)"podofo.Error", NULL, NULL); if (pdf::Error == NULL) return;