Compare commits

..

1 Commits
c10s ... c10

Author SHA1 Message Date
65bb56a65c import CS papers-47.0-6.git5de8d26c.el10 2025-11-11 08:57:48 +00:00
13 changed files with 272 additions and 898 deletions

8
.gitignore vendored
View File

@ -1,6 +1,2 @@
/papers-47.0-vendor.tar.xz
/papers-47.0.tar.xz
/papers-5de8d26c.tar.xz
/papers-5de8d26c-vendor.tar.xz
/papers-48.7.tar.xz
/papers-48.7-vendor.tar.xz
papers-5de8d26c-vendor.tar.xz
papers-5de8d26c.tar.xz

View File

@ -1,6 +0,0 @@
--- !Policy
product_versions:
- rhel-10
decision_context: osci_compose_gate
rules:
- !PassingTestCaseRule {test_case_name: desktop-qe.desktop-ci.tier1-gating.functional}

View File

@ -0,0 +1,98 @@
From 4de71d6b8276fb82c8ccbe1ed5ed8ca69bf2d612 Mon Sep 17 00:00:00 2001
From: Marek Kasik <mkasik@redhat.com>
Date: Tue, 28 Jan 2025 15:58:19 +0100
Subject: [PATCH] shell: Check value of "sidebar-page" key
Papers crashes when previously removed "bookmarks" sidebar is set as default
in "sidebar-page" key.
Checking whether the value of "sidebar-page" is among expected values avoids
the crash.
---
shell/src/sidebar.rs | 56 +++++++++++++++++++++++++++-----------------
1 file changed, 34 insertions(+), 22 deletions(-)
diff --git a/shell/src/sidebar.rs b/shell/src/sidebar.rs
index 476548835..394278a9a 100644
--- a/shell/src/sidebar.rs
+++ b/shell/src/sidebar.rs
@@ -114,25 +114,46 @@ mod imp {
self.stack.visible_child_name().map(|gs| gs.to_string())
}
+ fn set_visible_child_default(&self) {
+ let Some(document) = self.document() else {
+ return;
+ };
+ if self
+ .stack
+ .child_by_name("links")
+ .unwrap()
+ .dynamic_cast_ref::<PpsSidebarPage>()
+ .unwrap()
+ .support_document(&document)
+ {
+ self.stack.set_visible_child_name("links");
+ } else {
+ self.stack.set_visible_child_name("thumbnails");
+ }
+ }
+
fn set_visible_child_name(&self, name: Option<String>) {
let Some(document) = self.document() else {
return;
};
let Some(name) = name else {
- if self
- .stack
- .child_by_name("links")
- .unwrap()
- .dynamic_cast_ref::<PpsSidebarPage>()
- .unwrap()
- .support_document(&document)
- {
- self.stack.set_visible_child_name("links");
- } else {
- self.stack.set_visible_child_name("thumbnails");
- }
+ self.set_visible_child_default();
return;
};
+
+ if ![
+ "annotations",
+ "attachments",
+ "layers",
+ "links",
+ "thumbnails",
+ ]
+ .contains(&name.as_str())
+ {
+ self.set_visible_child_default();
+ return;
+ }
+
let page = self.stack.child_by_name(&name).unwrap();
if page
@@ -141,17 +162,8 @@ mod imp {
.support_document(&document)
{
self.stack.set_visible_child(&page);
- } else if self
- .stack
- .child_by_name("links")
- .unwrap()
- .dynamic_cast_ref::<PpsSidebarPage>()
- .unwrap()
- .support_document(&document)
- {
- self.stack.set_visible_child_name("links");
} else {
- self.stack.set_visible_child_name("thumbnails");
+ self.set_visible_child_default();
}
}
}
--
2.48.1

View File

@ -0,0 +1,37 @@
From ee88fa8e3488b64f930632cefbc5115cf3a406e0 Mon Sep 17 00:00:00 2001
From: Marek Kasik <mkasik@redhat.com>
Date: Fri, 31 Jan 2025 12:57:37 +0100
Subject: [PATCH 2/2] shell: Extend fast navigation
Enable navigation to the beginning of the document for "go-backwards" action
even if the page number is lower than 10.
Enable the same for "go-forward" action for the end of the document.
---
shell/src/document_view/actions.rs | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/shell/src/document_view/actions.rs b/shell/src/document_view/actions.rs
index a4036ab92..006b9f58f 100644
--- a/shell/src/document_view/actions.rs
+++ b/shell/src/document_view/actions.rs
@@ -655,6 +655,8 @@ impl imp::PpsDocumentView {
if current_page + 10 < n_pages {
obj.model.set_page(current_page + 10);
+ } else {
+ obj.model.set_page(n_pages - 1);
}
}
))
@@ -668,6 +670,8 @@ impl imp::PpsDocumentView {
if current_page - 10 >= 0 {
obj.model.set_page(current_page - 10);
+ } else {
+ obj.model.set_page(0);
}
}
))
--
2.48.1

View File

