[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1061380: bookworm-pu: package atril/1.26.0-2+deb12u2



Package: release.debian.org
Severity: normal
Tags: bookworm
User: release.debian.org@packages.debian.org
Usertags: pu
X-Debbugs-Cc: atril@packages.debian.org
Control: affects -1 + src:atril

Recently atril 1.26.0-2+deb12u1 has been accepted in bookworm for the
upcoming 12.5 point release. This upload brings a follow-up change (CVE
fix), also targetting the upcoming point release.

[ Reason ]
Fix CVE-2023-51698:
https://security-tracker.debian.org/tracker/CVE-2023-51698

[ Impact ]
CVE-2023-51698 stays unfixed and atril stays vulnerable to crafted .CBT
(Comic Book Tar) files 

[ Tests ]
I manually checked that common file formats such as PDF and ePub can
still be opened. Unfortunately, I don't own any comic book tarball files
and could not find any such files on the internet for free download. As the
patches have been cherry-picked from upstream, I assume that they are
well tested with actual .CBT files.

[ Risks ]
Comic book consumers who read their comic books with atril might be
affected if the patchset is not sane.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]

+atril (1.26.0-2+deb12u2) bookworm; urgency=medium
+
+  * debian/patches:
+    + Add 0005-Use-a-blank-line-at-most.patch and 0006-comics-Use-libarchive-
+      to-unpack-documents.patch. Use libarchive instead of external command for
+      extracing documents (CVE-2023-51698, closes: #1060751).
+
+ -- Mike Gabriel <sunweaver@debian.org>  Tue, 23 Jan 2024 10:08:40 +0100
+
+atril (1.26.0-2+deb12u1) bookworm; urgency=medium
+
+  * debian/patches:
+    + Add 1002-avoid-crash-on-certain-epub-files.patch. Avoid crashes when
+      opening certain epub files. (Closes: #972715).
+    + Add 0001-Accessibility-add-button-description.patch. Accessibility: add
+      'Hide sidebar' button description. (Cherry-picked from v1.26.1).
+    + Add 0003-epub-Fix-index-loading-for-certain-documents-look-fo.patch. Fix
+      index loading for certain epub documents. (Cherry-picked from v1.26.1).
+    + Add 0004-epub-add-fallback-for-malformed-epub-files-in-check_.patch. epub:
+      add fallback for malformed epub files in check_mime_type. (Cherry-picked from
+      v1.26.1).
+
+ -- Mike Gabriel <sunweaver@debian.org>  Sat, 06 Jan 2024 07:18:28 +0100

[ Other info ]
None.
diff -Nru atril-1.26.0/debian/changelog atril-1.26.0/debian/changelog
--- atril-1.26.0/debian/changelog	2022-10-27 11:00:10.000000000 +0200
+++ atril-1.26.0/debian/changelog	2024-01-23 10:08:40.000000000 +0100
@@ -1,3 +1,27 @@
+atril (1.26.0-2+deb12u2) bookworm; urgency=medium
+
+  * debian/patches:
+    + Add 0005-Use-a-blank-line-at-most.patch and 0006-comics-Use-libarchive-
+      to-unpack-documents.patch. Use libarchive instead of external command for
+      extracing documents (CVE-2023-51698, closes: #1060751).
+
+ -- Mike Gabriel <sunweaver@debian.org>  Tue, 23 Jan 2024 10:08:40 +0100
+
+atril (1.26.0-2+deb12u1) bookworm; urgency=medium
+
+  * debian/patches:
+    + Add 1002-avoid-crash-on-certain-epub-files.patch. Avoid crashes when
+      opening certain epub files. (Closes: #972715).
+    + Add 0001-Accessibility-add-button-description.patch. Accessibility: add
+      'Hide sidebar' button description. (Cherry-picked from v1.26.1).
+    + Add 0003-epub-Fix-index-loading-for-certain-documents-look-fo.patch. Fix
+      index loading for certain epub documents. (Cherry-picked from v1.26.1).
+    + Add 0004-epub-add-fallback-for-malformed-epub-files-in-check_.patch. epub:
+      add fallback for malformed epub files in check_mime_type. (Cherry-picked from
+      v1.26.1).
+
+ -- Mike Gabriel <sunweaver@debian.org>  Sat, 06 Jan 2024 07:18:28 +0100
+
 atril (1.26.0-2) unstable; urgency=medium
 
   [ Mike Gabriel ]
diff -Nru atril-1.26.0/debian/patches/0001-Accessibility-add-button-description.patch atril-1.26.0/debian/patches/0001-Accessibility-add-button-description.patch
--- atril-1.26.0/debian/patches/0001-Accessibility-add-button-description.patch	1970-01-01 01:00:00.000000000 +0100
+++ atril-1.26.0/debian/patches/0001-Accessibility-add-button-description.patch	2024-01-23 10:05:56.000000000 +0100
@@ -0,0 +1,47 @@
+From 9a981607b36488ea5d2ce8646540b1545e35ecd5 Mon Sep 17 00:00:00 2001
+From: Valentin Villenave <vvillenave@hypra.fr>
+Date: Tue, 26 Oct 2021 19:29:01 +0200
+Subject: [PATCH 01/10] Accessibility: add button description
+
+Signed-off-by: Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
+---
+ po/POTFILES.in     | 1 +
+ shell/ev-sidebar.c | 3 +++
+ 2 files changed, 4 insertions(+)
+
+diff --git a/po/POTFILES.in b/po/POTFILES.in
+index 02b9435..08ab5ec 100644
+--- a/po/POTFILES.in
++++ b/po/POTFILES.in
+@@ -67,6 +67,7 @@ shell/ev-password-view.c
+ shell/ev-properties-dialog.c
+ shell/ev-properties-fonts.c
+ shell/ev-properties-license.c
++shell/ev-sidebar.c
+ shell/ev-sidebar-annotations.c
+ shell/ev-sidebar-attachments.c
+ shell/ev-sidebar-bookmarks.c
+diff --git a/shell/ev-sidebar.c b/shell/ev-sidebar.c
+index b9173cd..0cdb6be 100644
+--- a/shell/ev-sidebar.c
++++ b/shell/ev-sidebar.c
+@@ -26,6 +26,8 @@
+ 
+ #include <string.h>
+ 
++#include <glib.h>
++#include <glib/gi18n.h>
+ #include <gtk/gtk.h>
+ #include <gdk/gdkkeysyms.h>
+ 
+@@ -362,6 +364,7 @@ ev_sidebar_init (EvSidebar *ev_sidebar)
+ 	g_signal_connect (close_button, "clicked",
+ 			  G_CALLBACK (ev_sidebar_close_clicked_cb),
+ 			  ev_sidebar);
++	gtk_widget_set_tooltip_text (close_button, _("Hide sidebar"));
+ 
+ 	image = gtk_image_new_from_icon_name ("window-close",
+ 	                                      GTK_ICON_SIZE_MENU);
+-- 
+2.39.2
+
diff -Nru atril-1.26.0/debian/patches/0003-epub-Fix-index-loading-for-certain-documents-look-fo.patch atril-1.26.0/debian/patches/0003-epub-Fix-index-loading-for-certain-documents-look-fo.patch
--- atril-1.26.0/debian/patches/0003-epub-Fix-index-loading-for-certain-documents-look-fo.patch	1970-01-01 01:00:00.000000000 +0100
+++ atril-1.26.0/debian/patches/0003-epub-Fix-index-loading-for-certain-documents-look-fo.patch	2024-01-23 10:05:56.000000000 +0100
@@ -0,0 +1,38 @@
+From 9f5d7343f79f6ff8295884df3229bc6696b4386c Mon Sep 17 00:00:00 2001
+From: Michael Webster <miketwebster@gmail.com>
+Date: Mon, 18 Jul 2022 10:43:47 -0400
+Subject: [PATCH 03/10] epub: Fix index loading for certain documents - look
+ for epub:type instead of epub:id.
+
+Add a null check as well.
+
+ref:
+https://help.apple.com/itc/booksassetguide/en.lproj/itc0f175a5b9.html#apdd3c4c6d1c0904
+https://idpf.org/epub/301/spec/epub-contentdocs-20140626.html#sec-xhtml-nav
+Signed-off-by: Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
+---
+ backend/epub/epub-document.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/backend/epub/epub-document.c b/backend/epub/epub-document.c
+index 52530f4..385d2fe 100644
+--- a/backend/epub/epub-document.c
++++ b/backend/epub/epub-document.c
+@@ -1201,7 +1201,13 @@ setup_index_from_navfile(gchar *tocpath)
+     GList *index = NULL;
+     open_xml_document(tocpath);
+     set_xml_root_node(NULL);
+-    xmlNodePtr nav = xml_get_pointer_to_node((xmlChar*)"nav",(xmlChar*)"id",(xmlChar*)"toc");
++    xmlNodePtr nav = xml_get_pointer_to_node((xmlChar*)"nav",(xmlChar*)"type",(xmlChar*)"toc");
++
++    if (nav == NULL) {
++        xml_free_doc();
++        return NULL;
++    }
++
+     xmlretval=NULL;
+     xml_parse_children_of_node(nav,(xmlChar*)"ol", NULL,NULL);
+     gchar *navdirend = g_strrstr(tocpath,"/");
+-- 
+2.39.2
+
diff -Nru atril-1.26.0/debian/patches/0004-epub-add-fallback-for-malformed-epub-files-in-check_.patch atril-1.26.0/debian/patches/0004-epub-add-fallback-for-malformed-epub-files-in-check_.patch
--- atril-1.26.0/debian/patches/0004-epub-add-fallback-for-malformed-epub-files-in-check_.patch	1970-01-01 01:00:00.000000000 +0100
+++ atril-1.26.0/debian/patches/0004-epub-add-fallback-for-malformed-epub-files-in-check_.patch	2024-01-23 10:05:56.000000000 +0100
@@ -0,0 +1,98 @@
+From c585b945d27e883908c437d12aa9c453db2143f4 Mon Sep 17 00:00:00 2001
+From: rbuj <robert.buj@gmail.com>
+Date: Sun, 7 Aug 2022 23:08:59 +0200
+Subject: [PATCH 04/10] epub: add fallback for malformed epub files in
+ check_mime_type
+
+Signed-off-by: Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
+---
+ backend/epub/epub-document.c | 66 +++++++++++++++++++++---------------
+ 1 file changed, 38 insertions(+), 28 deletions(-)
+
+diff --git a/backend/epub/epub-document.c b/backend/epub/epub-document.c
+index 385d2fe..451c884 100644
+--- a/backend/epub/epub-document.c
++++ b/backend/epub/epub-document.c
+@@ -625,41 +625,51 @@ xml_get_data_from_node(xmlNodePtr node,
+ static gboolean
+ check_mime_type(const gchar* uri,GError** error)
+ {
+-    GError * err = NULL ;
+-    const gchar* mimeFromFile = ev_file_get_mime_type(uri,FALSE,&err);
++    GError * err = NULL;
++    const gchar* mimeFromFile;
+ 
+-    gchar* mimetypes[] = {"application/epub+zip","application/x-booki+zip"};
+-    int typecount = 2;
+-    if ( !mimeFromFile )
++    mimeFromFile = ev_file_get_mime_type(uri, FALSE, &err);
++    if (mimeFromFile)
+     {
+-        if (err)    {
+-            g_propagate_error (error, err);
+-        }
+-        else    {
+-            g_set_error_literal (error,
+-                         EV_DOCUMENT_ERROR,
+-                         EV_DOCUMENT_ERROR_INVALID,
+-                         _("Unknown MIME Type"));
+-        }
+-        return FALSE;
+-    }
+-    else
+-    {
+-        int i=0;
+-        for (i=0; i < typecount ;i++) {
+-           if ( g_strcmp0(mimeFromFile, mimetypes[i]) == 0  ) {
++        const gchar* mimetypes[] = {"application/epub+zip", "application/x-booki+zip", NULL};
++        guint i;
++
++        for (i = 0; i < g_strv_length (mimetypes); i++) {
++           if (strcmp(mimeFromFile, mimetypes[i]) == 0)
+                 return TRUE;
+-           }
++	}
++
++        /* fallback for malformed epub files */
++        if (strcmp (mimeFromFile, "application/zip") == 0)
++        {
++            mimeFromFile = ev_file_get_mime_type (uri, TRUE, &err);
++            if (mimeFromFile)
++            {
++                for (i = 0; i < g_strv_length (mimetypes); i++) {
++                    if (g_strcmp0(mimeFromFile, mimetypes[i]) == 0)
++                        return TRUE;
++                }
++
++                /*We didn't find a match*/
++                g_set_error_literal (error,
++                                     EV_DOCUMENT_ERROR,
++                                     EV_DOCUMENT_ERROR_INVALID,
++                                     _("Not an ePub document"));
++
++                return FALSE;
++            }
+         }
++    }
+ 
+-        /*We didn't find a match*/
++    if (err)
++        g_propagate_error (error, err);
++    else
+         g_set_error_literal (error,
+-                     EV_DOCUMENT_ERROR,
+-                     EV_DOCUMENT_ERROR_INVALID,
+-                     _("Not an ePub document"));
++                             EV_DOCUMENT_ERROR,
++                             EV_DOCUMENT_ERROR_INVALID,
++                             _("Unknown MIME Type"));
+ 
+-        return FALSE;
+-    }
++    return FALSE;
+ }
+ 
+ static gboolean
+-- 
+2.39.2
+
diff -Nru atril-1.26.0/debian/patches/0005-Use-a-blank-line-at-most.patch atril-1.26.0/debian/patches/0005-Use-a-blank-line-at-most.patch
--- atril-1.26.0/debian/patches/0005-Use-a-blank-line-at-most.patch	1970-01-01 01:00:00.000000000 +0100
+++ atril-1.26.0/debian/patches/0005-Use-a-blank-line-at-most.patch	2024-01-23 10:08:40.000000000 +0100
@@ -0,0 +1,1866 @@
+From 658fab1f008bff7e1ec147d95baa04bc44c2fbcd Mon Sep 17 00:00:00 2001
+From: rbuj <robert.buj@gmail.com>
+Date: Wed, 27 Oct 2021 17:30:36 +0200
+Subject: [PATCH 1/2] Use a blank line at most
+
+Signed-off-by: Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
+---
+ backend/comics/comics-document.c              |  3 --
+ backend/djvu/djvu-document.c                  |  3 --
+ backend/djvu/djvu-links.c                     |  2 -
+ backend/djvu/djvu-text-page.c                 |  3 --
+ backend/djvu/djvu-text-page.h                 |  1 -
+ backend/dvi/dvi-document.c                    |  2 -
+ backend/dvi/fonts.c                           |  1 -
+ backend/dvi/mdvi-lib/afmparse.c               | 18 --------
+ backend/dvi/mdvi-lib/bitmap.c                 |  2 -
+ backend/dvi/mdvi-lib/color.c                  |  1 -
+ backend/dvi/mdvi-lib/color.h                  |  1 -
+ backend/dvi/mdvi-lib/fontmap.c                |  1 -
+ backend/dvi/mdvi-lib/hash.c                   |  1 -
+ backend/dvi/mdvi-lib/hash.h                   |  2 -
+ backend/dvi/mdvi-lib/mdvi.h                   |  2 -
+ backend/dvi/mdvi-lib/paper.h                  |  1 -
+ backend/dvi/mdvi-lib/special.c                |  1 -
+ backend/dvi/mdvi-lib/tfmfile.c                |  1 -
+ backend/epub/epub-document.c                  |  6 ---
+ backend/epub/minizip/ioapi.c                  |  6 ---
+ backend/epub/minizip/ioapi.h                  |  7 ---
+ backend/epub/minizip/unzip.c                  | 45 -------------------
+ backend/epub/minizip/unzip.h                  | 11 -----
+ backend/pdf/ev-poppler.h                      |  1 -
+ backend/pixbuf/pixbuf-document.c              |  1 -
+ backend/tiff/tiff-document.c                  |  1 -
+ backend/tiff/tiff2ps.c                        |  2 -
+ backend/xps/xps-document.c                    |  2 -
+ .../toolbar-editor/egg-editable-toolbar.c     |  2 -
+ .../toolbar-editor/egg-toolbar-editor.c       |  1 -
+ .../toolbar-editor/egg-toolbar-editor.h       |  2 -
+ .../toolbar-editor/egg-toolbars-model.c       |  2 -
+ cut-n-paste/zoom-control/ephy-zoom.c          |  1 -
+ libdocument/ev-document-attachments.c         |  1 -
+ libdocument/ev-document-factory.c             |  1 -
+ libdocument/ev-document-fonts.h               |  1 -
+ libdocument/ev-document-misc.c                |  1 -
+ libdocument/ev-document-security.h            |  1 -
+ libdocument/ev-document-text.c                |  1 -
+ libdocument/ev-document.c                     |  1 -
+ libdocument/ev-file-helpers.h                 |  1 -
+ libdocument/ev-form-field.h                   |  1 -
+ libdocument/ev-image.h                        |  1 -
+ libdocument/ev-render-context.h               |  2 -
+ libdocument/ev-transition-effect.h            |  2 -
+ libmisc/ev-page-action-widget.c               |  3 --
+ libview/ev-page-accessible.c                  |  2 -
+ libview/ev-pixbuf-cache.c                     |  6 ---
+ libview/ev-pixbuf-cache.h                     |  2 -
+ libview/ev-timeline.c                         |  2 -
+ libview/ev-timeline.h                         |  2 -
+ libview/ev-transition-animation.c             |  2 -
+ libview/ev-transition-animation.h             |  2 -
+ libview/ev-view-presentation.c                |  1 -
+ libview/ev-view.c                             |  2 -
+ libview/ev-web-view.c                         |  2 -
+ libview/ev-web-view.h                         |  1 -
+ previewer/ev-previewer-window.c               |  1 -
+ shell/eggfindbar.c                            |  1 -
+ shell/eggfindbar.h                            |  1 -
+ shell/ev-application.c                        |  2 -
+ shell/ev-bookmarks.h                          |  2 -
+ shell/ev-daemon.c                             |  1 -
+ shell/ev-history.c                            |  1 -
+ shell/ev-loading-message.c                    |  1 -
+ shell/ev-media-player-keys.h                  |  1 -
+ shell/ev-navigation-action.c                  |  1 -
+ shell/ev-open-recent-action.c                 |  1 -
+ shell/ev-password-view.c                      |  2 -
+ shell/ev-sidebar-attachments.c                |  1 -
+ shell/ev-sidebar-bookmarks.c                  |  1 -
+ shell/ev-sidebar-layers.c                     |  1 -
+ shell/ev-sidebar-links.c                      |  3 --
+ shell/ev-sidebar-links.h                      |  1 -
+ shell/ev-sidebar-page.c                       |  1 -
+ shell/ev-sidebar-page.h                       |  1 -
+ shell/ev-sidebar-thumbnails.c                 |  1 -
+ shell/ev-sidebar-thumbnails.h                 |  1 -
+ shell/ev-sidebar.c                            |  1 -
+ shell/ev-sidebar.h                            |  1 -
+ shell/ev-window.c                             |  9 ----
+ shell/ev-window.h                             |  2 -
+ shell/main.c                                  |  4 --
+ 83 files changed, 220 deletions(-)
+
+--- a/backend/comics/comics-document.c
++++ b/backend/comics/comics-document.c
+@@ -126,7 +126,6 @@
+ static char**     extract_argv                   (EvDocument *document,
+ 						  gint page);
+ 
+-
+ EV_BACKEND_REGISTER_WITH_CODE (ComicsDocument, comics_document,
+ 	{
+ 		EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
+@@ -186,7 +185,6 @@
+ 	return g_string_free (dest, FALSE);
+ }
+ 
+-
+ /* This function manages the command for decompressing a comic book */
+ static gboolean
+ comics_decompress_temp_dir (const gchar *command_decompress_tmp,
+@@ -627,7 +625,6 @@
+ 	return TRUE;
+ }
+ 
+-
+ static gboolean
+ comics_document_save (EvDocument *document,
+ 		      const char *uri,
+--- a/backend/djvu/djvu-document.c
++++ b/backend/djvu/djvu-document.c
+@@ -66,7 +66,6 @@
+       EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_SELECTION, djvu_selection_iface_init);
+      });
+ 
+-
+ #define EV_DJVU_ERROR ev_djvu_error_quark ()
+ 
+ static GQuark
+@@ -256,7 +255,6 @@
+ 	return TRUE;
+ }
+ 
+-
+ static gboolean
+ djvu_document_save (EvDocument  *document,
+ 		    const char  *uri,
+@@ -676,7 +674,6 @@
+ 		r->y2 = height - tmp * SCALE_FACTOR;
+ 	}
+ 
+-
+ 	return matches;
+ }
+ 
+--- a/backend/djvu/djvu-links.c
++++ b/backend/djvu/djvu-links.c
+@@ -181,7 +181,6 @@
+ 		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);
+@@ -342,7 +341,6 @@
+ 	return NULL;
+ }
+ 
+-
+ gboolean
+ djvu_links_has_document_links (EvDocumentLinks *document_links)
+ {
+--- a/backend/djvu/djvu-text-page.c
++++ b/backend/djvu/djvu-text-page.c
+@@ -23,7 +23,6 @@
+ #include <libdjvu/miniexp.h>
+ #include "djvu-text-page.h"
+ 
+-
+ /**
+  * djvu_text_page_selection_process:
+  * @page: #DjvuTextPage instance
+@@ -117,7 +116,6 @@
+ 	}
+ }
+ 
+-
+ static void
+ djvu_text_page_limits (DjvuTextPage *page,
+ 			  miniexp_t     p,
+@@ -404,7 +402,6 @@
+ 	g_free (search_text);
+ }
+ 
+-
+ /**
+  * djvu_text_page_prepare_search:
+  * @page: #DjvuTextPage instance
+--- a/backend/djvu/djvu-text-page.h
++++ b/backend/djvu/djvu-text-page.h
+@@ -25,7 +25,6 @@
+ #include <glib.h>
+ #include <libdjvu/miniexp.h>
+ 
+-
+ typedef struct _DjvuTextPage DjvuTextPage;
+ typedef struct _DjvuTextLink DjvuTextLink;
+ 
+--- a/backend/dvi/dvi-document.c
++++ b/backend/dvi/dvi-document.c
+@@ -110,7 +110,6 @@
+ 
+ 	mdvi_cairo_device_init (&dvi_document->context->device);
+ 
+-
+ 	dvi_document->base_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv
+ 		+ 2 * unit2pix(dvi_document->params->dpi, MDVI_HMARGIN) / dvi_document->params->hshrink;
+ 
+@@ -123,7 +122,6 @@
+ 	return TRUE;
+ }
+ 
+-
+ static gboolean
+ dvi_document_save (EvDocument  *document,
+ 		      const char  *uri,
+--- a/backend/dvi/fonts.c
++++ b/backend/dvi/fonts.c
+@@ -54,4 +54,3 @@
+ 	return;
+ }
+ 
+-
+--- a/backend/dvi/mdvi-lib/afmparse.c
++++ b/backend/dvi/mdvi-lib/afmparse.c
+@@ -81,13 +81,10 @@
+ 
+ #define MATCH(A,B)		(strncmp((A),(B), MAX_NAME) == 0)
+ 
+-
+-
+ /*************************** GLOBALS ***********************/
+ 
+ static char *ident = NULL; /* storage buffer for keywords */
+ 
+-
+ /* "shorts" for fast case statement
+  * The values of each of these enumerated items correspond to an entry in the
+  * table of strings defined below. Therefore, if you add a new string as
+@@ -175,7 +172,6 @@
+ 
+ } /* token */
+ 
+-
+ /*************************** linetoken *************************/
+ 
+ /*  "linetoken" will get read all tokens until the EOL character from
+@@ -203,7 +199,6 @@
+ 
+ } /* linetoken */
+ 
+-
+ /*************************** recognize *************************/
+ 
+ /*  This function tries to match a string to a known list of
+@@ -234,7 +229,6 @@
+ 
+ } /* recognize */
+ 
+-
+ /************************* parseGlobals *****************************/
+ 
+ /*  This function is called by "parseFile". It will parse the AFM File
+@@ -401,8 +395,6 @@
+ 
+ } /* parseGlobals */
+ 
+-
+-
+ #if 0 /* this function does not seem to be used anywhere */
+ /************************* initializeArray ************************/
+ 
+@@ -584,7 +576,6 @@
+ 
+ } /* parseCharWidths */
+ 
+-
+ /************************* parseCharMetrics ************************/
+ 
+ /*  This function is called by parseFile if the caller of parseFile
+@@ -694,8 +685,6 @@
+ 
+ } /* parseCharMetrics */
+ 
+-
+-
+ /************************* parseTrackKernData ***********************/
+ 
+ /*  This function is called by "parseFile". It will parse the AFM File
+@@ -797,7 +786,6 @@
+ 
+ } /* parseTrackKernData */
+ 
+-
+ /************************* parsePairKernData ************************/
+ 
+ /*  This function is called by "parseFile". It will parse the AFM File
+@@ -918,7 +906,6 @@
+ 
+ } /* parsePairKernData */
+ 
+-
+ /************************* parseCompCharData **************************/
+ 
+ /*  This function is called by "parseFile". It will parse the AFM File
+@@ -1043,12 +1030,8 @@
+ 
+ } /* parseCompCharData */
+ 
+-
+-
+-
+ /*************************** 'PUBLIC' FUNCTION ********************/
+ 
+-
+ /*************************** parseFile *****************************/
+ 
+ /*  parseFile is the only 'public' procedure available. It is called
+@@ -1078,7 +1061,6 @@
+ 
+     register char *keyword; /* used to store a token */
+ 
+-
+     /* storage data for the global variable ident */
+     ident = (char *) calloc(MAX_NAME, sizeof(char));
+     if (ident == NULL) {error = storageProblem; return(error);}
+--- a/backend/dvi/mdvi-lib/bitmap.c
++++ b/backend/dvi/mdvi-lib/bitmap.c
+@@ -117,7 +117,6 @@
+ 	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+ };
+ 
+-
+ /*
+  * next we have three bitmap functions to convert bitmaps in LSB bit order
+  * with 8, 16 and 32 bits per unit, to our internal format. The differences
+@@ -192,7 +191,6 @@
+ 	return bm;
+ }
+ 
+-
+ BITMAP	*bitmap_copy(BITMAP *bm)
+ {
+ 	BITMAP	*nb = bitmap_alloc(bm->width, bm->height);
+--- a/backend/dvi/mdvi-lib/color.c
++++ b/backend/dvi/mdvi-lib/color.c
+@@ -79,7 +79,6 @@
+ 
+ #define GAMMA_DIFF	0.005
+ 
+-
+ /* create a color table */
+ Ulong	*get_color_table(DviDevice *dev,
+ 			 int nlevels, Ulong fg, Ulong bg, double gamma, int density)
+--- a/backend/dvi/mdvi-lib/color.h
++++ b/backend/dvi/mdvi-lib/color.h
+@@ -16,7 +16,6 @@
+  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  */
+ 
+-
+ #ifndef _COLOR_H_
+ #define _COLOR_H_
+ 
+--- a/backend/dvi/mdvi-lib/fontmap.c
++++ b/backend/dvi/mdvi-lib/fontmap.c
+@@ -834,7 +834,6 @@
+ 	return 0;
+ }
+ 
+-
+ void	mdvi_flush_encodings(void)
+ {
+ 	DviEncoding *enc;
+--- a/backend/dvi/mdvi-lib/hash.c
++++ b/backend/dvi/mdvi-lib/hash.c
+@@ -21,7 +21,6 @@
+ 
+ /* simple hash tables for MDVI */
+ 
+-
+ struct _DviHashBucket {
+ 	DviHashBucket *next;
+ 	DviHashKey	key;
+--- a/backend/dvi/mdvi-lib/hash.h
++++ b/backend/dvi/mdvi-lib/hash.h
+@@ -3,7 +3,6 @@
+ 
+ /* Hash tables */
+ 
+-
+ typedef struct _DviHashBucket DviHashBucket;
+ typedef struct _DviHashTable DviHashTable;
+ 
+@@ -18,7 +17,6 @@
+ typedef int	(*DviHashComp) __PROTO((DviHashKey key1, DviHashKey key2));
+ typedef void	(*DviHashFree) __PROTO((DviHashKey key, void *data));
+ 
+-
+ struct _DviHashTable {
+ 	DviHashBucket	**buckets;
+ 	int	nbucks;
+--- a/backend/dvi/mdvi-lib/mdvi.h
++++ b/backend/dvi/mdvi-lib/mdvi.h
+@@ -414,7 +414,6 @@
+ 	int	step;		/* step */
+ };
+ 
+-
+ typedef void (*DviSpecialHandler)
+ 	__PROTO((DviContext *dvi, const char *prefix, const char *arg));
+ 
+@@ -600,7 +599,6 @@
+ 
+ extern int mdvi_encode_font __PROTO((DviParams *, DviFont *));
+ 
+-
+ /* font lookup functions */
+ extern int mdvi_register_font_type __PROTO((DviFontInfo *, int));
+ extern char **mdvi_list_font_class __PROTO((int));
+--- a/backend/dvi/mdvi-lib/paper.h
++++ b/backend/dvi/mdvi-lib/paper.h
+@@ -24,7 +24,6 @@
+ 	const char *height;
+ };
+ 
+-
+ extern int 	mdvi_get_paper_size __PROTO((const char *, DviPaper *));
+ extern DviPaperSpec* mdvi_get_paper_specs __PROTO((DviPaperClass));
+ extern void	mdvi_free_paper_specs __PROTO((DviPaperSpec *));
+--- a/backend/dvi/mdvi-lib/special.c
++++ b/backend/dvi/mdvi-lib/special.c
+@@ -212,7 +212,6 @@
+ {
+ 	DviSpecial *sp, *list;
+ 
+-
+ 	for(list = (DviSpecial *)specials.head; (sp = list); ) {
+ 		list = sp->next;
+ 		if(sp->prefix) mdvi_free(sp->prefix);
+--- a/backend/dvi/mdvi-lib/tfmfile.c
++++ b/backend/dvi/mdvi-lib/tfmfile.c
+@@ -258,7 +258,6 @@
+ 	/* allocate characters */
+ 	info->chars = xnalloc(TFMChar, size);
+ 
+-
+ #ifdef WORD_LITTLE_ENDIAN
+ 	/* byte-swap the three arrays at once (they are consecutive in memory) */
+ 	swap_array((Uint32 *)widths, nw + nh + nd);
+--- a/backend/epub/epub-document.c
++++ b/backend/epub/epub-document.c
+@@ -224,7 +224,6 @@
+     return TRUE;
+ }
+ 
+-
+ typedef struct _LinksCBStruct {
+     GtkTreeModel *model;
+     GtkTreeIter  *parent;
+@@ -413,7 +412,6 @@
+     return (g_remove (path_name));
+ }
+ 
+-
+ static gboolean
+ check_mime_type             (const gchar* uri,
+                              GError** error);
+@@ -1562,7 +1560,6 @@
+ 
+         gchar *csspath = g_strdup_printf("%s/atrilnightstyle.css",epub_document->documentdir);
+ 
+-
+         GFile *styles = g_file_new_for_path (csspath);
+         GOutputStream *outstream = (GOutputStream*)g_file_create(styles,G_FILE_CREATE_PRIVATE,NULL,NULL);
+         if ( g_output_stream_write((GOutputStream*)outstream,style,strlen(style),NULL,NULL) == -1 )
+@@ -1636,7 +1633,6 @@
+     g_list_foreach(index,(GFunc)page_set_function,contentList);
+ }
+ 
+-
+ static void
+ add_mathjax_script_node_to_file(gchar *filename, gchar *data)
+ {
+@@ -1784,7 +1780,6 @@
+     epub_document->docTitle = NULL;
+ }
+ 
+-
+ static void
+ epub_document_finalize (GObject *object)
+ {
+@@ -1826,7 +1821,6 @@
+     G_OBJECT_CLASS (epub_document_parent_class)->finalize (object);
+ }
+ 
+-
+ static void
+ epub_document_class_init (EpubDocumentClass *klass)
+ {
+--- a/backend/epub/minizip/ioapi.c
++++ b/backend/epub/minizip/ioapi.c
+@@ -25,7 +25,6 @@
+ #define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
+ #endif
+ 
+-
+ #include "ioapi.h"
+ 
+ voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)
+@@ -82,8 +81,6 @@
+     p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file;
+ }
+ 
+-
+-
+ static voidpf  ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode));
+ static uLong   ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size));
+ static uLong   ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size));
+@@ -128,7 +125,6 @@
+     return file;
+ }
+ 
+-
+ static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size)
+ {
+     uLong ret;
+@@ -150,7 +146,6 @@
+     return ret;
+ }
+ 
+-
+ static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream)
+ {
+     ZPOS64_T ret;
+@@ -206,7 +201,6 @@
+     return ret;
+ }
+ 
+-
+ static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream)
+ {
+     int ret;
+--- a/backend/epub/minizip/ioapi.h
++++ b/backend/epub/minizip/ioapi.h
+@@ -110,7 +110,6 @@
+ extern "C" {
+ #endif
+ 
+-
+ #define ZLIB_FILEFUNC_SEEK_CUR (1)
+ #define ZLIB_FILEFUNC_SEEK_END (2)
+ #define ZLIB_FILEFUNC_SEEK_SET (0)
+@@ -122,7 +121,6 @@
+ #define ZLIB_FILEFUNC_MODE_EXISTING (4)
+ #define ZLIB_FILEFUNC_MODE_CREATE   (8)
+ 
+-
+ #ifndef ZCALLBACK
+  #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
+    #define ZCALLBACK CALLBACK
+@@ -131,9 +129,6 @@
+  #endif
+ #endif
+ 
+-
+-
+-
+ typedef voidpf   (ZCALLBACK *open_file_func)      OF((voidpf opaque, const char* filename, int mode));
+ typedef uLong    (ZCALLBACK *read_file_func)      OF((voidpf opaque, voidpf stream, void* buf, uLong size));
+ typedef uLong    (ZCALLBACK *write_file_func)     OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
+@@ -143,7 +138,6 @@
+ typedef long     (ZCALLBACK *tell_file_func)      OF((voidpf opaque, voidpf stream));
+ typedef long     (ZCALLBACK *seek_file_func)      OF((voidpf opaque, voidpf stream, uLong offset, int origin));
+ 
+-
+ /* here is the "old" 32 bits structure structure */
+ typedef struct zlib_filefunc_def_s
+ {
+@@ -185,7 +179,6 @@
+     seek_file_func      zseek32_file;
+ } zlib_filefunc64_32_def;
+ 
+-
+ #define ZREAD64(filefunc,filestream,buf,size)     ((*((filefunc).zfile_func64.zread_file))   ((filefunc).zfile_func64.opaque,filestream,buf,size))
+ #define ZWRITE64(filefunc,filestream,buf,size)    ((*((filefunc).zfile_func64.zwrite_file))  ((filefunc).zfile_func64.opaque,filestream,buf,size))
+ //#define ZTELL64(filefunc,filestream)            ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream))
+--- a/backend/epub/minizip/unzip.c
++++ b/backend/epub/minizip/unzip.c
+@@ -12,7 +12,6 @@
+ 
+          For more info read MiniZip_info.txt
+ 
+-
+   ------------------------------------------------------------------------------------
+   Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of
+   compatibility with older software. The following is from the original crypt.c.
+@@ -48,7 +47,6 @@
+ 
+         Copyright (C) 2007-2008 Even Rouault
+ 
+-
+         Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again).
+   Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G
+                                 should only read the compressed/uncompressed size from the Zip64 format if
+@@ -63,7 +61,6 @@
+ 
+ */
+ 
+-
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -86,20 +83,17 @@
+ #   include <errno.h>
+ #endif
+ 
+-
+ #ifndef local
+ #  define local static
+ #endif
+ /* compile with -Dlocal if your debugger can't find static symbols */
+ 
+-
+ #ifndef CASESENSITIVITYDEFAULT_NO
+ #  if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES)
+ #    define CASESENSITIVITYDEFAULT_NO
+ #  endif
+ #endif
+ 
+-
+ #ifndef UNZ_BUFSIZE
+ #define UNZ_BUFSIZE (16384)
+ #endif
+@@ -118,14 +112,12 @@
+ #define SIZECENTRALDIRITEM (0x2e)
+ #define SIZEZIPLOCALHEADER (0x1e)
+ 
+-
+ /* unz_file_info_interntal contain internal info about a file in zipfile*/
+ typedef struct unz_file_info64_internal_s
+ {
+     ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */
+ } unz_file_info64_internal;
+ 
+-
+ /* file_in_zip_read_info_s contain internal information about a file in zipfile,
+     when reading and decompress it */
+ typedef struct
+@@ -156,7 +148,6 @@
+     int   raw;
+ } file_in_zip64_read_info_s;
+ 
+-
+ /* unz64_s contain internal information about the zipfile
+ */
+ typedef struct
+@@ -189,7 +180,6 @@
+ #    endif
+ } unz64_s;
+ 
+-
+ #ifndef NOUNCRYPT
+ #include "crypt.h"
+ #endif
+@@ -200,7 +190,6 @@
+    IN assertion: the stream s has been successfully opened for reading.
+ */
+ 
+-
+ local int unz64local_getByte OF((
+     const zlib_filefunc64_32_def* pzlib_filefunc_def,
+     voidpf filestream,
+@@ -224,7 +213,6 @@
+     }
+ }
+ 
+-
+ /* ===========================================================================
+    Reads a long in LSB order from the given gz_stream. Sets
+ */
+@@ -295,7 +283,6 @@
+     voidpf filestream,
+     ZPOS64_T *pX));
+ 
+-
+ local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def,
+                             voidpf filestream,
+                             ZPOS64_T *pX)
+@@ -364,7 +351,6 @@
+     }
+ }
+ 
+-
+ #ifdef  CASESENSITIVITYDEFAULT_NO
+ #define CASESENSITIVITYDEFAULTVALUE 2
+ #else
+@@ -418,7 +404,6 @@
+     if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+         return 0;
+ 
+-
+     uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
+ 
+     if (uMaxBack>uSizeFile)
+@@ -463,7 +448,6 @@
+     return uPosFound;
+ }
+ 
+-
+ /*
+   Locate the Central directory 64 of a zipfile (at the end, just before
+     the global comment)
+@@ -486,7 +470,6 @@
+     if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+         return 0;
+ 
+-
+     uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
+ 
+     if (uMaxBack>uSizeFile)
+@@ -605,8 +588,6 @@
+         us.z_filefunc = *pzlib_filefunc64_32_def;
+     us.is64bitOpenFunction = is64bitOpenFunction;
+ 
+-
+-
+     us.filestream = ZOPEN64(us.z_filefunc,
+                                                  path,
+                                                  ZLIB_FILEFUNC_MODE_READ |
+@@ -745,7 +726,6 @@
+     us.pfile_in_zip_read = NULL;
+     us.encrypted = 0;
+ 
+-
+     s=(unz64_s*)ALLOC(sizeof(unz64_s));
+     if( s != NULL)
+     {
+@@ -755,7 +735,6 @@
+     return (unzFile)s;
+ }
+ 
+-
+ extern unzFile ZEXPORT unzOpen2 (const char *path,
+                                         zlib_filefunc_def* pzlib_filefunc32_def)
+ {
+@@ -814,7 +793,6 @@
+     return UNZ_OK;
+ }
+ 
+-
+ /*
+   Write info about the ZipFile in the *pglobal_info structure.
+   No preparation of the structure is needed
+@@ -897,7 +875,6 @@
+               ZLIB_FILEFUNC_SEEK_SET)!=0)
+         err=UNZ_ERRNO;
+ 
+-
+     /* we check the magic */
+     if (err==UNZ_OK)
+     {
+@@ -1002,7 +979,6 @@
+     else
+         lSeek += file_info.size_file_extra;
+ 
+-
+     if ((err==UNZ_OK) && (file_info.size_file_extra != 0))
+     {
+                                 uLong acc = 0;
+@@ -1096,7 +1072,6 @@
+     else
+         lSeek+=file_info.size_file_comment;
+ 
+-
+     if ((err==UNZ_OK) && (pfile_info!=NULL))
+         *pfile_info=file_info;
+ 
+@@ -1106,8 +1081,6 @@
+     return err;
+ }
+ 
+-
+-
+ /*
+   Write info about the ZipFile in the *pglobal_info structure.
+   No preparation of the structure is needed
+@@ -1156,7 +1129,6 @@
+ 
+         pfile_info->tmu_date = file_info64.tmu_date,
+ 
+-
+         pfile_info->compressed_size = (uLong)file_info64.compressed_size;
+         pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size;
+ 
+@@ -1212,7 +1184,6 @@
+     return err;
+ }
+ 
+-
+ /*
+   Try locate the file szFileName in the zipfile.
+   For the iCaseSensitivity signification, see unzStringFileNameCompare
+@@ -1234,7 +1205,6 @@
+     ZPOS64_T num_fileSaved;
+     ZPOS64_T pos_in_central_dirSaved;
+ 
+-
+     if (file==NULL)
+         return UNZ_PARAMERROR;
+ 
+@@ -1278,7 +1248,6 @@
+     return err;
+ }
+ 
+-
+ /*
+ ///////////////////////////////////////////
+ // Contributed by Ryan Haksi (mailto://cryogen@infoserve.net)
+@@ -1391,7 +1360,6 @@
+                                 s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0)
+         return UNZ_ERRNO;
+ 
+-
+     if (err==UNZ_OK)
+     {
+         if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
+@@ -1594,7 +1562,6 @@
+     pfile_in_zip_read_info->rest_read_uncompressed =
+             s->cur_file_info.uncompressed_size ;
+ 
+-
+     pfile_in_zip_read_info->pos_in_zipfile =
+             s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER +
+               iSizeVar;
+@@ -1626,7 +1593,6 @@
+     }
+ #    endif
+ 
+-
+     return UNZ_OK;
+ }
+ 
+@@ -1687,7 +1653,6 @@
+     if (pfile_in_zip_read_info==NULL)
+         return UNZ_PARAMERROR;
+ 
+-
+     if (pfile_in_zip_read_info->read_buffer == NULL)
+         return UNZ_END_OF_LIST_OF_FILE;
+     if (len==0)
+@@ -1731,7 +1696,6 @@
+                       uReadThis)!=uReadThis)
+                 return UNZ_ERRNO;
+ 
+-
+ #            ifndef NOUNCRYPT
+             if(s->encrypted)
+             {
+@@ -1743,7 +1707,6 @@
+             }
+ #            endif
+ 
+-
+             pfile_in_zip_read_info->pos_in_zipfile += uReadThis;
+ 
+             pfile_in_zip_read_info->rest_read_compressed-=uReadThis;
+@@ -1874,7 +1837,6 @@
+     return err;
+ }
+ 
+-
+ /*
+   Give the current position in uncompressed data
+ */
+@@ -1909,7 +1871,6 @@
+     return pfile_in_zip_read_info->total_out_64;
+ }
+ 
+-
+ /*
+   return 1 if the end of file was reached, 0 elsewhere
+ */
+@@ -1931,8 +1892,6 @@
+         return 0;
+ }
+ 
+-
+-
+ /*
+ Read extra field from the current file (opened by unzOpenCurrentFile)
+ This is the local-header version of the extra field (sometimes, there is
+@@ -2007,7 +1966,6 @@
+     if (pfile_in_zip_read_info==NULL)
+         return UNZ_PARAMERROR;
+ 
+-
+     if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) &&
+         (!pfile_in_zip_read_info->raw))
+     {
+@@ -2015,7 +1973,6 @@
+             err=UNZ_CRCERROR;
+     }
+ 
+-
+     TRYFREE(pfile_in_zip_read_info->read_buffer);
+     pfile_in_zip_read_info->read_buffer = NULL;
+     if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED)
+@@ -2025,7 +1982,6 @@
+         BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream);
+ #endif
+ 
+-
+     pfile_in_zip_read_info->stream_initialised = 0;
+     TRYFREE(pfile_in_zip_read_info);
+ 
+@@ -2034,7 +1990,6 @@
+     return err;
+ }
+ 
+-
+ /*
+   Get the global comment string of the ZipFile, in the szComment buffer.
+   uSizeBuf is the size of the szComment buffer.
+--- a/backend/epub/minizip/unzip.h
++++ b/backend/epub/minizip/unzip.h
+@@ -70,7 +70,6 @@
+ typedef voidp unzFile;
+ #endif
+ 
+-
+ #define UNZ_OK                          (0)
+ #define UNZ_END_OF_LIST_OF_FILE         (-100)
+ #define UNZ_ERRNO                       (Z_ERRNO)
+@@ -162,7 +161,6 @@
+     (like 1 on Unix, 2 on Windows)
+ */
+ 
+-
+ extern unzFile ZEXPORT unzOpen OF((const char *path));
+ extern unzFile ZEXPORT unzOpen64 OF((const void *path));
+ /*
+@@ -180,7 +178,6 @@
+        does not describe the reality
+ */
+ 
+-
+ extern unzFile ZEXPORT unzOpen2 OF((const char *path,
+                                     zlib_filefunc_def* pzlib_filefunc_def));
+ /*
+@@ -212,7 +209,6 @@
+   No preparation of the structure is needed
+   return UNZ_OK if there is no problem. */
+ 
+-
+ extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
+                                            char *szComment,
+                                            uLong uSizeBuf));
+@@ -222,7 +218,6 @@
+   return the number of byte copied or an error code <0
+ */
+ 
+-
+ /***************************************************************************/
+ /* Unzip package allow you browse the directory of the zipfile */
+ 
+@@ -251,7 +246,6 @@
+   UNZ_END_OF_LIST_OF_FILE if the file is not found
+ */
+ 
+-
+ /* ****************************************** */
+ /* Ryan supplied functions */
+ /* unz_file_info contain information about a file in the zipfile */
+@@ -315,14 +309,12 @@
+             (commentBufferSize is the size of the buffer)
+ */
+ 
+-
+ /** Addition for GDAL : START */
+ 
+ extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file));
+ 
+ /** Addition for GDAL : END */
+ 
+-
+ /***************************************************************************/
+ /* for reading the content of the current zipfile, you can open it, read data
+    from it, and close it (you can close it before reading all the file)
+@@ -369,7 +361,6 @@
+          but you CANNOT set method parameter as NULL
+ */
+ 
+-
+ extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
+ /*
+   Close the file in zip opened with unzOpenCurrentFile
+@@ -428,8 +419,6 @@
+ extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos);
+ extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
+ 
+-
+-
+ #ifdef __cplusplus
+ }
+ #endif
+--- a/backend/pdf/ev-poppler.h
++++ b/backend/pdf/ev-poppler.h
+@@ -34,7 +34,6 @@
+ 
+ G_MODULE_EXPORT GType register_atril_backend (GTypeModule *module);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* __PDF_DOCUMENT_H__ */
+--- a/backend/pixbuf/pixbuf-document.c
++++ b/backend/pixbuf/pixbuf-document.c
+@@ -201,7 +201,6 @@
+ 	iface->get_dimensions = pixbuf_document_thumbnails_get_dimensions;
+ }
+ 
+-
+ static void
+ pixbuf_document_init (PixbufDocument *pixbuf_document)
+ {
+--- a/backend/tiff/tiff-document.c
++++ b/backend/tiff/tiff-document.c
+@@ -361,7 +361,6 @@
+ 	if (width <= 0 || height <= 0)
+ 		return NULL;
+ 
+-
+ 	if (width >= INT_MAX / 4)
+ 		/* overflow */
+ 		return NULL;
+--- a/backend/tiff/tiff2ps.c
++++ b/backend/tiff/tiff2ps.c
+@@ -449,7 +449,6 @@
+ 	return splitpage;
+ }
+ 
+-
+ void
+ tiff2ps_process_page(TIFF2PSContext* ctx, TIFF* tif, double pw, double ph,
+ 		     double lm, double bm, gboolean cnt)
+@@ -592,7 +591,6 @@
+ 	}
+ }
+ 
+-
+ static char DuplexPreamble[] = "\
+ %%BeginFeature: *Duplex True\n\
+ systemdict begin\n\
+--- a/backend/xps/xps-document.c
++++ b/backend/xps/xps-document.c
+@@ -176,7 +176,6 @@
+ 		EV_DOCUMENT_INFO_N_PAGES |
+ 		EV_DOCUMENT_INFO_PAPER_SIZE;
+ 
+-
+ 	if (gxps_document_get_n_pages (xps->doc) > 0) {
+ 		ev_document_get_page_size (document, 0,
+ 					   &(info->paper_width),
+@@ -483,7 +482,6 @@
+ 	iface->find_link_page = xps_document_links_find_link_page;
+ }
+ 
+-
+ /* EvDocumentPrint */
+ static void
+ xps_document_print_print_page (EvDocumentPrint *document,
+--- a/cut-n-paste/toolbar-editor/egg-editable-toolbar.c
++++ b/cut-n-paste/toolbar-editor/egg-editable-toolbar.c
+@@ -541,7 +541,6 @@
+     }
+ }
+ 
+-
+ static void
+ configure_item_tooltip (GtkToolItem *item)
+ {
+@@ -557,7 +556,6 @@
+     }
+ }
+ 
+-
+ static void
+ connect_widget_signals (GtkWidget *proxy, EggEditableToolbar *etoolbar)
+ {
+--- a/cut-n-paste/toolbar-editor/egg-toolbar-editor.c
++++ b/cut-n-paste/toolbar-editor/egg-toolbar-editor.c
+@@ -36,7 +36,6 @@
+   {EGG_TOOLBAR_ITEM_TYPE, GTK_TARGET_SAME_APP, 0},
+ };
+ 
+-
+ static void egg_toolbar_editor_finalize         (GObject *object);
+ static void update_editor_sheet                 (EggToolbarEditor *editor);
+ 
+--- a/cut-n-paste/toolbar-editor/egg-toolbar-editor.h
++++ b/cut-n-paste/toolbar-editor/egg-toolbar-editor.h
+@@ -34,7 +34,6 @@
+ #define EGG_IS_TOOLBAR_EDITOR_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TOOLBAR_EDITOR))
+ #define EGG_TOOLBAR_EDITOR_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TOOLBAR_EDITOR, EggToolbarEditorClass))
+ 
+-
+ typedef struct EggToolbarEditor EggToolbarEditor;
+ typedef struct EggToolbarEditorPrivate EggToolbarEditorPrivate;
+ 
+@@ -51,7 +50,6 @@
+   GtkBoxClass parent_class;
+ };
+ 
+-
+ GType            egg_toolbar_editor_get_type     (void);
+ GtkWidget        *egg_toolbar_editor_new         (GtkUIManager *manager,
+                                                   EggToolbarsModel *model);
+--- a/cut-n-paste/toolbar-editor/egg-toolbars-model.c
++++ b/cut-n-paste/toolbar-editor/egg-toolbars-model.c
+@@ -213,7 +213,6 @@
+   EggToolbarsItem *idata2;
+   GNode *toolbar, *item;
+ 
+-
+   for(toolbar = g_node_first_child (model->priv->toolbars);
+       toolbar != NULL; toolbar = g_node_next_sibling (toolbar))
+     {
+@@ -330,7 +329,6 @@
+ 		 0, toolbar_position);
+ }
+ 
+-
+ char *
+ egg_toolbars_model_get_data (EggToolbarsModel *model,
+                              GdkAtom           type,
+--- a/cut-n-paste/zoom-control/ephy-zoom.c
++++ b/cut-n-paste/zoom-control/ephy-zoom.c
+@@ -54,7 +54,6 @@
+ 	return n_zoom_levels - 1;
+ }
+ 
+-
+ float
+ ephy_zoom_get_changed_zoom_level (float level, gint steps)
+ {
+--- a/libdocument/ev-document-attachments.c
++++ b/libdocument/ev-document-attachments.c
+@@ -52,4 +52,3 @@
+ 	return iface->get_attachments (document_attachments);
+ }
+ 
+-
+--- a/libdocument/ev-document-factory.c
++++ b/libdocument/ev-document-factory.c
+@@ -106,7 +106,6 @@
+ 	return EV_COMPRESSION_NONE;
+ }
+ 
+-
+ /*
+  * get_document_from_uri:
+  * @uri: the document URI
+--- a/libdocument/ev-document-fonts.h
++++ b/libdocument/ev-document-fonts.h
+@@ -37,7 +37,6 @@
+ 
+ G_BEGIN_DECLS
+ 
+-
+ #define EV_TYPE_DOCUMENT_FONTS		  (ev_document_fonts_get_type ())
+ #define EV_DOCUMENT_FONTS(o)		  (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_DOCUMENT_FONTS, EvDocumentFonts))
+ #define EV_DOCUMENT_FONTS_IFACE(k)	  (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_DOCUMENT_FONTS, EvDocumentFontsInterface))
+--- a/libdocument/ev-document-misc.c
++++ b/libdocument/ev-document-misc.c
+@@ -149,7 +149,6 @@
+ 	}
+ }
+ 
+-
+ void
+ ev_document_misc_paint_one_page (cairo_t      *cr,
+ 				 GtkWidget    *widget,
+--- a/libdocument/ev-document-security.h
++++ b/libdocument/ev-document-security.h
+@@ -35,7 +35,6 @@
+ 
+ G_BEGIN_DECLS
+ 
+-
+ #define EV_TYPE_DOCUMENT_SECURITY		  (ev_document_security_get_type ())
+ #define EV_DOCUMENT_SECURITY(o)			  (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_DOCUMENT_SECURITY, EvDocumentSecurity))
+ #define EV_DOCUMENT_SECURITY_IFACE(k)	  	  (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_DOCUMENT_SECURITY, EvDocumentSecurityInterface))
+--- a/libdocument/ev-document-text.c
++++ b/libdocument/ev-document-text.c
+@@ -42,7 +42,6 @@
+ 	return iface->get_text (document_text, page);
+ }
+ 
+-
+ gboolean
+ ev_document_text_get_text_layout (EvDocumentText   *document_text,
+ 				  EvPage           *page,
+--- a/libdocument/ev-document.c
++++ b/libdocument/ev-document.c
+@@ -388,7 +388,6 @@
+ 	return klass->get_page (document, index);
+ }
+ 
+-
+ #ifdef ENABLE_SYNCTEX
+ static gboolean
+ _ev_document_support_synctex (EvDocument *document)
+--- a/libdocument/ev-file-helpers.h
++++ b/libdocument/ev-file-helpers.h
+@@ -66,7 +66,6 @@
+ 				       EvCompressionType  type,
+ 				       GError           **error);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* EV_FILE_HELPERS_H */
+--- a/libdocument/ev-form-field.h
++++ b/libdocument/ev-form-field.h
+@@ -209,7 +209,6 @@
+ GType        ev_form_field_signature_get_type (void) G_GNUC_CONST;
+ EvFormField *ev_form_field_signature_new      (gint                  id);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* !EV_FORM_FIELD_H */
+--- a/libdocument/ev-image.h
++++ b/libdocument/ev-image.h
+@@ -62,7 +62,6 @@
+ 					GdkPixbuf       *pixbuf);
+ const gchar *ev_image_get_tmp_uri      (EvImage         *image);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* __EV_IMAGE_H__ */
+--- a/libdocument/ev-render-context.h
++++ b/libdocument/ev-render-context.h
+@@ -52,7 +52,6 @@
+ 	gdouble scale;
+ };
+ 
+-
+ GType            ev_render_context_get_type        (void) G_GNUC_CONST;
+ EvRenderContext *ev_render_context_new             (EvPage          *page,
+ 						    gint             rotation,
+@@ -64,7 +63,6 @@
+ void             ev_render_context_set_scale       (EvRenderContext *rc,
+ 						    gdouble          scale);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* !EV_RENDER_CONTEXT */
+--- a/libdocument/ev-transition-effect.h
++++ b/libdocument/ev-transition-effect.h
+@@ -61,7 +61,6 @@
+ 	EV_TRANSITION_DIRECTION_OUTWARD
+ } EvTransitionEffectDirection;
+ 
+-
+ typedef struct EvTransitionEffect      EvTransitionEffect;
+ typedef struct EvTransitionEffectClass EvTransitionEffectClass;
+ 
+@@ -75,7 +74,6 @@
+ 	GObjectClass parent_class;
+ };
+ 
+-
+ GType                 ev_transition_effect_get_type           (void) G_GNUC_CONST;
+ 
+ EvTransitionEffect   *ev_transition_effect_new                (EvTransitionEffectType  type,
+--- a/libmisc/ev-page-action-widget.c
++++ b/libmisc/ev-page-action-widget.c
+@@ -305,7 +305,6 @@
+ 	return TRUE;
+ }
+ 
+-
+ static void
+ display_completion_text (GtkCellLayout      *cell_layout,
+ 			 GtkCellRenderer    *renderer,
+@@ -349,7 +348,6 @@
+ 			    EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+ 			    -1);
+ 
+-
+ 	if (link) {
+ 		text = ev_link_get_title (link);
+ 		g_object_unref (link);
+@@ -445,7 +443,6 @@
+ 	return filter_model;
+ }
+ 
+-
+ void
+ ev_page_action_widget_update_links_model (EvPageActionWidget *proxy, GtkTreeModel *model)
+ {
+--- a/libview/ev-page-accessible.c
++++ b/libview/ev-page-accessible.c
+@@ -37,7 +37,6 @@
+ 	gboolean          children_initialized;
+ };
+ 
+-
+ enum {
+ 	PROP_0,
+ 	PROP_VIEW_ACCESSIBLE,
+@@ -412,7 +411,6 @@
+ 	EvRectangle *next_word_end;
+ 	gint prev_offset, next_offset;
+ 
+-
+ 	if (!log_attrs[offset].is_white)
+ 		return FALSE;
+ 
+--- a/libview/ev-pixbuf-cache.c
++++ b/libview/ev-pixbuf-cache.c
+@@ -72,7 +72,6 @@
+ 	void (* job_finished) (EvPixbufCache *pixbuf_cache);
+ };
+ 
+-
+ enum
+ {
+ 	JOB_FINISHED,
+@@ -92,7 +91,6 @@
+ 						  gint                page,
+ 						  gfloat              scale);
+ 
+-
+ /* These are used for iterating through the prev and next arrays */
+ #define FIRST_VISIBLE_PREV(pixbuf_cache) \
+ 	(MAX (0, pixbuf_cache->preload_cache_size - pixbuf_cache->start_page))
+@@ -223,7 +221,6 @@
+ 	G_OBJECT_CLASS (ev_pixbuf_cache_parent_class)->dispose (object);
+ }
+ 
+-
+ EvPixbufCache *
+ ev_pixbuf_cache_new (GtkWidget       *view,
+ 		     EvDocumentModel *model,
+@@ -985,7 +982,6 @@
+ 	}
+ }
+ 
+-
+ void
+ ev_pixbuf_cache_style_changed (EvPixbufCache *pixbuf_cache)
+ {
+@@ -1265,7 +1261,6 @@
+ 	}
+ }
+ 
+-
+ /* Returns what the pixbuf cache thinks is */
+ 
+ GList *
+@@ -1356,4 +1351,3 @@
+ 		 EV_JOB_PRIORITY_URGENT);
+ }
+ 
+-
+--- a/libview/ev-pixbuf-cache.h
++++ b/libview/ev-pixbuf-cache.h
+@@ -39,8 +39,6 @@
+ #define EV_PIXBUF_CACHE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_PIXBUF_CACHE, EvPixbufCache))
+ #define EV_IS_PIXBUF_CACHE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_PIXBUF_CACHE))
+ 
+-
+-
+ /* The coordinates in the rect here are at scale == 1.0, so that we can ignore
+  * resizings.  There is one per page, maximum.
+  */
+--- a/libview/ev-timeline.c
++++ b/libview/ev-timeline.c
+@@ -56,10 +56,8 @@
+ 
+ static guint signals [LAST_SIGNAL] = { 0, };
+ 
+-
+ G_DEFINE_TYPE_WITH_PRIVATE (EvTimeline, ev_timeline, G_TYPE_OBJECT)
+ 
+-
+ static void
+ ev_timeline_init (EvTimeline *timeline)
+ {
+--- a/libview/ev-timeline.h
++++ b/libview/ev-timeline.h
+@@ -59,7 +59,6 @@
+ 				    gdouble     progress);
+ };
+ 
+-
+ GType                 ev_timeline_get_type           (void) G_GNUC_CONST;
+ 
+ EvTimeline           *ev_timeline_new                (guint                    duration);
+@@ -84,7 +83,6 @@
+ 
+ gdouble               ev_timeline_get_progress       (EvTimeline             *timeline);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* __EV_TIMELINE_H__ */
+--- a/libview/ev-transition-animation.c
++++ b/libview/ev-transition-animation.c
+@@ -41,10 +41,8 @@
+ 	PROP_DEST_SURFACE
+ };
+ 
+-
+ G_DEFINE_TYPE_WITH_PRIVATE (EvTransitionAnimation, ev_transition_animation, EV_TYPE_TIMELINE)
+ 
+-
+ static void
+ ev_transition_animation_init (EvTransitionAnimation *animation)
+ {
+--- a/libview/ev-transition-animation.h
++++ b/libview/ev-transition-animation.h
+@@ -50,7 +50,6 @@
+ 	EvTimelineClass parent_class;
+ };
+ 
+-
+ GType                   ev_transition_animation_get_type           (void) G_GNUC_CONST;
+ 
+ EvTransitionAnimation * ev_transition_animation_new                (EvTransitionEffect    *effect);
+@@ -67,7 +66,6 @@
+ 								    GdkRectangle           page_area);
+ gboolean                ev_transition_animation_ready              (EvTransitionAnimation *animation);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* __EV_TRANSITION_ANIMATION_H__ */
+--- a/libview/ev-view-presentation.c
++++ b/libview/ev-view-presentation.c
+@@ -1385,7 +1385,6 @@
+ 	return TRUE;
+ }
+ 
+-
+ static void
+ add_change_page_binding_keypad (GtkBindingSet  *binding_set,
+ 				guint           keyval,
+--- a/libview/ev-view.c
++++ b/libview/ev-view.c
+@@ -5621,7 +5621,6 @@
+ 
+ /*** Drawing ***/
+ 
+-
+ static void
+ draw_rubberband (EvView             *view,
+ 		 cairo_t            *cr,
+@@ -5652,7 +5651,6 @@
+ 	cairo_restore (cr);
+ }
+ 
+-
+ static void
+ highlight_find_results (EvView *view, cairo_t *cr, int page)
+ {
+--- a/libview/ev-web-view.c
++++ b/libview/ev-web-view.c
+@@ -31,7 +31,6 @@
+ #include "ev-document-model.h"
+ #include "ev-jobs.h"
+ 
+-
+  typedef enum {
+  	EV_WEB_VIEW_FIND_NEXT,
+  	EV_WEB_VIEW_FIND_PREV
+@@ -322,7 +321,6 @@
+ 	web_view_update_range_and_current_page (webview);
+ }
+ 
+-
+ gboolean
+ ev_web_view_next_page (EvWebView *webview)
+ {
+--- a/libview/ev-web-view.h
++++ b/libview/ev-web-view.h
+@@ -35,7 +35,6 @@
+ #include <glib-object.h>
+ G_BEGIN_DECLS
+ 
+-
+ typedef struct _EvWebView       EvWebView;
+ typedef struct _EvWebViewClass  EvWebViewClass;
+ 
+--- a/previewer/ev-previewer-window.c
++++ b/previewer/ev-previewer-window.c
+@@ -621,7 +621,6 @@
+ 	return object;
+ }
+ 
+-
+ static void
+ ev_previewer_window_class_init (EvPreviewerWindowClass *klass)
+ {
+--- a/shell/eggfindbar.c
++++ b/shell/eggfindbar.c
+@@ -573,7 +573,6 @@
+   g_object_thaw_notify (G_OBJECT (find_bar));
+ }
+ 
+-
+ /**
+  * egg_find_bar_get_search_string:
+  *
+--- a/shell/eggfindbar.h
++++ b/shell/eggfindbar.h
+@@ -77,4 +77,3 @@
+ 
+ #endif /* __EGG_FIND_BAR_H__ */
+ 
+-
+--- a/shell/ev-application.c
++++ b/shell/ev-application.c
+@@ -21,7 +21,6 @@
+  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  */
+ 
+-
+ #include <config.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -311,7 +310,6 @@
+ 	return empty_window;
+ }
+ 
+-
+ #ifdef ENABLE_DBUS
+ typedef struct {
+ 	gchar          *uri;
+--- a/shell/ev-bookmarks.h
++++ b/shell/ev-bookmarks.h
+@@ -50,8 +50,6 @@
+ void         ev_bookmarks_update        (EvBookmarks *bookmarks,
+                                          EvBookmark  *bookmark);
+ 
+-
+-
+ G_END_DECLS
+ 
+ #endif /* EV_BOOKMARKS_H */
+--- a/shell/ev-daemon.c
++++ b/shell/ev-daemon.c
+@@ -43,7 +43,6 @@
+ 
+ #define LOG g_debug
+ 
+-
+ #define EV_TYPE_DAEMON_APPLICATION              (ev_daemon_application_get_type ())
+ #define EV_DAEMON_APPLICATION(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), EV_TYPE_DAEMON_APPLICATION, EvDaemonApplication))
+ 
+--- a/shell/ev-history.c
++++ b/shell/ev-history.c
+@@ -24,7 +24,6 @@
+ 
+ #include "ev-history.h"
+ 
+-
+ enum
+ {
+ 	HISTORY_CHANGED,
+--- a/shell/ev-loading-message.c
++++ b/shell/ev-loading-message.c
+@@ -174,4 +174,3 @@
+         return message;
+ }
+ 
+-
+--- a/shell/ev-media-player-keys.h
++++ b/shell/ev-media-player-keys.h
+@@ -37,7 +37,6 @@
+ typedef struct _EvMediaPlayerKeys EvMediaPlayerKeys;
+ typedef struct _EvMediaPlayerKeysClass EvMediaPlayerKeysClass;
+ 
+-
+ GType	           ev_media_player_keys_get_type  (void) G_GNUC_CONST;
+ 
+ EvMediaPlayerKeys *ev_media_player_keys_new	  (void);
+--- a/shell/ev-navigation-action.c
++++ b/shell/ev-navigation-action.c
+@@ -26,7 +26,6 @@
+ #include "ev-navigation-action.h"
+ #include "ev-navigation-action-widget.h"
+ 
+-
+ enum
+ {
+ 	WIDGET_ACTIVATE_LINK,
+--- a/shell/ev-open-recent-action.c
++++ b/shell/ev-open-recent-action.c
+@@ -24,7 +24,6 @@
+ 
+ #include "ev-open-recent-action.h"
+ 
+-
+ enum {
+ 	ITEM_ACTIVATED,
+ 	N_SIGNALS
+--- a/shell/ev-password-view.c
++++ b/shell/ev-password-view.c
+@@ -18,7 +18,6 @@
+  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  */
+ 
+-
+ #ifdef HAVE_CONFIG_H
+ #include "config.h"
+ #endif
+@@ -47,7 +46,6 @@
+ 
+ static guint password_view_signals [LAST_SIGNAL] = { 0 };
+ 
+-
+ G_DEFINE_TYPE_WITH_PRIVATE (EvPasswordView, ev_password_view, GTK_TYPE_VIEWPORT)
+ 
+ static void
+--- a/shell/ev-sidebar-attachments.c
++++ b/shell/ev-sidebar-attachments.c
+@@ -631,7 +631,6 @@
+ 	g_object_unref (job);
+ }
+ 
+-
+ static void
+ ev_sidebar_attachments_document_changed_cb (EvDocumentModel      *model,
+ 					    GParamSpec           *pspec,
+--- a/shell/ev-sidebar-bookmarks.c
++++ b/shell/ev-sidebar-bookmarks.c
+@@ -122,7 +122,6 @@
+         GtkTreeModel              *model;
+         GtkTreeIter                iter;
+ 
+-
+         selection = gtk_tree_view_get_selection (tree_view);
+         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+                 GtkTreePath *path;
+--- a/shell/ev-sidebar-layers.c
++++ b/shell/ev-sidebar-layers.c
+@@ -239,7 +239,6 @@
+ 	gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
+ 				     GTK_SELECTION_NONE);
+ 
+-
+ 	column = gtk_tree_view_column_new ();
+ 
+ 	renderer = gtk_cell_renderer_toggle_new ();
+--- a/shell/ev-sidebar-links.c
++++ b/shell/ev-sidebar-links.c
+@@ -379,7 +379,6 @@
+ 	return FALSE;
+ }
+ 
+-
+ static void
+ ev_sidebar_links_construct (EvSidebarLinks *ev_sidebar_links)
+ {
+@@ -425,7 +424,6 @@
+ 					     "markup", EV_DOCUMENT_LINKS_COLUMN_MARKUP,
+ 					     NULL);
+ 
+-
+ 	renderer = gtk_cell_renderer_text_new ();
+ 	gtk_tree_view_column_pack_end (GTK_TREE_VIEW_COLUMN (column), renderer, FALSE);
+ 	gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
+@@ -566,7 +564,6 @@
+ 	}
+ }
+ 
+-
+ static gint
+ page_link_tree_sort (gconstpointer a, gconstpointer b, void *data)
+ {
+--- a/shell/ev-sidebar-links.h
++++ b/shell/ev-sidebar-links.h
+@@ -63,4 +63,3 @@
+ 
+ #endif /* __EV_SIDEBAR_LINKS_H__ */
+ 
+-
+--- a/shell/ev-sidebar-page.c
++++ b/shell/ev-sidebar-page.c
+@@ -74,7 +74,6 @@
+ 	return iface->get_label (sidebar_page);
+ }
+ 
+-
+ static void
+ ev_sidebar_page_default_init (EvSidebarPageInterface *iface)
+ {
+--- a/shell/ev-sidebar-page.h
++++ b/shell/ev-sidebar-page.h
+@@ -58,7 +58,6 @@
+ 				                 EvDocumentModel *model);
+ const gchar*  ev_sidebar_page_get_label         (EvSidebarPage *page);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* EV_SIDEBAR_PAGE */
+--- a/shell/ev-sidebar-thumbnails.c
++++ b/shell/ev-sidebar-thumbnails.c
+@@ -245,7 +245,6 @@
+ 	return cache;
+ }
+ 
+-
+ static void
+ ev_sidebar_thumbnails_dispose (GObject *object)
+ {
+--- a/shell/ev-sidebar-thumbnails.h
++++ b/shell/ev-sidebar-thumbnails.h
+@@ -56,4 +56,3 @@
+ 
+ #endif /* __EV_SIDEBAR_THUMBNAILS_H__ */
+ 
+-
+--- a/shell/ev-sidebar.c
++++ b/shell/ev-sidebar.c
+@@ -446,7 +446,6 @@
+ 	gtk_list_store_move_before(GTK_LIST_STORE(ev_sidebar->priv->page_model),
+ 					   &iter, NULL);
+ 
+-
+ 	/* Set the first item added as active */
+ 	gtk_tree_model_get_iter_first (ev_sidebar->priv->page_model, &iter);
+ 	gtk_tree_model_get (ev_sidebar->priv->page_model,
+--- a/shell/ev-sidebar.h
++++ b/shell/ev-sidebar.h
+@@ -64,4 +64,3 @@
+ 
+ #endif /* __EV_SIDEBAR_H__ */
+ 
+-
+--- a/shell/ev-window.c
++++ b/shell/ev-window.c
+@@ -418,7 +418,6 @@
+ 	G_GNUC_END_IGNORE_DEPRECATIONS;
+ }
+ 
+-
+ static void
+ ev_window_setup_action_sensitivity (EvWindow *ev_window)
+ {
+@@ -1555,7 +1554,6 @@
+ 		ev_document_model_set_scale (model, g_settings_get_double (settings, "zoom"));
+ }
+ 
+-
+ static void
+ ev_window_clear_thumbnail_job (EvWindow *ev_window)
+ {
+@@ -3636,7 +3634,6 @@
+ 		GtkWidget *dialog;
+ 		GError    *error = NULL;
+ 
+-
+ 		ev_print_operation_get_error (op, &error);
+ 
+ 		/* The message area is already used by
+@@ -3891,7 +3888,6 @@
+ 		return FALSE;
+ 	}
+ 
+-
+ 	text = g_markup_printf_escaped (_("Save a copy of document “%s” before closing?"),
+ 					gtk_window_get_title (GTK_WINDOW (ev_window)));
+ 
+@@ -4414,7 +4410,6 @@
+                                          _("Running in presentation mode"));
+ }
+ 
+-
+ static void
+ ev_window_uninhibit_screensaver (EvWindow *window)
+ {
+@@ -4727,7 +4722,6 @@
+ 	ev_window_update_actions (window);
+ }
+ 
+-
+ static void
+ ev_window_cmd_edit_rotate_left (GtkAction *action, EvWindow *ev_window)
+ {
+@@ -5977,7 +5971,6 @@
+ 		 * the new expanded window size.
+ 		 */
+ 
+-
+ 		if (ev_window->priv->chrome & EV_CHROME_SIDEBAR)
+ 		{
+ 			GtkAllocation alloc;
+@@ -6491,7 +6484,6 @@
+ 	{ "EditSaveSettings", NULL, N_("Save Current Settings as _Default"), "<control>T", NULL,
+ 	  G_CALLBACK (ev_window_cmd_edit_save_settings) },
+ 
+-
+         /* View menu */
+         { "ViewZoomIn", "zoom-in", N_("Zoom _In"), "<control>plus",
+           N_("Enlarge the document"),
+@@ -7746,7 +7738,6 @@
+         ev_atril_window_emit_document_loaded (window->priv->skeleton, window->priv->uri);
+ }
+ 
+-
+ #ifdef ENABLE_SYNCTEX
+ static gboolean
+ handle_sync_view_cb (EvAtrilWindow        *object,
+--- a/shell/ev-window.h
++++ b/shell/ev-window.h
+@@ -58,7 +58,6 @@
+ #define EV_IS_WINDOW_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_WINDOW))
+ #define EV_WINDOW_GET_CLASS(object)	(G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_WINDOW, EvWindowClass))
+ 
+-
+ struct _EvWindow {
+ 	GtkApplicationWindow base_instance;
+ 	EvWindowPrivate     *priv;
+@@ -87,7 +86,6 @@
+ 					 int		 last_page);
+ const gchar *	ev_window_get_dbus_object_path (EvWindow *ev_window);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* !EV_WINDOW_H */
+--- a/shell/main.c
++++ b/shell/main.c
+@@ -36,7 +36,6 @@
+ #include "eggsmclient.h"
+ #include "eggdesktopfile.h"
+ 
+-
+ static gchar   *ev_page_label;
+ static gchar   *ev_find_string;
+ static gint     ev_page_index = 0;
+@@ -48,7 +47,6 @@
+ static gchar   *print_settings;
+ static const char **file_arguments = NULL;
+ 
+-
+ static gboolean
+ option_version_cb (const gchar *option_name,
+                    const gchar *value,
+@@ -206,8 +204,6 @@
+ 			continue;
+ 		}
+ 
+-
+-
+ 		ev_application_open_uri_at_dest (EV_APP, uri, screen, dest,
+ 						 mode, ev_find_string,
+ 						 GDK_CURRENT_TIME);
diff -Nru atril-1.26.0/debian/patches/0006-comics-Use-libarchive-to-unpack-documents.patch atril-1.26.0/debian/patches/0006-comics-Use-libarchive-to-unpack-documents.patch
--- atril-1.26.0/debian/patches/0006-comics-Use-libarchive-to-unpack-documents.patch	1970-01-01 01:00:00.000000000 +0100
+++ atril-1.26.0/debian/patches/0006-comics-Use-libarchive-to-unpack-documents.patch	2024-01-23 10:06:08.000000000 +0100
@@ -0,0 +1,1814 @@
+From aa8e9dad472cbadc96719a8f521768aeeb0913f0 Mon Sep 17 00:00:00 2001
+From: lukefromdc <lukefromdc@hushmail.com>
+Date: Mon, 25 Dec 2023 15:11:04 -0500
+Subject: [PATCH 2/2] comics: Use libarchive to unpack documents
+
+This commit eliminates the use of external commands for opening
+comic documents, and uses libarchive instead.
+
+Signed-off-by: Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
+---
+ backend/comics/Makefile.am       |    5 +-
+ backend/comics/comics-document.c | 1143 +++++++++++-------------------
+ backend/comics/comics-document.h |    7 +-
+ backend/comics/ev-archive.c      |  323 +++++++++
+ backend/comics/ev-archive.h      |   56 ++
+ configure.ac                     |    3 +
+ libdocument/ev-document.h        |    1 +
+ 7 files changed, 799 insertions(+), 739 deletions(-)
+ create mode 100644 backend/comics/ev-archive.c
+ create mode 100644 backend/comics/ev-archive.h
+
+diff --git a/backend/comics/Makefile.am b/backend/comics/Makefile.am
+index b27f9b85..77f3dedb 100644
+--- a/backend/comics/Makefile.am
++++ b/backend/comics/Makefile.am
+@@ -12,12 +12,15 @@ backend_LTLIBRARIES = libcomicsdocument.la
+ 
+ libcomicsdocument_la_SOURCES = \
+ 	comics-document.c      \
+-	comics-document.h
++	comics-document.h      \
++	ev-archive.c     \
++	ev-archive.h
+ 
+ libcomicsdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS)
+ libcomicsdocument_la_LIBADD =				\
+ 	$(top_builddir)/libdocument/libatrildocument.la	\
+ 	$(BACKEND_LIBS)					\
++	$(COMICS_LIBS)					\
+ 	$(LIB_LIBS)
+ 
+ backend_in_files = comicsdocument.atril-backend.desktop.in
+diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c
+index ed02a35c..1da1eee2 100644
+--- a/backend/comics/comics-document.c
++++ b/backend/comics/comics-document.c
+@@ -30,24 +30,17 @@
+ #include <glib/gstdio.h>
+ #include <gio/gio.h>
+ 
+-#include <sys/wait.h>
+-
+ #include "comics-document.h"
+ #include "ev-document-misc.h"
+ #include "ev-document-thumbnails.h"
+ #include "ev-file-helpers.h"
++#include "ev-archive.h"
++#include <archive.h>
++#include <archive_entry.h>
+ 
+ #define EV_EOL "\n"
+ 
+-typedef enum
+-{
+-	RARLABS,
+-	GNAUNRAR,
+-	UNZIP,
+-	P7ZIP,
+-	TAR,
+-	UNARCHIVER
+-} ComicBookDecompressType;
++#define BLOCK_SIZE 10240
+ 
+ typedef struct _ComicsDocumentClass ComicsDocumentClass;
+ 
+@@ -58,398 +51,269 @@ struct _ComicsDocumentClass
+ 
+ struct _ComicsDocument
+ {
+-	EvDocument parent_instance;
+-
+-	gchar    *archive, *dir;
+-	GPtrArray *page_names;
+-	gchar    *selected_command, *alternative_command;
+-	gchar    *extract_command, *list_command, *decompress_tmp;
+-	gboolean regex_arg;
+-	gint     offset;
+-	ComicBookDecompressType command_usage;
++	EvDocument     parent_instance;
++	EvArchive     *archive;
++	gchar         *archive_path;
++	gchar         *archive_uri;
++	GPtrArray     *page_names; /* elem: char * */
++	GHashTable    *page_positions; /* key: char *, value: uint + 1 */
++
+ };
+ 
+-#define OFFSET_7Z 53
+-#define OFFSET_ZIP 2
+-#define NO_OFFSET 0
+-
+-/* For perfomance reasons of 7z* we've choosen to decompress on the temporary
+- * directory instead of decompressing on the stdout */
+-
+-/**
+- * @extract: command line arguments to pass to extract a file from the archive
+- *   to stdout.
+- * @list: command line arguments to list the archive contents
+- * @decompress_tmp: command line arguments to pass to extract the archive
+- *   into a directory.
+- * @regex_arg: whether the command can accept regex expressions
+- * @offset: the position offset of the filename on each line in the output of
+- *   running the @list command
+- */
+-typedef struct {
+-        char *extract;
+-        char *list;
+-        char *decompress_tmp;
+-        gboolean regex_arg;
+-        gint offset;
+-} ComicBookDecompressCommand;
++static void       
++comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface);
++EV_BACKEND_REGISTER_WITH_CODE (ComicsDocument, comics_document,
++    {
++        EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
++                        comics_document_document_thumbnails_iface_init);
++    } );
++
++#define FORMAT_UNKNOWN     0
++#define FORMAT_SUPPORTED   1
++#define FORMAT_UNSUPPORTED 2
++
++/* Returns a GHashTable of:
++ * <key>: file extensions
++ * <value>: degree of support in gdk-pixbuf */
++static GHashTable *
++get_image_extensions(void)
++{
++	GHashTable *extensions;
++	GSList *formats = gdk_pixbuf_get_formats ();
++	GSList *l;
++	guint i;
++	const char *known_image_formats[] = {
++		"png",
++		"jpg",
++		"jpeg",
++		"webp"
++	};
++
++	extensions = g_hash_table_new_full (g_str_hash, g_str_equal,
++					    g_free, NULL);
++	for (l = formats; l != NULL; l = l->next) {
++		int i;
++		gchar **ext = gdk_pixbuf_format_get_extensions (l->data);
++
++		for (i = 0; ext[i] != NULL; i++) {
++			g_hash_table_insert (extensions,
++					     g_strdup (ext[i]),
++					     GINT_TO_POINTER (FORMAT_SUPPORTED));
++		}
+ 
+-static const ComicBookDecompressCommand command_usage_def[] = {
+-        /* RARLABS unrar */
+-	{"%s p -c- -ierr --", "%s vb -c- -- %s", NULL             , FALSE, NO_OFFSET},
++		g_strfreev (ext);
++	}
++	g_slist_free (formats);
+ 
+-        /* GNA! unrar */
+-	{NULL               , "%s t %s"        , "%s -xf %s %s"   , FALSE, NO_OFFSET},
++	/* Add known image formats that aren't supported by gdk-pixbuf */
++	for (i = 0; i < G_N_ELEMENTS (known_image_formats); i++) {
++		if (!g_hash_table_lookup (extensions, known_image_formats[i])) {
++			g_hash_table_insert (extensions,
++					     g_strdup (known_image_formats[i]),
++					     GINT_TO_POINTER (FORMAT_UNSUPPORTED));
++		}
++	}
+ 
+-        /* unzip */
+-	{"%s -p -C --"      , "%s %s"          , NULL             , TRUE , OFFSET_ZIP},
++	return extensions;
++}
+ 
+-        /* 7zip */
+-	{NULL               , "%s l -- %s"     , "%s x -y %s -o%s", FALSE, OFFSET_7Z},
++static int
++has_supported_extension (const char *name,
++			 GHashTable *supported_extensions)
++{
++	gboolean ret = FALSE;
++	gchar *suffix;
++	suffix = g_strrstr (name, ".");
++	if (!suffix)
++		return ret;
+ 
+-        /* tar */
+-	{"%s -xOf"          , "%s -tf %s"      , NULL             , FALSE, NO_OFFSET},
++	suffix = g_ascii_strdown (suffix + 1, -1);
++	ret = GPOINTER_TO_INT (g_hash_table_lookup (supported_extensions, suffix));
++	g_free (suffix);
+ 
+-	/* UNARCHIVER */
+-	{"unar -o -"	    , "%s %s"	       , NULL		  , FALSE, NO_OFFSET}
+-};
++	return ret;
++}
+ 
+-static void       comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface);
++#define APPLE_DOUBLE_PREFIX "._"
++static gboolean
++is_apple_double (const char *name)
++{
++char *basename;
++	gboolean ret = FALSE;
+ 
+-static GSList*    get_supported_image_extensions (void);
+-static void       get_page_size_area_prepared_cb (GdkPixbufLoader *loader,
+-						  gpointer data);
+-static void       render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
+-						  gint width,
+-						  gint height,
+-						  gpointer data);
+-static char**     extract_argv                   (EvDocument *document,
+-						  gint page);
++	basename = g_path_get_basename (name);
++	if (basename == NULL) {
++		g_debug ("Filename '%s' doesn't have a basename?", name);
++		return ret;
++	}
++	ret = g_str_has_prefix (basename, APPLE_DOUBLE_PREFIX);
++	g_free (basename);
+ 
+-EV_BACKEND_REGISTER_WITH_CODE (ComicsDocument, comics_document,
+-	{
+-		EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
+-						comics_document_document_thumbnails_iface_init);
+-	} );
+-
+-/**
+- * comics_regex_quote:
+- * @unquoted_string: a literal string
+- *
+- * Quotes a string so unzip will not interpret the regex expressions of
+- * @unquoted_string. Basically, this functions uses [] to disable regex
+- * expressions. The return value must be freed with * g_free()
+- *
+- * Return value: quoted and disabled-regex string
+- **/
+-static gchar *
+-comics_regex_quote (const gchar *unquoted_string)
++	return ret;
++}
++
++static gboolean
++archive_reopen_if_needed (ComicsDocument  *comics_document,
++			  const char      *page_wanted,
++			  GError         **error)
+ {
+-	const gchar *p;
+-	GString *dest;
+-
+-	dest = g_string_new ("'");
+-
+-	p = unquoted_string;
+-
+-	while (*p) {
+-		switch (*p) {
+-			/* * matches a sequence of 0 or more characters */
+-			case ('*'):
+-			/* ? matches exactly 1 charactere */
+-			case ('?'):
+-			/* [...]  matches any single character found inside
+-			 * the brackets. Disabling the first bracket is enough.
+-			 */
+-			case ('['):
+-				g_string_append (dest, "[");
+-				g_string_append_c (dest, *p);
+-				g_string_append (dest, "]");
+-				break;
+-			/* Because \ escapes regex expressions that we are
+-			 * disabling for unzip, we need to disable \ too */
+-			case ('\\'):
+-				g_string_append (dest, "[\\\\]");
+-				break;
+-			/* Escape single quote inside the string */
+-			case ('\''):
+-				g_string_append (dest, "'\\''");
+-				break;
+-			default:
+-				g_string_append_c (dest, *p);
+-				break;
++	const char *current_page;
++	guint current_page_idx, page_wanted_idx;
++
++	if (ev_archive_at_entry (comics_document->archive)) {
++		current_page = ev_archive_get_entry_pathname (comics_document->archive);
++		if (current_page) {
++			current_page_idx = GPOINTER_TO_UINT (g_hash_table_lookup (comics_document->page_positions, current_page));
++			page_wanted_idx = GPOINTER_TO_UINT (g_hash_table_lookup (comics_document->page_positions, page_wanted));
++
++			if (current_page_idx != 0 &&
++			    page_wanted_idx != 0 &&
++			    page_wanted_idx > current_page_idx)
++				return TRUE;
+ 		}
+-		++p;
++
++		ev_archive_reset (comics_document->archive);
+ 	}
+-	g_string_append_c (dest, '\'');
+-	return g_string_free (dest, FALSE);
++return ev_archive_open_filename (comics_document->archive, comics_document->archive_path, error);
+ }
+ 
+-/* This function manages the command for decompressing a comic book */
+-static gboolean
+-comics_decompress_temp_dir (const gchar *command_decompress_tmp,
+-			    const gchar *command,
+-			    GError      **error)
++static GPtrArray *
++comics_document_list (ComicsDocument  *comics_document,
++		      GError         **error)
+ {
+-	gboolean success;
+-	gchar *std_out, *basename;
+-	GError *err = NULL;
+-	gint retval;
+-
+-	success = g_spawn_command_line_sync (command_decompress_tmp, &std_out,
+-					     NULL, &retval, &err);
+-	basename = g_path_get_basename (command);
+-	if (!success) {
+-		g_set_error (error,
+-			     EV_DOCUMENT_ERROR,
+-			     EV_DOCUMENT_ERROR_INVALID,
+-			     _("Error launching the command “%s” in order to "
+-			     "decompress the comic book: %s"),
+-			     basename,
+-			     err->message);
+-		g_error_free (err);
+-	} else if (WIFEXITED (retval)) {
+-		if (WEXITSTATUS (retval) == EXIT_SUCCESS) {
+-			g_free (std_out);
+-			g_free (basename);
+-			return TRUE;
+-		} else {
+-			g_set_error (error,
++GPtrArray *array = NULL;
++	gboolean has_encrypted_files, has_unsupported_images, has_archive_errors;
++	GHashTable *supported_extensions = NULL;
++
++	if (!ev_archive_open_filename (comics_document->archive, comics_document->archive_path, error)) {
++		if (*error != NULL) {
++			g_warning ("Fatal error handling archive (%s): %s", G_STRFUNC, (*error)->message);
++			g_clear_error (error);
++		}
++
++		g_set_error_literal (error,
+ 				     EV_DOCUMENT_ERROR,
+ 				     EV_DOCUMENT_ERROR_INVALID,
+-				     _("The command “%s” failed at "
+-				     "decompressing the comic book."),
+-				     basename);
+-			g_free (std_out);
++			     _("File is corrupted"));
++		goto out;
++	}
++
++	supported_extensions = get_image_extensions ();
++
++	has_encrypted_files = FALSE;
++	has_unsupported_images = FALSE;
++	has_archive_errors = FALSE;
++	array = g_ptr_array_sized_new (64);
++
++	while (1) {
++		const char *name;
++		int supported;
++
++		if (!ev_archive_read_next_header (comics_document->archive, error)) {
++			if (*error != NULL) {
++				g_debug ("Fatal error handling archive (%s): %s", G_STRFUNC, (*error)->message);
++				g_clear_error (error);
++				has_archive_errors = TRUE;
++				goto out;
++			}
++			break;
+ 		}
+-	} else {
+-		g_set_error (error,
+-			     EV_DOCUMENT_ERROR,
+-			     EV_DOCUMENT_ERROR_INVALID,
+-			     _("The command “%s” did not end normally."),
+-			     basename);
+-		g_free (std_out);
++
++		name = ev_archive_get_entry_pathname (comics_document->archive);
++		/* Ignore https://en.wikipedia.org/wiki/AppleSingle_and_AppleDouble_formats */
++		if (is_apple_double (name)) {
++			g_debug ("Not adding AppleDouble file '%s' to the list of files in the comics", name);
++			continue;
++		}
++
++		supported = has_supported_extension (name, supported_extensions);
++		if (supported == FORMAT_UNKNOWN) {
++			g_debug ("Not adding unsupported file '%s' to the list of files in the comics", name);
++			continue;
++		} else if (supported == FORMAT_UNSUPPORTED) {
++			g_debug ("Not adding unsupported image '%s' to the list of files in the comics", name);
++			has_unsupported_images = TRUE;
++			continue;
++		}
++
++		if (ev_archive_get_entry_is_encrypted (comics_document->archive)) {
++			g_debug ("Not adding encrypted file '%s' to the list of files in the comics", name);
++			has_encrypted_files = TRUE;
++			continue;
++		}
++
++		g_debug ("Adding '%s' to the list of files in the comics", name);
++		g_ptr_array_add (array, g_strdup (name));
+ 	}
+-	g_free (basename);
+-	return FALSE;
++out:
++	if (array->len == 0) {
++		g_ptr_array_free (array, TRUE);
++		array = NULL;
++
++		if (has_encrypted_files) {
++			g_set_error_literal (error,
++					     EV_DOCUMENT_ERROR,
++					     EV_DOCUMENT_ERROR_ENCRYPTED,
++					     _("Archive is encrypted"));
++		} else if (has_unsupported_images) {
++			g_set_error_literal (error,
++					     EV_DOCUMENT_ERROR,
++					     EV_DOCUMENT_ERROR_UNSUPPORTED_CONTENT,
++					     _("No supported images in archive"));
++		} else if (has_archive_errors) {
++			g_set_error_literal (error,
++					     EV_DOCUMENT_ERROR,
++					     EV_DOCUMENT_ERROR_INVALID,
++					     _("File is corrupted"));
++		} else {
++			g_set_error_literal (error,
++					     EV_DOCUMENT_ERROR,
++					     EV_DOCUMENT_ERROR_INVALID,
++					     _("No files in archive"));
++		}
++	}
++
++	if (supported_extensions)
++		g_hash_table_destroy (supported_extensions);
++	ev_archive_reset (comics_document->archive);
++	return array;
+ }
+ 
+-/* This function shows how to use the choosen command for decompressing a
+- * comic book file. It modifies fields of the ComicsDocument struct with
+- * this information */
+-static gboolean
+-comics_generate_command_lines (ComicsDocument *comics_document,
+-			       GError         **error)
++static GHashTable *
++save_positions (GPtrArray *page_names)
+ {
+-	gchar *quoted_file, *quoted_file_aux;
+-	gchar *quoted_command;
+-	ComicBookDecompressType type;
+-
+-	type = comics_document->command_usage;
+-	comics_document->regex_arg = command_usage_def[type].regex_arg;
+-	quoted_command = g_shell_quote (comics_document->selected_command);
+-	if (comics_document->regex_arg) {
+-		quoted_file = comics_regex_quote (comics_document->archive);
+-		quoted_file_aux = g_shell_quote (comics_document->archive);
+-		comics_document->list_command =
+-			   g_strdup_printf (command_usage_def[type].list,
+-			                    comics_document->alternative_command,
+-			                    quoted_file_aux);
+-		g_free (quoted_file_aux);
+-	} else {
+-		quoted_file = g_shell_quote (comics_document->archive);
+-		comics_document->list_command =
+-				g_strdup_printf (command_usage_def[type].list,
+-				                 quoted_command, quoted_file);
+-	}
+-	comics_document->extract_command =
+-			    g_strdup_printf (command_usage_def[type].extract,
+-				             quoted_command);
+-	comics_document->offset = command_usage_def[type].offset;
+-	if (command_usage_def[type].decompress_tmp) {
+-		comics_document->dir = ev_mkdtemp ("atril-comics-XXXXXX", error);
+-                if (comics_document->dir == NULL)
+-                        return FALSE;
+-
+-		/* unrar-free can't create directories, but ev_mkdtemp already created the dir */
+-
+-		comics_document->decompress_tmp =
+-			g_strdup_printf (command_usage_def[type].decompress_tmp,
+-					 quoted_command, quoted_file,
+-					 comics_document->dir);
+-		g_free (quoted_file);
+-		g_free (quoted_command);
+-
+-		if (!comics_decompress_temp_dir (comics_document->decompress_tmp,
+-		    comics_document->selected_command, error))
+-			return FALSE;
+-		else
+-			return TRUE;
+-	} else {
+-		g_free (quoted_file);
+-		g_free (quoted_command);
+-		return TRUE;
+-	}
++	guint i;
++	GHashTable *ht;
+ 
++	ht = g_hash_table_new (g_str_hash, g_str_equal);
++	for (i = 0; i < page_names->len; i++)
++		g_hash_table_insert (ht, page_names->pdata[i], GUINT_TO_POINTER(i + 1));
++	return ht;
+ }
+ 
+-/* This function chooses an external command for decompressing a comic
+- * book based on its mime tipe. */
++/*This function chooses the archive decompression support
++ * book based on its mime type. */
+ static gboolean
+-comics_check_decompress_command	(gchar          *mime_type,
++comics_check_decompress_support	(gchar          *mime_type,
+ 				 ComicsDocument *comics_document,
+ 				 GError         **error)
+ {
+-	gboolean success;
+-	gchar *std_out, *std_err;
+-	gint retval;
+-	GError *err = NULL;
+-
+-	/* FIXME, use proper cbr/cbz mime types once they're
+-	 * included in shared-mime-info */
+-
+ 	if (g_content_type_is_a (mime_type, "application/x-cbr") ||
+ 	    g_content_type_is_a (mime_type, "application/x-rar")) {
+-	        /* The RARLAB provides a no-charge proprietary (freeware)
+-	        * decompress-only client for Linux called unrar. Another
+-		* option is a GPLv2-licensed command-line tool developed by
+-		* the Gna! project. Confusingly enough, the free software RAR
+-		* decoder is also named unrar. For this reason we need to add
+-		* some lines for disambiguation. Sorry for the added the
+-		* complexity but it's life :)
+-		* Finally, some distributions, like Debian, rename this free
+-		* option as unrar-free.
+-		* */
+-		comics_document->selected_command =
+-					g_find_program_in_path ("unrar");
+-		if (comics_document->selected_command) {
+-			/* We only use std_err to avoid printing useless error
+-			 * messages on the terminal */
+-			success =
+-				g_spawn_command_line_sync (
+-				              comics_document->selected_command,
+-							   &std_out, &std_err,
+-							   &retval, &err);
+-			if (!success) {
+-				g_propagate_error (error, err);
+-				g_error_free (err);
+-				return FALSE;
+-			/* I don't check retval status because RARLAB unrar
+-			 * doesn't have a way to return 0 without involving an
+-			 * operation with a file*/
+-			} else if (WIFEXITED (retval)) {
+-				if (g_strrstr (std_out,"freeware") != NULL)
+-					/* The RARLAB freeware client */
+-					comics_document->command_usage = RARLABS;
+-				else
+-					/* The Gna! free software client */
+-					comics_document->command_usage = GNAUNRAR;
+-
+-				g_free (std_out);
+-				g_free (std_err);
+-				return TRUE;
+-			}
+-		}
+-		/* The Gna! free software client with Debian naming convention */
+-		comics_document->selected_command =
+-				g_find_program_in_path ("unrar-free");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = GNAUNRAR;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("lsar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = UNARCHIVER;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("bsdtar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = TAR;
++		if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_RAR))
+ 			return TRUE;
+-		}
+-
+ 	} else if (g_content_type_is_a (mime_type, "application/x-cbz") ||
+ 		   g_content_type_is_a (mime_type, "application/zip")) {
+-		/* InfoZIP's unzip program */
+-		comics_document->selected_command =
+-				g_find_program_in_path ("unzip");
+-		comics_document->alternative_command =
+-				g_find_program_in_path ("zipnote");
+-		if (comics_document->selected_command &&
+-		    comics_document->alternative_command) {
+-			comics_document->command_usage = UNZIP;
++		if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_ZIP))
+ 			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("bsdtar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = TAR;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("lsar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = UNARCHIVER;
+-			return TRUE;
+-		}
+-
+ 	} else if (g_content_type_is_a (mime_type, "application/x-cb7") ||
+ 		   g_content_type_is_a (mime_type, "application/x-7z-compressed")) {
+-		/* 7zr, 7za and 7z are the commands from the p7zip project able
+-		 * to decompress .7z files */
+-		comics_document->selected_command =
+-			g_find_program_in_path ("7zr");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = P7ZIP;
++		if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_7Z))
+ 			return TRUE;
+-		}
+-		comics_document->selected_command =
+-			g_find_program_in_path ("7za");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = P7ZIP;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-			g_find_program_in_path ("7z");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = P7ZIP;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("bsdtar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = TAR;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("lsar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = UNARCHIVER;
+-			return TRUE;
+-		}
+ 	} else if (g_content_type_is_a (mime_type, "application/x-cbt") ||
+ 		   g_content_type_is_a (mime_type, "application/x-tar")) {
+-		/* tar utility (Tape ARchive) */
+-		comics_document->selected_command =
+-				g_find_program_in_path ("tar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = TAR;
++	if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_TAR))
+ 			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("bsdtar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = TAR;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("lsar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = UNARCHIVER;
+-			return TRUE;
+-		}
+ 	} else {
+ 		g_set_error (error,
+ 			     EV_DOCUMENT_ERROR,
+@@ -461,8 +325,9 @@ comics_check_decompress_command	(gchar          *mime_type,
+ 	g_set_error_literal (error,
+ 			     EV_DOCUMENT_ERROR,
+ 			     EV_DOCUMENT_ERROR_INVALID,
+-			     _("Can't find an appropriate command to "
+-			     "decompress this type of comic book"));
++		             _("libarchive lacks support for this comic book’s "
++			     "compression, please contact your distributor"));
++
+ 	return FALSE;
+ }
+ 
+@@ -470,43 +335,15 @@ static int
+ sort_page_names (gconstpointer a,
+                  gconstpointer b)
+ {
+-	const char *name_1, *name_2;
+-	gchar *key_1, *key_2;
+-	gboolean sort_last_1, sort_last_2;
+-	int compare;
+-
+-	name_1 = * (const char **) a;
+-	name_2 = * (const char **) b;
+-
+-	#define SORT_LAST_CHAR1 '.'
+-	#define SORT_LAST_CHAR2 '#'
+-
+-	sort_last_1 = name_1[0] == SORT_LAST_CHAR1 || name_1[0] == SORT_LAST_CHAR2;
+-	sort_last_2 = name_2[0] == SORT_LAST_CHAR1 || name_2[0] == SORT_LAST_CHAR2;
+-
+-	#undef SORT_LAST_CHAR1
+-	#undef SORT_LAST_CHAR2
+-
+-	if (sort_last_1 && !sort_last_2)
+-	{
+-		compare = +1;
+-	}
+-	else if (!sort_last_1 && sort_last_2)
+-	{
+-		compare = -1;
+-	}
+-	else
+-	{
+-		key_1 = g_utf8_collate_key_for_filename (name_1, -1);
+-		key_2 = g_utf8_collate_key_for_filename (name_2, -1);
+-
+-		compare = strcmp (key_1, key_2);
+-
+-		g_free (key_1);
+-		g_free (key_2);
+-	}
+-
+-	return compare;
++	gchar *temp1, *temp2;
++	gint ret;
++	temp1 = g_utf8_collate_key_for_filename (* (const char **) a, -1);
++	temp2 = g_utf8_collate_key_for_filename (* (const char **) b, -1);
++	ret = strcmp (temp1, temp2);
++
++	g_free (temp1);
++	g_free (temp2);
++	return ret;
+ }
+ 
+ static gboolean
+@@ -515,50 +352,13 @@ comics_document_load (EvDocument *document,
+ 		      GError    **error)
+ {
+ 	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+-	GSList *supported_extensions;
+-	gchar *std_out;
+ 	gchar *mime_type;
+-	gchar **cb_files, *cb_file;
+-	gboolean success;
+-	int i, retval;
+-	GError *err = NULL;
+-
+-	comics_document->archive = g_filename_from_uri (uri, NULL, error);
+-	if (!comics_document->archive)
+-		return FALSE;
+-
+-	mime_type = ev_file_get_mime_type (uri, FALSE, &err);
+-	if (!mime_type) {
+-		if (err) {
+-			g_propagate_error (error, err);
+-		} else {
+-			g_set_error_literal (error,
+-					     EV_DOCUMENT_ERROR,
+-					     EV_DOCUMENT_ERROR_INVALID,
+-					     _("Unknown MIME Type"));
+-		}
+-
+-		return FALSE;
+-	}
+-
+-	if (!comics_check_decompress_command (mime_type, comics_document,
+-	error)) {
+-		g_free (mime_type);
+-		return FALSE;
+-	} else if (!comics_generate_command_lines (comics_document, error)) {
+-		   g_free (mime_type);
+-		return FALSE;
+-	}
++	GFile *file;
++	file = g_file_new_for_uri (uri);
++	comics_document->archive_path = g_file_get_path (file);
++	g_object_unref (file);
+ 
+-	g_free (mime_type);
+-
+-	/* Get list of files in archive */
+-	success = g_spawn_command_line_sync (comics_document->list_command,
+-					     &std_out, NULL, &retval, error);
+-
+-	if (!success) {
+-		return FALSE;
+-	} else if (!WIFEXITED(retval) || WEXITSTATUS(retval) != EXIT_SUCCESS) {
++	if (!comics_document->archive_path) {
+ 		g_set_error_literal (error,
+                                      EV_DOCUMENT_ERROR,
+                                      EV_DOCUMENT_ERROR_INVALID,
+@@ -566,58 +366,26 @@ comics_document_load (EvDocument *document,
+ 		return FALSE;
+ 	}
+ 
+-	/* FIXME: is this safe against filenames containing \n in the archive ? */
+-	cb_files = g_strsplit (std_out, EV_EOL, 0);
++	comics_document->archive_uri = g_strdup (uri);
++	mime_type = ev_file_get_mime_type (uri, FALSE, error);
+ 
+-	g_free (std_out);
++	if (mime_type == NULL)
++		return FALSE;
+ 
+-	if (!cb_files) {
+-		g_set_error_literal (error,
+-				     EV_DOCUMENT_ERROR,
+-				     EV_DOCUMENT_ERROR_INVALID,
+-				     _("No files in archive"));
++	if (!comics_check_decompress_support (mime_type, comics_document, error)) {
++		g_free (mime_type);
+ 		return FALSE;
+ 	}
+ 
+-        comics_document->page_names = g_ptr_array_sized_new (64);
+-
+-	supported_extensions = get_supported_image_extensions ();
+-	for (i = 0; cb_files[i] != NULL; i++) {
+-		if (comics_document->offset != NO_OFFSET) {
+-			if (g_utf8_strlen (cb_files[i],-1) >
+-			    comics_document->offset) {
+-				cb_file =
+-					g_utf8_offset_to_pointer (cb_files[i],
+-						       comics_document->offset);
+-			} else {
+-				continue;
+-			}
+-		} else {
+-			cb_file = cb_files[i];
+-		}
+-		gchar *suffix = g_strrstr (cb_file, ".");
+-		if (!suffix)
+-			continue;
+-		suffix = g_ascii_strdown (suffix + 1, -1);
+-		if (g_slist_find_custom (supported_extensions, suffix,
+-					 (GCompareFunc) strcmp) != NULL) {
+-                        g_ptr_array_add (comics_document->page_names,
+-                                         g_strstrip (g_strdup (cb_file)));
+-		}
+-		g_free (suffix);
+-	}
+-	g_strfreev (cb_files);
+-	g_slist_foreach (supported_extensions, (GFunc) g_free, NULL);
+-	g_slist_free (supported_extensions);
++	g_free (mime_type);
+ 
+-	if (comics_document->page_names->len == 0) {
+-		g_set_error (error,
+-			     EV_DOCUMENT_ERROR,
+-			     EV_DOCUMENT_ERROR_INVALID,
+-			     _("No images found in archive %s"),
+-			     uri);
++	/* Get list of files in archive */
++	comics_document->page_names = comics_document_list (comics_document, error);
++	if (!comics_document->page_names)
+ 		return FALSE;
+-	}
++
++	/* Keep an index */
++	comics_document->page_positions = save_positions (comics_document->page_names);
+ 
+         /* Now sort the pages */
+         g_ptr_array_sort (comics_document->page_names, sort_page_names);
+@@ -632,7 +400,7 @@ comics_document_save (EvDocument *document,
+ {
+ 	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+ 
+-	return ev_xfer_uri_simple (comics_document->archive, uri, error);
++	return ev_xfer_uri_simple (comics_document->archive_uri, uri, error);
+ }
+ 
+ static int
+@@ -646,6 +414,23 @@ comics_document_get_n_pages (EvDocument *document)
+ 	return comics_document->page_names->len;
+ }
+ 
++typedef struct {
++	gboolean got_info;
++	int height;
++	int width;
++} PixbufInfo;
++
++static void
++get_page_size_prepared_cb (GdkPixbufLoader *loader,
++			   int              width,
++			   int              height,
++			   PixbufInfo      *info)
++{
++	info->got_info = TRUE;
++	info->height = height;
++	info->width = width;
++}
++
+ static void
+ comics_document_get_page_size (EvDocument *document,
+ 			       EvPage     *page,
+@@ -653,74 +438,89 @@ comics_document_get_page_size (EvDocument *document,
+ 			       double     *height)
+ {
+ 	GdkPixbufLoader *loader;
+-	char **argv;
+-	guchar buf[1024];
+-	gboolean success, got_size = FALSE;
+-	gint outpipe = -1;
+-	GPid child_pid;
+-	gssize bytes;
+-	GdkPixbuf *pixbuf;
+-	gchar *filename;
++
+ 	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+ 
+-	if (!comics_document->decompress_tmp) {
+-		argv = extract_argv (document, page->index);
+-		success = g_spawn_async_with_pipes (NULL, argv, NULL,
+-						    G_SPAWN_SEARCH_PATH |
+-						    G_SPAWN_STDERR_TO_DEV_NULL,
+-						    NULL, NULL,
+-						    &child_pid,
+-						    NULL, &outpipe, NULL, NULL);
+-		g_strfreev (argv);
+-		g_return_if_fail (success == TRUE);
+-
+-		loader = gdk_pixbuf_loader_new ();
+-		g_signal_connect (loader, "area-prepared",
+-				  G_CALLBACK (get_page_size_area_prepared_cb),
+-				  &got_size);
+-
+-		while (outpipe >= 0) {
+-			bytes = read (outpipe, buf, 1024);
+-
+-			if (bytes > 0)
+-			gdk_pixbuf_loader_write (loader, buf, bytes, NULL);
+-			if (bytes <= 0 || got_size) {
+-				close (outpipe);
+-				outpipe = -1;
+-				gdk_pixbuf_loader_close (loader, NULL);
++	const char *page_path;
++	PixbufInfo info;
++	GError *error = NULL;
++
++	page_path = g_ptr_array_index (comics_document->page_names, page->index);
++
++	if (!archive_reopen_if_needed (comics_document, page_path, &error)) {
++		g_warning ("Fatal error opening archive: %s", error->message);
++		g_error_free (error);
++		return;
++	}
++
++	loader = gdk_pixbuf_loader_new ();
++	info.got_info = FALSE;
++	g_signal_connect (loader, "size-prepared",
++			  G_CALLBACK (get_page_size_prepared_cb),
++			  &info);
++
++	while (1) {
++		const char *name;
++		GError *error = NULL;
++
++		if (!ev_archive_read_next_header (comics_document->archive, &error)) {
++			if (error != NULL) {
++				g_warning ("Fatal error handling archive (%s): %s", G_STRFUNC, error->message);
++				g_error_free (error);
+ 			}
++			break;
+ 		}
+-		pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+-		if (pixbuf) {
+-			if (width)
+-				*width = gdk_pixbuf_get_width (pixbuf);
+-			if (height)
+-				*height = gdk_pixbuf_get_height (pixbuf);
+-		}
+-		g_spawn_close_pid (child_pid);
+-		g_object_unref (loader);
+-	} else {
+-		filename = g_build_filename (comics_document->dir,
+-                                             (char *) comics_document->page_names->pdata[page->index],
+-					     NULL);
+-		pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+-		if (pixbuf) {
+-			if (width)
+-				*width = gdk_pixbuf_get_width (pixbuf);
+-			if (height)
+-				*height = gdk_pixbuf_get_height (pixbuf);
+-			g_object_unref (pixbuf);
++
++		name = ev_archive_get_entry_pathname (comics_document->archive);
++		if (g_strcmp0 (name, page_path) == 0) {
++			char buf[BLOCK_SIZE];
++			gssize read;
++			gint64 left;
++
++			left = ev_archive_get_entry_size (comics_document->archive);
++			read = ev_archive_read_data (comics_document->archive, buf,
++						     MIN(BLOCK_SIZE, left), &error);
++			while (read > 0 && !info.got_info) {
++				if (!gdk_pixbuf_loader_write (loader, (guchar *) buf, read, &error)) {
++					read = -1;
++					break;
++				}
++				left -= read;
++				read = ev_archive_read_data (comics_document->archive, buf,
++							     MIN(BLOCK_SIZE, left), &error);
++			}
++			if (read < 0) {
++				g_warning ("Fatal error reading '%s' in archive: %s", name, error->message);
++				g_error_free (error);
++			}
++			break;
+ 		}
+-		g_free (filename);
++	}
++
++	gdk_pixbuf_loader_close (loader, NULL);
++	g_object_unref (loader);
++
++	if (info.got_info) {
++		if (width)
++			*width = info.width;
++		if (height)
++			*height = info.height;
+ 	}
+ }
+ 
+ static void
+-get_page_size_area_prepared_cb (GdkPixbufLoader *loader,
+-				gpointer         data)
++render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
++				gint             width,
++				gint             height,
++				EvRenderContext *rc)
+ {
+-	gboolean *got_size = data;
+-	*got_size = TRUE;
++	// int scaled_width, scaled_height;
++	double scale = rc->scale;
++	int w = (width  * scale + 0.5);
++	int h = (height * scale + 0.5);
++
++	// ev_render_context_compute_scaled_size (rc, width, height, &scaled_width, &scaled_height);
++	gdk_pixbuf_loader_set_size (loader, w, h);
+ }
+ 
+ static GdkPixbuf *
+@@ -728,69 +528,68 @@ comics_document_render_pixbuf (EvDocument      *document,
+ 			       EvRenderContext *rc)
+ {
+ 	GdkPixbufLoader *loader;
+-	GdkPixbuf *rotated_pixbuf, *tmp_pixbuf;
+-	char **argv;
+-	guchar buf[4096];
+-	gboolean success;
+-	gint outpipe = -1;
+-	GPid child_pid;
+-	gssize bytes;
+-	gint width, height;
+-	gchar *filename;
++	GdkPixbuf *tmp_pixbuf;
++	GdkPixbuf *rotated_pixbuf = NULL;
+ 	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
++	const char *page_path;
++	GError *error = NULL;
++
++	page_path = g_ptr_array_index (comics_document->page_names, rc->page->index);
++
++	if (!archive_reopen_if_needed (comics_document, page_path, &error)) {
++		g_warning ("Fatal error opening archive: %s", error->message);
++		g_error_free (error);
++		return NULL;
++	}
+ 
+-	if (!comics_document->decompress_tmp) {
+-		argv = extract_argv (document, rc->page->index);
+-		success = g_spawn_async_with_pipes (NULL, argv, NULL,
+-						    G_SPAWN_SEARCH_PATH |
+-						    G_SPAWN_STDERR_TO_DEV_NULL,
+-						    NULL, NULL,
+-						    &child_pid,
+-						    NULL, &outpipe, NULL, NULL);
+-		g_strfreev (argv);
+-		g_return_val_if_fail (success == TRUE, NULL);
+-
+-		loader = gdk_pixbuf_loader_new ();
+-		g_signal_connect (loader, "size-prepared",
+-				  G_CALLBACK (render_pixbuf_size_prepared_cb),
+-				  &rc->scale);
+-
+-		while (outpipe >= 0) {
+-			bytes = read (outpipe, buf, 4096);
+-
+-			if (bytes > 0) {
+-				gdk_pixbuf_loader_write (loader, buf, bytes,
+-				NULL);
++	loader = gdk_pixbuf_loader_new ();
++	g_signal_connect (loader, "size-prepared",
++			  G_CALLBACK (render_pixbuf_size_prepared_cb),
++			  rc);
++
++	while (1) {
++		const char *name;
++
++		if (!ev_archive_read_next_header (comics_document->archive, &error)) {
++			if (error != NULL) {
++				g_warning ("Fatal error handling archive (%s): %s", G_STRFUNC, error->message);
++				g_error_free (error);
++			}
++			break;
++		}
++
++		name = ev_archive_get_entry_pathname (comics_document->archive);
++		if (g_strcmp0 (name, page_path) == 0) {
++			size_t size = ev_archive_get_entry_size (comics_document->archive);
++			char *buf;
++			ssize_t read;
++
++			buf = g_malloc (size);
++			read = ev_archive_read_data (comics_document->archive, buf, size, &error);
++			if (read <= 0) {
++				if (read < 0) {
++					g_warning ("Fatal error reading '%s' in archive: %s", name, error->message);
++					g_error_free (error);
++				} else {
++					g_warning ("Read an empty file from the archive");
++				}
+ 			} else {
+-				close (outpipe);
+-				gdk_pixbuf_loader_close (loader, NULL);
+-				outpipe = -1;
++				gdk_pixbuf_loader_write (loader, (guchar *) buf, size, NULL);
+ 			}
++			g_free (buf);
++			gdk_pixbuf_loader_close (loader, NULL);
++			break;
+ 		}
+-		tmp_pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+-		rotated_pixbuf =
+-			gdk_pixbuf_rotate_simple (tmp_pixbuf,
+-						  360 - rc->rotation);
+-		g_spawn_close_pid (child_pid);
+-		g_object_unref (loader);
+-	} else {
+-		filename =
+-			g_build_filename (comics_document->dir,
+-                                          (char *) comics_document->page_names->pdata[rc->page->index],
+-					  NULL);
+-
+-		gdk_pixbuf_get_file_info (filename, &width, &height);
+-
+-		tmp_pixbuf =
+-			gdk_pixbuf_new_from_file_at_size (
+-				    filename, width * (rc->scale) + 0.5,
+-				    height * (rc->scale) + 0.5, NULL);
+-		rotated_pixbuf =
+-			gdk_pixbuf_rotate_simple (tmp_pixbuf,
+-						  360 - rc->rotation);
+-		g_free (filename);
+-		g_object_unref (tmp_pixbuf);
+ 	}
++	tmp_pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
++	if (tmp_pixbuf) {
++		if ((rc->rotation % 360) == 0)
++			rotated_pixbuf = g_object_ref (tmp_pixbuf);
++		else
++			rotated_pixbuf = gdk_pixbuf_rotate_simple (tmp_pixbuf,
++								   360 - rc->rotation);
++	}
++	g_object_unref (loader);
+ 	return rotated_pixbuf;
+ }
+ 
+@@ -802,79 +601,26 @@ comics_document_render (EvDocument      *document,
+ 	cairo_surface_t *surface;
+ 
+ 	pixbuf = comics_document_render_pixbuf (document, rc);
++	if (!pixbuf)
++		return NULL;
+ 	surface = ev_document_misc_surface_from_pixbuf (pixbuf);
+-	g_object_unref (pixbuf);
+-
++	g_clear_object (&pixbuf);
+ 	return surface;
+ }
+ 
+-static void
+-render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
+-				gint             width,
+-				gint             height,
+-				gpointer         data)
+-{
+-	double *scale = data;
+-	int w = (width  * (*scale) + 0.5);
+-	int h = (height * (*scale) + 0.5);
+-
+-	gdk_pixbuf_loader_set_size (loader, w, h);
+-}
+-
+-/**
+- * comics_remove_dir: Removes a directory recursively.
+- * Returns:
+- *   	0 if it was successfully deleted,
+- * 	-1 if an error occurred
+- */
+-static int
+-comics_remove_dir (gchar *path_name)
+-{
+-	GDir  *content_dir;
+-	const gchar *filename;
+-	gchar *filename_with_path;
+-
+-	if (g_file_test (path_name, G_FILE_TEST_IS_DIR)) {
+-		content_dir = g_dir_open  (path_name, 0, NULL);
+-		filename  = g_dir_read_name (content_dir);
+-		while (filename) {
+-			filename_with_path =
+-				g_build_filename (path_name,
+-						  filename, NULL);
+-			comics_remove_dir (filename_with_path);
+-			g_free (filename_with_path);
+-			filename = g_dir_read_name (content_dir);
+-		}
+-		g_dir_close (content_dir);
+-	}
+-	/* Note from g_remove() documentation: on Windows, it is in general not
+-	 * possible to remove a file that is open to some process, or mapped
+-	 * into memory.*/
+-	return (g_remove (path_name));
+-}
+-
+ static void
+ comics_document_finalize (GObject *object)
+ {
+ 	ComicsDocument *comics_document = COMICS_DOCUMENT (object);
+ 
+-	if (comics_document->decompress_tmp) {
+-		if (comics_remove_dir (comics_document->dir) == -1)
+-			g_warning (_("There was an error deleting “%s”."),
+-				   comics_document->dir);
+-		g_free (comics_document->dir);
+-	}
+-
+ 	if (comics_document->page_names) {
+                 g_ptr_array_foreach (comics_document->page_names, (GFunc) g_free, NULL);
+                 g_ptr_array_free (comics_document->page_names, TRUE);
+ 	}
+-
+-	g_free (comics_document->archive);
+-	g_free (comics_document->selected_command);
+-	g_free (comics_document->alternative_command);
+-	g_free (comics_document->extract_command);
+-	g_free (comics_document->list_command);
++	g_clear_pointer (&comics_document->page_positions, g_hash_table_destroy);
++	g_clear_object (&comics_document->archive);
++	g_free (comics_document->archive_path);
++	g_free (comics_document->archive_uri);
+ 
+ 	G_OBJECT_CLASS (comics_document_parent_class)->finalize (object);
+ }
+@@ -897,33 +643,7 @@ comics_document_class_init (ComicsDocumentClass *klass)
+ static void
+ comics_document_init (ComicsDocument *comics_document)
+ {
+-	comics_document->archive = NULL;
+-	comics_document->page_names = NULL;
+-	comics_document->extract_command = NULL;
+-}
+-
+-/* Returns a list of file extensions supported by gdk-pixbuf */
+-static GSList*
+-get_supported_image_extensions(void)
+-{
+-	GSList *extensions = NULL;
+-	GSList *formats = gdk_pixbuf_get_formats ();
+-	GSList *l;
+-
+-	for (l = formats; l != NULL; l = l->next) {
+-		int i;
+-		gchar **ext = gdk_pixbuf_format_get_extensions (l->data);
+-
+-		for (i = 0; ext[i] != NULL; i++) {
+-			extensions = g_slist_append (extensions,
+-						     g_strdup (ext[i]));
+-		}
+-
+-		g_strfreev (ext);
+-	}
+-
+-	g_slist_free (formats);
+-	return extensions;
++	comics_document->archive = ev_archive_new ();
+ }
+ 
+ static GdkPixbuf *
+@@ -971,48 +691,3 @@ comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *i
+ 	iface->get_thumbnail = comics_document_thumbnails_get_thumbnail;
+ 	iface->get_dimensions = comics_document_thumbnails_get_dimensions;
+ }
+-
+-static char**
+-extract_argv (EvDocument *document, gint page)
+-{
+-	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+-	char **argv;
+-	char *command_line, *quoted_archive, *quoted_filename;
+-	GError *err = NULL;
+-
+-	if (g_strrstr (comics_document->page_names->pdata[page], "--checkpoint-action="))
+-	{
+-		g_warning ("File unsupported\n");
+-		gtk_main_quit ();
+-	}
+-
+-        if (page >= comics_document->page_names->len)
+-                return NULL;
+-
+-	if (comics_document->regex_arg) {
+-		quoted_archive = g_shell_quote (comics_document->archive);
+-		quoted_filename =
+-			comics_regex_quote (comics_document->page_names->pdata[page]);
+-	} else {
+-		quoted_archive = g_shell_quote (comics_document->archive);
+-		quoted_filename = g_shell_quote (comics_document->page_names->pdata[page]);
+-	}
+-
+-	command_line = g_strdup_printf ("%s %s %s",
+-					comics_document->extract_command,
+-					quoted_archive,
+-					quoted_filename);
+-	g_free (quoted_archive);
+-	g_free (quoted_filename);
+-
+-	g_shell_parse_argv (command_line, NULL, &argv, &err);
+-	g_free (command_line);
+-
+-	if (err) {
+-		g_warning (_("Error %s"), err->message);
+-		g_error_free (err);
+-		return NULL;
+-	}
+-
+-	return argv;
+-}
+diff --git a/backend/comics/comics-document.h b/backend/comics/comics-document.h
+index f6a4b440..4417a69f 100644
+--- a/backend/comics/comics-document.h
++++ b/backend/comics/comics-document.h
+@@ -16,9 +16,9 @@
+  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  */
+ 
+-#ifndef __COMICS_DOCUMENT_H__
+-#define __COMICS_DOCUMENT_H__
++#pragma once
+ 
++#include "ev-macros.h"
+ #include "ev-document.h"
+ 
+ G_BEGIN_DECLS
+@@ -30,9 +30,8 @@ G_BEGIN_DECLS
+ typedef struct _ComicsDocument ComicsDocument;
+ 
+ GType                 comics_document_get_type (void) G_GNUC_CONST;
++GType                 register_atril_backend  (GTypeModule *module);
+ 
+-G_MODULE_EXPORT GType register_atril_backend  (GTypeModule *module);
+ 
+ G_END_DECLS
+ 
+-#endif /* __COMICS_DOCUMENT_H__ */
+diff --git a/backend/comics/ev-archive.c b/backend/comics/ev-archive.c
+new file mode 100644
+index 00000000..568e1621
+--- /dev/null
++++ b/backend/comics/ev-archive.c
+@@ -0,0 +1,323 @@
++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
++/*
++ * Copyright (C) 2017, Bastien Nocera <hadess@hadess.net>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This program 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.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#include "config.h"
++#include "ev-archive.h"
++
++#include <archive.h>
++#include <archive_entry.h>
++#include <gio/gio.h>
++
++#define BUFFER_SIZE (64 * 1024)
++
++struct _EvArchive {
++	GObject parent_instance;
++	EvArchiveType type;
++
++	/* libarchive */
++	struct archive *libar;
++	struct archive_entry *libar_entry;
++};
++
++G_DEFINE_TYPE(EvArchive, ev_archive, G_TYPE_OBJECT);
++
++static void
++ev_archive_finalize (GObject *object)
++{
++	EvArchive *archive = EV_ARCHIVE (object);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		g_clear_pointer (&archive->libar, archive_free);
++		break;
++	default:
++		break;
++	}
++
++	G_OBJECT_CLASS (ev_archive_parent_class)->finalize (object);
++}
++
++static void
++ev_archive_class_init (EvArchiveClass *klass)
++{
++        GObjectClass *object_class = (GObjectClass *) klass;
++
++        object_class->finalize = ev_archive_finalize;
++}
++
++EvArchive *
++ev_archive_new (void)
++{
++	return g_object_new (EV_TYPE_ARCHIVE, NULL);
++}
++
++static void
++libarchive_set_archive_type (EvArchive *archive,
++			     EvArchiveType archive_type)
++{
++	archive->type = archive_type;
++	archive->libar = archive_read_new ();
++
++	if (archive_type == EV_ARCHIVE_TYPE_ZIP)
++		archive_read_support_format_zip (archive->libar);
++	else if (archive_type == EV_ARCHIVE_TYPE_7Z)
++		archive_read_support_format_7zip (archive->libar);
++	else if (archive_type == EV_ARCHIVE_TYPE_TAR)
++		archive_read_support_format_tar (archive->libar);
++	else if (archive_type == EV_ARCHIVE_TYPE_RAR) {
++		archive_read_support_format_rar (archive->libar);
++		archive_read_support_format_rar5 (archive->libar);
++	} else
++		g_assert_not_reached ();
++}
++
++EvArchiveType
++ev_archive_get_archive_type (EvArchive *archive)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), EV_ARCHIVE_TYPE_NONE);
++
++	return archive->type;
++}
++
++gboolean
++ev_archive_set_archive_type (EvArchive *archive,
++			     EvArchiveType archive_type)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
++	g_return_val_if_fail (archive->type == EV_ARCHIVE_TYPE_NONE, FALSE);
++
++	switch (archive_type) {
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		libarchive_set_archive_type (archive, archive_type);
++		break;
++	default:
++		g_assert_not_reached ();
++	}
++
++	return TRUE;
++}
++
++gboolean
++ev_archive_open_filename (EvArchive   *archive,
++			  const char  *path,
++			  GError     **error)
++{
++	int r;
++
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
++	g_return_val_if_fail (path != NULL, FALSE);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_NONE:
++		g_assert_not_reached ();
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		r = archive_read_open_filename (archive->libar, path, BUFFER_SIZE);
++		if (r != ARCHIVE_OK) {
++			g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
++				     "Error opening archive: %s", archive_error_string (archive->libar));
++			return FALSE;
++		}
++		return TRUE;
++	}
++
++	return FALSE;
++}
++
++static gboolean
++libarchive_read_next_header (EvArchive *archive,
++			     GError   **error)
++{
++	while (1) {
++		int r;
++
++		r = archive_read_next_header (archive->libar, &archive->libar_entry);
++		if (r != ARCHIVE_OK) {
++			if (r != ARCHIVE_EOF)
++				g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
++					     "Error reading archive: %s", archive_error_string (archive->libar));
++			return FALSE;
++		}
++
++		if (archive_entry_filetype (archive->libar_entry) != AE_IFREG) {
++			g_debug ("Skipping '%s' as it's not a regular file",
++				 archive_entry_pathname (archive->libar_entry));
++			continue;
++		}
++
++		g_debug ("At header for file '%s'", archive_entry_pathname (archive->libar_entry));
++
++		break;
++	}
++
++	return TRUE;
++}
++
++gboolean
++ev_archive_read_next_header (EvArchive *archive,
++			     GError   **error)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_NONE:
++		g_assert_not_reached ();
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		return libarchive_read_next_header (archive, error);
++	}
++
++	return FALSE;
++}
++
++gboolean
++ev_archive_at_entry (EvArchive *archive)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
++
++	return (archive->libar_entry != NULL);
++}
++
++const char *
++ev_archive_get_entry_pathname (EvArchive *archive)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), NULL);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, NULL);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_NONE:
++		g_assert_not_reached ();
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		g_return_val_if_fail (archive->libar_entry != NULL, NULL);
++		return archive_entry_pathname (archive->libar_entry);
++	}
++
++	return NULL;
++}
++
++gint64
++ev_archive_get_entry_size (EvArchive *archive)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_NONE:
++		g_assert_not_reached ();
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		g_return_val_if_fail (archive->libar_entry != NULL, -1);
++		return archive_entry_size (archive->libar_entry);
++	}
++
++	return -1;
++}
++
++gboolean
++ev_archive_get_entry_is_encrypted (EvArchive *archive)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_NONE:
++		g_assert_not_reached ();
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		g_return_val_if_fail (archive->libar_entry != NULL, -1);
++		return archive_entry_is_encrypted (archive->libar_entry);
++	}
++
++	return FALSE;
++}
++
++gssize
++ev_archive_read_data (EvArchive *archive,
++		      void      *buf,
++		      gsize      count,
++		      GError   **error)
++{
++	gssize r = -1;
++
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_NONE:
++		g_assert_not_reached ();
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		g_return_val_if_fail (archive->libar_entry != NULL, -1);
++		r = archive_read_data (archive->libar, buf, count);
++		if (r < 0) {
++			g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
++				     "Failed to decompress data: %s", archive_error_string (archive->libar));
++		}
++		break;
++	}
++
++	return r;
++}
++
++void
++ev_archive_reset (EvArchive *archive)
++{
++	g_return_if_fail (EV_IS_ARCHIVE (archive));
++	g_return_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		g_clear_pointer (&archive->libar, archive_free);
++		libarchive_set_archive_type (archive, archive->type);
++		archive->libar_entry = NULL;
++		break;
++	default:
++		g_assert_not_reached ();
++	}
++}
++
++static void
++ev_archive_init (EvArchive *archive)
++{
++}
+diff --git a/backend/comics/ev-archive.h b/backend/comics/ev-archive.h
+new file mode 100644
+index 00000000..b4e1399c
+--- /dev/null
++++ b/backend/comics/ev-archive.h
+@@ -0,0 +1,56 @@
++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
++/*
++ * Copyright (C) 2017, Bastien Nocera <hadess@hadess.net>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This program 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.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#pragma once
++
++#include <glib-object.h>
++
++G_BEGIN_DECLS
++
++#define EV_TYPE_ARCHIVE ev_archive_get_type ()
++G_DECLARE_FINAL_TYPE (EvArchive, ev_archive, EV, ARCHIVE, GObject)
++
++typedef enum {
++	EV_ARCHIVE_TYPE_NONE = 0,
++	EV_ARCHIVE_TYPE_RAR,
++	EV_ARCHIVE_TYPE_ZIP,
++	EV_ARCHIVE_TYPE_7Z,
++	EV_ARCHIVE_TYPE_TAR
++} EvArchiveType;
++
++EvArchive     *ev_archive_new                (void);
++gboolean       ev_archive_set_archive_type   (EvArchive     *archive,
++					      EvArchiveType  archive_type);
++EvArchiveType  ev_archive_get_archive_type   (EvArchive     *archive);
++gboolean       ev_archive_open_filename      (EvArchive     *archive,
++					      const char    *path,
++					      GError       **error);
++gboolean       ev_archive_read_next_header   (EvArchive     *archive,
++					      GError       **error);
++gboolean       ev_archive_at_entry           (EvArchive     *archive);
++const char    *ev_archive_get_entry_pathname (EvArchive     *archive);
++gint64         ev_archive_get_entry_size     (EvArchive     *archive);
++gboolean       ev_archive_get_entry_is_encrypted (EvArchive *archive);
++gssize         ev_archive_read_data          (EvArchive     *archive,
++					      void          *buf,
++					      gsize          count,
++					      GError       **error);
++void           ev_archive_reset              (EvArchive     *archive);
++
++G_END_DECLS
+diff --git a/configure.ac b/configure.ac
+index e25de054..d5830716 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -580,8 +580,11 @@ AC_ARG_ENABLE(comics,
+     [enable_comics=$enableval],
+     [enable_comics=yes])
+ 
++COMICS_DEPS="libarchive"
+ if test "x$enable_comics" = "xyes"; then
+     AC_DEFINE([ENABLE_COMICS], [1], [Enable support for comics.])
++    PKG_CHECK_MODULES([COMICS], [$COMICS_DEPS])
++    AC_SUBST(COMICS_LIBS)
+ fi
+ AM_CONDITIONAL(ENABLE_COMICS, test x$enable_comics = xyes)
+ 
+diff --git a/libdocument/ev-document.h b/libdocument/ev-document.h
+index 67f53abb..8f54e6b5 100644
+--- a/libdocument/ev-document.h
++++ b/libdocument/ev-document.h
+@@ -59,6 +59,7 @@ typedef struct _EvDocumentPrivate EvDocumentPrivate;
+ typedef enum
+ {
+         EV_DOCUMENT_ERROR_INVALID,
++        EV_DOCUMENT_ERROR_UNSUPPORTED_CONTENT,
+         EV_DOCUMENT_ERROR_ENCRYPTED
+ } EvDocumentError;
+ 
+-- 
+2.39.2
+
diff -Nru atril-1.26.0/debian/patches/1002-avoid-crash-on-certain-epub-files.patch atril-1.26.0/debian/patches/1002-avoid-crash-on-certain-epub-files.patch
--- atril-1.26.0/debian/patches/1002-avoid-crash-on-certain-epub-files.patch	1970-01-01 01:00:00.000000000 +0100
+++ atril-1.26.0/debian/patches/1002-avoid-crash-on-certain-epub-files.patch	2024-01-23 10:05:56.000000000 +0100
@@ -0,0 +1,27 @@
+Description: Avoid crash on certain epub files
+
+Author: Bernhard Übelacker <bernhardu@mailbox.org>
+Bug-Debian: https://bugs.debian.org/972715
+Forwarded: no
+Last-Update: 2020-11-01
+
+--- a/backend/epub/epub-document.c
++++ b/backend/epub/epub-document.c
+@@ -1279,7 +1279,7 @@
+             xml_parse_children_of_node(navLabel,(xmlChar*)"text",NULL,NULL);
+             linknode *newnode = g_new0(linknode,1);
+             newnode->linktext = NULL;
+-            while (newnode->linktext == NULL) {
++            while (xmlretval && newnode->linktext == NULL) {
+                 newnode->linktext = (gchar*)xml_get_data_from_node(xmlretval,XML_KEYWORD,NULL);
+                 xmlretval = xmlretval->next;
+             }
+@@ -1597,7 +1597,7 @@
+     contentListNode *pagedata;
+ 
+     guint flag=0;
+-    while (!flag) {
++    while (listiter && !flag) {
+         pagedata = listiter->data;
+         if (link_present_on_page(Link->pagelink, pagedata->value)) {
+             flag=1;
diff -Nru atril-1.26.0/debian/patches/series atril-1.26.0/debian/patches/series
--- atril-1.26.0/debian/patches/series	2022-10-27 10:40:29.000000000 +0200
+++ atril-1.26.0/debian/patches/series	2024-01-23 10:06:53.000000000 +0100
@@ -1 +1,7 @@
 1001-webkit2gtk4.1.patch
+1002-avoid-crash-on-certain-epub-files.patch
+0001-Accessibility-add-button-description.patch
+0003-epub-Fix-index-loading-for-certain-documents-look-fo.patch
+0004-epub-add-fallback-for-malformed-epub-files-in-check_.patch
+0005-Use-a-blank-line-at-most.patch
+0006-comics-Use-libarchive-to-unpack-documents.patch
diff -Nru atril-1.26.0/debian/changelog atril-1.26.0/debian/changelog
--- atril-1.26.0/debian/changelog	2024-01-06 07:18:28.000000000 +0100
+++ atril-1.26.0/debian/changelog	2024-01-23 10:08:40.000000000 +0100
@@ -1,3 +1,12 @@
+atril (1.26.0-2+deb12u2) bookworm; urgency=medium
+
+  * debian/patches:
+    + Add 0005-Use-a-blank-line-at-most.patch and 0006-comics-Use-libarchive-
+      to-unpack-documents.patch. Use libarchive instead of external command for
+      extracing documents (CVE-2023-51698, closes: #1060751).
+
+ -- Mike Gabriel <sunweaver@debian.org>  Tue, 23 Jan 2024 10:08:40 +0100
+
 atril (1.26.0-2+deb12u1) bookworm; urgency=medium
 
   * debian/patches:
diff -Nru atril-1.26.0/debian/patches/0005-Use-a-blank-line-at-most.patch atril-1.26.0/debian/patches/0005-Use-a-blank-line-at-most.patch
--- atril-1.26.0/debian/patches/0005-Use-a-blank-line-at-most.patch	1970-01-01 01:00:00.000000000 +0100
+++ atril-1.26.0/debian/patches/0005-Use-a-blank-line-at-most.patch	2024-01-23 10:08:40.000000000 +0100
@@ -0,0 +1,1866 @@
+From 658fab1f008bff7e1ec147d95baa04bc44c2fbcd Mon Sep 17 00:00:00 2001
+From: rbuj <robert.buj@gmail.com>
+Date: Wed, 27 Oct 2021 17:30:36 +0200
+Subject: [PATCH 1/2] Use a blank line at most
+
+Signed-off-by: Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
+---
+ backend/comics/comics-document.c              |  3 --
+ backend/djvu/djvu-document.c                  |  3 --
+ backend/djvu/djvu-links.c                     |  2 -
+ backend/djvu/djvu-text-page.c                 |  3 --
+ backend/djvu/djvu-text-page.h                 |  1 -
+ backend/dvi/dvi-document.c                    |  2 -
+ backend/dvi/fonts.c                           |  1 -
+ backend/dvi/mdvi-lib/afmparse.c               | 18 --------
+ backend/dvi/mdvi-lib/bitmap.c                 |  2 -
+ backend/dvi/mdvi-lib/color.c                  |  1 -
+ backend/dvi/mdvi-lib/color.h                  |  1 -
+ backend/dvi/mdvi-lib/fontmap.c                |  1 -
+ backend/dvi/mdvi-lib/hash.c                   |  1 -
+ backend/dvi/mdvi-lib/hash.h                   |  2 -
+ backend/dvi/mdvi-lib/mdvi.h                   |  2 -
+ backend/dvi/mdvi-lib/paper.h                  |  1 -
+ backend/dvi/mdvi-lib/special.c                |  1 -
+ backend/dvi/mdvi-lib/tfmfile.c                |  1 -
+ backend/epub/epub-document.c                  |  6 ---
+ backend/epub/minizip/ioapi.c                  |  6 ---
+ backend/epub/minizip/ioapi.h                  |  7 ---
+ backend/epub/minizip/unzip.c                  | 45 -------------------
+ backend/epub/minizip/unzip.h                  | 11 -----
+ backend/pdf/ev-poppler.h                      |  1 -
+ backend/pixbuf/pixbuf-document.c              |  1 -
+ backend/tiff/tiff-document.c                  |  1 -
+ backend/tiff/tiff2ps.c                        |  2 -
+ backend/xps/xps-document.c                    |  2 -
+ .../toolbar-editor/egg-editable-toolbar.c     |  2 -
+ .../toolbar-editor/egg-toolbar-editor.c       |  1 -
+ .../toolbar-editor/egg-toolbar-editor.h       |  2 -
+ .../toolbar-editor/egg-toolbars-model.c       |  2 -
+ cut-n-paste/zoom-control/ephy-zoom.c          |  1 -
+ libdocument/ev-document-attachments.c         |  1 -
+ libdocument/ev-document-factory.c             |  1 -
+ libdocument/ev-document-fonts.h               |  1 -
+ libdocument/ev-document-misc.c                |  1 -
+ libdocument/ev-document-security.h            |  1 -
+ libdocument/ev-document-text.c                |  1 -
+ libdocument/ev-document.c                     |  1 -
+ libdocument/ev-file-helpers.h                 |  1 -
+ libdocument/ev-form-field.h                   |  1 -
+ libdocument/ev-image.h                        |  1 -
+ libdocument/ev-render-context.h               |  2 -
+ libdocument/ev-transition-effect.h            |  2 -
+ libmisc/ev-page-action-widget.c               |  3 --
+ libview/ev-page-accessible.c                  |  2 -
+ libview/ev-pixbuf-cache.c                     |  6 ---
+ libview/ev-pixbuf-cache.h                     |  2 -
+ libview/ev-timeline.c                         |  2 -
+ libview/ev-timeline.h                         |  2 -
+ libview/ev-transition-animation.c             |  2 -
+ libview/ev-transition-animation.h             |  2 -
+ libview/ev-view-presentation.c                |  1 -
+ libview/ev-view.c                             |  2 -
+ libview/ev-web-view.c                         |  2 -
+ libview/ev-web-view.h                         |  1 -
+ previewer/ev-previewer-window.c               |  1 -
+ shell/eggfindbar.c                            |  1 -
+ shell/eggfindbar.h                            |  1 -
+ shell/ev-application.c                        |  2 -
+ shell/ev-bookmarks.h                          |  2 -
+ shell/ev-daemon.c                             |  1 -
+ shell/ev-history.c                            |  1 -
+ shell/ev-loading-message.c                    |  1 -
+ shell/ev-media-player-keys.h                  |  1 -
+ shell/ev-navigation-action.c                  |  1 -
+ shell/ev-open-recent-action.c                 |  1 -
+ shell/ev-password-view.c                      |  2 -
+ shell/ev-sidebar-attachments.c                |  1 -
+ shell/ev-sidebar-bookmarks.c                  |  1 -
+ shell/ev-sidebar-layers.c                     |  1 -
+ shell/ev-sidebar-links.c                      |  3 --
+ shell/ev-sidebar-links.h                      |  1 -
+ shell/ev-sidebar-page.c                       |  1 -
+ shell/ev-sidebar-page.h                       |  1 -
+ shell/ev-sidebar-thumbnails.c                 |  1 -
+ shell/ev-sidebar-thumbnails.h                 |  1 -
+ shell/ev-sidebar.c                            |  1 -
+ shell/ev-sidebar.h                            |  1 -
+ shell/ev-window.c                             |  9 ----
+ shell/ev-window.h                             |  2 -
+ shell/main.c                                  |  4 --
+ 83 files changed, 220 deletions(-)
+
+--- a/backend/comics/comics-document.c
++++ b/backend/comics/comics-document.c
+@@ -126,7 +126,6 @@
+ static char**     extract_argv                   (EvDocument *document,
+ 						  gint page);
+ 
+-
+ EV_BACKEND_REGISTER_WITH_CODE (ComicsDocument, comics_document,
+ 	{
+ 		EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
+@@ -186,7 +185,6 @@
+ 	return g_string_free (dest, FALSE);
+ }
+ 
+-
+ /* This function manages the command for decompressing a comic book */
+ static gboolean
+ comics_decompress_temp_dir (const gchar *command_decompress_tmp,
+@@ -627,7 +625,6 @@
+ 	return TRUE;
+ }
+ 
+-
+ static gboolean
+ comics_document_save (EvDocument *document,
+ 		      const char *uri,
+--- a/backend/djvu/djvu-document.c
++++ b/backend/djvu/djvu-document.c
+@@ -66,7 +66,6 @@
+       EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_SELECTION, djvu_selection_iface_init);
+      });
+ 
+-
+ #define EV_DJVU_ERROR ev_djvu_error_quark ()
+ 
+ static GQuark
+@@ -256,7 +255,6 @@
+ 	return TRUE;
+ }
+ 
+-
+ static gboolean
+ djvu_document_save (EvDocument  *document,
+ 		    const char  *uri,
+@@ -676,7 +674,6 @@
+ 		r->y2 = height - tmp * SCALE_FACTOR;
+ 	}
+ 
+-
+ 	return matches;
+ }
+ 
+--- a/backend/djvu/djvu-links.c
++++ b/backend/djvu/djvu-links.c
+@@ -181,7 +181,6 @@
+ 		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);
+@@ -342,7 +341,6 @@
+ 	return NULL;
+ }
+ 
+-
+ gboolean
+ djvu_links_has_document_links (EvDocumentLinks *document_links)
+ {
+--- a/backend/djvu/djvu-text-page.c
++++ b/backend/djvu/djvu-text-page.c
+@@ -23,7 +23,6 @@
+ #include <libdjvu/miniexp.h>
+ #include "djvu-text-page.h"
+ 
+-
+ /**
+  * djvu_text_page_selection_process:
+  * @page: #DjvuTextPage instance
+@@ -117,7 +116,6 @@
+ 	}
+ }
+ 
+-
+ static void
+ djvu_text_page_limits (DjvuTextPage *page,
+ 			  miniexp_t     p,
+@@ -404,7 +402,6 @@
+ 	g_free (search_text);
+ }
+ 
+-
+ /**
+  * djvu_text_page_prepare_search:
+  * @page: #DjvuTextPage instance
+--- a/backend/djvu/djvu-text-page.h
++++ b/backend/djvu/djvu-text-page.h
+@@ -25,7 +25,6 @@
+ #include <glib.h>
+ #include <libdjvu/miniexp.h>
+ 
+-
+ typedef struct _DjvuTextPage DjvuTextPage;
+ typedef struct _DjvuTextLink DjvuTextLink;
+ 
+--- a/backend/dvi/dvi-document.c
++++ b/backend/dvi/dvi-document.c
+@@ -110,7 +110,6 @@
+ 
+ 	mdvi_cairo_device_init (&dvi_document->context->device);
+ 
+-
+ 	dvi_document->base_width = dvi_document->context->dvi_page_w * dvi_document->context->params.conv
+ 		+ 2 * unit2pix(dvi_document->params->dpi, MDVI_HMARGIN) / dvi_document->params->hshrink;
+ 
+@@ -123,7 +122,6 @@
+ 	return TRUE;
+ }
+ 
+-
+ static gboolean
+ dvi_document_save (EvDocument  *document,
+ 		      const char  *uri,
+--- a/backend/dvi/fonts.c
++++ b/backend/dvi/fonts.c
+@@ -54,4 +54,3 @@
+ 	return;
+ }
+ 
+-
+--- a/backend/dvi/mdvi-lib/afmparse.c
++++ b/backend/dvi/mdvi-lib/afmparse.c
+@@ -81,13 +81,10 @@
+ 
+ #define MATCH(A,B)		(strncmp((A),(B), MAX_NAME) == 0)
+ 
+-
+-
+ /*************************** GLOBALS ***********************/
+ 
+ static char *ident = NULL; /* storage buffer for keywords */
+ 
+-
+ /* "shorts" for fast case statement
+  * The values of each of these enumerated items correspond to an entry in the
+  * table of strings defined below. Therefore, if you add a new string as
+@@ -175,7 +172,6 @@
+ 
+ } /* token */
+ 
+-
+ /*************************** linetoken *************************/
+ 
+ /*  "linetoken" will get read all tokens until the EOL character from
+@@ -203,7 +199,6 @@
+ 
+ } /* linetoken */
+ 
+-
+ /*************************** recognize *************************/
+ 
+ /*  This function tries to match a string to a known list of
+@@ -234,7 +229,6 @@
+ 
+ } /* recognize */
+ 
+-
+ /************************* parseGlobals *****************************/
+ 
+ /*  This function is called by "parseFile". It will parse the AFM File
+@@ -401,8 +395,6 @@
+ 
+ } /* parseGlobals */
+ 
+-
+-
+ #if 0 /* this function does not seem to be used anywhere */
+ /************************* initializeArray ************************/
+ 
+@@ -584,7 +576,6 @@
+ 
+ } /* parseCharWidths */
+ 
+-
+ /************************* parseCharMetrics ************************/
+ 
+ /*  This function is called by parseFile if the caller of parseFile
+@@ -694,8 +685,6 @@
+ 
+ } /* parseCharMetrics */
+ 
+-
+-
+ /************************* parseTrackKernData ***********************/
+ 
+ /*  This function is called by "parseFile". It will parse the AFM File
+@@ -797,7 +786,6 @@
+ 
+ } /* parseTrackKernData */
+ 
+-
+ /************************* parsePairKernData ************************/
+ 
+ /*  This function is called by "parseFile". It will parse the AFM File
+@@ -918,7 +906,6 @@
+ 
+ } /* parsePairKernData */
+ 
+-
+ /************************* parseCompCharData **************************/
+ 
+ /*  This function is called by "parseFile". It will parse the AFM File
+@@ -1043,12 +1030,8 @@
+ 
+ } /* parseCompCharData */
+ 
+-
+-
+-
+ /*************************** 'PUBLIC' FUNCTION ********************/
+ 
+-
+ /*************************** parseFile *****************************/
+ 
+ /*  parseFile is the only 'public' procedure available. It is called
+@@ -1078,7 +1061,6 @@
+ 
+     register char *keyword; /* used to store a token */
+ 
+-
+     /* storage data for the global variable ident */
+     ident = (char *) calloc(MAX_NAME, sizeof(char));
+     if (ident == NULL) {error = storageProblem; return(error);}
+--- a/backend/dvi/mdvi-lib/bitmap.c
++++ b/backend/dvi/mdvi-lib/bitmap.c
+@@ -117,7 +117,6 @@
+ 	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+ };
+ 
+-
+ /*
+  * next we have three bitmap functions to convert bitmaps in LSB bit order
+  * with 8, 16 and 32 bits per unit, to our internal format. The differences
+@@ -192,7 +191,6 @@
+ 	return bm;
+ }
+ 
+-
+ BITMAP	*bitmap_copy(BITMAP *bm)
+ {
+ 	BITMAP	*nb = bitmap_alloc(bm->width, bm->height);
+--- a/backend/dvi/mdvi-lib/color.c
++++ b/backend/dvi/mdvi-lib/color.c
+@@ -79,7 +79,6 @@
+ 
+ #define GAMMA_DIFF	0.005
+ 
+-
+ /* create a color table */
+ Ulong	*get_color_table(DviDevice *dev,
+ 			 int nlevels, Ulong fg, Ulong bg, double gamma, int density)
+--- a/backend/dvi/mdvi-lib/color.h
++++ b/backend/dvi/mdvi-lib/color.h
+@@ -16,7 +16,6 @@
+  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  */
+ 
+-
+ #ifndef _COLOR_H_
+ #define _COLOR_H_
+ 
+--- a/backend/dvi/mdvi-lib/fontmap.c
++++ b/backend/dvi/mdvi-lib/fontmap.c
+@@ -834,7 +834,6 @@
+ 	return 0;
+ }
+ 
+-
+ void	mdvi_flush_encodings(void)
+ {
+ 	DviEncoding *enc;
+--- a/backend/dvi/mdvi-lib/hash.c
++++ b/backend/dvi/mdvi-lib/hash.c
+@@ -21,7 +21,6 @@
+ 
+ /* simple hash tables for MDVI */
+ 
+-
+ struct _DviHashBucket {
+ 	DviHashBucket *next;
+ 	DviHashKey	key;
+--- a/backend/dvi/mdvi-lib/hash.h
++++ b/backend/dvi/mdvi-lib/hash.h
+@@ -3,7 +3,6 @@
+ 
+ /* Hash tables */
+ 
+-
+ typedef struct _DviHashBucket DviHashBucket;
+ typedef struct _DviHashTable DviHashTable;
+ 
+@@ -18,7 +17,6 @@
+ typedef int	(*DviHashComp) __PROTO((DviHashKey key1, DviHashKey key2));
+ typedef void	(*DviHashFree) __PROTO((DviHashKey key, void *data));
+ 
+-
+ struct _DviHashTable {
+ 	DviHashBucket	**buckets;
+ 	int	nbucks;
+--- a/backend/dvi/mdvi-lib/mdvi.h
++++ b/backend/dvi/mdvi-lib/mdvi.h
+@@ -414,7 +414,6 @@
+ 	int	step;		/* step */
+ };
+ 
+-
+ typedef void (*DviSpecialHandler)
+ 	__PROTO((DviContext *dvi, const char *prefix, const char *arg));
+ 
+@@ -600,7 +599,6 @@
+ 
+ extern int mdvi_encode_font __PROTO((DviParams *, DviFont *));
+ 
+-
+ /* font lookup functions */
+ extern int mdvi_register_font_type __PROTO((DviFontInfo *, int));
+ extern char **mdvi_list_font_class __PROTO((int));
+--- a/backend/dvi/mdvi-lib/paper.h
++++ b/backend/dvi/mdvi-lib/paper.h
+@@ -24,7 +24,6 @@
+ 	const char *height;
+ };
+ 
+-
+ extern int 	mdvi_get_paper_size __PROTO((const char *, DviPaper *));
+ extern DviPaperSpec* mdvi_get_paper_specs __PROTO((DviPaperClass));
+ extern void	mdvi_free_paper_specs __PROTO((DviPaperSpec *));
+--- a/backend/dvi/mdvi-lib/special.c
++++ b/backend/dvi/mdvi-lib/special.c
+@@ -212,7 +212,6 @@
+ {
+ 	DviSpecial *sp, *list;
+ 
+-
+ 	for(list = (DviSpecial *)specials.head; (sp = list); ) {
+ 		list = sp->next;
+ 		if(sp->prefix) mdvi_free(sp->prefix);
+--- a/backend/dvi/mdvi-lib/tfmfile.c
++++ b/backend/dvi/mdvi-lib/tfmfile.c
+@@ -258,7 +258,6 @@
+ 	/* allocate characters */
+ 	info->chars = xnalloc(TFMChar, size);
+ 
+-
+ #ifdef WORD_LITTLE_ENDIAN
+ 	/* byte-swap the three arrays at once (they are consecutive in memory) */
+ 	swap_array((Uint32 *)widths, nw + nh + nd);
+--- a/backend/epub/epub-document.c
++++ b/backend/epub/epub-document.c
+@@ -224,7 +224,6 @@
+     return TRUE;
+ }
+ 
+-
+ typedef struct _LinksCBStruct {
+     GtkTreeModel *model;
+     GtkTreeIter  *parent;
+@@ -413,7 +412,6 @@
+     return (g_remove (path_name));
+ }
+ 
+-
+ static gboolean
+ check_mime_type             (const gchar* uri,
+                              GError** error);
+@@ -1562,7 +1560,6 @@
+ 
+         gchar *csspath = g_strdup_printf("%s/atrilnightstyle.css",epub_document->documentdir);
+ 
+-
+         GFile *styles = g_file_new_for_path (csspath);
+         GOutputStream *outstream = (GOutputStream*)g_file_create(styles,G_FILE_CREATE_PRIVATE,NULL,NULL);
+         if ( g_output_stream_write((GOutputStream*)outstream,style,strlen(style),NULL,NULL) == -1 )
+@@ -1636,7 +1633,6 @@
+     g_list_foreach(index,(GFunc)page_set_function,contentList);
+ }
+ 
+-
+ static void
+ add_mathjax_script_node_to_file(gchar *filename, gchar *data)
+ {
+@@ -1784,7 +1780,6 @@
+     epub_document->docTitle = NULL;
+ }
+ 
+-
+ static void
+ epub_document_finalize (GObject *object)
+ {
+@@ -1826,7 +1821,6 @@
+     G_OBJECT_CLASS (epub_document_parent_class)->finalize (object);
+ }
+ 
+-
+ static void
+ epub_document_class_init (EpubDocumentClass *klass)
+ {
+--- a/backend/epub/minizip/ioapi.c
++++ b/backend/epub/minizip/ioapi.c
+@@ -25,7 +25,6 @@
+ #define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
+ #endif
+ 
+-
+ #include "ioapi.h"
+ 
+ voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)
+@@ -82,8 +81,6 @@
+     p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file;
+ }
+ 
+-
+-
+ static voidpf  ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode));
+ static uLong   ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size));
+ static uLong   ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size));
+@@ -128,7 +125,6 @@
+     return file;
+ }
+ 
+-
+ static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size)
+ {
+     uLong ret;
+@@ -150,7 +146,6 @@
+     return ret;
+ }
+ 
+-
+ static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream)
+ {
+     ZPOS64_T ret;
+@@ -206,7 +201,6 @@
+     return ret;
+ }
+ 
+-
+ static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream)
+ {
+     int ret;
+--- a/backend/epub/minizip/ioapi.h
++++ b/backend/epub/minizip/ioapi.h
+@@ -110,7 +110,6 @@
+ extern "C" {
+ #endif
+ 
+-
+ #define ZLIB_FILEFUNC_SEEK_CUR (1)
+ #define ZLIB_FILEFUNC_SEEK_END (2)
+ #define ZLIB_FILEFUNC_SEEK_SET (0)
+@@ -122,7 +121,6 @@
+ #define ZLIB_FILEFUNC_MODE_EXISTING (4)
+ #define ZLIB_FILEFUNC_MODE_CREATE   (8)
+ 
+-
+ #ifndef ZCALLBACK
+  #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
+    #define ZCALLBACK CALLBACK
+@@ -131,9 +129,6 @@
+  #endif
+ #endif
+ 
+-
+-
+-
+ typedef voidpf   (ZCALLBACK *open_file_func)      OF((voidpf opaque, const char* filename, int mode));
+ typedef uLong    (ZCALLBACK *read_file_func)      OF((voidpf opaque, voidpf stream, void* buf, uLong size));
+ typedef uLong    (ZCALLBACK *write_file_func)     OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
+@@ -143,7 +138,6 @@
+ typedef long     (ZCALLBACK *tell_file_func)      OF((voidpf opaque, voidpf stream));
+ typedef long     (ZCALLBACK *seek_file_func)      OF((voidpf opaque, voidpf stream, uLong offset, int origin));
+ 
+-
+ /* here is the "old" 32 bits structure structure */
+ typedef struct zlib_filefunc_def_s
+ {
+@@ -185,7 +179,6 @@
+     seek_file_func      zseek32_file;
+ } zlib_filefunc64_32_def;
+ 
+-
+ #define ZREAD64(filefunc,filestream,buf,size)     ((*((filefunc).zfile_func64.zread_file))   ((filefunc).zfile_func64.opaque,filestream,buf,size))
+ #define ZWRITE64(filefunc,filestream,buf,size)    ((*((filefunc).zfile_func64.zwrite_file))  ((filefunc).zfile_func64.opaque,filestream,buf,size))
+ //#define ZTELL64(filefunc,filestream)            ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream))
+--- a/backend/epub/minizip/unzip.c
++++ b/backend/epub/minizip/unzip.c
+@@ -12,7 +12,6 @@
+ 
+          For more info read MiniZip_info.txt
+ 
+-
+   ------------------------------------------------------------------------------------
+   Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of
+   compatibility with older software. The following is from the original crypt.c.
+@@ -48,7 +47,6 @@
+ 
+         Copyright (C) 2007-2008 Even Rouault
+ 
+-
+         Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again).
+   Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G
+                                 should only read the compressed/uncompressed size from the Zip64 format if
+@@ -63,7 +61,6 @@
+ 
+ */
+ 
+-
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -86,20 +83,17 @@
+ #   include <errno.h>
+ #endif
+ 
+-
+ #ifndef local
+ #  define local static
+ #endif
+ /* compile with -Dlocal if your debugger can't find static symbols */
+ 
+-
+ #ifndef CASESENSITIVITYDEFAULT_NO
+ #  if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES)
+ #    define CASESENSITIVITYDEFAULT_NO
+ #  endif
+ #endif
+ 
+-
+ #ifndef UNZ_BUFSIZE
+ #define UNZ_BUFSIZE (16384)
+ #endif
+@@ -118,14 +112,12 @@
+ #define SIZECENTRALDIRITEM (0x2e)
+ #define SIZEZIPLOCALHEADER (0x1e)
+ 
+-
+ /* unz_file_info_interntal contain internal info about a file in zipfile*/
+ typedef struct unz_file_info64_internal_s
+ {
+     ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */
+ } unz_file_info64_internal;
+ 
+-
+ /* file_in_zip_read_info_s contain internal information about a file in zipfile,
+     when reading and decompress it */
+ typedef struct
+@@ -156,7 +148,6 @@
+     int   raw;
+ } file_in_zip64_read_info_s;
+ 
+-
+ /* unz64_s contain internal information about the zipfile
+ */
+ typedef struct
+@@ -189,7 +180,6 @@
+ #    endif
+ } unz64_s;
+ 
+-
+ #ifndef NOUNCRYPT
+ #include "crypt.h"
+ #endif
+@@ -200,7 +190,6 @@
+    IN assertion: the stream s has been successfully opened for reading.
+ */
+ 
+-
+ local int unz64local_getByte OF((
+     const zlib_filefunc64_32_def* pzlib_filefunc_def,
+     voidpf filestream,
+@@ -224,7 +213,6 @@
+     }
+ }
+ 
+-
+ /* ===========================================================================
+    Reads a long in LSB order from the given gz_stream. Sets
+ */
+@@ -295,7 +283,6 @@
+     voidpf filestream,
+     ZPOS64_T *pX));
+ 
+-
+ local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def,
+                             voidpf filestream,
+                             ZPOS64_T *pX)
+@@ -364,7 +351,6 @@
+     }
+ }
+ 
+-
+ #ifdef  CASESENSITIVITYDEFAULT_NO
+ #define CASESENSITIVITYDEFAULTVALUE 2
+ #else
+@@ -418,7 +404,6 @@
+     if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+         return 0;
+ 
+-
+     uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
+ 
+     if (uMaxBack>uSizeFile)
+@@ -463,7 +448,6 @@
+     return uPosFound;
+ }
+ 
+-
+ /*
+   Locate the Central directory 64 of a zipfile (at the end, just before
+     the global comment)
+@@ -486,7 +470,6 @@
+     if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+         return 0;
+ 
+-
+     uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
+ 
+     if (uMaxBack>uSizeFile)
+@@ -605,8 +588,6 @@
+         us.z_filefunc = *pzlib_filefunc64_32_def;
+     us.is64bitOpenFunction = is64bitOpenFunction;
+ 
+-
+-
+     us.filestream = ZOPEN64(us.z_filefunc,
+                                                  path,
+                                                  ZLIB_FILEFUNC_MODE_READ |
+@@ -745,7 +726,6 @@
+     us.pfile_in_zip_read = NULL;
+     us.encrypted = 0;
+ 
+-
+     s=(unz64_s*)ALLOC(sizeof(unz64_s));
+     if( s != NULL)
+     {
+@@ -755,7 +735,6 @@
+     return (unzFile)s;
+ }
+ 
+-
+ extern unzFile ZEXPORT unzOpen2 (const char *path,
+                                         zlib_filefunc_def* pzlib_filefunc32_def)
+ {
+@@ -814,7 +793,6 @@
+     return UNZ_OK;
+ }
+ 
+-
+ /*
+   Write info about the ZipFile in the *pglobal_info structure.
+   No preparation of the structure is needed
+@@ -897,7 +875,6 @@
+               ZLIB_FILEFUNC_SEEK_SET)!=0)
+         err=UNZ_ERRNO;
+ 
+-
+     /* we check the magic */
+     if (err==UNZ_OK)
+     {
+@@ -1002,7 +979,6 @@
+     else
+         lSeek += file_info.size_file_extra;
+ 
+-
+     if ((err==UNZ_OK) && (file_info.size_file_extra != 0))
+     {
+                                 uLong acc = 0;
+@@ -1096,7 +1072,6 @@
+     else
+         lSeek+=file_info.size_file_comment;
+ 
+-
+     if ((err==UNZ_OK) && (pfile_info!=NULL))
+         *pfile_info=file_info;
+ 
+@@ -1106,8 +1081,6 @@
+     return err;
+ }
+ 
+-
+-
+ /*
+   Write info about the ZipFile in the *pglobal_info structure.
+   No preparation of the structure is needed
+@@ -1156,7 +1129,6 @@
+ 
+         pfile_info->tmu_date = file_info64.tmu_date,
+ 
+-
+         pfile_info->compressed_size = (uLong)file_info64.compressed_size;
+         pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size;
+ 
+@@ -1212,7 +1184,6 @@
+     return err;
+ }
+ 
+-
+ /*
+   Try locate the file szFileName in the zipfile.
+   For the iCaseSensitivity signification, see unzStringFileNameCompare
+@@ -1234,7 +1205,6 @@
+     ZPOS64_T num_fileSaved;
+     ZPOS64_T pos_in_central_dirSaved;
+ 
+-
+     if (file==NULL)
+         return UNZ_PARAMERROR;
+ 
+@@ -1278,7 +1248,6 @@
+     return err;
+ }
+ 
+-
+ /*
+ ///////////////////////////////////////////
+ // Contributed by Ryan Haksi (mailto://cryogen@infoserve.net)
+@@ -1391,7 +1360,6 @@
+                                 s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0)
+         return UNZ_ERRNO;
+ 
+-
+     if (err==UNZ_OK)
+     {
+         if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
+@@ -1594,7 +1562,6 @@
+     pfile_in_zip_read_info->rest_read_uncompressed =
+             s->cur_file_info.uncompressed_size ;
+ 
+-
+     pfile_in_zip_read_info->pos_in_zipfile =
+             s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER +
+               iSizeVar;
+@@ -1626,7 +1593,6 @@
+     }
+ #    endif
+ 
+-
+     return UNZ_OK;
+ }
+ 
+@@ -1687,7 +1653,6 @@
+     if (pfile_in_zip_read_info==NULL)
+         return UNZ_PARAMERROR;
+ 
+-
+     if (pfile_in_zip_read_info->read_buffer == NULL)
+         return UNZ_END_OF_LIST_OF_FILE;
+     if (len==0)
+@@ -1731,7 +1696,6 @@
+                       uReadThis)!=uReadThis)
+                 return UNZ_ERRNO;
+ 
+-
+ #            ifndef NOUNCRYPT
+             if(s->encrypted)
+             {
+@@ -1743,7 +1707,6 @@
+             }
+ #            endif
+ 
+-
+             pfile_in_zip_read_info->pos_in_zipfile += uReadThis;
+ 
+             pfile_in_zip_read_info->rest_read_compressed-=uReadThis;
+@@ -1874,7 +1837,6 @@
+     return err;
+ }
+ 
+-
+ /*
+   Give the current position in uncompressed data
+ */
+@@ -1909,7 +1871,6 @@
+     return pfile_in_zip_read_info->total_out_64;
+ }
+ 
+-
+ /*
+   return 1 if the end of file was reached, 0 elsewhere
+ */
+@@ -1931,8 +1892,6 @@
+         return 0;
+ }
+ 
+-
+-
+ /*
+ Read extra field from the current file (opened by unzOpenCurrentFile)
+ This is the local-header version of the extra field (sometimes, there is
+@@ -2007,7 +1966,6 @@
+     if (pfile_in_zip_read_info==NULL)
+         return UNZ_PARAMERROR;
+ 
+-
+     if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) &&
+         (!pfile_in_zip_read_info->raw))
+     {
+@@ -2015,7 +1973,6 @@
+             err=UNZ_CRCERROR;
+     }
+ 
+-
+     TRYFREE(pfile_in_zip_read_info->read_buffer);
+     pfile_in_zip_read_info->read_buffer = NULL;
+     if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED)
+@@ -2025,7 +1982,6 @@
+         BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream);
+ #endif
+ 
+-
+     pfile_in_zip_read_info->stream_initialised = 0;
+     TRYFREE(pfile_in_zip_read_info);
+ 
+@@ -2034,7 +1990,6 @@
+     return err;
+ }
+ 
+-
+ /*
+   Get the global comment string of the ZipFile, in the szComment buffer.
+   uSizeBuf is the size of the szComment buffer.
+--- a/backend/epub/minizip/unzip.h
++++ b/backend/epub/minizip/unzip.h
+@@ -70,7 +70,6 @@
+ typedef voidp unzFile;
+ #endif
+ 
+-
+ #define UNZ_OK                          (0)
+ #define UNZ_END_OF_LIST_OF_FILE         (-100)
+ #define UNZ_ERRNO                       (Z_ERRNO)
+@@ -162,7 +161,6 @@
+     (like 1 on Unix, 2 on Windows)
+ */
+ 
+-
+ extern unzFile ZEXPORT unzOpen OF((const char *path));
+ extern unzFile ZEXPORT unzOpen64 OF((const void *path));
+ /*
+@@ -180,7 +178,6 @@
+        does not describe the reality
+ */
+ 
+-
+ extern unzFile ZEXPORT unzOpen2 OF((const char *path,
+                                     zlib_filefunc_def* pzlib_filefunc_def));
+ /*
+@@ -212,7 +209,6 @@
+   No preparation of the structure is needed
+   return UNZ_OK if there is no problem. */
+ 
+-
+ extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
+                                            char *szComment,
+                                            uLong uSizeBuf));
+@@ -222,7 +218,6 @@
+   return the number of byte copied or an error code <0
+ */
+ 
+-
+ /***************************************************************************/
+ /* Unzip package allow you browse the directory of the zipfile */
+ 
+@@ -251,7 +246,6 @@
+   UNZ_END_OF_LIST_OF_FILE if the file is not found
+ */
+ 
+-
+ /* ****************************************** */
+ /* Ryan supplied functions */
+ /* unz_file_info contain information about a file in the zipfile */
+@@ -315,14 +309,12 @@
+             (commentBufferSize is the size of the buffer)
+ */
+ 
+-
+ /** Addition for GDAL : START */
+ 
+ extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file));
+ 
+ /** Addition for GDAL : END */
+ 
+-
+ /***************************************************************************/
+ /* for reading the content of the current zipfile, you can open it, read data
+    from it, and close it (you can close it before reading all the file)
+@@ -369,7 +361,6 @@
+          but you CANNOT set method parameter as NULL
+ */
+ 
+-
+ extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
+ /*
+   Close the file in zip opened with unzOpenCurrentFile
+@@ -428,8 +419,6 @@
+ extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos);
+ extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
+ 
+-
+-
+ #ifdef __cplusplus
+ }
+ #endif
+--- a/backend/pdf/ev-poppler.h
++++ b/backend/pdf/ev-poppler.h
+@@ -34,7 +34,6 @@
+ 
+ G_MODULE_EXPORT GType register_atril_backend (GTypeModule *module);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* __PDF_DOCUMENT_H__ */
+--- a/backend/pixbuf/pixbuf-document.c
++++ b/backend/pixbuf/pixbuf-document.c
+@@ -201,7 +201,6 @@
+ 	iface->get_dimensions = pixbuf_document_thumbnails_get_dimensions;
+ }
+ 
+-
+ static void
+ pixbuf_document_init (PixbufDocument *pixbuf_document)
+ {
+--- a/backend/tiff/tiff-document.c
++++ b/backend/tiff/tiff-document.c
+@@ -361,7 +361,6 @@
+ 	if (width <= 0 || height <= 0)
+ 		return NULL;
+ 
+-
+ 	if (width >= INT_MAX / 4)
+ 		/* overflow */
+ 		return NULL;
+--- a/backend/tiff/tiff2ps.c
++++ b/backend/tiff/tiff2ps.c
+@@ -449,7 +449,6 @@
+ 	return splitpage;
+ }
+ 
+-
+ void
+ tiff2ps_process_page(TIFF2PSContext* ctx, TIFF* tif, double pw, double ph,
+ 		     double lm, double bm, gboolean cnt)
+@@ -592,7 +591,6 @@
+ 	}
+ }
+ 
+-
+ static char DuplexPreamble[] = "\
+ %%BeginFeature: *Duplex True\n\
+ systemdict begin\n\
+--- a/backend/xps/xps-document.c
++++ b/backend/xps/xps-document.c
+@@ -176,7 +176,6 @@
+ 		EV_DOCUMENT_INFO_N_PAGES |
+ 		EV_DOCUMENT_INFO_PAPER_SIZE;
+ 
+-
+ 	if (gxps_document_get_n_pages (xps->doc) > 0) {
+ 		ev_document_get_page_size (document, 0,
+ 					   &(info->paper_width),
+@@ -483,7 +482,6 @@
+ 	iface->find_link_page = xps_document_links_find_link_page;
+ }
+ 
+-
+ /* EvDocumentPrint */
+ static void
+ xps_document_print_print_page (EvDocumentPrint *document,
+--- a/cut-n-paste/toolbar-editor/egg-editable-toolbar.c
++++ b/cut-n-paste/toolbar-editor/egg-editable-toolbar.c
+@@ -541,7 +541,6 @@
+     }
+ }
+ 
+-
+ static void
+ configure_item_tooltip (GtkToolItem *item)
+ {
+@@ -557,7 +556,6 @@
+     }
+ }
+ 
+-
+ static void
+ connect_widget_signals (GtkWidget *proxy, EggEditableToolbar *etoolbar)
+ {
+--- a/cut-n-paste/toolbar-editor/egg-toolbar-editor.c
++++ b/cut-n-paste/toolbar-editor/egg-toolbar-editor.c
+@@ -36,7 +36,6 @@
+   {EGG_TOOLBAR_ITEM_TYPE, GTK_TARGET_SAME_APP, 0},
+ };
+ 
+-
+ static void egg_toolbar_editor_finalize         (GObject *object);
+ static void update_editor_sheet                 (EggToolbarEditor *editor);
+ 
+--- a/cut-n-paste/toolbar-editor/egg-toolbar-editor.h
++++ b/cut-n-paste/toolbar-editor/egg-toolbar-editor.h
+@@ -34,7 +34,6 @@
+ #define EGG_IS_TOOLBAR_EDITOR_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TOOLBAR_EDITOR))
+ #define EGG_TOOLBAR_EDITOR_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TOOLBAR_EDITOR, EggToolbarEditorClass))
+ 
+-
+ typedef struct EggToolbarEditor EggToolbarEditor;
+ typedef struct EggToolbarEditorPrivate EggToolbarEditorPrivate;
+ 
+@@ -51,7 +50,6 @@
+   GtkBoxClass parent_class;
+ };
+ 
+-
+ GType            egg_toolbar_editor_get_type     (void);
+ GtkWidget        *egg_toolbar_editor_new         (GtkUIManager *manager,
+                                                   EggToolbarsModel *model);
+--- a/cut-n-paste/toolbar-editor/egg-toolbars-model.c
++++ b/cut-n-paste/toolbar-editor/egg-toolbars-model.c
+@@ -213,7 +213,6 @@
+   EggToolbarsItem *idata2;
+   GNode *toolbar, *item;
+ 
+-
+   for(toolbar = g_node_first_child (model->priv->toolbars);
+       toolbar != NULL; toolbar = g_node_next_sibling (toolbar))
+     {
+@@ -330,7 +329,6 @@
+ 		 0, toolbar_position);
+ }
+ 
+-
+ char *
+ egg_toolbars_model_get_data (EggToolbarsModel *model,
+                              GdkAtom           type,
+--- a/cut-n-paste/zoom-control/ephy-zoom.c
++++ b/cut-n-paste/zoom-control/ephy-zoom.c
+@@ -54,7 +54,6 @@
+ 	return n_zoom_levels - 1;
+ }
+ 
+-
+ float
+ ephy_zoom_get_changed_zoom_level (float level, gint steps)
+ {
+--- a/libdocument/ev-document-attachments.c
++++ b/libdocument/ev-document-attachments.c
+@@ -52,4 +52,3 @@
+ 	return iface->get_attachments (document_attachments);
+ }
+ 
+-
+--- a/libdocument/ev-document-factory.c
++++ b/libdocument/ev-document-factory.c
+@@ -106,7 +106,6 @@
+ 	return EV_COMPRESSION_NONE;
+ }
+ 
+-
+ /*
+  * get_document_from_uri:
+  * @uri: the document URI
+--- a/libdocument/ev-document-fonts.h
++++ b/libdocument/ev-document-fonts.h
+@@ -37,7 +37,6 @@
+ 
+ G_BEGIN_DECLS
+ 
+-
+ #define EV_TYPE_DOCUMENT_FONTS		  (ev_document_fonts_get_type ())
+ #define EV_DOCUMENT_FONTS(o)		  (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_DOCUMENT_FONTS, EvDocumentFonts))
+ #define EV_DOCUMENT_FONTS_IFACE(k)	  (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_DOCUMENT_FONTS, EvDocumentFontsInterface))
+--- a/libdocument/ev-document-misc.c
++++ b/libdocument/ev-document-misc.c
+@@ -149,7 +149,6 @@
+ 	}
+ }
+ 
+-
+ void
+ ev_document_misc_paint_one_page (cairo_t      *cr,
+ 				 GtkWidget    *widget,
+--- a/libdocument/ev-document-security.h
++++ b/libdocument/ev-document-security.h
+@@ -35,7 +35,6 @@
+ 
+ G_BEGIN_DECLS
+ 
+-
+ #define EV_TYPE_DOCUMENT_SECURITY		  (ev_document_security_get_type ())
+ #define EV_DOCUMENT_SECURITY(o)			  (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_DOCUMENT_SECURITY, EvDocumentSecurity))
+ #define EV_DOCUMENT_SECURITY_IFACE(k)	  	  (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_DOCUMENT_SECURITY, EvDocumentSecurityInterface))
+--- a/libdocument/ev-document-text.c
++++ b/libdocument/ev-document-text.c
+@@ -42,7 +42,6 @@
+ 	return iface->get_text (document_text, page);
+ }
+ 
+-
+ gboolean
+ ev_document_text_get_text_layout (EvDocumentText   *document_text,
+ 				  EvPage           *page,
+--- a/libdocument/ev-document.c
++++ b/libdocument/ev-document.c
+@@ -388,7 +388,6 @@
+ 	return klass->get_page (document, index);
+ }
+ 
+-
+ #ifdef ENABLE_SYNCTEX
+ static gboolean
+ _ev_document_support_synctex (EvDocument *document)
+--- a/libdocument/ev-file-helpers.h
++++ b/libdocument/ev-file-helpers.h
+@@ -66,7 +66,6 @@
+ 				       EvCompressionType  type,
+ 				       GError           **error);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* EV_FILE_HELPERS_H */
+--- a/libdocument/ev-form-field.h
++++ b/libdocument/ev-form-field.h
+@@ -209,7 +209,6 @@
+ GType        ev_form_field_signature_get_type (void) G_GNUC_CONST;
+ EvFormField *ev_form_field_signature_new      (gint                  id);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* !EV_FORM_FIELD_H */
+--- a/libdocument/ev-image.h
++++ b/libdocument/ev-image.h
+@@ -62,7 +62,6 @@
+ 					GdkPixbuf       *pixbuf);
+ const gchar *ev_image_get_tmp_uri      (EvImage         *image);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* __EV_IMAGE_H__ */
+--- a/libdocument/ev-render-context.h
++++ b/libdocument/ev-render-context.h
+@@ -52,7 +52,6 @@
+ 	gdouble scale;
+ };
+ 
+-
+ GType            ev_render_context_get_type        (void) G_GNUC_CONST;
+ EvRenderContext *ev_render_context_new             (EvPage          *page,
+ 						    gint             rotation,
+@@ -64,7 +63,6 @@
+ void             ev_render_context_set_scale       (EvRenderContext *rc,
+ 						    gdouble          scale);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* !EV_RENDER_CONTEXT */
+--- a/libdocument/ev-transition-effect.h
++++ b/libdocument/ev-transition-effect.h
+@@ -61,7 +61,6 @@
+ 	EV_TRANSITION_DIRECTION_OUTWARD
+ } EvTransitionEffectDirection;
+ 
+-
+ typedef struct EvTransitionEffect      EvTransitionEffect;
+ typedef struct EvTransitionEffectClass EvTransitionEffectClass;
+ 
+@@ -75,7 +74,6 @@
+ 	GObjectClass parent_class;
+ };
+ 
+-
+ GType                 ev_transition_effect_get_type           (void) G_GNUC_CONST;
+ 
+ EvTransitionEffect   *ev_transition_effect_new                (EvTransitionEffectType  type,
+--- a/libmisc/ev-page-action-widget.c
++++ b/libmisc/ev-page-action-widget.c
+@@ -305,7 +305,6 @@
+ 	return TRUE;
+ }
+ 
+-
+ static void
+ display_completion_text (GtkCellLayout      *cell_layout,
+ 			 GtkCellRenderer    *renderer,
+@@ -349,7 +348,6 @@
+ 			    EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+ 			    -1);
+ 
+-
+ 	if (link) {
+ 		text = ev_link_get_title (link);
+ 		g_object_unref (link);
+@@ -445,7 +443,6 @@
+ 	return filter_model;
+ }
+ 
+-
+ void
+ ev_page_action_widget_update_links_model (EvPageActionWidget *proxy, GtkTreeModel *model)
+ {
+--- a/libview/ev-page-accessible.c
++++ b/libview/ev-page-accessible.c
+@@ -37,7 +37,6 @@
+ 	gboolean          children_initialized;
+ };
+ 
+-
+ enum {
+ 	PROP_0,
+ 	PROP_VIEW_ACCESSIBLE,
+@@ -412,7 +411,6 @@
+ 	EvRectangle *next_word_end;
+ 	gint prev_offset, next_offset;
+ 
+-
+ 	if (!log_attrs[offset].is_white)
+ 		return FALSE;
+ 
+--- a/libview/ev-pixbuf-cache.c
++++ b/libview/ev-pixbuf-cache.c
+@@ -72,7 +72,6 @@
+ 	void (* job_finished) (EvPixbufCache *pixbuf_cache);
+ };
+ 
+-
+ enum
+ {
+ 	JOB_FINISHED,
+@@ -92,7 +91,6 @@
+ 						  gint                page,
+ 						  gfloat              scale);
+ 
+-
+ /* These are used for iterating through the prev and next arrays */
+ #define FIRST_VISIBLE_PREV(pixbuf_cache) \
+ 	(MAX (0, pixbuf_cache->preload_cache_size - pixbuf_cache->start_page))
+@@ -223,7 +221,6 @@
+ 	G_OBJECT_CLASS (ev_pixbuf_cache_parent_class)->dispose (object);
+ }
+ 
+-
+ EvPixbufCache *
+ ev_pixbuf_cache_new (GtkWidget       *view,
+ 		     EvDocumentModel *model,
+@@ -985,7 +982,6 @@
+ 	}
+ }
+ 
+-
+ void
+ ev_pixbuf_cache_style_changed (EvPixbufCache *pixbuf_cache)
+ {
+@@ -1265,7 +1261,6 @@
+ 	}
+ }
+ 
+-
+ /* Returns what the pixbuf cache thinks is */
+ 
+ GList *
+@@ -1356,4 +1351,3 @@
+ 		 EV_JOB_PRIORITY_URGENT);
+ }
+ 
+-
+--- a/libview/ev-pixbuf-cache.h
++++ b/libview/ev-pixbuf-cache.h
+@@ -39,8 +39,6 @@
+ #define EV_PIXBUF_CACHE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_PIXBUF_CACHE, EvPixbufCache))
+ #define EV_IS_PIXBUF_CACHE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_PIXBUF_CACHE))
+ 
+-
+-
+ /* The coordinates in the rect here are at scale == 1.0, so that we can ignore
+  * resizings.  There is one per page, maximum.
+  */
+--- a/libview/ev-timeline.c
++++ b/libview/ev-timeline.c
+@@ -56,10 +56,8 @@
+ 
+ static guint signals [LAST_SIGNAL] = { 0, };
+ 
+-
+ G_DEFINE_TYPE_WITH_PRIVATE (EvTimeline, ev_timeline, G_TYPE_OBJECT)
+ 
+-
+ static void
+ ev_timeline_init (EvTimeline *timeline)
+ {
+--- a/libview/ev-timeline.h
++++ b/libview/ev-timeline.h
+@@ -59,7 +59,6 @@
+ 				    gdouble     progress);
+ };
+ 
+-
+ GType                 ev_timeline_get_type           (void) G_GNUC_CONST;
+ 
+ EvTimeline           *ev_timeline_new                (guint                    duration);
+@@ -84,7 +83,6 @@
+ 
+ gdouble               ev_timeline_get_progress       (EvTimeline             *timeline);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* __EV_TIMELINE_H__ */
+--- a/libview/ev-transition-animation.c
++++ b/libview/ev-transition-animation.c
+@@ -41,10 +41,8 @@
+ 	PROP_DEST_SURFACE
+ };
+ 
+-
+ G_DEFINE_TYPE_WITH_PRIVATE (EvTransitionAnimation, ev_transition_animation, EV_TYPE_TIMELINE)
+ 
+-
+ static void
+ ev_transition_animation_init (EvTransitionAnimation *animation)
+ {
+--- a/libview/ev-transition-animation.h
++++ b/libview/ev-transition-animation.h
+@@ -50,7 +50,6 @@
+ 	EvTimelineClass parent_class;
+ };
+ 
+-
+ GType                   ev_transition_animation_get_type           (void) G_GNUC_CONST;
+ 
+ EvTransitionAnimation * ev_transition_animation_new                (EvTransitionEffect    *effect);
+@@ -67,7 +66,6 @@
+ 								    GdkRectangle           page_area);
+ gboolean                ev_transition_animation_ready              (EvTransitionAnimation *animation);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* __EV_TRANSITION_ANIMATION_H__ */
+--- a/libview/ev-view-presentation.c
++++ b/libview/ev-view-presentation.c
+@@ -1385,7 +1385,6 @@
+ 	return TRUE;
+ }
+ 
+-
+ static void
+ add_change_page_binding_keypad (GtkBindingSet  *binding_set,
+ 				guint           keyval,
+--- a/libview/ev-view.c
++++ b/libview/ev-view.c
+@@ -5621,7 +5621,6 @@
+ 
+ /*** Drawing ***/
+ 
+-
+ static void
+ draw_rubberband (EvView             *view,
+ 		 cairo_t            *cr,
+@@ -5652,7 +5651,6 @@
+ 	cairo_restore (cr);
+ }
+ 
+-
+ static void
+ highlight_find_results (EvView *view, cairo_t *cr, int page)
+ {
+--- a/libview/ev-web-view.c
++++ b/libview/ev-web-view.c
+@@ -31,7 +31,6 @@
+ #include "ev-document-model.h"
+ #include "ev-jobs.h"
+ 
+-
+  typedef enum {
+  	EV_WEB_VIEW_FIND_NEXT,
+  	EV_WEB_VIEW_FIND_PREV
+@@ -322,7 +321,6 @@
+ 	web_view_update_range_and_current_page (webview);
+ }
+ 
+-
+ gboolean
+ ev_web_view_next_page (EvWebView *webview)
+ {
+--- a/libview/ev-web-view.h
++++ b/libview/ev-web-view.h
+@@ -35,7 +35,6 @@
+ #include <glib-object.h>
+ G_BEGIN_DECLS
+ 
+-
+ typedef struct _EvWebView       EvWebView;
+ typedef struct _EvWebViewClass  EvWebViewClass;
+ 
+--- a/previewer/ev-previewer-window.c
++++ b/previewer/ev-previewer-window.c
+@@ -621,7 +621,6 @@
+ 	return object;
+ }
+ 
+-
+ static void
+ ev_previewer_window_class_init (EvPreviewerWindowClass *klass)
+ {
+--- a/shell/eggfindbar.c
++++ b/shell/eggfindbar.c
+@@ -573,7 +573,6 @@
+   g_object_thaw_notify (G_OBJECT (find_bar));
+ }
+ 
+-
+ /**
+  * egg_find_bar_get_search_string:
+  *
+--- a/shell/eggfindbar.h
++++ b/shell/eggfindbar.h
+@@ -77,4 +77,3 @@
+ 
+ #endif /* __EGG_FIND_BAR_H__ */
+ 
+-
+--- a/shell/ev-application.c
++++ b/shell/ev-application.c
+@@ -21,7 +21,6 @@
+  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  */
+ 
+-
+ #include <config.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -311,7 +310,6 @@
+ 	return empty_window;
+ }
+ 
+-
+ #ifdef ENABLE_DBUS
+ typedef struct {
+ 	gchar          *uri;
+--- a/shell/ev-bookmarks.h
++++ b/shell/ev-bookmarks.h
+@@ -50,8 +50,6 @@
+ void         ev_bookmarks_update        (EvBookmarks *bookmarks,
+                                          EvBookmark  *bookmark);
+ 
+-
+-
+ G_END_DECLS
+ 
+ #endif /* EV_BOOKMARKS_H */
+--- a/shell/ev-daemon.c
++++ b/shell/ev-daemon.c
+@@ -43,7 +43,6 @@
+ 
+ #define LOG g_debug
+ 
+-
+ #define EV_TYPE_DAEMON_APPLICATION              (ev_daemon_application_get_type ())
+ #define EV_DAEMON_APPLICATION(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), EV_TYPE_DAEMON_APPLICATION, EvDaemonApplication))
+ 
+--- a/shell/ev-history.c
++++ b/shell/ev-history.c
+@@ -24,7 +24,6 @@
+ 
+ #include "ev-history.h"
+ 
+-
+ enum
+ {
+ 	HISTORY_CHANGED,
+--- a/shell/ev-loading-message.c
++++ b/shell/ev-loading-message.c
+@@ -174,4 +174,3 @@
+         return message;
+ }
+ 
+-
+--- a/shell/ev-media-player-keys.h
++++ b/shell/ev-media-player-keys.h
+@@ -37,7 +37,6 @@
+ typedef struct _EvMediaPlayerKeys EvMediaPlayerKeys;
+ typedef struct _EvMediaPlayerKeysClass EvMediaPlayerKeysClass;
+ 
+-
+ GType	           ev_media_player_keys_get_type  (void) G_GNUC_CONST;
+ 
+ EvMediaPlayerKeys *ev_media_player_keys_new	  (void);
+--- a/shell/ev-navigation-action.c
++++ b/shell/ev-navigation-action.c
+@@ -26,7 +26,6 @@
+ #include "ev-navigation-action.h"
+ #include "ev-navigation-action-widget.h"
+ 
+-
+ enum
+ {
+ 	WIDGET_ACTIVATE_LINK,
+--- a/shell/ev-open-recent-action.c
++++ b/shell/ev-open-recent-action.c
+@@ -24,7 +24,6 @@
+ 
+ #include "ev-open-recent-action.h"
+ 
+-
+ enum {
+ 	ITEM_ACTIVATED,
+ 	N_SIGNALS
+--- a/shell/ev-password-view.c
++++ b/shell/ev-password-view.c
+@@ -18,7 +18,6 @@
+  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  */
+ 
+-
+ #ifdef HAVE_CONFIG_H
+ #include "config.h"
+ #endif
+@@ -47,7 +46,6 @@
+ 
+ static guint password_view_signals [LAST_SIGNAL] = { 0 };
+ 
+-
+ G_DEFINE_TYPE_WITH_PRIVATE (EvPasswordView, ev_password_view, GTK_TYPE_VIEWPORT)
+ 
+ static void
+--- a/shell/ev-sidebar-attachments.c
++++ b/shell/ev-sidebar-attachments.c
+@@ -631,7 +631,6 @@
+ 	g_object_unref (job);
+ }
+ 
+-
+ static void
+ ev_sidebar_attachments_document_changed_cb (EvDocumentModel      *model,
+ 					    GParamSpec           *pspec,
+--- a/shell/ev-sidebar-bookmarks.c
++++ b/shell/ev-sidebar-bookmarks.c
+@@ -122,7 +122,6 @@
+         GtkTreeModel              *model;
+         GtkTreeIter                iter;
+ 
+-
+         selection = gtk_tree_view_get_selection (tree_view);
+         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+                 GtkTreePath *path;
+--- a/shell/ev-sidebar-layers.c
++++ b/shell/ev-sidebar-layers.c
+@@ -239,7 +239,6 @@
+ 	gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
+ 				     GTK_SELECTION_NONE);
+ 
+-
+ 	column = gtk_tree_view_column_new ();
+ 
+ 	renderer = gtk_cell_renderer_toggle_new ();
+--- a/shell/ev-sidebar-links.c
++++ b/shell/ev-sidebar-links.c
+@@ -379,7 +379,6 @@
+ 	return FALSE;
+ }
+ 
+-
+ static void
+ ev_sidebar_links_construct (EvSidebarLinks *ev_sidebar_links)
+ {
+@@ -425,7 +424,6 @@
+ 					     "markup", EV_DOCUMENT_LINKS_COLUMN_MARKUP,
+ 					     NULL);
+ 
+-
+ 	renderer = gtk_cell_renderer_text_new ();
+ 	gtk_tree_view_column_pack_end (GTK_TREE_VIEW_COLUMN (column), renderer, FALSE);
+ 	gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
+@@ -566,7 +564,6 @@
+ 	}
+ }
+ 
+-
+ static gint
+ page_link_tree_sort (gconstpointer a, gconstpointer b, void *data)
+ {
+--- a/shell/ev-sidebar-links.h
++++ b/shell/ev-sidebar-links.h
+@@ -63,4 +63,3 @@
+ 
+ #endif /* __EV_SIDEBAR_LINKS_H__ */
+ 
+-
+--- a/shell/ev-sidebar-page.c
++++ b/shell/ev-sidebar-page.c
+@@ -74,7 +74,6 @@
+ 	return iface->get_label (sidebar_page);
+ }
+ 
+-
+ static void
+ ev_sidebar_page_default_init (EvSidebarPageInterface *iface)
+ {
+--- a/shell/ev-sidebar-page.h
++++ b/shell/ev-sidebar-page.h
+@@ -58,7 +58,6 @@
+ 				                 EvDocumentModel *model);
+ const gchar*  ev_sidebar_page_get_label         (EvSidebarPage *page);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* EV_SIDEBAR_PAGE */
+--- a/shell/ev-sidebar-thumbnails.c
++++ b/shell/ev-sidebar-thumbnails.c
+@@ -245,7 +245,6 @@
+ 	return cache;
+ }
+ 
+-
+ static void
+ ev_sidebar_thumbnails_dispose (GObject *object)
+ {
+--- a/shell/ev-sidebar-thumbnails.h
++++ b/shell/ev-sidebar-thumbnails.h
+@@ -56,4 +56,3 @@
+ 
+ #endif /* __EV_SIDEBAR_THUMBNAILS_H__ */
+ 
+-
+--- a/shell/ev-sidebar.c
++++ b/shell/ev-sidebar.c
+@@ -446,7 +446,6 @@
+ 	gtk_list_store_move_before(GTK_LIST_STORE(ev_sidebar->priv->page_model),
+ 					   &iter, NULL);
+ 
+-
+ 	/* Set the first item added as active */
+ 	gtk_tree_model_get_iter_first (ev_sidebar->priv->page_model, &iter);
+ 	gtk_tree_model_get (ev_sidebar->priv->page_model,
+--- a/shell/ev-sidebar.h
++++ b/shell/ev-sidebar.h
+@@ -64,4 +64,3 @@
+ 
+ #endif /* __EV_SIDEBAR_H__ */
+ 
+-
+--- a/shell/ev-window.c
++++ b/shell/ev-window.c
+@@ -418,7 +418,6 @@
+ 	G_GNUC_END_IGNORE_DEPRECATIONS;
+ }
+ 
+-
+ static void
+ ev_window_setup_action_sensitivity (EvWindow *ev_window)
+ {
+@@ -1555,7 +1554,6 @@
+ 		ev_document_model_set_scale (model, g_settings_get_double (settings, "zoom"));
+ }
+ 
+-
+ static void
+ ev_window_clear_thumbnail_job (EvWindow *ev_window)
+ {
+@@ -3636,7 +3634,6 @@
+ 		GtkWidget *dialog;
+ 		GError    *error = NULL;
+ 
+-
+ 		ev_print_operation_get_error (op, &error);
+ 
+ 		/* The message area is already used by
+@@ -3891,7 +3888,6 @@
+ 		return FALSE;
+ 	}
+ 
+-
+ 	text = g_markup_printf_escaped (_("Save a copy of document “%s” before closing?"),
+ 					gtk_window_get_title (GTK_WINDOW (ev_window)));
+ 
+@@ -4414,7 +4410,6 @@
+                                          _("Running in presentation mode"));
+ }
+ 
+-
+ static void
+ ev_window_uninhibit_screensaver (EvWindow *window)
+ {
+@@ -4727,7 +4722,6 @@
+ 	ev_window_update_actions (window);
+ }
+ 
+-
+ static void
+ ev_window_cmd_edit_rotate_left (GtkAction *action, EvWindow *ev_window)
+ {
+@@ -5977,7 +5971,6 @@
+ 		 * the new expanded window size.
+ 		 */
+ 
+-
+ 		if (ev_window->priv->chrome & EV_CHROME_SIDEBAR)
+ 		{
+ 			GtkAllocation alloc;
+@@ -6491,7 +6484,6 @@
+ 	{ "EditSaveSettings", NULL, N_("Save Current Settings as _Default"), "<control>T", NULL,
+ 	  G_CALLBACK (ev_window_cmd_edit_save_settings) },
+ 
+-
+         /* View menu */
+         { "ViewZoomIn", "zoom-in", N_("Zoom _In"), "<control>plus",
+           N_("Enlarge the document"),
+@@ -7746,7 +7738,6 @@
+         ev_atril_window_emit_document_loaded (window->priv->skeleton, window->priv->uri);
+ }
+ 
+-
+ #ifdef ENABLE_SYNCTEX
+ static gboolean
+ handle_sync_view_cb (EvAtrilWindow        *object,
+--- a/shell/ev-window.h
++++ b/shell/ev-window.h
+@@ -58,7 +58,6 @@
+ #define EV_IS_WINDOW_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_WINDOW))
+ #define EV_WINDOW_GET_CLASS(object)	(G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_WINDOW, EvWindowClass))
+ 
+-
+ struct _EvWindow {
+ 	GtkApplicationWindow base_instance;
+ 	EvWindowPrivate     *priv;
+@@ -87,7 +86,6 @@
+ 					 int		 last_page);
+ const gchar *	ev_window_get_dbus_object_path (EvWindow *ev_window);
+ 
+-
+ G_END_DECLS
+ 
+ #endif /* !EV_WINDOW_H */
+--- a/shell/main.c
++++ b/shell/main.c
+@@ -36,7 +36,6 @@
+ #include "eggsmclient.h"
+ #include "eggdesktopfile.h"
+ 
+-
+ static gchar   *ev_page_label;
+ static gchar   *ev_find_string;
+ static gint     ev_page_index = 0;
+@@ -48,7 +47,6 @@
+ static gchar   *print_settings;
+ static const char **file_arguments = NULL;
+ 
+-
+ static gboolean
+ option_version_cb (const gchar *option_name,
+                    const gchar *value,
+@@ -206,8 +204,6 @@
+ 			continue;
+ 		}
+ 
+-
+-
+ 		ev_application_open_uri_at_dest (EV_APP, uri, screen, dest,
+ 						 mode, ev_find_string,
+ 						 GDK_CURRENT_TIME);
diff -Nru atril-1.26.0/debian/patches/0006-comics-Use-libarchive-to-unpack-documents.patch atril-1.26.0/debian/patches/0006-comics-Use-libarchive-to-unpack-documents.patch
--- atril-1.26.0/debian/patches/0006-comics-Use-libarchive-to-unpack-documents.patch	1970-01-01 01:00:00.000000000 +0100
+++ atril-1.26.0/debian/patches/0006-comics-Use-libarchive-to-unpack-documents.patch	2024-01-23 10:06:08.000000000 +0100
@@ -0,0 +1,1814 @@
+From aa8e9dad472cbadc96719a8f521768aeeb0913f0 Mon Sep 17 00:00:00 2001
+From: lukefromdc <lukefromdc@hushmail.com>
+Date: Mon, 25 Dec 2023 15:11:04 -0500
+Subject: [PATCH 2/2] comics: Use libarchive to unpack documents
+
+This commit eliminates the use of external commands for opening
+comic documents, and uses libarchive instead.
+
+Signed-off-by: Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
+---
+ backend/comics/Makefile.am       |    5 +-
+ backend/comics/comics-document.c | 1143 +++++++++++-------------------
+ backend/comics/comics-document.h |    7 +-
+ backend/comics/ev-archive.c      |  323 +++++++++
+ backend/comics/ev-archive.h      |   56 ++
+ configure.ac                     |    3 +
+ libdocument/ev-document.h        |    1 +
+ 7 files changed, 799 insertions(+), 739 deletions(-)
+ create mode 100644 backend/comics/ev-archive.c
+ create mode 100644 backend/comics/ev-archive.h
+
+diff --git a/backend/comics/Makefile.am b/backend/comics/Makefile.am
+index b27f9b85..77f3dedb 100644
+--- a/backend/comics/Makefile.am
++++ b/backend/comics/Makefile.am
+@@ -12,12 +12,15 @@ backend_LTLIBRARIES = libcomicsdocument.la
+ 
+ libcomicsdocument_la_SOURCES = \
+ 	comics-document.c      \
+-	comics-document.h
++	comics-document.h      \
++	ev-archive.c     \
++	ev-archive.h
+ 
+ libcomicsdocument_la_LDFLAGS = $(BACKEND_LIBTOOL_FLAGS)
+ libcomicsdocument_la_LIBADD =				\
+ 	$(top_builddir)/libdocument/libatrildocument.la	\
+ 	$(BACKEND_LIBS)					\
++	$(COMICS_LIBS)					\
+ 	$(LIB_LIBS)
+ 
+ backend_in_files = comicsdocument.atril-backend.desktop.in
+diff --git a/backend/comics/comics-document.c b/backend/comics/comics-document.c
+index ed02a35c..1da1eee2 100644
+--- a/backend/comics/comics-document.c
++++ b/backend/comics/comics-document.c
+@@ -30,24 +30,17 @@
+ #include <glib/gstdio.h>
+ #include <gio/gio.h>
+ 
+-#include <sys/wait.h>
+-
+ #include "comics-document.h"
+ #include "ev-document-misc.h"
+ #include "ev-document-thumbnails.h"
+ #include "ev-file-helpers.h"
++#include "ev-archive.h"
++#include <archive.h>
++#include <archive_entry.h>
+ 
+ #define EV_EOL "\n"
+ 
+-typedef enum
+-{
+-	RARLABS,
+-	GNAUNRAR,
+-	UNZIP,
+-	P7ZIP,
+-	TAR,
+-	UNARCHIVER
+-} ComicBookDecompressType;
++#define BLOCK_SIZE 10240
+ 
+ typedef struct _ComicsDocumentClass ComicsDocumentClass;
+ 
+@@ -58,398 +51,269 @@ struct _ComicsDocumentClass
+ 
+ struct _ComicsDocument
+ {
+-	EvDocument parent_instance;
+-
+-	gchar    *archive, *dir;
+-	GPtrArray *page_names;
+-	gchar    *selected_command, *alternative_command;
+-	gchar    *extract_command, *list_command, *decompress_tmp;
+-	gboolean regex_arg;
+-	gint     offset;
+-	ComicBookDecompressType command_usage;
++	EvDocument     parent_instance;
++	EvArchive     *archive;
++	gchar         *archive_path;
++	gchar         *archive_uri;
++	GPtrArray     *page_names; /* elem: char * */
++	GHashTable    *page_positions; /* key: char *, value: uint + 1 */
++
+ };
+ 
+-#define OFFSET_7Z 53
+-#define OFFSET_ZIP 2
+-#define NO_OFFSET 0
+-
+-/* For perfomance reasons of 7z* we've choosen to decompress on the temporary
+- * directory instead of decompressing on the stdout */
+-
+-/**
+- * @extract: command line arguments to pass to extract a file from the archive
+- *   to stdout.
+- * @list: command line arguments to list the archive contents
+- * @decompress_tmp: command line arguments to pass to extract the archive
+- *   into a directory.
+- * @regex_arg: whether the command can accept regex expressions
+- * @offset: the position offset of the filename on each line in the output of
+- *   running the @list command
+- */
+-typedef struct {
+-        char *extract;
+-        char *list;
+-        char *decompress_tmp;
+-        gboolean regex_arg;
+-        gint offset;
+-} ComicBookDecompressCommand;
++static void       
++comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface);
++EV_BACKEND_REGISTER_WITH_CODE (ComicsDocument, comics_document,
++    {
++        EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
++                        comics_document_document_thumbnails_iface_init);
++    } );
++
++#define FORMAT_UNKNOWN     0
++#define FORMAT_SUPPORTED   1
++#define FORMAT_UNSUPPORTED 2
++
++/* Returns a GHashTable of:
++ * <key>: file extensions
++ * <value>: degree of support in gdk-pixbuf */
++static GHashTable *
++get_image_extensions(void)
++{
++	GHashTable *extensions;
++	GSList *formats = gdk_pixbuf_get_formats ();
++	GSList *l;
++	guint i;
++	const char *known_image_formats[] = {
++		"png",
++		"jpg",
++		"jpeg",
++		"webp"
++	};
++
++	extensions = g_hash_table_new_full (g_str_hash, g_str_equal,
++					    g_free, NULL);
++	for (l = formats; l != NULL; l = l->next) {
++		int i;
++		gchar **ext = gdk_pixbuf_format_get_extensions (l->data);
++
++		for (i = 0; ext[i] != NULL; i++) {
++			g_hash_table_insert (extensions,
++					     g_strdup (ext[i]),
++					     GINT_TO_POINTER (FORMAT_SUPPORTED));
++		}
+ 
+-static const ComicBookDecompressCommand command_usage_def[] = {
+-        /* RARLABS unrar */
+-	{"%s p -c- -ierr --", "%s vb -c- -- %s", NULL             , FALSE, NO_OFFSET},
++		g_strfreev (ext);
++	}
++	g_slist_free (formats);
+ 
+-        /* GNA! unrar */
+-	{NULL               , "%s t %s"        , "%s -xf %s %s"   , FALSE, NO_OFFSET},
++	/* Add known image formats that aren't supported by gdk-pixbuf */
++	for (i = 0; i < G_N_ELEMENTS (known_image_formats); i++) {
++		if (!g_hash_table_lookup (extensions, known_image_formats[i])) {
++			g_hash_table_insert (extensions,
++					     g_strdup (known_image_formats[i]),
++					     GINT_TO_POINTER (FORMAT_UNSUPPORTED));
++		}
++	}
+ 
+-        /* unzip */
+-	{"%s -p -C --"      , "%s %s"          , NULL             , TRUE , OFFSET_ZIP},
++	return extensions;
++}
+ 
+-        /* 7zip */
+-	{NULL               , "%s l -- %s"     , "%s x -y %s -o%s", FALSE, OFFSET_7Z},
++static int
++has_supported_extension (const char *name,
++			 GHashTable *supported_extensions)
++{
++	gboolean ret = FALSE;
++	gchar *suffix;
++	suffix = g_strrstr (name, ".");
++	if (!suffix)
++		return ret;
+ 
+-        /* tar */
+-	{"%s -xOf"          , "%s -tf %s"      , NULL             , FALSE, NO_OFFSET},
++	suffix = g_ascii_strdown (suffix + 1, -1);
++	ret = GPOINTER_TO_INT (g_hash_table_lookup (supported_extensions, suffix));
++	g_free (suffix);
+ 
+-	/* UNARCHIVER */
+-	{"unar -o -"	    , "%s %s"	       , NULL		  , FALSE, NO_OFFSET}
+-};
++	return ret;
++}
+ 
+-static void       comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface);
++#define APPLE_DOUBLE_PREFIX "._"
++static gboolean
++is_apple_double (const char *name)
++{
++char *basename;
++	gboolean ret = FALSE;
+ 
+-static GSList*    get_supported_image_extensions (void);
+-static void       get_page_size_area_prepared_cb (GdkPixbufLoader *loader,
+-						  gpointer data);
+-static void       render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
+-						  gint width,
+-						  gint height,
+-						  gpointer data);
+-static char**     extract_argv                   (EvDocument *document,
+-						  gint page);
++	basename = g_path_get_basename (name);
++	if (basename == NULL) {
++		g_debug ("Filename '%s' doesn't have a basename?", name);
++		return ret;
++	}
++	ret = g_str_has_prefix (basename, APPLE_DOUBLE_PREFIX);
++	g_free (basename);
+ 
+-EV_BACKEND_REGISTER_WITH_CODE (ComicsDocument, comics_document,
+-	{
+-		EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
+-						comics_document_document_thumbnails_iface_init);
+-	} );
+-
+-/**
+- * comics_regex_quote:
+- * @unquoted_string: a literal string
+- *
+- * Quotes a string so unzip will not interpret the regex expressions of
+- * @unquoted_string. Basically, this functions uses [] to disable regex
+- * expressions. The return value must be freed with * g_free()
+- *
+- * Return value: quoted and disabled-regex string
+- **/
+-static gchar *
+-comics_regex_quote (const gchar *unquoted_string)
++	return ret;
++}
++
++static gboolean
++archive_reopen_if_needed (ComicsDocument  *comics_document,
++			  const char      *page_wanted,
++			  GError         **error)
+ {
+-	const gchar *p;
+-	GString *dest;
+-
+-	dest = g_string_new ("'");
+-
+-	p = unquoted_string;
+-
+-	while (*p) {
+-		switch (*p) {
+-			/* * matches a sequence of 0 or more characters */
+-			case ('*'):
+-			/* ? matches exactly 1 charactere */
+-			case ('?'):
+-			/* [...]  matches any single character found inside
+-			 * the brackets. Disabling the first bracket is enough.
+-			 */
+-			case ('['):
+-				g_string_append (dest, "[");
+-				g_string_append_c (dest, *p);
+-				g_string_append (dest, "]");
+-				break;
+-			/* Because \ escapes regex expressions that we are
+-			 * disabling for unzip, we need to disable \ too */
+-			case ('\\'):
+-				g_string_append (dest, "[\\\\]");
+-				break;
+-			/* Escape single quote inside the string */
+-			case ('\''):
+-				g_string_append (dest, "'\\''");
+-				break;
+-			default:
+-				g_string_append_c (dest, *p);
+-				break;
++	const char *current_page;
++	guint current_page_idx, page_wanted_idx;
++
++	if (ev_archive_at_entry (comics_document->archive)) {
++		current_page = ev_archive_get_entry_pathname (comics_document->archive);
++		if (current_page) {
++			current_page_idx = GPOINTER_TO_UINT (g_hash_table_lookup (comics_document->page_positions, current_page));
++			page_wanted_idx = GPOINTER_TO_UINT (g_hash_table_lookup (comics_document->page_positions, page_wanted));
++
++			if (current_page_idx != 0 &&
++			    page_wanted_idx != 0 &&
++			    page_wanted_idx > current_page_idx)
++				return TRUE;
+ 		}
+-		++p;
++
++		ev_archive_reset (comics_document->archive);
+ 	}
+-	g_string_append_c (dest, '\'');
+-	return g_string_free (dest, FALSE);
++return ev_archive_open_filename (comics_document->archive, comics_document->archive_path, error);
+ }
+ 
+-/* This function manages the command for decompressing a comic book */
+-static gboolean
+-comics_decompress_temp_dir (const gchar *command_decompress_tmp,
+-			    const gchar *command,
+-			    GError      **error)
++static GPtrArray *
++comics_document_list (ComicsDocument  *comics_document,
++		      GError         **error)
+ {
+-	gboolean success;
+-	gchar *std_out, *basename;
+-	GError *err = NULL;
+-	gint retval;
+-
+-	success = g_spawn_command_line_sync (command_decompress_tmp, &std_out,
+-					     NULL, &retval, &err);
+-	basename = g_path_get_basename (command);
+-	if (!success) {
+-		g_set_error (error,
+-			     EV_DOCUMENT_ERROR,
+-			     EV_DOCUMENT_ERROR_INVALID,
+-			     _("Error launching the command “%s” in order to "
+-			     "decompress the comic book: %s"),
+-			     basename,
+-			     err->message);
+-		g_error_free (err);
+-	} else if (WIFEXITED (retval)) {
+-		if (WEXITSTATUS (retval) == EXIT_SUCCESS) {
+-			g_free (std_out);
+-			g_free (basename);
+-			return TRUE;
+-		} else {
+-			g_set_error (error,
++GPtrArray *array = NULL;
++	gboolean has_encrypted_files, has_unsupported_images, has_archive_errors;
++	GHashTable *supported_extensions = NULL;
++
++	if (!ev_archive_open_filename (comics_document->archive, comics_document->archive_path, error)) {
++		if (*error != NULL) {
++			g_warning ("Fatal error handling archive (%s): %s", G_STRFUNC, (*error)->message);
++			g_clear_error (error);
++		}
++
++		g_set_error_literal (error,
+ 				     EV_DOCUMENT_ERROR,
+ 				     EV_DOCUMENT_ERROR_INVALID,
+-				     _("The command “%s” failed at "
+-				     "decompressing the comic book."),
+-				     basename);
+-			g_free (std_out);
++			     _("File is corrupted"));
++		goto out;
++	}
++
++	supported_extensions = get_image_extensions ();
++
++	has_encrypted_files = FALSE;
++	has_unsupported_images = FALSE;
++	has_archive_errors = FALSE;
++	array = g_ptr_array_sized_new (64);
++
++	while (1) {
++		const char *name;
++		int supported;
++
++		if (!ev_archive_read_next_header (comics_document->archive, error)) {
++			if (*error != NULL) {
++				g_debug ("Fatal error handling archive (%s): %s", G_STRFUNC, (*error)->message);
++				g_clear_error (error);
++				has_archive_errors = TRUE;
++				goto out;
++			}
++			break;
+ 		}
+-	} else {
+-		g_set_error (error,
+-			     EV_DOCUMENT_ERROR,
+-			     EV_DOCUMENT_ERROR_INVALID,
+-			     _("The command “%s” did not end normally."),
+-			     basename);
+-		g_free (std_out);
++
++		name = ev_archive_get_entry_pathname (comics_document->archive);
++		/* Ignore https://en.wikipedia.org/wiki/AppleSingle_and_AppleDouble_formats */
++		if (is_apple_double (name)) {
++			g_debug ("Not adding AppleDouble file '%s' to the list of files in the comics", name);
++			continue;
++		}
++
++		supported = has_supported_extension (name, supported_extensions);
++		if (supported == FORMAT_UNKNOWN) {
++			g_debug ("Not adding unsupported file '%s' to the list of files in the comics", name);
++			continue;
++		} else if (supported == FORMAT_UNSUPPORTED) {
++			g_debug ("Not adding unsupported image '%s' to the list of files in the comics", name);
++			has_unsupported_images = TRUE;
++			continue;
++		}
++
++		if (ev_archive_get_entry_is_encrypted (comics_document->archive)) {
++			g_debug ("Not adding encrypted file '%s' to the list of files in the comics", name);
++			has_encrypted_files = TRUE;
++			continue;
++		}
++
++		g_debug ("Adding '%s' to the list of files in the comics", name);
++		g_ptr_array_add (array, g_strdup (name));
+ 	}
+-	g_free (basename);
+-	return FALSE;
++out:
++	if (array->len == 0) {
++		g_ptr_array_free (array, TRUE);
++		array = NULL;
++
++		if (has_encrypted_files) {
++			g_set_error_literal (error,
++					     EV_DOCUMENT_ERROR,
++					     EV_DOCUMENT_ERROR_ENCRYPTED,
++					     _("Archive is encrypted"));
++		} else if (has_unsupported_images) {
++			g_set_error_literal (error,
++					     EV_DOCUMENT_ERROR,
++					     EV_DOCUMENT_ERROR_UNSUPPORTED_CONTENT,
++					     _("No supported images in archive"));
++		} else if (has_archive_errors) {
++			g_set_error_literal (error,
++					     EV_DOCUMENT_ERROR,
++					     EV_DOCUMENT_ERROR_INVALID,
++					     _("File is corrupted"));
++		} else {
++			g_set_error_literal (error,
++					     EV_DOCUMENT_ERROR,
++					     EV_DOCUMENT_ERROR_INVALID,
++					     _("No files in archive"));
++		}
++	}
++
++	if (supported_extensions)
++		g_hash_table_destroy (supported_extensions);
++	ev_archive_reset (comics_document->archive);
++	return array;
+ }
+ 
+-/* This function shows how to use the choosen command for decompressing a
+- * comic book file. It modifies fields of the ComicsDocument struct with
+- * this information */
+-static gboolean
+-comics_generate_command_lines (ComicsDocument *comics_document,
+-			       GError         **error)
++static GHashTable *
++save_positions (GPtrArray *page_names)
+ {
+-	gchar *quoted_file, *quoted_file_aux;
+-	gchar *quoted_command;
+-	ComicBookDecompressType type;
+-
+-	type = comics_document->command_usage;
+-	comics_document->regex_arg = command_usage_def[type].regex_arg;
+-	quoted_command = g_shell_quote (comics_document->selected_command);
+-	if (comics_document->regex_arg) {
+-		quoted_file = comics_regex_quote (comics_document->archive);
+-		quoted_file_aux = g_shell_quote (comics_document->archive);
+-		comics_document->list_command =
+-			   g_strdup_printf (command_usage_def[type].list,
+-			                    comics_document->alternative_command,
+-			                    quoted_file_aux);
+-		g_free (quoted_file_aux);
+-	} else {
+-		quoted_file = g_shell_quote (comics_document->archive);
+-		comics_document->list_command =
+-				g_strdup_printf (command_usage_def[type].list,
+-				                 quoted_command, quoted_file);
+-	}
+-	comics_document->extract_command =
+-			    g_strdup_printf (command_usage_def[type].extract,
+-				             quoted_command);
+-	comics_document->offset = command_usage_def[type].offset;
+-	if (command_usage_def[type].decompress_tmp) {
+-		comics_document->dir = ev_mkdtemp ("atril-comics-XXXXXX", error);
+-                if (comics_document->dir == NULL)
+-                        return FALSE;
+-
+-		/* unrar-free can't create directories, but ev_mkdtemp already created the dir */
+-
+-		comics_document->decompress_tmp =
+-			g_strdup_printf (command_usage_def[type].decompress_tmp,
+-					 quoted_command, quoted_file,
+-					 comics_document->dir);
+-		g_free (quoted_file);
+-		g_free (quoted_command);
+-
+-		if (!comics_decompress_temp_dir (comics_document->decompress_tmp,
+-		    comics_document->selected_command, error))
+-			return FALSE;
+-		else
+-			return TRUE;
+-	} else {
+-		g_free (quoted_file);
+-		g_free (quoted_command);
+-		return TRUE;
+-	}
++	guint i;
++	GHashTable *ht;
+ 
++	ht = g_hash_table_new (g_str_hash, g_str_equal);
++	for (i = 0; i < page_names->len; i++)
++		g_hash_table_insert (ht, page_names->pdata[i], GUINT_TO_POINTER(i + 1));
++	return ht;
+ }
+ 
+-/* This function chooses an external command for decompressing a comic
+- * book based on its mime tipe. */
++/*This function chooses the archive decompression support
++ * book based on its mime type. */
+ static gboolean
+-comics_check_decompress_command	(gchar          *mime_type,
++comics_check_decompress_support	(gchar          *mime_type,
+ 				 ComicsDocument *comics_document,
+ 				 GError         **error)
+ {
+-	gboolean success;
+-	gchar *std_out, *std_err;
+-	gint retval;
+-	GError *err = NULL;
+-
+-	/* FIXME, use proper cbr/cbz mime types once they're
+-	 * included in shared-mime-info */
+-
+ 	if (g_content_type_is_a (mime_type, "application/x-cbr") ||
+ 	    g_content_type_is_a (mime_type, "application/x-rar")) {
+-	        /* The RARLAB provides a no-charge proprietary (freeware)
+-	        * decompress-only client for Linux called unrar. Another
+-		* option is a GPLv2-licensed command-line tool developed by
+-		* the Gna! project. Confusingly enough, the free software RAR
+-		* decoder is also named unrar. For this reason we need to add
+-		* some lines for disambiguation. Sorry for the added the
+-		* complexity but it's life :)
+-		* Finally, some distributions, like Debian, rename this free
+-		* option as unrar-free.
+-		* */
+-		comics_document->selected_command =
+-					g_find_program_in_path ("unrar");
+-		if (comics_document->selected_command) {
+-			/* We only use std_err to avoid printing useless error
+-			 * messages on the terminal */
+-			success =
+-				g_spawn_command_line_sync (
+-				              comics_document->selected_command,
+-							   &std_out, &std_err,
+-							   &retval, &err);
+-			if (!success) {
+-				g_propagate_error (error, err);
+-				g_error_free (err);
+-				return FALSE;
+-			/* I don't check retval status because RARLAB unrar
+-			 * doesn't have a way to return 0 without involving an
+-			 * operation with a file*/
+-			} else if (WIFEXITED (retval)) {
+-				if (g_strrstr (std_out,"freeware") != NULL)
+-					/* The RARLAB freeware client */
+-					comics_document->command_usage = RARLABS;
+-				else
+-					/* The Gna! free software client */
+-					comics_document->command_usage = GNAUNRAR;
+-
+-				g_free (std_out);
+-				g_free (std_err);
+-				return TRUE;
+-			}
+-		}
+-		/* The Gna! free software client with Debian naming convention */
+-		comics_document->selected_command =
+-				g_find_program_in_path ("unrar-free");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = GNAUNRAR;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("lsar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = UNARCHIVER;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("bsdtar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = TAR;
++		if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_RAR))
+ 			return TRUE;
+-		}
+-
+ 	} else if (g_content_type_is_a (mime_type, "application/x-cbz") ||
+ 		   g_content_type_is_a (mime_type, "application/zip")) {
+-		/* InfoZIP's unzip program */
+-		comics_document->selected_command =
+-				g_find_program_in_path ("unzip");
+-		comics_document->alternative_command =
+-				g_find_program_in_path ("zipnote");
+-		if (comics_document->selected_command &&
+-		    comics_document->alternative_command) {
+-			comics_document->command_usage = UNZIP;
++		if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_ZIP))
+ 			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("bsdtar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = TAR;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("lsar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = UNARCHIVER;
+-			return TRUE;
+-		}
+-
+ 	} else if (g_content_type_is_a (mime_type, "application/x-cb7") ||
+ 		   g_content_type_is_a (mime_type, "application/x-7z-compressed")) {
+-		/* 7zr, 7za and 7z are the commands from the p7zip project able
+-		 * to decompress .7z files */
+-		comics_document->selected_command =
+-			g_find_program_in_path ("7zr");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = P7ZIP;
++		if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_7Z))
+ 			return TRUE;
+-		}
+-		comics_document->selected_command =
+-			g_find_program_in_path ("7za");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = P7ZIP;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-			g_find_program_in_path ("7z");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = P7ZIP;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("bsdtar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = TAR;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("lsar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = UNARCHIVER;
+-			return TRUE;
+-		}
+ 	} else if (g_content_type_is_a (mime_type, "application/x-cbt") ||
+ 		   g_content_type_is_a (mime_type, "application/x-tar")) {
+-		/* tar utility (Tape ARchive) */
+-		comics_document->selected_command =
+-				g_find_program_in_path ("tar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = TAR;
++	if (ev_archive_set_archive_type (comics_document->archive, EV_ARCHIVE_TYPE_TAR))
+ 			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("bsdtar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = TAR;
+-			return TRUE;
+-		}
+-		comics_document->selected_command =
+-				g_find_program_in_path ("lsar");
+-		if (comics_document->selected_command) {
+-			comics_document->command_usage = UNARCHIVER;
+-			return TRUE;
+-		}
+ 	} else {
+ 		g_set_error (error,
+ 			     EV_DOCUMENT_ERROR,
+@@ -461,8 +325,9 @@ comics_check_decompress_command	(gchar          *mime_type,
+ 	g_set_error_literal (error,
+ 			     EV_DOCUMENT_ERROR,
+ 			     EV_DOCUMENT_ERROR_INVALID,
+-			     _("Can't find an appropriate command to "
+-			     "decompress this type of comic book"));
++		             _("libarchive lacks support for this comic book’s "
++			     "compression, please contact your distributor"));
++
+ 	return FALSE;
+ }
+ 
+@@ -470,43 +335,15 @@ static int
+ sort_page_names (gconstpointer a,
+                  gconstpointer b)
+ {
+-	const char *name_1, *name_2;
+-	gchar *key_1, *key_2;
+-	gboolean sort_last_1, sort_last_2;
+-	int compare;
+-
+-	name_1 = * (const char **) a;
+-	name_2 = * (const char **) b;
+-
+-	#define SORT_LAST_CHAR1 '.'
+-	#define SORT_LAST_CHAR2 '#'
+-
+-	sort_last_1 = name_1[0] == SORT_LAST_CHAR1 || name_1[0] == SORT_LAST_CHAR2;
+-	sort_last_2 = name_2[0] == SORT_LAST_CHAR1 || name_2[0] == SORT_LAST_CHAR2;
+-
+-	#undef SORT_LAST_CHAR1
+-	#undef SORT_LAST_CHAR2
+-
+-	if (sort_last_1 && !sort_last_2)
+-	{
+-		compare = +1;
+-	}
+-	else if (!sort_last_1 && sort_last_2)
+-	{
+-		compare = -1;
+-	}
+-	else
+-	{
+-		key_1 = g_utf8_collate_key_for_filename (name_1, -1);
+-		key_2 = g_utf8_collate_key_for_filename (name_2, -1);
+-
+-		compare = strcmp (key_1, key_2);
+-
+-		g_free (key_1);
+-		g_free (key_2);
+-	}
+-
+-	return compare;
++	gchar *temp1, *temp2;
++	gint ret;
++	temp1 = g_utf8_collate_key_for_filename (* (const char **) a, -1);
++	temp2 = g_utf8_collate_key_for_filename (* (const char **) b, -1);
++	ret = strcmp (temp1, temp2);
++
++	g_free (temp1);
++	g_free (temp2);
++	return ret;
+ }
+ 
+ static gboolean
+@@ -515,50 +352,13 @@ comics_document_load (EvDocument *document,
+ 		      GError    **error)
+ {
+ 	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+-	GSList *supported_extensions;
+-	gchar *std_out;
+ 	gchar *mime_type;
+-	gchar **cb_files, *cb_file;
+-	gboolean success;
+-	int i, retval;
+-	GError *err = NULL;
+-
+-	comics_document->archive = g_filename_from_uri (uri, NULL, error);
+-	if (!comics_document->archive)
+-		return FALSE;
+-
+-	mime_type = ev_file_get_mime_type (uri, FALSE, &err);
+-	if (!mime_type) {
+-		if (err) {
+-			g_propagate_error (error, err);
+-		} else {
+-			g_set_error_literal (error,
+-					     EV_DOCUMENT_ERROR,
+-					     EV_DOCUMENT_ERROR_INVALID,
+-					     _("Unknown MIME Type"));
+-		}
+-
+-		return FALSE;
+-	}
+-
+-	if (!comics_check_decompress_command (mime_type, comics_document,
+-	error)) {
+-		g_free (mime_type);
+-		return FALSE;
+-	} else if (!comics_generate_command_lines (comics_document, error)) {
+-		   g_free (mime_type);
+-		return FALSE;
+-	}
++	GFile *file;
++	file = g_file_new_for_uri (uri);
++	comics_document->archive_path = g_file_get_path (file);
++	g_object_unref (file);
+ 
+-	g_free (mime_type);
+-
+-	/* Get list of files in archive */
+-	success = g_spawn_command_line_sync (comics_document->list_command,
+-					     &std_out, NULL, &retval, error);
+-
+-	if (!success) {
+-		return FALSE;
+-	} else if (!WIFEXITED(retval) || WEXITSTATUS(retval) != EXIT_SUCCESS) {
++	if (!comics_document->archive_path) {
+ 		g_set_error_literal (error,
+                                      EV_DOCUMENT_ERROR,
+                                      EV_DOCUMENT_ERROR_INVALID,
+@@ -566,58 +366,26 @@ comics_document_load (EvDocument *document,
+ 		return FALSE;
+ 	}
+ 
+-	/* FIXME: is this safe against filenames containing \n in the archive ? */
+-	cb_files = g_strsplit (std_out, EV_EOL, 0);
++	comics_document->archive_uri = g_strdup (uri);
++	mime_type = ev_file_get_mime_type (uri, FALSE, error);
+ 
+-	g_free (std_out);
++	if (mime_type == NULL)
++		return FALSE;
+ 
+-	if (!cb_files) {
+-		g_set_error_literal (error,
+-				     EV_DOCUMENT_ERROR,
+-				     EV_DOCUMENT_ERROR_INVALID,
+-				     _("No files in archive"));
++	if (!comics_check_decompress_support (mime_type, comics_document, error)) {
++		g_free (mime_type);
+ 		return FALSE;
+ 	}
+ 
+-        comics_document->page_names = g_ptr_array_sized_new (64);
+-
+-	supported_extensions = get_supported_image_extensions ();
+-	for (i = 0; cb_files[i] != NULL; i++) {
+-		if (comics_document->offset != NO_OFFSET) {
+-			if (g_utf8_strlen (cb_files[i],-1) >
+-			    comics_document->offset) {
+-				cb_file =
+-					g_utf8_offset_to_pointer (cb_files[i],
+-						       comics_document->offset);
+-			} else {
+-				continue;
+-			}
+-		} else {
+-			cb_file = cb_files[i];
+-		}
+-		gchar *suffix = g_strrstr (cb_file, ".");
+-		if (!suffix)
+-			continue;
+-		suffix = g_ascii_strdown (suffix + 1, -1);
+-		if (g_slist_find_custom (supported_extensions, suffix,
+-					 (GCompareFunc) strcmp) != NULL) {
+-                        g_ptr_array_add (comics_document->page_names,
+-                                         g_strstrip (g_strdup (cb_file)));
+-		}
+-		g_free (suffix);
+-	}
+-	g_strfreev (cb_files);
+-	g_slist_foreach (supported_extensions, (GFunc) g_free, NULL);
+-	g_slist_free (supported_extensions);
++	g_free (mime_type);
+ 
+-	if (comics_document->page_names->len == 0) {
+-		g_set_error (error,
+-			     EV_DOCUMENT_ERROR,
+-			     EV_DOCUMENT_ERROR_INVALID,
+-			     _("No images found in archive %s"),
+-			     uri);
++	/* Get list of files in archive */
++	comics_document->page_names = comics_document_list (comics_document, error);
++	if (!comics_document->page_names)
+ 		return FALSE;
+-	}
++
++	/* Keep an index */
++	comics_document->page_positions = save_positions (comics_document->page_names);
+ 
+         /* Now sort the pages */
+         g_ptr_array_sort (comics_document->page_names, sort_page_names);
+@@ -632,7 +400,7 @@ comics_document_save (EvDocument *document,
+ {
+ 	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+ 
+-	return ev_xfer_uri_simple (comics_document->archive, uri, error);
++	return ev_xfer_uri_simple (comics_document->archive_uri, uri, error);
+ }
+ 
+ static int
+@@ -646,6 +414,23 @@ comics_document_get_n_pages (EvDocument *document)
+ 	return comics_document->page_names->len;
+ }
+ 
++typedef struct {
++	gboolean got_info;
++	int height;
++	int width;
++} PixbufInfo;
++
++static void
++get_page_size_prepared_cb (GdkPixbufLoader *loader,
++			   int              width,
++			   int              height,
++			   PixbufInfo      *info)
++{
++	info->got_info = TRUE;
++	info->height = height;
++	info->width = width;
++}
++
+ static void
+ comics_document_get_page_size (EvDocument *document,
+ 			       EvPage     *page,
+@@ -653,74 +438,89 @@ comics_document_get_page_size (EvDocument *document,
+ 			       double     *height)
+ {
+ 	GdkPixbufLoader *loader;
+-	char **argv;
+-	guchar buf[1024];
+-	gboolean success, got_size = FALSE;
+-	gint outpipe = -1;
+-	GPid child_pid;
+-	gssize bytes;
+-	GdkPixbuf *pixbuf;
+-	gchar *filename;
++
+ 	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+ 
+-	if (!comics_document->decompress_tmp) {
+-		argv = extract_argv (document, page->index);
+-		success = g_spawn_async_with_pipes (NULL, argv, NULL,
+-						    G_SPAWN_SEARCH_PATH |
+-						    G_SPAWN_STDERR_TO_DEV_NULL,
+-						    NULL, NULL,
+-						    &child_pid,
+-						    NULL, &outpipe, NULL, NULL);
+-		g_strfreev (argv);
+-		g_return_if_fail (success == TRUE);
+-
+-		loader = gdk_pixbuf_loader_new ();
+-		g_signal_connect (loader, "area-prepared",
+-				  G_CALLBACK (get_page_size_area_prepared_cb),
+-				  &got_size);
+-
+-		while (outpipe >= 0) {
+-			bytes = read (outpipe, buf, 1024);
+-
+-			if (bytes > 0)
+-			gdk_pixbuf_loader_write (loader, buf, bytes, NULL);
+-			if (bytes <= 0 || got_size) {
+-				close (outpipe);
+-				outpipe = -1;
+-				gdk_pixbuf_loader_close (loader, NULL);
++	const char *page_path;
++	PixbufInfo info;
++	GError *error = NULL;
++
++	page_path = g_ptr_array_index (comics_document->page_names, page->index);
++
++	if (!archive_reopen_if_needed (comics_document, page_path, &error)) {
++		g_warning ("Fatal error opening archive: %s", error->message);
++		g_error_free (error);
++		return;
++	}
++
++	loader = gdk_pixbuf_loader_new ();
++	info.got_info = FALSE;
++	g_signal_connect (loader, "size-prepared",
++			  G_CALLBACK (get_page_size_prepared_cb),
++			  &info);
++
++	while (1) {
++		const char *name;
++		GError *error = NULL;
++
++		if (!ev_archive_read_next_header (comics_document->archive, &error)) {
++			if (error != NULL) {
++				g_warning ("Fatal error handling archive (%s): %s", G_STRFUNC, error->message);
++				g_error_free (error);
+ 			}
++			break;
+ 		}
+-		pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+-		if (pixbuf) {
+-			if (width)
+-				*width = gdk_pixbuf_get_width (pixbuf);
+-			if (height)
+-				*height = gdk_pixbuf_get_height (pixbuf);
+-		}
+-		g_spawn_close_pid (child_pid);
+-		g_object_unref (loader);
+-	} else {
+-		filename = g_build_filename (comics_document->dir,
+-                                             (char *) comics_document->page_names->pdata[page->index],
+-					     NULL);
+-		pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+-		if (pixbuf) {
+-			if (width)
+-				*width = gdk_pixbuf_get_width (pixbuf);
+-			if (height)
+-				*height = gdk_pixbuf_get_height (pixbuf);
+-			g_object_unref (pixbuf);
++
++		name = ev_archive_get_entry_pathname (comics_document->archive);
++		if (g_strcmp0 (name, page_path) == 0) {
++			char buf[BLOCK_SIZE];
++			gssize read;
++			gint64 left;
++
++			left = ev_archive_get_entry_size (comics_document->archive);
++			read = ev_archive_read_data (comics_document->archive, buf,
++						     MIN(BLOCK_SIZE, left), &error);
++			while (read > 0 && !info.got_info) {
++				if (!gdk_pixbuf_loader_write (loader, (guchar *) buf, read, &error)) {
++					read = -1;
++					break;
++				}
++				left -= read;
++				read = ev_archive_read_data (comics_document->archive, buf,
++							     MIN(BLOCK_SIZE, left), &error);
++			}
++			if (read < 0) {
++				g_warning ("Fatal error reading '%s' in archive: %s", name, error->message);
++				g_error_free (error);
++			}
++			break;
+ 		}
+-		g_free (filename);
++	}
++
++	gdk_pixbuf_loader_close (loader, NULL);
++	g_object_unref (loader);
++
++	if (info.got_info) {
++		if (width)
++			*width = info.width;
++		if (height)
++			*height = info.height;
+ 	}
+ }
+ 
+ static void
+-get_page_size_area_prepared_cb (GdkPixbufLoader *loader,
+-				gpointer         data)
++render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
++				gint             width,
++				gint             height,
++				EvRenderContext *rc)
+ {
+-	gboolean *got_size = data;
+-	*got_size = TRUE;
++	// int scaled_width, scaled_height;
++	double scale = rc->scale;
++	int w = (width  * scale + 0.5);
++	int h = (height * scale + 0.5);
++
++	// ev_render_context_compute_scaled_size (rc, width, height, &scaled_width, &scaled_height);
++	gdk_pixbuf_loader_set_size (loader, w, h);
+ }
+ 
+ static GdkPixbuf *
+@@ -728,69 +528,68 @@ comics_document_render_pixbuf (EvDocument      *document,
+ 			       EvRenderContext *rc)
+ {
+ 	GdkPixbufLoader *loader;
+-	GdkPixbuf *rotated_pixbuf, *tmp_pixbuf;
+-	char **argv;
+-	guchar buf[4096];
+-	gboolean success;
+-	gint outpipe = -1;
+-	GPid child_pid;
+-	gssize bytes;
+-	gint width, height;
+-	gchar *filename;
++	GdkPixbuf *tmp_pixbuf;
++	GdkPixbuf *rotated_pixbuf = NULL;
+ 	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
++	const char *page_path;
++	GError *error = NULL;
++
++	page_path = g_ptr_array_index (comics_document->page_names, rc->page->index);
++
++	if (!archive_reopen_if_needed (comics_document, page_path, &error)) {
++		g_warning ("Fatal error opening archive: %s", error->message);
++		g_error_free (error);
++		return NULL;
++	}
+ 
+-	if (!comics_document->decompress_tmp) {
+-		argv = extract_argv (document, rc->page->index);
+-		success = g_spawn_async_with_pipes (NULL, argv, NULL,
+-						    G_SPAWN_SEARCH_PATH |
+-						    G_SPAWN_STDERR_TO_DEV_NULL,
+-						    NULL, NULL,
+-						    &child_pid,
+-						    NULL, &outpipe, NULL, NULL);
+-		g_strfreev (argv);
+-		g_return_val_if_fail (success == TRUE, NULL);
+-
+-		loader = gdk_pixbuf_loader_new ();
+-		g_signal_connect (loader, "size-prepared",
+-				  G_CALLBACK (render_pixbuf_size_prepared_cb),
+-				  &rc->scale);
+-
+-		while (outpipe >= 0) {
+-			bytes = read (outpipe, buf, 4096);
+-
+-			if (bytes > 0) {
+-				gdk_pixbuf_loader_write (loader, buf, bytes,
+-				NULL);
++	loader = gdk_pixbuf_loader_new ();
++	g_signal_connect (loader, "size-prepared",
++			  G_CALLBACK (render_pixbuf_size_prepared_cb),
++			  rc);
++
++	while (1) {
++		const char *name;
++
++		if (!ev_archive_read_next_header (comics_document->archive, &error)) {
++			if (error != NULL) {
++				g_warning ("Fatal error handling archive (%s): %s", G_STRFUNC, error->message);
++				g_error_free (error);
++			}
++			break;
++		}
++
++		name = ev_archive_get_entry_pathname (comics_document->archive);
++		if (g_strcmp0 (name, page_path) == 0) {
++			size_t size = ev_archive_get_entry_size (comics_document->archive);
++			char *buf;
++			ssize_t read;
++
++			buf = g_malloc (size);
++			read = ev_archive_read_data (comics_document->archive, buf, size, &error);
++			if (read <= 0) {
++				if (read < 0) {
++					g_warning ("Fatal error reading '%s' in archive: %s", name, error->message);
++					g_error_free (error);
++				} else {
++					g_warning ("Read an empty file from the archive");
++				}
+ 			} else {
+-				close (outpipe);
+-				gdk_pixbuf_loader_close (loader, NULL);
+-				outpipe = -1;
++				gdk_pixbuf_loader_write (loader, (guchar *) buf, size, NULL);
+ 			}
++			g_free (buf);
++			gdk_pixbuf_loader_close (loader, NULL);
++			break;
+ 		}
+-		tmp_pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+-		rotated_pixbuf =
+-			gdk_pixbuf_rotate_simple (tmp_pixbuf,
+-						  360 - rc->rotation);
+-		g_spawn_close_pid (child_pid);
+-		g_object_unref (loader);
+-	} else {
+-		filename =
+-			g_build_filename (comics_document->dir,
+-                                          (char *) comics_document->page_names->pdata[rc->page->index],
+-					  NULL);
+-
+-		gdk_pixbuf_get_file_info (filename, &width, &height);
+-
+-		tmp_pixbuf =
+-			gdk_pixbuf_new_from_file_at_size (
+-				    filename, width * (rc->scale) + 0.5,
+-				    height * (rc->scale) + 0.5, NULL);
+-		rotated_pixbuf =
+-			gdk_pixbuf_rotate_simple (tmp_pixbuf,
+-						  360 - rc->rotation);
+-		g_free (filename);
+-		g_object_unref (tmp_pixbuf);
+ 	}
++	tmp_pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
++	if (tmp_pixbuf) {
++		if ((rc->rotation % 360) == 0)
++			rotated_pixbuf = g_object_ref (tmp_pixbuf);
++		else
++			rotated_pixbuf = gdk_pixbuf_rotate_simple (tmp_pixbuf,
++								   360 - rc->rotation);
++	}
++	g_object_unref (loader);
+ 	return rotated_pixbuf;
+ }
+ 
+@@ -802,79 +601,26 @@ comics_document_render (EvDocument      *document,
+ 	cairo_surface_t *surface;
+ 
+ 	pixbuf = comics_document_render_pixbuf (document, rc);
++	if (!pixbuf)
++		return NULL;
+ 	surface = ev_document_misc_surface_from_pixbuf (pixbuf);
+-	g_object_unref (pixbuf);
+-
++	g_clear_object (&pixbuf);
+ 	return surface;
+ }
+ 
+-static void
+-render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
+-				gint             width,
+-				gint             height,
+-				gpointer         data)
+-{
+-	double *scale = data;
+-	int w = (width  * (*scale) + 0.5);
+-	int h = (height * (*scale) + 0.5);
+-
+-	gdk_pixbuf_loader_set_size (loader, w, h);
+-}
+-
+-/**
+- * comics_remove_dir: Removes a directory recursively.
+- * Returns:
+- *   	0 if it was successfully deleted,
+- * 	-1 if an error occurred
+- */
+-static int
+-comics_remove_dir (gchar *path_name)
+-{
+-	GDir  *content_dir;
+-	const gchar *filename;
+-	gchar *filename_with_path;
+-
+-	if (g_file_test (path_name, G_FILE_TEST_IS_DIR)) {
+-		content_dir = g_dir_open  (path_name, 0, NULL);
+-		filename  = g_dir_read_name (content_dir);
+-		while (filename) {
+-			filename_with_path =
+-				g_build_filename (path_name,
+-						  filename, NULL);
+-			comics_remove_dir (filename_with_path);
+-			g_free (filename_with_path);
+-			filename = g_dir_read_name (content_dir);
+-		}
+-		g_dir_close (content_dir);
+-	}
+-	/* Note from g_remove() documentation: on Windows, it is in general not
+-	 * possible to remove a file that is open to some process, or mapped
+-	 * into memory.*/
+-	return (g_remove (path_name));
+-}
+-
+ static void
+ comics_document_finalize (GObject *object)
+ {
+ 	ComicsDocument *comics_document = COMICS_DOCUMENT (object);
+ 
+-	if (comics_document->decompress_tmp) {
+-		if (comics_remove_dir (comics_document->dir) == -1)
+-			g_warning (_("There was an error deleting “%s”."),
+-				   comics_document->dir);
+-		g_free (comics_document->dir);
+-	}
+-
+ 	if (comics_document->page_names) {
+                 g_ptr_array_foreach (comics_document->page_names, (GFunc) g_free, NULL);
+                 g_ptr_array_free (comics_document->page_names, TRUE);
+ 	}
+-
+-	g_free (comics_document->archive);
+-	g_free (comics_document->selected_command);
+-	g_free (comics_document->alternative_command);
+-	g_free (comics_document->extract_command);
+-	g_free (comics_document->list_command);
++	g_clear_pointer (&comics_document->page_positions, g_hash_table_destroy);
++	g_clear_object (&comics_document->archive);
++	g_free (comics_document->archive_path);
++	g_free (comics_document->archive_uri);
+ 
+ 	G_OBJECT_CLASS (comics_document_parent_class)->finalize (object);
+ }
+@@ -897,33 +643,7 @@ comics_document_class_init (ComicsDocumentClass *klass)
+ static void
+ comics_document_init (ComicsDocument *comics_document)
+ {
+-	comics_document->archive = NULL;
+-	comics_document->page_names = NULL;
+-	comics_document->extract_command = NULL;
+-}
+-
+-/* Returns a list of file extensions supported by gdk-pixbuf */
+-static GSList*
+-get_supported_image_extensions(void)
+-{
+-	GSList *extensions = NULL;
+-	GSList *formats = gdk_pixbuf_get_formats ();
+-	GSList *l;
+-
+-	for (l = formats; l != NULL; l = l->next) {
+-		int i;
+-		gchar **ext = gdk_pixbuf_format_get_extensions (l->data);
+-
+-		for (i = 0; ext[i] != NULL; i++) {
+-			extensions = g_slist_append (extensions,
+-						     g_strdup (ext[i]));
+-		}
+-
+-		g_strfreev (ext);
+-	}
+-
+-	g_slist_free (formats);
+-	return extensions;
++	comics_document->archive = ev_archive_new ();
+ }
+ 
+ static GdkPixbuf *
+@@ -971,48 +691,3 @@ comics_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *i
+ 	iface->get_thumbnail = comics_document_thumbnails_get_thumbnail;
+ 	iface->get_dimensions = comics_document_thumbnails_get_dimensions;
+ }
+-
+-static char**
+-extract_argv (EvDocument *document, gint page)
+-{
+-	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
+-	char **argv;
+-	char *command_line, *quoted_archive, *quoted_filename;
+-	GError *err = NULL;
+-
+-	if (g_strrstr (comics_document->page_names->pdata[page], "--checkpoint-action="))
+-	{
+-		g_warning ("File unsupported\n");
+-		gtk_main_quit ();
+-	}
+-
+-        if (page >= comics_document->page_names->len)
+-                return NULL;
+-
+-	if (comics_document->regex_arg) {
+-		quoted_archive = g_shell_quote (comics_document->archive);
+-		quoted_filename =
+-			comics_regex_quote (comics_document->page_names->pdata[page]);
+-	} else {
+-		quoted_archive = g_shell_quote (comics_document->archive);
+-		quoted_filename = g_shell_quote (comics_document->page_names->pdata[page]);
+-	}
+-
+-	command_line = g_strdup_printf ("%s %s %s",
+-					comics_document->extract_command,
+-					quoted_archive,
+-					quoted_filename);
+-	g_free (quoted_archive);
+-	g_free (quoted_filename);
+-
+-	g_shell_parse_argv (command_line, NULL, &argv, &err);
+-	g_free (command_line);
+-
+-	if (err) {
+-		g_warning (_("Error %s"), err->message);
+-		g_error_free (err);
+-		return NULL;
+-	}
+-
+-	return argv;
+-}
+diff --git a/backend/comics/comics-document.h b/backend/comics/comics-document.h
+index f6a4b440..4417a69f 100644
+--- a/backend/comics/comics-document.h
++++ b/backend/comics/comics-document.h
+@@ -16,9 +16,9 @@
+  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  */
+ 
+-#ifndef __COMICS_DOCUMENT_H__
+-#define __COMICS_DOCUMENT_H__
++#pragma once
+ 
++#include "ev-macros.h"
+ #include "ev-document.h"
+ 
+ G_BEGIN_DECLS
+@@ -30,9 +30,8 @@ G_BEGIN_DECLS
+ typedef struct _ComicsDocument ComicsDocument;
+ 
+ GType                 comics_document_get_type (void) G_GNUC_CONST;
++GType                 register_atril_backend  (GTypeModule *module);
+ 
+-G_MODULE_EXPORT GType register_atril_backend  (GTypeModule *module);
+ 
+ G_END_DECLS
+ 
+-#endif /* __COMICS_DOCUMENT_H__ */
+diff --git a/backend/comics/ev-archive.c b/backend/comics/ev-archive.c
+new file mode 100644
+index 00000000..568e1621
+--- /dev/null
++++ b/backend/comics/ev-archive.c
+@@ -0,0 +1,323 @@
++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
++/*
++ * Copyright (C) 2017, Bastien Nocera <hadess@hadess.net>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This program 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.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#include "config.h"
++#include "ev-archive.h"
++
++#include <archive.h>
++#include <archive_entry.h>
++#include <gio/gio.h>
++
++#define BUFFER_SIZE (64 * 1024)
++
++struct _EvArchive {
++	GObject parent_instance;
++	EvArchiveType type;
++
++	/* libarchive */
++	struct archive *libar;
++	struct archive_entry *libar_entry;
++};
++
++G_DEFINE_TYPE(EvArchive, ev_archive, G_TYPE_OBJECT);
++
++static void
++ev_archive_finalize (GObject *object)
++{
++	EvArchive *archive = EV_ARCHIVE (object);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		g_clear_pointer (&archive->libar, archive_free);
++		break;
++	default:
++		break;
++	}
++
++	G_OBJECT_CLASS (ev_archive_parent_class)->finalize (object);
++}
++
++static void
++ev_archive_class_init (EvArchiveClass *klass)
++{
++        GObjectClass *object_class = (GObjectClass *) klass;
++
++        object_class->finalize = ev_archive_finalize;
++}
++
++EvArchive *
++ev_archive_new (void)
++{
++	return g_object_new (EV_TYPE_ARCHIVE, NULL);
++}
++
++static void
++libarchive_set_archive_type (EvArchive *archive,
++			     EvArchiveType archive_type)
++{
++	archive->type = archive_type;
++	archive->libar = archive_read_new ();
++
++	if (archive_type == EV_ARCHIVE_TYPE_ZIP)
++		archive_read_support_format_zip (archive->libar);
++	else if (archive_type == EV_ARCHIVE_TYPE_7Z)
++		archive_read_support_format_7zip (archive->libar);
++	else if (archive_type == EV_ARCHIVE_TYPE_TAR)
++		archive_read_support_format_tar (archive->libar);
++	else if (archive_type == EV_ARCHIVE_TYPE_RAR) {
++		archive_read_support_format_rar (archive->libar);
++		archive_read_support_format_rar5 (archive->libar);
++	} else
++		g_assert_not_reached ();
++}
++
++EvArchiveType
++ev_archive_get_archive_type (EvArchive *archive)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), EV_ARCHIVE_TYPE_NONE);
++
++	return archive->type;
++}
++
++gboolean
++ev_archive_set_archive_type (EvArchive *archive,
++			     EvArchiveType archive_type)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
++	g_return_val_if_fail (archive->type == EV_ARCHIVE_TYPE_NONE, FALSE);
++
++	switch (archive_type) {
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		libarchive_set_archive_type (archive, archive_type);
++		break;
++	default:
++		g_assert_not_reached ();
++	}
++
++	return TRUE;
++}
++
++gboolean
++ev_archive_open_filename (EvArchive   *archive,
++			  const char  *path,
++			  GError     **error)
++{
++	int r;
++
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
++	g_return_val_if_fail (path != NULL, FALSE);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_NONE:
++		g_assert_not_reached ();
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		r = archive_read_open_filename (archive->libar, path, BUFFER_SIZE);
++		if (r != ARCHIVE_OK) {
++			g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
++				     "Error opening archive: %s", archive_error_string (archive->libar));
++			return FALSE;
++		}
++		return TRUE;
++	}
++
++	return FALSE;
++}
++
++static gboolean
++libarchive_read_next_header (EvArchive *archive,
++			     GError   **error)
++{
++	while (1) {
++		int r;
++
++		r = archive_read_next_header (archive->libar, &archive->libar_entry);
++		if (r != ARCHIVE_OK) {
++			if (r != ARCHIVE_EOF)
++				g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
++					     "Error reading archive: %s", archive_error_string (archive->libar));
++			return FALSE;
++		}
++
++		if (archive_entry_filetype (archive->libar_entry) != AE_IFREG) {
++			g_debug ("Skipping '%s' as it's not a regular file",
++				 archive_entry_pathname (archive->libar_entry));
++			continue;
++		}
++
++		g_debug ("At header for file '%s'", archive_entry_pathname (archive->libar_entry));
++
++		break;
++	}
++
++	return TRUE;
++}
++
++gboolean
++ev_archive_read_next_header (EvArchive *archive,
++			     GError   **error)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_NONE:
++		g_assert_not_reached ();
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		return libarchive_read_next_header (archive, error);
++	}
++
++	return FALSE;
++}
++
++gboolean
++ev_archive_at_entry (EvArchive *archive)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
++
++	return (archive->libar_entry != NULL);
++}
++
++const char *
++ev_archive_get_entry_pathname (EvArchive *archive)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), NULL);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, NULL);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_NONE:
++		g_assert_not_reached ();
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		g_return_val_if_fail (archive->libar_entry != NULL, NULL);
++		return archive_entry_pathname (archive->libar_entry);
++	}
++
++	return NULL;
++}
++
++gint64
++ev_archive_get_entry_size (EvArchive *archive)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_NONE:
++		g_assert_not_reached ();
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		g_return_val_if_fail (archive->libar_entry != NULL, -1);
++		return archive_entry_size (archive->libar_entry);
++	}
++
++	return -1;
++}
++
++gboolean
++ev_archive_get_entry_is_encrypted (EvArchive *archive)
++{
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_NONE:
++		g_assert_not_reached ();
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		g_return_val_if_fail (archive->libar_entry != NULL, -1);
++		return archive_entry_is_encrypted (archive->libar_entry);
++	}
++
++	return FALSE;
++}
++
++gssize
++ev_archive_read_data (EvArchive *archive,
++		      void      *buf,
++		      gsize      count,
++		      GError   **error)
++{
++	gssize r = -1;
++
++	g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1);
++	g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_NONE:
++		g_assert_not_reached ();
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		g_return_val_if_fail (archive->libar_entry != NULL, -1);
++		r = archive_read_data (archive->libar, buf, count);
++		if (r < 0) {
++			g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
++				     "Failed to decompress data: %s", archive_error_string (archive->libar));
++		}
++		break;
++	}
++
++	return r;
++}
++
++void
++ev_archive_reset (EvArchive *archive)
++{
++	g_return_if_fail (EV_IS_ARCHIVE (archive));
++	g_return_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE);
++
++	switch (archive->type) {
++	case EV_ARCHIVE_TYPE_RAR:
++	case EV_ARCHIVE_TYPE_ZIP:
++	case EV_ARCHIVE_TYPE_7Z:
++	case EV_ARCHIVE_TYPE_TAR:
++		g_clear_pointer (&archive->libar, archive_free);
++		libarchive_set_archive_type (archive, archive->type);
++		archive->libar_entry = NULL;
++		break;
++	default:
++		g_assert_not_reached ();
++	}
++}
++
++static void
++ev_archive_init (EvArchive *archive)
++{
++}
+diff --git a/backend/comics/ev-archive.h b/backend/comics/ev-archive.h
+new file mode 100644
+index 00000000..b4e1399c
+--- /dev/null
++++ b/backend/comics/ev-archive.h
+@@ -0,0 +1,56 @@
++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
++/*
++ * Copyright (C) 2017, Bastien Nocera <hadess@hadess.net>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This program 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.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++ */
++
++#pragma once
++
++#include <glib-object.h>
++
++G_BEGIN_DECLS
++
++#define EV_TYPE_ARCHIVE ev_archive_get_type ()
++G_DECLARE_FINAL_TYPE (EvArchive, ev_archive, EV, ARCHIVE, GObject)
++
++typedef enum {
++	EV_ARCHIVE_TYPE_NONE = 0,
++	EV_ARCHIVE_TYPE_RAR,
++	EV_ARCHIVE_TYPE_ZIP,
++	EV_ARCHIVE_TYPE_7Z,
++	EV_ARCHIVE_TYPE_TAR
++} EvArchiveType;
++
++EvArchive     *ev_archive_new                (void);
++gboolean       ev_archive_set_archive_type   (EvArchive     *archive,
++					      EvArchiveType  archive_type);
++EvArchiveType  ev_archive_get_archive_type   (EvArchive     *archive);
++gboolean       ev_archive_open_filename      (EvArchive     *archive,
++					      const char    *path,
++					      GError       **error);
++gboolean       ev_archive_read_next_header   (EvArchive     *archive,
++					      GError       **error);
++gboolean       ev_archive_at_entry           (EvArchive     *archive);
++const char    *ev_archive_get_entry_pathname (EvArchive     *archive);
++gint64         ev_archive_get_entry_size     (EvArchive     *archive);
++gboolean       ev_archive_get_entry_is_encrypted (EvArchive *archive);
++gssize         ev_archive_read_data          (EvArchive     *archive,
++					      void          *buf,
++					      gsize          count,
++					      GError       **error);
++void           ev_archive_reset              (EvArchive     *archive);
++
++G_END_DECLS
+diff --git a/configure.ac b/configure.ac
+index e25de054..d5830716 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -580,8 +580,11 @@ AC_ARG_ENABLE(comics,
+     [enable_comics=$enableval],
+     [enable_comics=yes])
+ 
++COMICS_DEPS="libarchive"
+ if test "x$enable_comics" = "xyes"; then
+     AC_DEFINE([ENABLE_COMICS], [1], [Enable support for comics.])
++    PKG_CHECK_MODULES([COMICS], [$COMICS_DEPS])
++    AC_SUBST(COMICS_LIBS)
+ fi
+ AM_CONDITIONAL(ENABLE_COMICS, test x$enable_comics = xyes)
+ 
+diff --git a/libdocument/ev-document.h b/libdocument/ev-document.h
+index 67f53abb..8f54e6b5 100644
+--- a/libdocument/ev-document.h
++++ b/libdocument/ev-document.h
+@@ -59,6 +59,7 @@ typedef struct _EvDocumentPrivate EvDocumentPrivate;
+ typedef enum
+ {
+         EV_DOCUMENT_ERROR_INVALID,
++        EV_DOCUMENT_ERROR_UNSUPPORTED_CONTENT,
+         EV_DOCUMENT_ERROR_ENCRYPTED
+ } EvDocumentError;
+ 
+-- 
+2.39.2
+
diff -Nru atril-1.26.0/debian/patches/series atril-1.26.0/debian/patches/series
--- atril-1.26.0/debian/patches/series	2024-01-06 07:18:28.000000000 +0100
+++ atril-1.26.0/debian/patches/series	2024-01-23 10:06:53.000000000 +0100
@@ -3,3 +3,5 @@
 0001-Accessibility-add-button-description.patch
 0003-epub-Fix-index-loading-for-certain-documents-look-fo.patch
 0004-epub-add-fallback-for-malformed-epub-files-in-check_.patch
+0005-Use-a-blank-line-at-most.patch
+0006-comics-Use-libarchive-to-unpack-documents.patch

Reply to: