blob: c4f8d24f23d8c31a792176ce497c01a422c8711f [file] [log] [blame]
/* poppler-media.cc: glib interface to MediaRendition
*
* Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
*
* 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 <cerrno>
#include <goo/gfile.h>
#include "poppler-media.h"
#include "poppler-private.h"
/**
* SECTION: poppler-media
* @short_description: Media
* @title: PopplerMedia
*/
typedef struct _PopplerMediaClass PopplerMediaClass;
struct _PopplerMedia
{
GObject parent_instance;
gchar *filename;
gboolean auto_play;
gboolean show_controls;
gfloat repeat_count;
gchar *mime_type;
Object stream;
};
struct _PopplerMediaClass
{
GObjectClass parent_class;
};
G_DEFINE_TYPE(PopplerMedia, poppler_media, G_TYPE_OBJECT)
static void poppler_media_finalize(GObject *object)
{
PopplerMedia *media = POPPLER_MEDIA(object);
if (media->filename) {
g_free(media->filename);
media->filename = nullptr;
}
if (media->mime_type) {
g_free(media->mime_type);
media->mime_type = nullptr;
}
media->stream = Object();
G_OBJECT_CLASS(poppler_media_parent_class)->finalize(object);
}
static void poppler_media_class_init(PopplerMediaClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
gobject_class->finalize = poppler_media_finalize;
}
static void poppler_media_init(PopplerMedia *media) { }
PopplerMedia *_poppler_media_new(const MediaRendition *poppler_media)
{
PopplerMedia *media;
g_assert(poppler_media != nullptr);
media = POPPLER_MEDIA(g_object_new(POPPLER_TYPE_MEDIA, nullptr));
if (poppler_media->getIsEmbedded()) {
const GooString *mime_type;
media->stream = poppler_media->getEmbbededStreamObject()->copy();
mime_type = poppler_media->getContentType();
if (mime_type) {
media->mime_type = g_strdup(mime_type->c_str());
}
} else {
media->filename = g_strdup(poppler_media->getFileName()->c_str());
}
const MediaParameters *mp = poppler_media->getBEParameters();
mp = mp ? mp : poppler_media->getMHParameters();
media->auto_play = mp ? mp->autoPlay : false;
media->show_controls = mp ? mp->showControls : false;
media->repeat_count = mp ? mp->repeatCount : 1.f;
return media;
}
/**
* poppler_media_get_filename:
* @poppler_media: a #PopplerMedia
*
* Returns the media clip filename, in case of non-embedded media. filename might be
* a local relative or absolute path or a URI
*
* Return value: a filename, return value is owned by #PopplerMedia and should not be freed
*
* Since: 0.14
*/
const gchar *poppler_media_get_filename(PopplerMedia *poppler_media)
{
g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), NULL);
g_return_val_if_fail(!poppler_media->stream.isStream(), NULL);
return poppler_media->filename;
}
/**
* poppler_media_is_embedded:
* @poppler_media: a #PopplerMedia
*
* Whether the media clip is embedded in the PDF. If the result is %TRUE, the embedded stream
* can be saved with poppler_media_save() or poppler_media_save_to_callback() function.
* If the result is %FALSE, the media clip filename can be retrieved with
* poppler_media_get_filename() function.
*
* Return value: %TRUE if media clip is embedded, %FALSE otherwise
*
* Since: 0.14
*/
gboolean poppler_media_is_embedded(PopplerMedia *poppler_media)
{
g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
return poppler_media->stream.isStream();
}
/**
* poppler_media_get_auto_play:
* @poppler_media: a #PopplerMedia
*
* Returns the auto-play parameter.
*
* Return value: %TRUE if media should auto-play, %FALSE otherwise
*
* Since: 20.04.0
*/
gboolean poppler_media_get_auto_play(PopplerMedia *poppler_media)
{
g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
return poppler_media->auto_play;
}
/**
* poppler_media_get_show_controls:
* @poppler_media: a #PopplerMedia
*
* Returns the show controls parameter.
*
* Return value: %TRUE if media should show controls, %FALSE otherwise
*
* Since: 20.04.0
*/
gboolean poppler_media_get_show_controls(PopplerMedia *poppler_media)
{
g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
return poppler_media->show_controls;
}
/**
* poppler_media_get_repeat_count:
* @poppler_media: a #PopplerMedia
*
* Returns the repeat count parameter.
*
* Return value: Repeat count parameter (float)
*
* Since: 20.04.0
*/
gfloat poppler_media_get_repeat_count(PopplerMedia *poppler_media)
{
g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
return poppler_media->repeat_count;
}
/**
* poppler_media_get_mime_type:
* @poppler_media: a #PopplerMedia
*
* Returns the media clip mime-type
*
* Return value: the mime-type, return value is owned by #PopplerMedia and should not be freed
*
* Since: 0.14
*/
const gchar *poppler_media_get_mime_type(PopplerMedia *poppler_media)
{
g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), NULL);
return poppler_media->mime_type;
}
static gboolean save_helper(const gchar *buf, gsize count, gpointer data, GError **error)
{
FILE *f = (FILE *)data;
gsize n;
n = fwrite(buf, 1, count, f);
if (n != count) {
int errsv = errno;
g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), "Error writing to media file: %s", g_strerror(errsv));
return FALSE;
}
return TRUE;
}
/**
* poppler_media_save:
* @poppler_media: a #PopplerMedia
* @filename: name of file to save
* @error: (allow-none): return location for error, or %NULL.
*
* Saves embedded stream of @poppler_media to a file indicated by @filename.
* If @error is set, %FALSE will be returned.
* Possible errors include those in the #G_FILE_ERROR domain
* and whatever the save function generates.
*
* Return value: %TRUE, if the file successfully saved
*
* Since: 0.14
*/
gboolean poppler_media_save(PopplerMedia *poppler_media, const char *filename, GError **error)
{
gboolean result;
FILE *f;
g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
g_return_val_if_fail(poppler_media->stream.isStream(), FALSE);
f = openFile(filename, "wb");
if (f == nullptr) {
gchar *display_name = g_filename_display_name(filename);
g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "Failed to open '%s' for writing: %s", display_name, g_strerror(errno));
g_free(display_name);
return FALSE;
}
result = poppler_media_save_to_callback(poppler_media, save_helper, f, error);
if (fclose(f) < 0) {
gchar *display_name = g_filename_display_name(filename);
g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "Failed to close '%s', all data may not have been saved: %s", display_name, g_strerror(errno));
g_free(display_name);
return FALSE;
}
return result;
}
#ifndef G_OS_WIN32
/**
* poppler_media_save_to_fd:
* @poppler_media: a #PopplerMedia
* @fd: a valid file descriptor open for writing
* @error: (allow-none): return location for error, or %NULL.
*
* Saves embedded stream of @poppler_media to a file referred to by @fd.
* If @error is set, %FALSE will be returned.
* Possible errors include those in the #G_FILE_ERROR domain
* and whatever the save function generates.
* Note that this function takes ownership of @fd; you must not operate on it
* again, nor close it.
*
* Return value: %TRUE, if the file successfully saved
*
* Since: 21.12.0
*/
gboolean poppler_media_save_to_fd(PopplerMedia *poppler_media, int fd, GError **error)
{
gboolean result;
FILE *f;
g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
g_return_val_if_fail(poppler_media->stream.isStream(), FALSE);
f = fdopen(fd, "wb");
if (f == nullptr) {
int errsv = errno;
g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), "Failed to open FD %d for writing: %s", fd, g_strerror(errsv));
close(fd);
return FALSE;
}
result = poppler_media_save_to_callback(poppler_media, save_helper, f, error);
if (fclose(f) < 0) {
int errsv = errno;
g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), "Failed to close FD %d, all data may not have been saved: %s", fd, g_strerror(errsv));
return FALSE;
}
return result;
}
#endif /* !G_OS_WIN32 */
#define BUF_SIZE 1024
/**
* poppler_media_save_to_callback:
* @poppler_media: a #PopplerMedia
* @save_func: (scope call): a function that is called to save each block of data that the save routine generates.
* @user_data: user data to pass to the save function.
* @error: (allow-none): return location for error, or %NULL.
*
* Saves embedded stream of @poppler_media by feeding the produced data to @save_func. Can be used
* when you want to store the media clip stream to something other than a file, such as
* an in-memory buffer or a socket. If @error is set, %FALSE will be
* returned. Possible errors include those in the #G_FILE_ERROR domain and
* whatever the save function generates.
*
* Return value: %TRUE, if the save successfully completed
*
* Since: 0.14
*/
gboolean poppler_media_save_to_callback(PopplerMedia *poppler_media, PopplerMediaSaveFunc save_func, gpointer user_data, GError **error)
{
Stream *stream;
gchar buf[BUF_SIZE];
int i;
gboolean eof_reached = FALSE;
g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE);
g_return_val_if_fail(poppler_media->stream.isStream(), FALSE);
stream = poppler_media->stream.getStream();
stream->reset();
do {
int data;
for (i = 0; i < BUF_SIZE; i++) {
data = stream->getChar();
if (data == EOF) {
eof_reached = TRUE;
break;
}
buf[i] = data;
}
if (i > 0) {
if (!(save_func)(buf, i, user_data, error)) {
stream->close();
return FALSE;
}
}
} while (!eof_reached);
stream->close();
return TRUE;
}