blob: 51a6f95f9729f40527743b18caaf43310be9cdd3 [file] [log] [blame]
/* poppler-attachment.cc: glib wrapper for poppler
* Copyright (C) 2006, Red Hat, Inc.
*
* 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.h"
#include "poppler-private.h"
#include <new>
/**
* SECTION:poppler-attachment
* @short_description: Attachments
* @title: PopplerAttachment
*/
/* FIXME: We need to add gettext support sometime */
#define _(x) (x)
struct PopplerAttachmentPrivate
{
Object obj_stream {};
GDateTime *mtime;
GDateTime *ctime;
};
static void poppler_attachment_finalize(GObject *obj);
G_DEFINE_TYPE_WITH_PRIVATE(PopplerAttachment, poppler_attachment, G_TYPE_OBJECT)
#define GET_PRIVATE(obj) ((PopplerAttachmentPrivate *)poppler_attachment_get_instance_private(obj))
static void poppler_attachment_init(PopplerAttachment *attachment)
{
void *place;
place = GET_PRIVATE(attachment);
new (place) PopplerAttachmentPrivate();
}
static void poppler_attachment_class_init(PopplerAttachmentClass *klass)
{
G_OBJECT_CLASS(klass)->finalize = poppler_attachment_finalize;
}
static void poppler_attachment_finalize(GObject *obj)
{
PopplerAttachment *attachment;
PopplerAttachmentPrivate *priv;
attachment = (PopplerAttachment *)obj;
priv = GET_PRIVATE(attachment);
if (attachment->name) {
g_free(attachment->name);
}
attachment->name = nullptr;
if (attachment->description) {
g_free(attachment->description);
}
attachment->description = nullptr;
if (attachment->checksum) {
g_string_free(attachment->checksum, TRUE);
}
attachment->checksum = nullptr;
g_clear_pointer(&priv->mtime, g_date_time_unref);
g_clear_pointer(&priv->ctime, g_date_time_unref);
priv->~PopplerAttachmentPrivate();
G_OBJECT_CLASS(poppler_attachment_parent_class)->finalize(obj);
}
/* Public functions */
PopplerAttachment *_poppler_attachment_new(FileSpec *emb_file)
{
PopplerAttachment *attachment;
PopplerAttachmentPrivate *priv;
EmbFile *embFile;
g_assert(emb_file != nullptr);
attachment = (PopplerAttachment *)g_object_new(POPPLER_TYPE_ATTACHMENT, nullptr);
priv = GET_PRIVATE(attachment);
if (emb_file->getFileName()) {
attachment->name = _poppler_goo_string_to_utf8(emb_file->getFileName());
}
if (emb_file->getDescription()) {
attachment->description = _poppler_goo_string_to_utf8(emb_file->getDescription());
}
embFile = emb_file->getEmbeddedFile();
if (embFile != nullptr && embFile->streamObject()->isStream()) {
attachment->size = embFile->size();
if (embFile->createDate()) {
priv->ctime = _poppler_convert_pdf_date_to_date_time(embFile->createDate());
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
/* This will overflow on dates from after 2038. This field is
* deprecated, only kept for backward compatibility. */
attachment->ctime = (GTime)g_date_time_to_unix(priv->ctime);
G_GNUC_END_IGNORE_DEPRECATIONS
}
if (embFile->modDate()) {
priv->mtime = _poppler_convert_pdf_date_to_date_time(embFile->modDate());
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
/* This will overflow on dates from after 2038. This field is
* deprecated, only kept for backward compatibility. */
attachment->mtime = (GTime)g_date_time_to_unix(priv->mtime);
G_GNUC_END_IGNORE_DEPRECATIONS
}
if (embFile->checksum() && embFile->checksum()->getLength() > 0) {
attachment->checksum = g_string_new_len(embFile->checksum()->c_str(), embFile->checksum()->getLength());
}
priv->obj_stream = embFile->streamObject()->copy();
} else {
g_warning("Missing stream object for embedded file");
g_clear_object(&attachment);
}
return attachment;
}
/**
* poppler_attachment_get_checksum:
* @attachment: a #PopplerAttachment
*
* Returns: The attachment's checksum.
*
* Since: 20.09.0
*/
const GString *poppler_attachment_get_checksum(PopplerAttachment *attachment)
{
return attachment->checksum;
}
/**
* poppler_attachment_get_ctime:
* @attachment: a #PopplerAttachment
*
* Returns: (transfer none) (nullable): The attachment's creation date and time
* as a #GDateTime, or %NULL if the creation date and time is not available.
*
* Since: 20.09.0
*/
GDateTime *poppler_attachment_get_ctime(PopplerAttachment *attachment)
{
return GET_PRIVATE(attachment)->ctime;
}
/**
* poppler_attachment_get_description:
* @attachment: a #PopplerAttachment
*
* Returns: The attachment's descriptive text.
*
* Since: 20.09.0
*/
const gchar *poppler_attachment_get_description(PopplerAttachment *attachment)
{
return attachment->description;
}
/**
* poppler_attachment_get_mtime:
* @attachment: a #PopplerAttachment
*
* Returns: (transfer none) (nullable): The attachment's modification date and
* time as a #GDateTime, or %NULL if the modification date and time is not
* available.
*
* Since: 20.09.0
*/
GDateTime *poppler_attachment_get_mtime(PopplerAttachment *attachment)
{
return GET_PRIVATE(attachment)->mtime;
}
/**
* poppler_attachment_get_name:
* @attachment: a #PopplerAttachment
*
* Returns: The attachment's name.
*
* Since: 20.09.0
*/
const gchar *poppler_attachment_get_name(PopplerAttachment *attachment)
{
return attachment->name;
}
/**
* poppler_attachment_get_size:
* @attachment: a #PopplerAttachment
*
* Returns: The attachment's size.
*
* Since: 20.09.0
*/
gsize poppler_attachment_get_size(PopplerAttachment *attachment)
{
return attachment->size;
}
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 image file: %s"), g_strerror(errsv));
return FALSE;
}
return TRUE;
}
/**
* poppler_attachment_save:
* @attachment: A #PopplerAttachment.
* @filename: name of file to save
* @error: (allow-none): return location for error, or %NULL.
*
* Saves @attachment 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
**/
gboolean poppler_attachment_save(PopplerAttachment *attachment, const char *filename, GError **error)
{
gboolean result;
FILE *f;
g_return_val_if_fail(POPPLER_IS_ATTACHMENT(attachment), 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_attachment_save_to_callback(attachment, 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_attachment_save_to_fd:
* @attachment: A #PopplerAttachment.
* @fd: a valid file descriptor open for writing
* @error: (allow-none): return location for error, or %NULL.
*
* Saves @attachment 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_attachment_save_to_fd(PopplerAttachment *attachment, int fd, GError **error)
{
gboolean result;
FILE *f;
g_return_val_if_fail(POPPLER_IS_ATTACHMENT(attachment), FALSE);
g_return_val_if_fail(fd != -1, FALSE);
g_return_val_if_fail(error == nullptr || *error == nullptr, 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_attachment_save_to_callback(attachment, 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_attachment_save_to_callback:
* @attachment: A #PopplerAttachment.
* @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 @attachment by feeding the produced data to @save_func. Can be used
* when you want to store the attachment 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
**/
gboolean poppler_attachment_save_to_callback(PopplerAttachment *attachment, PopplerAttachmentSaveFunc save_func, gpointer user_data, GError **error)
{
PopplerAttachmentPrivate *priv;
Stream *stream;
gchar buf[BUF_SIZE];
int i;
gboolean eof_reached = FALSE;
g_return_val_if_fail(POPPLER_IS_ATTACHMENT(attachment), FALSE);
priv = GET_PRIVATE(attachment);
stream = priv->obj_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)) {
return FALSE;
}
}
} while (!eof_reached);
return TRUE;
}