diff mupdf-source/source/xps/xps-outline.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/source/xps/xps-outline.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,169 @@
+// Copyright (C) 2004-2021 Artifex Software, Inc.
+//
+// This file is part of MuPDF.
+//
+// MuPDF is free software: you can redistribute it and/or modify it under the
+// terms of the GNU Affero General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or (at your option)
+// any later version.
+//
+// MuPDF 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 Affero General Public License for more
+// details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
+//
+// Alternative licensing terms are available from the licensor.
+// For commercial licensing, see <https://www.artifex.com/> or contact
+// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
+// CA 94129, USA, for further information.
+
+#include "mupdf/fitz.h"
+#include "xps-imp.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+/*
+ * Parse the document structure / outline parts referenced from fixdoc relationships.
+ */
+
+static fz_outline *
+xps_lookup_last_outline_at_level(fz_context *ctx, xps_document *doc, fz_outline *node, int level, int target_level)
+{
+	while (node->next)
+		node = node->next;
+	if (level == target_level || !node->down)
+		return node;
+	return xps_lookup_last_outline_at_level(ctx, doc, node->down, level + 1, target_level);
+}
+
+static fz_outline *
+xps_parse_document_outline(fz_context *ctx, xps_document *doc, fz_xml *root)
+{
+	fz_xml *node;
+	fz_outline *head = NULL, *entry, *tail;
+	int last_level = 1, this_level;
+	for (node = fz_xml_down(root); node; node = fz_xml_next(node))
+	{
+		if (fz_xml_is_tag(node, "OutlineEntry"))
+		{
+			char *level = fz_xml_att(node, "OutlineLevel");
+			char *target = fz_xml_att(node, "OutlineTarget");
+			char *description = fz_xml_att(node, "Description");
+			if (!target || !description)
+				continue;
+
+			entry = fz_new_outline(ctx);
+			entry->title = Memento_label(fz_strdup(ctx, description), "outline_title");
+			entry->uri = Memento_label(fz_strdup(ctx, target), "outline_uri");
+			entry->page = xps_lookup_link_target(ctx, (fz_document*)doc, target).loc;
+			entry->down = NULL;
+			entry->next = NULL;
+
+			this_level = level ? atoi(level) : 1;
+
+			if (!head)
+			{
+				head = entry;
+			}
+			else
+			{
+				tail = xps_lookup_last_outline_at_level(ctx, doc, head, 1, this_level);
+				if (this_level > last_level)
+					tail->down = entry;
+				else
+					tail->next = entry;
+			}
+
+			last_level = this_level;
+		}
+	}
+	return head;
+}
+
+static fz_outline *
+xps_parse_document_structure(fz_context *ctx, xps_document *doc, fz_xml *root)
+{
+	fz_xml *node;
+	if (fz_xml_is_tag(root, "DocumentStructure"))
+	{
+		node = fz_xml_down(root);
+		if (node && fz_xml_is_tag(node, "DocumentStructure.Outline"))
+		{
+			node = fz_xml_down(node);
+			if (node && fz_xml_is_tag(node, "DocumentOutline"))
+				return xps_parse_document_outline(ctx, doc, node);
+		}
+	}
+	return NULL;
+}
+
+static fz_outline *
+xps_load_document_structure(fz_context *ctx, xps_document *doc, xps_fixdoc *fixdoc)
+{
+	xps_part *part;
+	fz_xml_doc *xml = NULL;
+	fz_outline *outline = NULL;
+
+	fz_var(xml);
+
+	part = xps_read_part(ctx, doc, fixdoc->outline);
+	fz_try(ctx)
+	{
+		xml = fz_parse_xml(ctx, part->data, 0);
+		outline = xps_parse_document_structure(ctx, doc, fz_xml_root(xml));
+	}
+	fz_always(ctx)
+	{
+		fz_drop_xml(ctx, xml);
+		xps_drop_part(ctx, doc, part);
+	}
+	fz_catch(ctx)
+	{
+		fz_rethrow(ctx);
+	}
+
+	return outline;
+}
+
+fz_outline *
+xps_load_outline(fz_context *ctx, fz_document *doc_)
+{
+	xps_document *doc = (xps_document*)doc_;
+	xps_fixdoc *fixdoc;
+	fz_outline *head = NULL, *tail, *outline = NULL;
+
+	for (fixdoc = doc->first_fixdoc; fixdoc; fixdoc = fixdoc->next)
+	{
+		if (fixdoc->outline)
+		{
+			fz_try(ctx)
+			{
+				outline = xps_load_document_structure(ctx, doc, fixdoc);
+			}
+			fz_catch(ctx)
+			{
+				fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
+				fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
+				fz_report_error(ctx);
+				outline = NULL;
+			}
+			if (!outline)
+				continue;
+
+			if (!head)
+				head = outline;
+			else
+			{
+				while (tail->next)
+					tail = tail->next;
+				tail->next = outline;
+			}
+			tail = outline;
+		}
+	}
+	return head;
+}