@ -0,0 +1,26 @@
From 7be0a67e000358476dda43edc74ea756d3066e18 Mon Sep 17 00:00:00 2001
From: Marek Kasik <mkasik@redhat.com>
Date: Fri, 31 Jan 2025 12:40:03 +0100
Subject: [PATCH 1/2] shell: Fix "go-backwards" action
The go-backwards action did not work before due to a typo in its name.
---
shell/src/document_view/actions.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/shell/src/document_view/actions.rs b/shell/src/document_view/actions.rs
index bdf744431..a4036ab92 100644
--- a/shell/src/document_view/actions.rs
+++ b/shell/src/document_view/actions.rs
@@ -659,7 +659,7 @@ impl imp::PpsDocumentView {
}
))
.build(),
- gio::ActionEntryBuilder::new("go-back")
+ gio::ActionEntryBuilder::new("go-backwards")
.activate(glib::clone!(
#[weak(rename_to = obj)]
self,
--
2.48.1

27
papers-47.0-help.patch Normal file
View File

@ -0,0 +1,27 @@
From fd4b99f3713218bbb0f42a3736f029ced66dbc4d Mon Sep 17 00:00:00 2001
From: Marek Kasik <mkasik@redhat.com>
Date: Tue, 25 Mar 2025 14:14:59 +0100
Subject: [PATCH] help: List "search-not-found.png" in media files
"search-not-found.png" is referenced in help pages but not listed
among media files in help/meson.build. This causes that the file
is not installed and showing of the help can fail.
---
help/meson.build | 1 +
1 file changed, 1 insertion(+)
diff --git a/help/meson.build b/help/meson.build
index 42bab279c..96996a794 100644
--- a/help/meson.build
+++ b/help/meson.build
@@ -80,6 +80,7 @@ if enable_user_doc
'figures/annotations-nav-to-page.png',
'figures/emblem-system-symbolic.svg',
'figures/org.gnome.Papers.svg',
+ 'figures/search-not-found.png',
'figures/zoom.png',
]
--
2.49.0

View File

@ -0,0 +1,22 @@
--- papers-47.0/meson.build
+++ papers-47.0/meson.build
@@ -140,7 +140,7 @@ po_dir = join_paths(source_root, 'po')
top_inc = include_directories('.')
glib_req_version = '>= 2.75.0'
-gtk_req_version = '>= 4.17.1'
+gtk_req_version = '>= 4.16.7'
libaw_req_version = '>= 1.6'
exempi_req_version = '>= 2.0'
--- papers-47.0/shell/resources/meson.build
+++ papers-47.0/shell/resources/meson.build
@@ -11,6 +11,6 @@ papers_resources = gnome.compile_resourc
source_dir: [data_dir, data_build_dir],
dependencies: metainfo_file,
gresource_bundle: true,
-)[0]
+)
-config_h.set_quoted('RESOURCES_FILE', papers_resources.full_path())
+config_h.set_quoted('RESOURCES_FILE', papers_resources[0].full_path())

View File

