diff mupdf-source/platform/x11/prog_stream.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/platform/x11/prog_stream.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,278 @@
+// 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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "curl_stream.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/time.h>
+#include <unistd.h>
+#include <pthread.h>
+#endif
+
+/* File stream - progressive reading to simulate http download */
+
+typedef struct prog_state
+{
+	FILE *file;
+	int64_t length;
+	int64_t available; /* guarded by lock below */
+	int kbps;
+	int64_t start_time; /* in milliseconds since epoch */
+	unsigned char buffer[4096];
+	void (*on_data)(void*,int);
+	void *on_data_arg;
+
+	/* We assume either Windows threads or pthreads here. */
+#ifdef _WIN32
+	void *thread;
+	DWORD thread_id;
+	HANDLE mutex;
+#else
+	pthread_t thread;
+	pthread_mutex_t mutex;
+#endif
+} prog_state;
+
+static int64_t get_current_time(void)
+{
+#ifdef _WIN32
+	return (int)GetTickCount();
+#else
+	struct timeval now;
+	gettimeofday(&now, NULL);
+	return (int64_t)now.tv_sec*1000 + now.tv_usec/1000;
+#endif
+}
+
+#ifdef _WIN32
+static int locked;
+
+static void
+lock(prog_state *state)
+{
+	WaitForSingleObject(state->mutex, INFINITE);
+	assert(locked == 0);
+	locked = 1;
+}
+
+static void
+unlock(prog_state *state)
+{
+	assert(locked == 1);
+	locked = 0;
+	ReleaseMutex(state->mutex);
+}
+#else
+static void
+lock(prog_state *state)
+{
+	pthread_mutex_lock(&state->mutex);
+}
+
+static void
+unlock(prog_state *state)
+{
+	pthread_mutex_unlock(&state->mutex);
+}
+#endif
+
+static int next_prog(fz_context *ctx, fz_stream *stm, size_t len)
+{
+	prog_state *ps = (prog_state *)stm->state;
+	size_t n;
+
+	if (len > sizeof(ps->buffer))
+		len = sizeof(ps->buffer);
+
+	/* Simulate not all data available yet. */
+	lock(ps);
+	if (ps->available < ps->length)
+	{
+		if (stm->pos > ps->available || ps->available - stm->pos <= 0)
+		{
+			unlock(ps);
+			fz_throw(ctx, FZ_ERROR_TRYLATER, "Not enough data yet");
+		}
+		len = fz_mini(len, ps->available - stm->pos);
+	}
+	unlock(ps);
+
+	n = fread(ps->buffer, 1, len, ps->file);
+	stm->rp = ps->buffer;
+	stm->wp = ps->buffer + n;
+	stm->pos += (int64_t)n;
+
+	if (n < len)
+	{
+		if (ferror(ps->file))
+			fz_throw(ctx, FZ_ERROR_GENERIC, "prog read error: %s", strerror(errno));
+		if (n == 0)
+			return EOF;
+	}
+	return *stm->rp++;
+}
+
+static void seek_prog(fz_context *ctx, fz_stream *stm, int64_t offset, int whence)
+{
+	prog_state *ps = (prog_state *)stm->state;
+
+	if (whence == SEEK_END)
+	{
+		whence = SEEK_SET;
+		offset += ps->length;
+	}
+	else if (whence == SEEK_CUR)
+	{
+		whence = SEEK_SET;
+		offset += stm->pos;
+	}
+
+	if (fseek(ps->file, offset, whence) != 0)
+		fz_throw(ctx, FZ_ERROR_GENERIC, "cannot seek: %s", strerror(errno));
+	stm->pos = offset;
+	stm->wp = stm->rp;
+}
+
+static void close_prog(fz_context *ctx, void *state)
+{
+	prog_state *ps = (prog_state *)state;
+	int n = fclose(ps->file);
+	if (n < 0)
+		fz_warn(ctx, "cannot fclose: %s", strerror(errno));
+	fz_free(ctx, state);
+}
+
+static void fetcher_thread(prog_state *ps)
+{
+	int complete = 0;
+
+	ps->start_time = get_current_time();
+
+	while (!complete)
+	{
+		/* Wait a while. */
+#ifdef _WIN32
+		Sleep(200);
+#else
+		usleep(200000);
+#endif
+
+		lock(ps);
+
+		/* Simulate more data having arrived. */
+		if (ps->available < ps->length)
+		{
+			int64_t av = (get_current_time() - ps->start_time) * ps->kbps;
+			if (av > ps->length)
+				av = ps->length;
+			ps->available = av;
+		}
+		else
+		{
+			complete = 1;
+		}
+
+		unlock(ps);
+
+		/* Ping callback with new data. */
+		if (ps->on_data)
+			ps->on_data(ps->on_data_arg, complete);
+	}
+}
+
+#ifdef _WIN32
+static DWORD WINAPI win_thread(void *lparam)
+{
+	fetcher_thread((prog_state *)lparam);
+	return 0;
+}
+#else
+static void *pthread_thread(void *arg)
+{
+	fetcher_thread((prog_state *)arg);
+	return NULL;
+}
+#endif
+
+static fz_stream *
+fz_open_file_ptr_progressive(fz_context *ctx, FILE *file, int kbps, void (*on_data)(void*,int), void *opaque)
+{
+	fz_stream *stm;
+	prog_state *state;
+
+	state = fz_malloc_struct(ctx, prog_state);
+	state->file = file;
+	state->kbps = kbps;
+	state->available = 0;
+
+	state->on_data = on_data;
+	state->on_data_arg = opaque;
+
+	fseek(state->file, 0, SEEK_END);
+	state->length = ftell(state->file);
+	fseek(state->file, 0, SEEK_SET);
+
+#ifdef _WIN32
+	state->mutex = CreateMutex(NULL, FALSE, NULL);
+	if (state->mutex == NULL)
+		fz_throw(ctx, FZ_ERROR_GENERIC, "mutex creation failed");
+
+	state->thread = CreateThread(NULL, 0, win_thread, state, 0, &state->thread_id);
+	if (state->thread == NULL)
+		fz_throw(ctx, FZ_ERROR_GENERIC, "thread creation failed");
+#else
+	if (pthread_mutex_init(&state->mutex, NULL))
+		fz_throw(ctx, FZ_ERROR_GENERIC, "mutex creation failed");
+
+	if (pthread_create(&state->thread, NULL, pthread_thread, state))
+		fz_throw(ctx, FZ_ERROR_GENERIC, "thread creation failed");
+#endif
+
+	stm = fz_new_stream(ctx, state, next_prog, close_prog);
+	stm->progressive = 1;
+	stm->seek = seek_prog;
+
+	return stm;
+}
+
+fz_stream *
+fz_open_file_progressive(fz_context *ctx, const char *name, int kbps, void (*on_data)(void*,int), void *opaque)
+{
+	FILE *f;
+#ifdef _WIN32
+	f = fz_fopen_utf8(name, "rb");
+#else
+	f = fopen(name, "rb");
+#endif
+	if (f == NULL)
+		fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open %s", name);
+	return fz_open_file_ptr_progressive(ctx, f, kbps, on_data, opaque);
+}