void Model::DjVuDocument::loadProperties(QStandardItemModel* propertiesModel) const { Document::loadProperties(propertiesModel); QMutexLocker mutexLocker(&m_mutex); propertiesModel->setColumnCount(2); miniexp_t annoExp; while(true) { annoExp = ddjvu_document_get_anno(m_document, TRUE); if(annoExp == miniexp_dummy) { clearMessageQueue(m_context, true); } else { break; } } const int annoLength = miniexp_length(annoExp); for(int annoN = 0; annoN < annoLength; ++annoN) { miniexp_t listExp = miniexp_nth(annoN, annoExp); const int listLength = miniexp_length(listExp); if(listLength <= 1 || qstrncmp(miniexp_to_name(miniexp_nth(0, listExp)), "metadata", 8) != 0) { continue; } for(int listN = 1; listN < listLength; ++listN) { miniexp_t keyValueExp = miniexp_nth(listN, listExp); if(miniexp_length(keyValueExp) != 2) { continue; } const QString key = QString::fromUtf8(miniexp_to_name(miniexp_nth(0, keyValueExp))); const QString value = QString::fromUtf8(miniexp_to_str(miniexp_nth(1, keyValueExp))); if(!key.isEmpty() && !value.isEmpty()) { propertiesModel->appendRow(QList< QStandardItem* >() << new QStandardItem(key) << new QStandardItem(value)); } } } ddjvu_miniexp_release(m_document, annoExp); }
static void loadOutline(miniexp_t outlineExp, int offset, QStandardItem* parent, const QHash< QString, int >& indexByName) { const int outlineLength = miniexp_length(outlineExp); for(int outlineN = qMax(0, offset); outlineN < outlineLength; ++outlineN) { miniexp_t bookmarkExp = miniexp_nth(outlineN, outlineExp); const int bookmarkLength = miniexp_length(bookmarkExp); if(bookmarkLength <= 1 || !miniexp_stringp(miniexp_nth(0, bookmarkExp)) || !miniexp_stringp(miniexp_nth(1, bookmarkExp))) { continue; } const QString title = QString::fromUtf8(miniexp_to_str(miniexp_nth(0, bookmarkExp))); QString destination = QString::fromUtf8(miniexp_to_str(miniexp_nth(1, bookmarkExp))); if(!title.isEmpty() && !destination.isEmpty()) { if(destination.at(0) == QLatin1Char('#')) { destination.remove(0,1); bool ok = false; int destinationPage = destination.toInt(&ok); if(!ok) { if(indexByName.contains(destination)) { destinationPage = indexByName[destination] + 1; } else { continue; } } QStandardItem* item = new QStandardItem(title); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setData(destinationPage, Qt::UserRole + 1); QStandardItem* pageItem = item->clone(); pageItem->setText(QString::number(destinationPage)); pageItem->setTextAlignment(Qt::AlignRight); parent->appendRow(QList< QStandardItem* >() << item << pageItem); if(bookmarkLength >= 3) { loadOutline(bookmarkExp, 2, item, indexByName); } } } } }
static int walkTableOfContent(lua_State *L, miniexp_t r, int *count, int depth) { depth++; miniexp_t lista = miniexp_cdr(r); // go inside bookmars in the list int length = miniexp_length(r); int counter = 0; const char* page_name; int page_number; while(counter < length-1) { lua_pushnumber(L, *count); lua_newtable(L); lua_pushstring(L, "page"); page_name = miniexp_to_str(miniexp_car(miniexp_cdr(miniexp_nth(counter, lista)))); if(page_name != NULL && page_name[0] == '#') { errno = 0; page_number = strtol(page_name + 1, NULL, 10); if(!errno) { lua_pushnumber(L, page_number); } else { /* we can not parse this as a number, TODO: parse page names */ lua_pushnumber(L, -1); } } else { /* something we did not expect here */ lua_pushnumber(L, -1); } lua_settable(L, -3); lua_pushstring(L, "depth"); lua_pushnumber(L, depth); lua_settable(L, -3); lua_pushstring(L, "title"); lua_pushstring(L, miniexp_to_str(miniexp_car(miniexp_nth(counter, lista)))); lua_settable(L, -3); lua_settable(L, -3); (*count)++; if (miniexp_length(miniexp_cdr(miniexp_nth(counter, lista))) > 1) { walkTableOfContent(L, miniexp_cdr(miniexp_nth(counter, lista)), count, depth); } counter++; } return 0; }
jint* get_djvu_hyperlink_area(ddjvu_pageinfo_t *page_info, miniexp_t sexp, int &type, int &len) { miniexp_t iter; iter = sexp; DEBUG("Hyperlink area %s", miniexp_to_name(miniexp_car(sexp))); if (miniexp_car(iter) == miniexp_symbol("rect")) type = 1; else if (miniexp_car(iter) == miniexp_symbol("oval")) type = 2; else if (miniexp_car(iter) == miniexp_symbol("poly")) type = 3; else return NULL; len = miniexp_length(iter); jint* array = new jint[len]; int x, i = 0; iter = miniexp_cdr(iter); while (iter != miniexp_nil) { if (!number_from_miniexp(miniexp_car(iter), &x)) break; iter = miniexp_cdr(iter); array[i++] = (jint) x; if (i >= len) break; } len = i; if ((type == 1 || type == 2) && len == 4) { int miny, width, height; miny = array[1]; width = array[2]; height = array[3]; array[1] = (page_info->height - (miny + height)); array[2] = array[0] + width; array[3] = (page_info->height - miny); } if (type == 3 && (len % 2) == 0) { int ccc; for (int k = 1; k < len; k += 2) { ccc = array[k]; array[k] = (page_info->height - ccc); } } return array; }
static QString loadText(miniexp_t textExp, const QRect& rect, int pageHeight) { const int textLength = miniexp_length(textExp); if(textLength >= 6 && miniexp_symbolp(miniexp_nth(0, textExp))) { const int xmin = miniexp_to_int(miniexp_nth(1, textExp)); const int ymin = miniexp_to_int(miniexp_nth(2, textExp)); const int xmax = miniexp_to_int(miniexp_nth(3, textExp)); const int ymax = miniexp_to_int(miniexp_nth(4, textExp)); if(rect.intersects(QRect(xmin, pageHeight - ymax, xmax - xmin, ymax - ymin))) { if(qstrncmp(miniexp_to_name(miniexp_nth(0, textExp)), "word", 4) == 0) { return QString::fromUtf8(miniexp_to_str(miniexp_nth(5, textExp))); } else { QStringList text; for(int textN = 5; textN < textLength; ++textN) { text.append(loadText(miniexp_nth(textN, textExp), rect, pageHeight)); } if(qstrncmp(miniexp_to_name(miniexp_nth(0, textExp)), "line", 4) == 0) { return text.join(" "); } else { return text.join("\n"); } } } } return QString(); }
void Model::DjVuDocument::loadOutline(QStandardItemModel* outlineModel) const { Document::loadOutline(outlineModel); QMutexLocker mutexLocker(&m_mutex); miniexp_t outlineExp; while(true) { outlineExp = ddjvu_document_get_outline(m_document); if(outlineExp == miniexp_dummy) { clearMessageQueue(m_context, true); } else { break; } } if(miniexp_length(outlineExp) <= 1) { return; } if(qstrncmp(miniexp_to_name(miniexp_nth(0, outlineExp)), "bookmarks", 9) != 0) { return; } ::loadOutline(outlineExp, 1, outlineModel->invisibleRootItem(), m_indexByName); ddjvu_miniexp_release(m_document, outlineExp); }
static gboolean get_djvu_hyperlink_area (ddjvu_pageinfo_t *page_info, miniexp_t sexp, EvMapping *ev_link_mapping) { miniexp_t iter; iter = sexp; if ((miniexp_car (iter) == miniexp_symbol ("rect") || miniexp_car (iter) == miniexp_symbol ("oval")) && miniexp_length (iter) == 5) { /* FIXME: get bounding box for (oval) since Evince doesn't support shaped links */ int minx, miny, width, height; iter = miniexp_cdr (iter); if (!number_from_miniexp (miniexp_car (iter), &minx)) goto unknown_link; iter = miniexp_cdr (iter); if (!number_from_miniexp (miniexp_car (iter), &miny)) goto unknown_link; iter = miniexp_cdr (iter); if (!number_from_miniexp (miniexp_car (iter), &width)) goto unknown_link; iter = miniexp_cdr (iter); if (!number_from_miniexp (miniexp_car (iter), &height)) goto unknown_link; ev_link_mapping->area.x1 = minx; ev_link_mapping->area.x2 = (minx + width); ev_link_mapping->area.y1 = (page_info->height - (miny + height)); ev_link_mapping->area.y2 = (page_info->height - miny); } else if (miniexp_car (iter) == miniexp_symbol ("poly") && miniexp_length (iter) >= 5 && miniexp_length (iter) % 2 == 1) { /* FIXME: get bounding box since Evince doesn't support shaped links */ int minx = G_MAXINT, miny = G_MAXINT; int maxx = G_MININT, maxy = G_MININT; iter = miniexp_cdr(iter); while (iter != miniexp_nil) { int x, y; if (!number_from_miniexp (miniexp_car(iter), &x)) goto unknown_link; iter = miniexp_cdr (iter); if (!number_from_miniexp (miniexp_car(iter), &y)) goto unknown_link; iter = miniexp_cdr (iter); minx = MIN (minx, x); miny = MIN (miny, y); maxx = MAX (maxx, x); maxy = MAX (maxy, y); } ev_link_mapping->area.x1 = minx; ev_link_mapping->area.x2 = maxx; ev_link_mapping->area.y1 = (page_info->height - maxy); ev_link_mapping->area.y2 = (page_info->height - miny); } else { /* unknown */ goto unknown_link; } return TRUE; unknown_link: g_warning("DjvuLibre error: Unknown hyperlink area %s", miniexp_to_name(miniexp_car(sexp))); return FALSE; }
/** * Builds the index GtkTreeModel from DjVu s-expr * * (bookmarks * ("title1" "dest1" * ("title12" "dest12" * ... ) * ... ) * ("title2" "dest2" * ... ) * ... ) */ static void build_tree (const DjvuDocument *djvu_document, GtkTreeModel *model, GtkTreeIter *parent, miniexp_t iter) { const char *title, *link_dest; char *title_markup; EvLinkAction *ev_action = NULL; EvLink *ev_link = NULL; GtkTreeIter tree_iter; if (miniexp_car (iter) == miniexp_symbol ("bookmarks")) { /* The (bookmarks) cons */ iter = miniexp_cdr (iter); } else if ( miniexp_length (iter) >= 2 ) { gchar *utf8_title = NULL; /* An entry */ if (!string_from_miniexp (miniexp_car (iter), &title)) goto unknown_entry; if (!string_from_miniexp (miniexp_cadr (iter), &link_dest)) goto unknown_entry; if (!g_utf8_validate (title, -1, NULL)) { utf8_title = str_to_utf8 (title); title_markup = g_markup_escape_text (utf8_title, -1); } else { title_markup = g_markup_escape_text (title, -1); } ev_action = get_djvu_link_action (djvu_document, link_dest, -1); if (ev_action) { ev_link = ev_link_new (utf8_title ? utf8_title : title, ev_action); gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent); gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter, EV_DOCUMENT_LINKS_COLUMN_MARKUP, title_markup, EV_DOCUMENT_LINKS_COLUMN_LINK, ev_link, EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE, -1); g_object_unref (ev_action); g_object_unref (ev_link); } else { gtk_tree_store_append (GTK_TREE_STORE (model), &tree_iter, parent); gtk_tree_store_set (GTK_TREE_STORE (model), &tree_iter, EV_DOCUMENT_LINKS_COLUMN_MARKUP, title_markup, EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE, -1); } g_free (title_markup); g_free (utf8_title); iter = miniexp_cddr (iter); parent = &tree_iter; } else { goto unknown_entry; } for (; iter != miniexp_nil; iter = miniexp_cdr (iter)) { build_tree (djvu_document, model, parent, miniexp_car (iter)); } return; unknown_entry: g_warning ("DjvuLibre error: Unknown entry in bookmarks"); return; }
QList< QRectF > Model::DjVuPage::search(const QString& text, bool matchCase) const { QMutexLocker mutexLocker(&m_parent->m_mutex); miniexp_t pageTextExp; while(true) { pageTextExp = ddjvu_document_get_pagetext(m_parent->m_document, m_index, "word"); if(pageTextExp == miniexp_dummy) { clearMessageQueue(m_parent->m_context, true); } else { break; } } QList< miniexp_t > words; QList< QRectF > results; words.append(pageTextExp); QRectF rect; int index = 0; while(!words.isEmpty()) { miniexp_t textExp = words.takeFirst(); const int textLength = miniexp_length(textExp); if(textLength >= 6 && miniexp_symbolp(miniexp_nth(0, textExp))) { if(qstrncmp(miniexp_to_name(miniexp_nth(0, textExp)), "word", 4) == 0) { const QString word = QString::fromUtf8(miniexp_to_str(miniexp_nth(5, textExp))); if(text.indexOf(word, index, matchCase ? Qt::CaseSensitive : Qt::CaseInsensitive) == index) { const int xmin = miniexp_to_int(miniexp_nth(1, textExp)); const int ymin = miniexp_to_int(miniexp_nth(2, textExp)); const int xmax = miniexp_to_int(miniexp_nth(3, textExp)); const int ymax = miniexp_to_int(miniexp_nth(4, textExp)); rect = rect.united(QRectF(xmin, m_size.height() - ymax, xmax - xmin, ymax - ymin)); index += word.length(); while(text.length() > index && text.at(index).isSpace()) { ++index; } if(text.length() == index) { results.append(rect); rect = QRectF(); index = 0; } } else { rect = QRectF(); index = 0; } } else { for(int textN = 5; textN < textLength; ++textN) { words.append(miniexp_nth(textN, textExp)); } } } } ddjvu_miniexp_release(m_parent->m_document, pageTextExp); QTransform transform = QTransform::fromScale(72.0 / m_resolution, 72.0 / m_resolution); for(int index = 0; index < results.size(); ++index) { results[index] = transform.mapRect(results[index]); } return results; }
QList< Model::Link* > Model::DjVuPage::links() const { QMutexLocker mutexLocker(&m_parent->m_mutex); QList< Link* > links; miniexp_t pageAnnoExp; while(true) { pageAnnoExp = ddjvu_document_get_pageanno(m_parent->m_document, m_index); if(pageAnnoExp == miniexp_dummy) { clearMessageQueue(m_parent->m_context, true); } else { break; } } const int pageAnnoLength = miniexp_length(pageAnnoExp); for(int pageAnnoN = 0; pageAnnoN < pageAnnoLength; ++pageAnnoN) { miniexp_t linkExp = miniexp_nth(pageAnnoN, pageAnnoExp); if(miniexp_length(linkExp) <= 3 || qstrncmp(miniexp_to_name(miniexp_nth(0, linkExp)), "maparea", 7 ) != 0 || !miniexp_symbolp(miniexp_nth(0, miniexp_nth(3, linkExp)))) { continue; } const QString type = QString::fromUtf8(miniexp_to_name(miniexp_nth(0, miniexp_nth(3, linkExp)))); if(type == QLatin1String("rect") || type == QLatin1String("oval") || type == QLatin1String("poly")) { // boundary QPainterPath boundary; miniexp_t areaExp = miniexp_nth(3, linkExp); const int areaLength = miniexp_length( areaExp ); if(areaLength == 5 && (type == QLatin1String("rect") || type == QLatin1String("oval"))) { QPoint p(miniexp_to_int(miniexp_nth(1, areaExp)), miniexp_to_int(miniexp_nth(2, areaExp))); QSize s(miniexp_to_int(miniexp_nth(3, areaExp)), miniexp_to_int(miniexp_nth(4, areaExp))); p.setY(m_size.height() - s.height() - p.y()); const QRectF r(p, s); if(type == QLatin1String("rect")) { boundary.addRect(r); } else { boundary.addEllipse(r); } } else if(areaLength > 0 && areaLength % 2 == 1 && type == QLatin1String("poly")) { QPolygon polygon; for(int areaExpN = 1; areaExpN < areaLength; areaExpN += 2) { QPoint p(miniexp_to_int(miniexp_nth(areaExpN, areaExp)), miniexp_to_int(miniexp_nth(areaExpN + 1, areaExp))); p.setY(m_size.height() - p.y()); polygon << p; } boundary.addPolygon(polygon); } if(boundary.isEmpty()) { continue; } boundary = QTransform::fromScale(1.0 / m_size.width(), 1.0 / m_size.height()).map(boundary); // target QString target; miniexp_t targetExp = miniexp_nth(1, linkExp); if(miniexp_stringp(targetExp)) { target = QString::fromUtf8(miniexp_to_str(miniexp_nth(1, linkExp))); } else if(miniexp_length(targetExp) == 3 && qstrncmp(miniexp_to_name(miniexp_nth(0, targetExp)), "url", 3) == 0) { target = QString::fromUtf8(miniexp_to_str(miniexp_nth(1, targetExp))); } if(target.isEmpty()) { continue; } if(target.at(0) == QLatin1Char('#')) { target.remove(0, 1); bool ok = false; int targetPage = target.toInt(&ok); if(!ok) { if(m_parent->m_indexByName.contains(target)) { targetPage = m_parent->m_indexByName[target] + 1; } else { continue; } } else { targetPage = (target.at(0) == QLatin1Char('+') || target.at(0) == QLatin1Char('-')) ? m_index + targetPage : targetPage; } links.append(new Link(boundary, targetPage)); } else { links.append(new Link(boundary, target)); } } } ddjvu_miniexp_release(m_parent->m_document, pageAnnoExp); return links; }
/* * Return a table like following: * { * -- a line entry * 1 = { * 1 = {word="This", x0=377, y0=4857, x1=2427, y1=5089}, * 2 = {word="is", x0=377, y0=4857, x1=2427, y1=5089}, * 3 = {word="Word", x0=377, y0=4857, x1=2427, y1=5089}, * 4 = {word="List", x0=377, y0=4857, x1=2427, y1=5089}, * x0 = 377, y0 = 4857, x1 = 2427, y1 = 5089, * }, * * -- an other line entry * 2 = { * 1 = {word="This", x0=377, y0=4857, x1=2427, y1=5089}, * 2 = {word="is", x0=377, y0=4857, x1=2427, y1=5089}, * x0 = 377, y0 = 4857, x1 = 2427, y1 = 5089, * }, * } */ static int getPageText(lua_State *L) { DjvuDocument *doc = (DjvuDocument*) luaL_checkudata(L, 1, "djvudocument"); int pageno = luaL_checkint(L, 2); /* get page height for coordinates transform */ ddjvu_pageinfo_t info; ddjvu_status_t r; while ((r=ddjvu_document_get_pageinfo( doc->doc_ref, pageno-1, &info))<DDJVU_JOB_OK) { handle(L, doc->context, TRUE); } if (r>=DDJVU_JOB_FAILED) return luaL_error(L, "cannot get page #%d information", pageno); /* start retrieving page text */ miniexp_t sexp, se_line, se_word; int i = 1, j = 1, counter_l = 1, counter_w=1, nr_line = 0, nr_word = 0; const char *word = NULL; while ((sexp = ddjvu_document_get_pagetext(doc->doc_ref, pageno-1, "word")) == miniexp_dummy) { handle(L, doc->context, True); } /* throuw page info and obtain lines info, after this, sexp's entries * are lines. */ sexp = miniexp_cdr(sexp); /* get number of lines in a page */ nr_line = miniexp_length(sexp); /* table that contains all the lines */ lua_newtable(L); counter_l = 1; for(i = 1; i <= nr_line; i++) { /* retrive one line entry */ se_line = miniexp_nth(i, sexp); nr_word = miniexp_length(se_line); if (nr_word == 0) { continue; } /* subtable that contains words in a line */ lua_pushnumber(L, counter_l); lua_newtable(L); counter_l++; /* set line position */ lua_pushstring(L, "x0"); lua_pushnumber(L, miniexp_to_int(miniexp_nth(1, se_line))); lua_settable(L, -3); lua_pushstring(L, "y1"); lua_pushnumber(L, info.height - miniexp_to_int(miniexp_nth(2, se_line))); lua_settable(L, -3); lua_pushstring(L, "x1"); lua_pushnumber(L, miniexp_to_int(miniexp_nth(3, se_line))); lua_settable(L, -3); lua_pushstring(L, "y0"); lua_pushnumber(L, info.height - miniexp_to_int(miniexp_nth(4, se_line))); lua_settable(L, -3); /* now loop through each word in the line */ counter_w = 1; for(j = 1; j <= nr_word; j++) { /* retrive one word entry */ se_word = miniexp_nth(j, se_line); /* check to see whether the entry is empty */ word = miniexp_to_str(miniexp_nth(5, se_word)); if (!word) { continue; } /* create table that contains info for a word */ lua_pushnumber(L, counter_w); lua_newtable(L); counter_w++; /* set word info */ lua_pushstring(L, "x0"); lua_pushnumber(L, miniexp_to_int(miniexp_nth(1, se_word))); lua_settable(L, -3); lua_pushstring(L, "y1"); lua_pushnumber(L, info.height - miniexp_to_int(miniexp_nth(2, se_word))); lua_settable(L, -3); lua_pushstring(L, "x1"); lua_pushnumber(L, miniexp_to_int(miniexp_nth(3, se_word))); lua_settable(L, -3); lua_pushstring(L, "y0"); lua_pushnumber(L, info.height - miniexp_to_int(miniexp_nth(4, se_word))); lua_settable(L, -3); lua_pushstring(L, "word"); lua_pushstring(L, word); lua_settable(L, -3); /* set word entry to line subtable */ lua_settable(L, -3); } /* end of for (j) */ /* set line entry to page text table */ lua_settable(L, -3); } /* end of for (i) */ return 1; }
static bool exp_to_rect(miniexp_t expression, zathura_rectangle_t* rect) { if ((miniexp_car(expression) == miniexp_symbol("rect") || miniexp_car(expression) == miniexp_symbol("oval")) && miniexp_length(expression) == 5) { int min_x = 0; int min_y = 0; int width = 0; int height = 0; miniexp_t iter = miniexp_cdr(expression); if (exp_to_int(miniexp_car(iter), &min_x) == false) { return false; } iter = miniexp_cdr(iter); if (exp_to_int(miniexp_car(iter), &min_y) == false) { return false; } iter = miniexp_cdr(iter); if (exp_to_int(miniexp_car(iter), &width) == false) { return false; } iter = miniexp_cdr(iter); if (exp_to_int(miniexp_car(iter), &height) == false) { return false; } rect->x1 = min_x; rect->x2 = min_x + width; rect->y1 = min_y; rect->y2 = min_y + height; } else if (miniexp_car(expression) == miniexp_symbol("poly") && miniexp_length(expression) >= 5) { int min_x = 0; int min_y = 0; int max_x = 0; int max_y = 0; miniexp_t iter = miniexp_cdr(expression); while (iter != miniexp_nil) { int x = 0; int y = 0; if (exp_to_int(miniexp_car(iter), &x) == false) { return false; } iter = miniexp_cdr(iter); if (exp_to_int(miniexp_car(iter), &y) == false) { return false; } iter = miniexp_cdr(iter); min_x = MIN(min_x, x); min_y = MIN(min_y, y); max_x = MAX(max_x, x); max_y = MAX(max_y, y); } rect->x1 = min_x; rect->x2 = max_x; rect->y1 = min_y; rect->y2 = max_y; } return true; }