@ -1,609 +0,0 @@
From 633a8b188558a3bc9d309ba2554ff665791eb3b7 Mon Sep 17 00:00:00 2001
From: Lukáš Tyrychtr <ltyrycht@redhat.com>
Date: Fri, 28 Mar 2025 09:54:20 +0100
Subject: libview: implement AccessibleText for PpsViewPage
Emit proper notifications from the text implementation.
Backport assisted-by Claude <noreply@anthropic.com>
diff --git a/libview/pps-view.c b/libview/pps-view.c
index ec6c1f8d7..e3152b9dc 100644
--- a/libview/pps-view.c
+++ b/libview/pps-view.c
@@ -258,7 +258,9 @@ static void pps_view_stop_signature_rect (PpsView *view);
static void pps_view_update_primary_selection (PpsView *view);
-G_DEFINE_TYPE_WITH_CODE (PpsView, pps_view, GTK_TYPE_WIDGET, G_ADD_PRIVATE (PpsView) G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
+static void pps_view_accessible_text_init (GtkAccessibleTextInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (PpsView, pps_view, GTK_TYPE_WIDGET, G_ADD_PRIVATE (PpsView) G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL) G_IMPLEMENT_INTERFACE (GTK_TYPE_ACCESSIBLE_TEXT, pps_view_accessible_text_init))
#define GET_PRIVATE(o) pps_view_get_instance_private (o)
@@ -3855,6 +3857,7 @@ pps_view_set_caret_cursor_position (PpsView *view,
g_signal_emit (view, signals[SIGNAL_CURSOR_MOVED], 0,
priv->cursor_page, priv->cursor_offset);
+ gtk_accessible_text_update_caret_position (GTK_ACCESSIBLE_TEXT (view));
if (priv->caret_enabled && cursor_is_in_visible_page (view))
gtk_widget_queue_draw (GTK_WIDGET (view));
@@ -4910,6 +4913,7 @@ position_caret_cursor_for_event (PpsView *view,
priv->cursor_line_offset = area.x;
g_signal_emit (view, signals[SIGNAL_CURSOR_MOVED], 0, priv->cursor_page, priv->cursor_offset);
+ gtk_accessible_text_update_caret_position (GTK_ACCESSIBLE_TEXT (view));
if (redraw) {
gtk_widget_queue_draw (GTK_WIDGET (view));
@@ -6197,6 +6201,7 @@ pps_view_move_cursor (PpsView *view,
rect.y += scroll_y;
_pps_view_ensure_rectangle_is_visible (view, priv->cursor_page, &rect);
g_signal_emit (view, signals[SIGNAL_CURSOR_MOVED], 0, priv->cursor_page, priv->cursor_offset);
+ gtk_accessible_text_update_caret_position (GTK_ACCESSIBLE_TEXT (view));
clear_selection (view);
return TRUE;
}
@@ -6236,6 +6241,7 @@ pps_view_move_cursor (PpsView *view,
_pps_view_ensure_rectangle_is_visible (view, priv->cursor_page, &rect);
g_signal_emit (view, signals[SIGNAL_CURSOR_MOVED], 0, priv->cursor_page, priv->cursor_offset);
+ gtk_accessible_text_update_caret_position (GTK_ACCESSIBLE_TEXT (view));
gtk_widget_queue_draw (GTK_WIDGET (view));
@@ -7026,6 +7032,541 @@ zoom_gesture_scale_changed_cb (GtkGestureZoom *gesture,
pps_view_zoom (view, factor);
}
+/* ATs expect to be able to identify sentence boundaries based on content. Valid,
+ * content-based boundaries may be present at the end of a newline, for instance
+ * at the end of a heading within a document. Thus being able to distinguish hard
+ * returns from soft returns is necessary. However, the text we get from Poppler
+ * for non-tagged PDFs has "\n" inserted at the end of each line resulting in a
+ * broken accessibility implementation w.r.t. sentences.
+ */
+static gboolean
+treat_as_soft_return (PpsView *self,
+ gint page,
+ PangoLogAttr *log_attrs,
+ gint offset)
+{
+ PpsViewPrivate *priv = GET_PRIVATE (self);
+ PpsRectangle *areas = NULL;
+ guint n_areas = 0;
+ gdouble line_spacing, this_line_height, next_word_width;
+ PpsRectangle *this_line_start;
+ PpsRectangle *this_line_end;
+ PpsRectangle *next_line_start;
+ PpsRectangle *next_line_end;
+ PpsRectangle *next_word_end;
+ gint prev_offset, next_offset;
+
+ if (!log_attrs[offset].is_white)
+ return FALSE;
+
+ pps_page_cache_get_text_layout (priv->page_cache, page, &areas, &n_areas);
+ if (n_areas <= offset + 1)
+ return FALSE;
+
+ prev_offset = offset - 1;
+ next_offset = offset + 1;
+
+ /* In wrapped text, the character at the start of the next line starts a word.
+ * Examples where this condition might fail include bullets and images. But it
+ * also includes things like "(", so also check the next character.
+ */
+ if (!log_attrs[next_offset].is_word_start &&
+ (next_offset + 1 >= n_areas || !log_attrs[next_offset + 1].is_word_start))
+ return FALSE;
+
+ /* In wrapped text, the chars on either side of the newline have very similar heights.
+ * Examples where this condition might fail include a newline at the end of a heading,
+ * and a newline at the end of a paragraph that is followed by a heading.
+ */
+ this_line_end = areas + prev_offset;
+ next_line_start = areas + next_offset;
+
+ this_line_height = this_line_end->y2 - this_line_end->y1;
+ if (ABS (this_line_height - (next_line_start->y2 - next_line_start->y1)) > 0.25)
+ return FALSE;
+
+ /* If there is significant white space between this line and the next, odds are this
+ * is not a soft return in wrapped text. Lines within a typical paragraph are at most
+ * double-spaced. If the spacing is more than that, assume a hard return is present.
+ */
+ line_spacing = next_line_start->y1 - this_line_end->y2;
+ if (line_spacing - this_line_height > 1)
+ return FALSE;
+
+ /* Lines within a typical paragraph have *reasonably* similar x1 coordinates. But
+ * we cannot count on them being nearly identical. Examples where indentation can
+ * be present in wrapped text include indenting the first line of the paragraph,
+ * and hanging indents (e.g. in the works cited within an academic paper). So we'll
+ * be somewhat tolerant here.
+ */
+ while (prev_offset > 0 && !log_attrs[prev_offset].is_mandatory_break)
+ prev_offset--;
+ this_line_start = areas + prev_offset;
+ if (ABS (this_line_start->x1 - next_line_start->x1) > 20)
+ return FALSE;
+
+ /* Ditto for x2, but this line might be short due to a wide word on the next line. */
+ while (next_offset < n_areas && !log_attrs[next_offset].is_word_end)
+ next_offset++;
+ next_word_end = areas + next_offset;
+ next_word_width = next_word_end->x2 - next_line_start->x1;
+
+ while (next_offset < n_areas && !log_attrs[next_offset + 1].is_mandatory_break)
+ next_offset++;
+ next_line_end = areas + next_offset;
+ if (next_line_end->x2 - (this_line_end->x2 + next_word_width) > 20)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gchar *
+get_substring (GtkAccessibleText *text,
+ unsigned int start,
+ unsigned int end)
+{
+ PpsView *self = PPS_VIEW (text);
+ PpsViewPrivate *priv = GET_PRIVATE (self);
+ gchar *substring, *normalized;
+ const gchar *page_text;
+
+ page_text = pps_page_cache_get_text (priv->page_cache, priv->cursor_page);
+ if (!page_text)
+ return NULL;
+ if (end > g_utf8_strlen (page_text, -1))
+ end = strlen (page_text);
+ start = CLAMP (start, 0, end);
+
+ substring = g_utf8_substring (page_text, start, end);
+ normalized = g_utf8_normalize (substring, -1, G_NORMALIZE_NFKC);
+ g_free (substring);
+
+ return normalized;
+}
+
+static GBytes *
+pps_view_accessible_text_get_contents (GtkAccessibleText *text,
+ unsigned int start,
+ unsigned int end)
+{
+ gchar *substring = get_substring (text, start, end);
+ if (!substring)
+ return g_bytes_new (NULL, 0);
+
+ return g_bytes_new_take (substring, strlen (substring));
+}
+
+static void
+get_range_for_granularity (GtkAccessibleText *text,
+ GtkAccessibleTextGranularity granularity,
+ unsigned int offset,
+ unsigned int *start_offset,
+ unsigned int *end_offset)
+{
+ PpsView *self = PPS_VIEW (text);
+ PpsViewPrivate *priv = GET_PRIVATE (self);
+ gint start = 0;
+ gint end = 0;
+ PangoLogAttr *log_attrs = NULL;
+ gulong n_attrs;
+
+ if (!priv->page_cache)
+ return;
+
+ pps_page_cache_get_text_log_attrs (priv->page_cache, priv->cursor_page, &log_attrs, &n_attrs);
+ if (!log_attrs)
+ return;
+
+ if (offset >= n_attrs)
+ return;
+
+ switch (granularity) {
+ case GTK_ACCESSIBLE_TEXT_GRANULARITY_CHARACTER:
+ start = offset;
+ end = offset + 1;
+ break;
+ case GTK_ACCESSIBLE_TEXT_GRANULARITY_WORD:
+ start = offset;
+ while (start > 0 && !log_attrs[start].is_word_start)
+ start--;
+ end = offset + 1;
+ while (end < n_attrs && !log_attrs[end].is_word_start)
+ end++;
+ break;
+ case GTK_ACCESSIBLE_TEXT_GRANULARITY_SENTENCE:
+ for (start = offset; start > 0; start--) {
+ if (log_attrs[start].is_mandatory_break && treat_as_soft_return (self, priv->cursor_page, log_attrs, start - 1))
+ continue;
+ if (log_attrs[start].is_sentence_start)
+ break;
+ }
+ for (end = offset + 1; end < n_attrs; end++) {
+ if (log_attrs[end].is_mandatory_break && treat_as_soft_return (self, priv->cursor_page, log_attrs, end - 1))
+ continue;
+ if (log_attrs[end].is_sentence_start)
+ break;
+ }
+ break;
+ case GTK_ACCESSIBLE_TEXT_GRANULARITY_LINE:
+ start = offset;
+ while (start > 0 && !log_attrs[start].is_mandatory_break)
+ start--;
+ end = offset + 1;
+ while (end < n_attrs && !log_attrs[end].is_mandatory_break)
+ end++;
+ break;
+ case GTK_ACCESSIBLE_TEXT_GRANULARITY_PARAGRAPH:
+ /* FIXME: There is likely more than one paragraph on the page, so try to deduce it properly */
+ start = 0;
+ end = n_attrs;
+ }
+
+ *start_offset = start;
+ *end_offset = end;
+}
+
+static GBytes *
+pps_view_accessible_text_get_contents_at (GtkAccessibleText *text,
+ unsigned int offset,
+ GtkAccessibleTextGranularity granularity,
+ unsigned int *start_offset,
+ unsigned int *end_offset)
+{
+ gchar *substring;
+
+ get_range_for_granularity (text, granularity, offset, start_offset, end_offset);
+ substring = get_substring (text, *start_offset, *end_offset);
+
+ /* If newlines appear inside the text of a sentence (i.e. between the start and
+ * end offsets returned by get_substring), it interferes with
+ * the prosody of text-to-speech based-solutions such as a screen reader because
+ * speech synthesizers tend to pause after the newline char as if it were the end
+ * of the sentence.
+ */
+ if (granularity == GTK_ACCESSIBLE_TEXT_GRANULARITY_SENTENCE)
+ g_strdelimit (substring, "\n", ' ');
+
+ return g_bytes_new_take (substring, strlen (substring));
+}
+
+static unsigned int
+pps_view_accessible_text_get_caret_position (GtkAccessibleText *text)
+{
+ PpsView *self = PPS_VIEW (text);
+ PpsViewPrivate *priv = GET_PRIVATE (self);
+
+ if (priv->caret_enabled)
+ return priv->cursor_offset;
+
+ return 0;
+}
+
+static gboolean
+get_selection_bounds (PpsView *self,
+ gint *start_offset,
+ gint *end_offset)
+{
+ PpsViewPrivate *priv = GET_PRIVATE (self);
+ cairo_rectangle_int_t rect;
+ cairo_region_t *region;
+ gint start, end;
+ gdouble scale = pps_document_model_get_scale (priv->model);
+
+ region = pps_pixbuf_cache_get_selection_region (priv->pixbuf_cache,
+ priv->cursor_page,
+ scale);
+
+ if (!region || cairo_region_is_empty (region))
+ return FALSE;
+
+ cairo_region_get_rectangle (region, 0, &rect);
+ start = _pps_view_get_caret_cursor_offset_at_doc_point (self,
+ priv->cursor_page,
+ rect.x / scale,
+ (rect.y + (rect.height / 2)) / scale);
+ if (start == -1)
+ return FALSE;
+
+ cairo_region_get_rectangle (region,
+ cairo_region_num_rectangles (region) - 1,
+ &rect);
+ end = _pps_view_get_caret_cursor_offset_at_doc_point (self,
+ priv->cursor_page,
+ (rect.x + rect.width) / scale,
+ (rect.y + (rect.height / 2)) / scale);
+ if (end == -1)
+ return FALSE;
+
+ *start_offset = start;
+ *end_offset = end;
+
+ return TRUE;
+}
+
+static gboolean
+pps_view_accessible_text_get_selection (GtkAccessibleText *text,
+ gsize *n_ranges,
+ GtkAccessibleTextRange **ranges)
+{
+ PpsView *self = PPS_VIEW (text);
+
+ *n_ranges = 0;
+
+ gint start = 0, end = 0;
+ if (get_selection_bounds (self, &start, &end) && start != end) {
+ *n_ranges = 1;
+ *ranges = g_new (GtkAccessibleTextRange, 1);
+ (*ranges)[0].start = start;
+ (*ranges)[0].length = end - start;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+fill_run_attributes (PangoAttrList *attrs,
+ const gchar *text,
+ unsigned int offset,
+ unsigned int *start_offset,
+ unsigned int *end_offset,
+ GStrvBuilder *names,
+ GStrvBuilder *values)
+{
+ PangoAttrString *pango_string;
+ PangoAttrInt *pango_int;
+ PangoAttrColor *pango_color;
+ PangoAttrIterator *iter;
+ gint i, start, end;
+ gboolean has_attrs = FALSE;
+ glong text_length;
+ gchar *attr_value;
+
+ text_length = g_utf8_strlen (text, -1);
+ if (offset < 0 || offset >= text_length)
+ return;
+
+ /* Check if there are attributes for the offset,
+ * and set the attributes range if positive */
+ iter = pango_attr_list_get_iterator (attrs);
+ i = g_utf8_offset_to_pointer (text, offset) - text;
+
+ do {
+ pango_attr_iterator_range (iter, &start, &end);
+ if (i >= start && i < end) {
+ *start_offset = g_utf8_pointer_to_offset (text, text + start);
+ if (end == G_MAXINT) /* Last iterator */
+ end = text_length;
+ *end_offset = g_utf8_pointer_to_offset (text, text + end);
+ has_attrs = TRUE;
+ }
+ } while (!has_attrs && pango_attr_iterator_next (iter));
+
+ if (!has_attrs) {
+ pango_attr_iterator_destroy (iter);
+ return;
+ }
+
+ /* Fill the GtkAccessibleText attributes from the Pango attributes */
+ pango_string = (PangoAttrString *) pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY);
+ if (pango_string) {
+ g_strv_builder_add (names, GTK_ACCESSIBLE_ATTRIBUTE_FAMILY);
+ g_strv_builder_add (values, pango_string->value);
+ }
+
+ pango_int = (PangoAttrInt *) pango_attr_iterator_get (iter, PANGO_ATTR_SIZE);
+ if (pango_int) {
+ attr_value = g_strdup_printf ("%i", pango_int->value / PANGO_SCALE);
+ g_strv_builder_add (names, GTK_ACCESSIBLE_ATTRIBUTE_SIZE);
+ g_strv_builder_add (values, attr_value);
+ }
+
+ pango_int = (PangoAttrInt *) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
+ if (pango_int) {
+ g_strv_builder_add (names, GTK_ACCESSIBLE_ATTRIBUTE_UNDERLINE);
+ g_strv_builder_add (values, pango_int->value ? GTK_ACCESSIBLE_ATTRIBUTE_UNDERLINE_NONE : GTK_ACCESSIBLE_ATTRIBUTE_UNDERLINE_SINGLE);
+ }
+ pango_color = (PangoAttrColor *) pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
+ if (pango_color) {
+ attr_value = g_strdup_printf ("%u,%u,%u",
+ pango_color->color.red,
+ pango_color->color.green,
+ pango_color->color.blue);
+ g_strv_builder_add (names, GTK_ACCESSIBLE_ATTRIBUTE_FOREGROUND);
+ g_strv_builder_add (values, attr_value);
+ }
+
+ pango_attr_iterator_destroy (iter);
+}
+
+static gboolean
+pps_view_accessible_text_get_attributes (GtkAccessibleText *text,
+ unsigned int offset,
+ gsize *n_ranges,
+ GtkAccessibleTextRange **ranges,
+ char ***attribute_names,
+ char ***attribute_values)
+{
+ PpsView *self = PPS_VIEW (text);
+ PpsViewPrivate *priv = GET_PRIVATE (self);
+ PangoAttrList *attrs;
+ const gchar *page_text;
+ unsigned int start_offset = 0;
+ unsigned int end_offset = 0;
+ PpsPageCache *cache = priv->page_cache;
+
+ if (offset < 0)
+ goto no_attrs;
+
+ if (!cache)
+ goto no_attrs;
+
+ page_text = pps_page_cache_get_text (cache, priv->cursor_page);
+ if (!page_text)
+ goto no_attrs;
+
+ attrs = pps_page_cache_get_text_attrs (cache, priv->cursor_page);
+ if (!attrs)
+ goto no_attrs;
+
+ GStrvBuilder *names = g_strv_builder_new ();
+ GStrvBuilder *values = g_strv_builder_new ();
+ fill_run_attributes (attrs, page_text, offset, &start_offset, &end_offset, names, values);
+ *n_ranges = 1;
+ *ranges = g_new (GtkAccessibleTextRange, 1);
+ (*ranges)[0].start = start_offset;
+ (*ranges)[0].length = end_offset - start_offset;
+ *attribute_names = g_strv_builder_end (names);
+ *attribute_values = g_strv_builder_end (values);
+ g_strv_builder_unref (names);
+ g_strv_builder_unref (values);
+ if (attribute_names[0] && attribute_names[0][0])
+ return TRUE;
+ else {
+ *n_ranges = 0;
+ return FALSE;
+ }
+no_attrs:
+ *n_ranges = 0;
+ *attribute_names = g_new0 (char *, 1);
+ *attribute_values = g_new0 (char *, 1);
+ return FALSE;
+}
+
+static void
+pps_view_accessible_text_get_default_attributes (GtkAccessibleText *text,
+ char ***attribute_names,
+ char ***attribute_values)
+{
+ /* No default attributes */
+ *attribute_names = g_new0 (char *, 1);
+ *attribute_values = g_new0 (char *, 1);
+ return;
+}
+
+static gboolean
+pps_view_accessible_text_get_extents (GtkAccessibleText *text,
+ unsigned int start,
+ unsigned int end,
+ graphene_rect_t *extents)
+{
+ PpsView *self = PPS_VIEW (text);
+ PpsViewPrivate *priv = GET_PRIVATE (self);
+ GtkWidget *toplevel;
+ PpsRectangle *areas = NULL;
+ PpsRectangle *doc_rect;
+ guint n_areas = 0;
+ graphene_point_t widget_point;
+ GdkRectangle view_rect;
+ PpsPageCache *cache = priv->page_cache;
+
+ if (!cache)
+ return FALSE;
+
+ pps_page_cache_get_text_layout (cache, priv->cursor_page, &areas, &n_areas);
+ if (!areas || start < 0 || start >= n_areas || end >= n_areas || start > end)
+ return FALSE;
+
+ extents->origin.x = 0;
+ extents->origin.y = 0;
+ extents->size.width = 0;
+ extents->size.height = 0;
+ toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (self)));
+ for (int offset = start; offset <= end; offset++) {
+ doc_rect = areas + offset;
+ _pps_view_transform_doc_rect_to_view_rect (self, priv->cursor_page, doc_rect, &view_rect);
+
+ if (!gtk_widget_compute_point (GTK_WIDGET (self), toplevel, graphene_point_zero (), &widget_point))
+ return FALSE;
+ view_rect.x += widget_point.x;
+ view_rect.y += widget_point.y;
+
+ extents->origin.x = MAX (extents->origin.x, view_rect.x);
+ extents->origin.y = MAX (extents->origin.y, view_rect.y);
+ extents->size.width = MAX (extents->size.width, view_rect.width);
+ extents->size.height = MAX (extents->size.height, view_rect.height);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+pps_view_accessible_text_get_offset (GtkAccessibleText *text,
+ const graphene_point_t *point,
+ unsigned int *offset)
+{
+ PpsView *self = PPS_VIEW (text);
+ PpsViewPrivate *priv = GET_PRIVATE (self);
+ GtkWidget *toplevel;
+ PpsRectangle *areas = NULL;
+ PpsRectangle *rect = NULL;
+ guint n_areas = 0;
+ guint i;
+ graphene_point_t widget_point;
+ gdouble view_x, view_y;
+ PpsPoint doc_point;
+ PpsPageCache *cache = priv->page_cache;
+
+ if (!cache)
+ return FALSE;
+
+ pps_page_cache_get_text_layout (cache, priv->cursor_page, &areas, &n_areas);
+ if (!areas)
+ return FALSE;
+
+ view_x = point->x;
+ view_y = point->y;
+ toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (self)));
+ if (!gtk_widget_compute_point (GTK_WIDGET (self), toplevel, graphene_point_zero (), &widget_point))
+ return FALSE;
+ view_x -= widget_point.x;
+ view_y -= widget_point.y;
+
+ doc_point = pps_view_get_doc_point_for_page (self, priv->cursor_page, view_x, view_y);
+
+ for (i = 0; i < n_areas; i++) {
+ rect = areas + i;
+ if (doc_point.x >= rect->x1 && doc_point.x <= rect->x2 &&
+ doc_point.y >= rect->y1 && doc_point.y <= rect->y2)
+ *offset = i;
+ }
+
+ return TRUE;
+}
+
+static void
+pps_view_accessible_text_init (GtkAccessibleTextInterface *iface)
+{
+ iface->get_contents = pps_view_accessible_text_get_contents;
+ iface->get_contents_at = pps_view_accessible_text_get_contents_at;
+ iface->get_caret_position = pps_view_accessible_text_get_caret_position;
+ iface->get_selection = pps_view_accessible_text_get_selection;
+ iface->get_attributes = pps_view_accessible_text_get_attributes;
+ iface->get_default_attributes = pps_view_accessible_text_get_default_attributes;
+ iface->get_extents = pps_view_accessible_text_get_extents;
+ iface->get_offset = pps_view_accessible_text_get_offset;
+}
+
static void
pps_view_class_init (PpsViewClass *class)
{
@@ -8427,6 +8975,7 @@ merge_selection_region (PpsView *view,
priv->selection_info.selections = new_list;
pps_pixbuf_cache_set_selection_list (priv->pixbuf_cache, new_list);
g_signal_emit (view, signals[SIGNAL_SELECTION_CHANGED], 0, NULL);
+ gtk_accessible_text_update_selection_bound (GTK_ACCESSIBLE_TEXT (view));
new_list_ptr = new_list;
old_list_ptr = old_list;
--
2.51.1

View File

@ -1,121 +0,0 @@
--- papers-48.7/meson.build
+++ papers-48.7/meson.build
@@ -140,7 +140,7 @@ po_dir = join_paths(source_root, 'po')
top_inc = include_directories('.')
glib_req_version = '>= 2.75.0'
-gtk_req_version = '>= 4.17.1'
+gtk_req_version = '>= 4.16.7'
libaw_req_version = '>= 1.6'
exempi_req_version = '>= 2.0'
@@ -270,7 +270,7 @@ elif get_option('djvu').auto()
endif
# *** PDF ***
-poppler_req_version = '>= 25.01.0'
+poppler_req_version = '>= 24.02.0'
poppler_glib_dep = dependency('poppler-glib', version: poppler_req_version, required: get_option('pdf'))
enable_pdf = poppler_glib_dep.found()
--- papers-48.7/shell/resources/meson.build
+++ papers-48.7/shell/resources/meson.build
@@ -11,6 +11,6 @@ papers_resources = gnome.compile_resourc
source_dir: [data_dir, data_build_dir],
dependencies: metainfo_file,
gresource_bundle: true,
-)[0]
+)
-config_h.set_quoted('RESOURCES_FILE', papers_resources.full_path())
+config_h.set_quoted('RESOURCES_FILE', papers_resources[0].full_path())
--- papers-48.7/libdocument/backend/pdf/pps-poppler.c
+++ papers-48.7/libdocument/backend/pdf/pps-poppler.c
@@ -4058,20 +4058,24 @@ pps_backend_query_type (void)
static PopplerCertificateInfo *
find_poppler_certificate_info (PpsCertificateInfo *certificate_info)
{
- g_autolist (PopplerCertificateInfo) signing_certificates = NULL;
+ GList *signing_certificates = poppler_get_available_signing_certificates ();
+ PopplerCertificateInfo *ret = NULL;
g_autofree char *certificate_id = NULL;
- signing_certificates = poppler_get_available_signing_certificates ();
g_object_get (certificate_info, "id", &certificate_id, NULL);
for (GList *list = signing_certificates; list != NULL && list->data != NULL; list = list->next) {
PopplerCertificateInfo *certificate_info = list->data;
- if (g_strcmp0 (certificate_id, poppler_certificate_info_get_id (certificate_info)) == 0)
- return poppler_certificate_info_copy (certificate_info);
+ if (g_strcmp0 (certificate_id, poppler_certificate_info_get_id (certificate_info)) == 0) {
+ ret = poppler_certificate_info_copy (certificate_info);
+ break;
+ }
}
- return NULL;
+ g_clear_list (&signing_certificates, (GDestroyNotify) poppler_certificate_info_free);
+
+ return ret;
}
static void
@@ -4098,8 +4102,8 @@ pdf_document_signatures_sign (PpsDocumen
gpointer user_data)
{
PdfDocument *self = PDF_DOCUMENT (document);
- g_autoptr (PopplerSigningData) signing_data = poppler_signing_data_new ();
- g_autoptr (PopplerCertificateInfo) cert_info = NULL;
+ PopplerSigningData *signing_data = poppler_signing_data_new ();
+ PopplerCertificateInfo *cert_info;
g_autoptr (PpsCertificateInfo) cinfo = NULL;
g_autoptr (GTask) task = NULL;
g_autofree gchar *uuid = NULL;
@@ -4212,7 +4216,7 @@ pdf_document_set_password_callback (PpsD
static GList *
pdf_document_get_available_signing_certificates (PpsDocumentSignatures *document)
{
- g_autolist (PopplerCertificateInfo) signing_certs = poppler_get_available_signing_certificates ();
+ GList *signing_certs = poppler_get_available_signing_certificates ();
GList *ev_certs = NULL;
for (GList *list = signing_certs; list != NULL && list->data != NULL; list = list->next) {
@@ -4225,6 +4229,8 @@ pdf_document_get_available_signing_certi
ev_certs = g_list_append (ev_certs, cert_info);
}
+ g_clear_list (&signing_certs, (GDestroyNotify) poppler_certificate_info_free);
+
return ev_certs;
}
@@ -4275,7 +4275,7 @@ pdf_document_signatures_get_signatures (
for (iter = signature_fields; iter != NULL; iter = iter->next) {
PopplerFormField *field = iter->data;
- g_autoptr (PopplerSignatureInfo) info = NULL;
+ PopplerSignatureInfo *info = NULL;
PpsSignature *signature = NULL;
PpsSignatureStatus signature_status;
PpsCertificateStatus certificate_status;
@@ -4291,8 +4291,10 @@ pdf_document_signatures_get_signatures (
POPPLER_SIGNATURE_VALIDATION_FLAG_USE_AIA_CERTIFICATE_FETCH,
NULL,
NULL);
- if (info == NULL || poppler_signature_info_get_certificate_info (info) == NULL)
+ if (info == NULL || poppler_signature_info_get_certificate_info (info) == NULL) {
+ poppler_signature_info_free (info);
continue;
+ }
switch (poppler_signature_info_get_signature_status (info)) {
case POPPLER_SIGNATURE_VALID:
@@ -4368,6 +4370,7 @@ pdf_document_signatures_get_signatures (
} else {
g_warning ("Could not get certificate info for a signature!");
}
+ poppler_signature_info_free (info);
}
g_clear_list (&signature_fields, g_object_unref);

View File

@ -1,117 +0,0 @@
From 66dd2a1d2c327916cc0be4aa184e4388762f73f6 Mon Sep 17 00:00:00 2001
From: Marek Kasik <mkasik@redhat.com>
Date: Fri, 12 Dec 2025 12:02:13 +0100
Subject: [PATCH 1/3] shell: Show radio button for single cert
Show radio button instead of check button when there is only 1 signing
certificate. Make it insensitive so it can not be unselected.
---
shell/src/document_view/io.rs | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/shell/src/document_view/io.rs b/shell/src/document_view/io.rs
index 422da4879..75eaefa5c 100644
--- a/shell/src/document_view/io.rs
+++ b/shell/src/document_view/io.rs
@@ -538,7 +538,10 @@ impl imp::PpsDocumentView {
.css_classes(["content"])
.build();
- let mut check_button_group = None;
+ // Create dummy button to group with
+ let check_button_group = Some(gtk::CheckButton::new());
+ let multi_certs = certs.len() > 1;
+ let mut first_cert = true;
for cert in certs {
let row = adw::ActionRow::builder()
@@ -547,6 +550,7 @@ impl imp::PpsDocumentView {
.build();
let check_button = gtk::CheckButton::builder()
+ .sensitive(multi_certs)
.valign(gtk::Align::Center)
.build();
@@ -577,12 +581,12 @@ impl imp::PpsDocumentView {
check_button.set_group(check_button_group.as_ref());
- if check_button_group.is_none() {
+ if first_cert {
check_button.set_active(true);
- check_button_group = Some(check_button);
}
list_box.insert(&row, -1);
+ first_cert = false;
}
list_box.select_row(list_box.row_at_index(0).as_ref());
--
2.52.0
From f7d74779003c84bec3c0ccca5cb8fa9c88365725 Mon Sep 17 00:00:00 2001
From: Lucas Baudin <lbaudin@gnome.org>
Date: Thu, 18 Dec 2025 14:35:47 +0000
Subject: [PATCH 2/3] shell: Keep a reference to check_button_group
Keep a strong reference to check_button_group to avoid it being freed.
---
shell/src/document_view/io.rs | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/shell/src/document_view/io.rs b/shell/src/document_view/io.rs
index 75eaefa5c..4a768d7b2 100644
--- a/shell/src/document_view/io.rs
+++ b/shell/src/document_view/io.rs
@@ -550,7 +550,6 @@ impl imp::PpsDocumentView {
.build();
let check_button = gtk::CheckButton::builder()
- .sensitive(multi_certs)
.valign(gtk::Align::Center)
.build();
@@ -558,9 +557,14 @@ impl imp::PpsDocumentView {
row.set_activatable_widget(Some(&check_button));
check_button.connect_toggled(glib::clone!(
+ /* Keep a strong reference to check_button_group to avoid it being freed */
+ #[strong(rename_to = group)]
+ check_button_group,
#[weak(rename_to = obj)]
self,
move |button| {
+ /* make sure the strong reference to group is not disposed */
+ let _ = group.as_ref().unwrap();
if button.is_active() {
let row = button
.ancestor(adw::ActionRow::static_type())
--
2.52.0
From e15ab6f70de98b1e1692e9998b2cffef8189b3a9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Ka=C5=A1=C3=ADk?= <mkasik@redhat.com>
Date: Thu, 18 Dec 2025 14:36:56 +0000
Subject: [PATCH 3/3] shell: Remove redundant variable
---
shell/src/document_view/io.rs | 1 -
1 file changed, 1 deletion(-)
diff --git a/shell/src/document_view/io.rs b/shell/src/document_view/io.rs
index 4a768d7b2..ec9863c75 100644
--- a/shell/src/document_view/io.rs
+++ b/shell/src/document_view/io.rs
@@ -540,7 +540,6 @@ impl imp::PpsDocumentView {
// Create dummy button to group with
let check_button_group = Some(gtk::CheckButton::new());
- let multi_certs = certs.len() > 1;
let mut first_cert = true;
for cert in certs {
--
2.52.0

View File

@ -1,20 +0,0 @@
From bc8bee5a81c20d3e6b414fa8783fdbfe79e9356b Mon Sep 17 00:00:00 2001
From: Lukáš Tyrychtr <ltyrycht@redhat.com>
Date: Thu, 20 Mar 2025 15:05:03 +0100
Subject: libview: Use correct roles for pps-view and pps-view-page
Modified by Marek Kasik for backport.
diff --git a/libview/pps-view.c b/libview/pps-view.c
index cededf462..2b3e094a9 100644
--- a/libview/pps-view.c
+++ b/libview/pps-view.c
@@ -7644,6 +7644,8 @@ pps_view_class_init (PpsViewClass *class)
GDK_KEY_a, GDK_CONTROL_MASK,
(GtkShortcutFunc) pps_view_select_all,
NULL);
+
+ gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_DOCUMENT);
}
static void

View File

@ -1,4 +1,15 @@
## START: Set by rpmautospec
## (rpmautospec version 0.6.5)
## RPMAUTOSPEC: autorelease, autochangelog
%define autorelease(e:s:pb:n) %{?-p:0.}%{lua:
release_number = 6;
base_release_number = tonumber(rpm.expand("%{?-b*}%{!?-b:1}"));
print(release_number + base_release_number - 1);
}%{?-e:.%{-e*}}%{?-s:.%{-s*}}%{!?-n:%{?dist}}
## END: Set by rpmautospec
%global tarball_version %%(echo %{version} | tr '~' '.')
%global commit 5de8d26c
%if 0%{?rhel}
%global bundled_rust_deps 1
@ -11,8 +22,8 @@
%global __provides_exclude_from ^(%{_libdir}/papers/.*\\.so|%{_libdir}/nautilus/extensions-4/.*\\.so)$
Name: papers
Version: 48.7
Release: %autorelease
Version: 47.0
Release: %autorelease -s git%{commit}
Summary: View multipage documents
# papers itself is:
@ -28,23 +39,28 @@ SourceLicense: GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-2.0-or-later AND
# MIT AND (MIT OR Apache-2.0)
# MIT OR Apache-2.0
# Unlicense OR MIT
# Zlib
License: GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-2.0-or-later AND LGPL-2.1-or-later AND MIT AND Zlib AND libtiff AND (MIT OR Apache-2.0) AND Unicode-DFS-2016 AND (Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT) AND (BSD-2-Clause OR Apache-2.0 OR MIT) AND BSD-3-Clause AND (Unlicense OR MIT)
License: GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-2.0-or-later AND LGPL-2.1-or-later AND MIT AND libtiff AND (MIT OR Apache-2.0) AND Unicode-DFS-2016 AND (Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT) AND (BSD-2-Clause OR Apache-2.0 OR MIT) AND BSD-3-Clause AND (Unlicense OR MIT)
URL: https://gitlab.gnome.org/GNOME/Incubator/papers
Source: https://download.gnome.org/sources/papers/48/papers-%{tarball_version}.tar.xz
Source: papers-%{commit}.tar.xz
%if 0%{?bundled_rust_deps}
# To generate vendored cargo sources:
# tar xf papers-%%{tarball_version}.tar.xz ; pushd papers-%%{tarball_version} ; \
# cargo vendor && tar Jcvf ../papers-%%{tarball_version}-vendor.tar.xz vendor/ ; popd
Source1: papers-%{tarball_version}-vendor.tar.xz
# tar xf papers-%%{tarball_version}.tar.xz ; pushd papers-%%{tarball_version}/shell-rs ; \
# cargo vendor && tar Jcvf ../../papers-%%{tarball_version}-vendor.tar.xz ../shell-rs/vendor/ ; popd
Source1: papers-%{commit}-vendor.tar.xz
%endif
# Lower the requirement as we don't have the required versions
Patch: papers-48.7-lower-requirements.patch
# Patch to make Papers build with snapshot
Patch: papers-47.0-snapshot-5de8d26c.patch
Patch: papers-48.7-implement-accessible-text-for-pps-view.patch
Patch: papers-48.7-use-correct-role-for-pps-view.patch
Patch: papers-48.7-single-signature-selection.patch
# Patch to avoid crash when sidebar-page has unexpected value
Patch: papers-47.0-check-sidebar-page.patch
# Fix fast navigation
Patch: papers-47.0-go-backwards-action.patch
Patch: papers-47.0-fast-navigation.patch
# Fix help
Patch: papers-47.0-help.patch
# https://fedoraproject.org/wiki/Changes/EncourageI686LeafRemoval
ExcludeArch: %{ix86}
@ -145,15 +161,18 @@ This package brings the Papers thumbnailer independently from Papers.
%autosetup -p1 -n papers-%{tarball_version} %{?bundled_rust_deps:-a1}
%if 0%{?bundled_rust_deps}
%cargo_prep -v vendor
%cargo_prep -v shell/vendor
%else
rm shell/Cargo.lock
%cargo_prep
%endif
%if !0%{?bundled_rust_deps}
%generate_buildrequires
cd shell
%cargo_generate_buildrequires -a -t
cd ~-
%endif
@ -167,6 +186,7 @@ This package brings the Papers thumbnailer independently from Papers.
%meson_build
cd shell
%cargo_license_summary -a
%{cargo_license -a} > LICENSE.dependencies
%if 0%{?bundled_rust_deps}
@ -181,6 +201,8 @@ cd ~-
%install
%meson_install
# Remove unused symbolic link
%find_lang papers --with-gnome
@ -194,11 +216,12 @@ desktop-file-validate $RPM_BUILD_ROOT%{_datadir}/applications/*.desktop
%files -f papers.lang
%doc README.md
%license COPYING
%license LICENSE.dependencies
%license shell/LICENSE.dependencies
%if 0%{?bundled_rust_deps}
%license cargo-vendor.txt
%license shell/cargo-vendor.txt
%endif
%{_bindir}/papers
# internal library not used by other apps, which is why it is not in -libs
%{_datadir}/applications/org.gnome.Papers.desktop
%{_datadir}/glib-2.0/schemas/org.gnome.Papers.gschema.xml
%{_datadir}/icons/hicolor/scalable/apps/org.gnome.Papers.svg
@ -240,4 +263,22 @@ desktop-file-validate $RPM_BUILD_ROOT%{_datadir}/applications/*.desktop
%changelog
%autochangelog
## START: Generated by rpmautospec
* Thu Mar 27 2025 Marek Kasik <mkasik@redhat.com> - 47.0-6.git5de8d26c
- Fix showing of help
* Mon Feb 03 2025 Marek Kasik <mkasik@redhat.com> - 47.0-5.git5de8d26c
- Fix fast navigation
* Wed Jan 29 2025 Marek Kasik <mkasik@redhat.com> - 47.0-4.git5de8d26c
- Check value of "sidebar-page" key
* Mon Jan 20 2025 Marek Kasik <mkasik@redhat.com> - 47.0-3.git5de8d26c
- Update Papers to current git snapshot
* Wed Nov 20 2024 Tomas Pelka <tpelka@redhat.com> - 47.0-2
- Update file gating.yaml
* Wed Nov 20 2024 Tomas Popela <tpopela@redhat.com> - 47.0-1
- Initial import
## END: Generated by rpmautospec

View File

@ -1,2 +1,2 @@
SHA512 (papers-48.7.tar.xz) = db95e3ae87cfdd99849e5bb07d77f4ec7b698dbd9f515cac60978475cbbb887cf8ee57446851139dbca9b13294f8b3e4cd106718784a6a94bdf49f545ff0c13b
SHA512 (papers-48.7-vendor.tar.xz) = 6c70a10e01d6508b82bcf5cdb9e989b95cad53d05feba569b5a3be1d88f1b82a890de3c5d29f2d6dbebadcf84bf4773115a7f8d1c15d8cd06fc4983cf587a1d6
SHA512 (papers-5de8d26c-vendor.tar.xz) = 4eee4f04eb7ee77fb36ddba5d75bccfde47c398df5086a7ffa61e7b916d0f7779a44d6596bb16ec36107a7b84d2b2f56829358c55aeb7e3a7b0ca8f3fa2f1caf
SHA512 (papers-5de8d26c.tar.xz) = b456f9c871caf8fe217c234dc41cbbe772d95822a808af2aa95c0745b35a67fe41a4d76d9accbde51f212ec1185bef3dd33db015b83e93673cda8259f6d843ff