librsvg source for verification 2026-05-22
This commit is contained in:
1
gdk-pixbuf-loader/COPYING.LIB
Symbolic link
1
gdk-pixbuf-loader/COPYING.LIB
Symbolic link
@@ -0,0 +1 @@
|
||||
../COPYING.LIB
|
||||
30
gdk-pixbuf-loader/Cargo.toml
Normal file
30
gdk-pixbuf-loader/Cargo.toml
Normal file
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "pixbufloader-svg"
|
||||
version = "0.0.1"
|
||||
authors = ["Alberto Ruiz <aruiz@gnome.org>"]
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
gdk-pixbuf.workspace = true
|
||||
gio.workspace = true
|
||||
glib.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
system-deps.workspace = true
|
||||
|
||||
[package.metadata.system-deps]
|
||||
librsvg = { name = "librsvg-2.0-uninstalled", version = "2.57", fallback-names = [
|
||||
"librsvg-2.0",
|
||||
] }
|
||||
|
||||
[package.metadata.capi.header]
|
||||
enabled = false
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(check)'] }
|
||||
14
gdk-pixbuf-loader/build.rs
Normal file
14
gdk-pixbuf-loader/build.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
#[cfg(any(check, docsrs))]
|
||||
fn probe_system_deps() {
|
||||
// do not probe libraries since librsvg-2 is generated by Meson
|
||||
}
|
||||
|
||||
#[cfg(not(any(check, docsrs)))]
|
||||
fn probe_system_deps() {
|
||||
// do not probe libraries since the docs.rs environment doesn't have them
|
||||
system_deps::Config::new().probe().unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
probe_system_deps();
|
||||
}
|
||||
2
gdk-pixbuf-loader/gdk-pixbuf-loader.symbols
Normal file
2
gdk-pixbuf-loader/gdk-pixbuf-loader.symbols
Normal file
@@ -0,0 +1,2 @@
|
||||
fill_info
|
||||
fill_vtable
|
||||
219
gdk-pixbuf-loader/io-svg.c
Normal file
219
gdk-pixbuf-loader/io-svg.c
Normal file
@@ -0,0 +1,219 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
|
||||
/* GdkPixbuf library - SVG image loader
|
||||
*
|
||||
* Copyright (C) 2002 Matthias Clasen
|
||||
* Copyright (C) 2002-2004 Dom Lachowicz
|
||||
*
|
||||
* Authors: Matthias Clasen <maclas@gmx.de>
|
||||
* Dom Lachowicz <cinamod@hotmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define RSVG_DISABLE_DEPRECATION_WARNINGS
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <librsvg/rsvg.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
typedef struct {
|
||||
RsvgHandle *handle;
|
||||
|
||||
GdkPixbufModuleUpdatedFunc updated_func;
|
||||
GdkPixbufModulePreparedFunc prepared_func;
|
||||
GdkPixbufModuleSizeFunc size_func;
|
||||
|
||||
gboolean first_write;
|
||||
|
||||
gpointer user_data;
|
||||
} SvgContext;
|
||||
|
||||
G_MODULE_EXPORT void fill_vtable (GdkPixbufModule *module);
|
||||
G_MODULE_EXPORT void fill_info (GdkPixbufFormat *info);
|
||||
|
||||
enum {
|
||||
ERROR_WRITING = 1,
|
||||
ERROR_DISPLAYING_IMAGE
|
||||
} RsvgLoaderErrorReasons;
|
||||
|
||||
static void
|
||||
rsvg_propagate_error (GError ** err,
|
||||
const char * reason,
|
||||
gint code)
|
||||
{
|
||||
if (err) {
|
||||
*err = NULL;
|
||||
g_set_error (err, rsvg_error_quark (), code, "%s", reason);
|
||||
}
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gdk_pixbuf__svg_image_begin_load (GdkPixbufModuleSizeFunc size_func,
|
||||
GdkPixbufModulePreparedFunc prepared_func,
|
||||
GdkPixbufModuleUpdatedFunc updated_func,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
SvgContext *context = g_new0 (SvgContext, 1);
|
||||
|
||||
if (error)
|
||||
*error = NULL;
|
||||
|
||||
context->first_write = TRUE;
|
||||
context->size_func = size_func;
|
||||
|
||||
context->prepared_func = prepared_func;
|
||||
context->updated_func = updated_func;
|
||||
context->user_data = user_data;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static void
|
||||
emit_updated (SvgContext *context, GdkPixbuf *pixbuf)
|
||||
{
|
||||
if (context->updated_func != NULL)
|
||||
(* context->updated_func) (pixbuf,
|
||||
0, 0,
|
||||
gdk_pixbuf_get_width (pixbuf),
|
||||
gdk_pixbuf_get_height (pixbuf),
|
||||
context->user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
emit_prepared (SvgContext *context, GdkPixbuf *pixbuf)
|
||||
{
|
||||
if (context->prepared_func != NULL)
|
||||
(* context->prepared_func) (pixbuf, NULL, context->user_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_pixbuf__svg_image_load_increment (gpointer data,
|
||||
const guchar *buf, guint size,
|
||||
GError **error)
|
||||
{
|
||||
SvgContext *context = (SvgContext *)data;
|
||||
|
||||
if (error)
|
||||
*error = NULL;
|
||||
|
||||
if (context->first_write == TRUE) {
|
||||
context->first_write = FALSE;
|
||||
|
||||
context->handle = rsvg_handle_new ();
|
||||
|
||||
if (!context->handle) {
|
||||
rsvg_propagate_error (error, "Error displaying image", ERROR_DISPLAYING_IMAGE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rsvg_handle_set_size_callback (context->handle, context->size_func, context->user_data, NULL);
|
||||
}
|
||||
|
||||
if (!context->handle) {
|
||||
rsvg_propagate_error (error, "Error displaying image", ERROR_DISPLAYING_IMAGE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!rsvg_handle_write (context->handle, buf, size, error)) {
|
||||
g_clear_error(error);
|
||||
rsvg_propagate_error (error, "Error writing", ERROR_WRITING);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_pixbuf__svg_image_stop_load (gpointer data, GError **error)
|
||||
{
|
||||
SvgContext *context = (SvgContext *)data;
|
||||
GdkPixbuf *pixbuf;
|
||||
gboolean result = TRUE;
|
||||
|
||||
if (error)
|
||||
*error = NULL;
|
||||
|
||||
if (!context->handle) {
|
||||
rsvg_propagate_error (error, "Error displaying image", ERROR_DISPLAYING_IMAGE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!rsvg_handle_close (context->handle, error)) {
|
||||
g_object_unref (context->handle);
|
||||
g_free (context);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pixbuf = rsvg_handle_get_pixbuf (context->handle);
|
||||
|
||||
if (pixbuf != NULL) {
|
||||
emit_prepared (context, pixbuf);
|
||||
emit_updated (context, pixbuf);
|
||||
g_object_unref (pixbuf);
|
||||
}
|
||||
else {
|
||||
rsvg_propagate_error (error, "Error displaying image", ERROR_DISPLAYING_IMAGE);
|
||||
result = FALSE;
|
||||
}
|
||||
|
||||
g_object_unref (context->handle);
|
||||
g_free (context);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
fill_vtable (GdkPixbufModule *module)
|
||||
{
|
||||
module->begin_load = gdk_pixbuf__svg_image_begin_load;
|
||||
module->stop_load = gdk_pixbuf__svg_image_stop_load;
|
||||
module->load_increment = gdk_pixbuf__svg_image_load_increment;
|
||||
}
|
||||
|
||||
void
|
||||
fill_info (GdkPixbufFormat *info)
|
||||
{
|
||||
static const GdkPixbufModulePattern signature[] = {
|
||||
{ " <svg", "* ", 100 },
|
||||
{ " <!DOCTYPE svg", "* ", 100 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static const gchar *mime_types[] = { /* yes folks, i actually have run into all of these in the wild... */
|
||||
"image/svg+xml",
|
||||
"image/svg",
|
||||
"image/svg-xml",
|
||||
"image/vnd.adobe.svg+xml",
|
||||
"text/xml-svg",
|
||||
"image/svg+xml-compressed",
|
||||
NULL
|
||||
};
|
||||
static const gchar *extensions[] = {
|
||||
"svg",
|
||||
"svgz",
|
||||
"svg.gz",
|
||||
NULL
|
||||
};
|
||||
|
||||
info->name = "svg";
|
||||
info->signature = (GdkPixbufModulePattern *) signature;
|
||||
info->description = "Scalable Vector Graphics";
|
||||
info->mime_types = (gchar **) mime_types;
|
||||
info->extensions = (gchar **) extensions;
|
||||
info->flags = GDK_PIXBUF_FORMAT_SCALABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
|
||||
info->license = "LGPL";
|
||||
}
|
||||
4
gdk-pixbuf-loader/librsvg.thumbnailer.in
Normal file
4
gdk-pixbuf-loader/librsvg.thumbnailer.in
Normal file
@@ -0,0 +1,4 @@
|
||||
[Thumbnailer Entry]
|
||||
TryExec=@bindir@/gdk-pixbuf-thumbnailer
|
||||
Exec=@bindir@/gdk-pixbuf-thumbnailer -s %s %u %o
|
||||
MimeType=image/svg+xml;image/svg+xml-compressed;
|
||||
77
gdk-pixbuf-loader/meson.build
Normal file
77
gdk-pixbuf-loader/meson.build
Normal file
@@ -0,0 +1,77 @@
|
||||
pixbufloader_sources = files(
|
||||
'Cargo.toml',
|
||||
'src/lib.rs',
|
||||
)
|
||||
|
||||
prefix = get_option('prefix')
|
||||
|
||||
pixbufloader = custom_target(
|
||||
'pixbufloader-svg',
|
||||
build_by_default: true,
|
||||
output: ['@0@pixbufloader_svg.@1@'.format(cargo_dylib_prefix, ext_dynamic)],
|
||||
console: true,
|
||||
install: true,
|
||||
install_dir: pixbuf_dep.get_variable(pkgconfig: 'gdk_pixbuf_moduledir', pkgconfig_define: ['prefix', prefix]),
|
||||
depends: librsvg_rust_dep,
|
||||
depend_files: pixbufloader_sources,
|
||||
env: extra_env,
|
||||
command: [
|
||||
cargo_wrapper,
|
||||
cargo_wrapper_args,
|
||||
'--command=build',
|
||||
'--current-build-dir', '@OUTDIR@',
|
||||
'--current-source-dir', meson.current_source_dir(),
|
||||
'--packages', 'pixbufloader-svg',
|
||||
'--extension', ext_dynamic
|
||||
]
|
||||
)
|
||||
|
||||
pixbuf_thumbnailer = configure_file(
|
||||
input: 'librsvg.thumbnailer.in',
|
||||
output: 'librsvg.thumbnailer',
|
||||
configuration: {
|
||||
'bindir': get_option('prefix') / get_option('bindir')
|
||||
},
|
||||
install: true,
|
||||
install_dir: get_option('datadir') / 'thumbnailers'
|
||||
)
|
||||
|
||||
if meson.can_run_host_binaries()
|
||||
gdk_pixbuf_query_loaders = find_program(pixbuf_dep.get_variable(pkgconfig: 'gdk_pixbuf_query_loaders', default_value: 'gdk-pixbuf-query-loaders')).path()
|
||||
else
|
||||
gdk_pixbuf_query_loaders = pixbuf_dep.get_variable(pkgconfig: 'gdk_pixbuf_query_loaders', pkgconfig_define: ['prefix', prefix])
|
||||
endif
|
||||
|
||||
pixbufloader_svg_install_args = [
|
||||
'--gdk-pixbuf-moduledir',
|
||||
pixbuf_dep.get_variable(pkgconfig: 'gdk_pixbuf_moduledir', pkgconfig_define: ['prefix', prefix]),
|
||||
gdk_pixbuf_query_loaders,
|
||||
pixbuf_dep.get_variable(pkgconfig: 'gdk_pixbuf_cache_file', pkgconfig_define: ['prefix', prefix])
|
||||
]
|
||||
|
||||
# Tell people to run gdk-pixbuf-query-loaders manually for cross builds
|
||||
if not meson.can_run_host_binaries()
|
||||
pixbufloader_svg_install_args += '--show-cross-message'
|
||||
endif
|
||||
|
||||
meson.add_install_script(
|
||||
find_program('meson_install.py', native: true),
|
||||
pixbufloader_svg_install_args
|
||||
)
|
||||
|
||||
if build_tests
|
||||
test(
|
||||
'Rust tests (pixbufloader-svg)',
|
||||
cargo_wrapper,
|
||||
timeout: -1, # no timeout
|
||||
args: [
|
||||
cargo_wrapper_args,
|
||||
'--current-build-dir', meson.current_build_dir(),
|
||||
'--command=test',
|
||||
'--current-source-dir', meson.current_source_dir(),
|
||||
'--packages', 'pixbufloader-svg',
|
||||
],
|
||||
env: extra_env,
|
||||
depends: pixbufloader
|
||||
)
|
||||
endif
|
||||
34
gdk-pixbuf-loader/meson_install.py
Normal file
34
gdk-pixbuf-loader/meson_install.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
argparse = ArgumentParser('Deploy loaders.cache')
|
||||
|
||||
argparse.add_argument('--gdk-pixbuf-moduledir', type=Path)
|
||||
argparse.add_argument('gdk_pixbuf_queryloaders', type=Path)
|
||||
argparse.add_argument('gdk_pixbuf_cache_file', type=Path)
|
||||
argparse.add_argument('--show-cross-message', action='store_true')
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = argparse.parse_args()
|
||||
|
||||
cache_file: Path = args.gdk_pixbuf_cache_file
|
||||
|
||||
if args.show_cross_message or os.environ.get("DESTDIR"):
|
||||
print('*** Note: Please run gdk-pixbuf-queryloaders manually ' +
|
||||
'against the newly-built gdkpixbuf-svg loader', file=sys.stderr)
|
||||
else:
|
||||
env = os.environ.copy()
|
||||
env['GDK_PIXBUF_MODULEDIR'] = Path(args.gdk_pixbuf_moduledir).as_posix()
|
||||
with cache_file.open('w', encoding='utf-8') as f:
|
||||
subprocess.run(
|
||||
[Path(args.gdk_pixbuf_queryloaders).as_posix()],
|
||||
env=env,
|
||||
stdout=f,
|
||||
check=True
|
||||
)
|
||||
|
||||
378
gdk-pixbuf-loader/src/lib.rs
Normal file
378
gdk-pixbuf-loader/src/lib.rs
Normal file
@@ -0,0 +1,378 @@
|
||||
use std::ptr::null_mut;
|
||||
|
||||
use gdk_pixbuf::ffi::{
|
||||
GDK_PIXBUF_FORMAT_SCALABLE, GDK_PIXBUF_FORMAT_THREADSAFE, GdkPixbufFormat, GdkPixbufModule,
|
||||
GdkPixbufModulePattern, GdkPixbufModulePreparedFunc, GdkPixbufModuleSizeFunc,
|
||||
GdkPixbufModuleUpdatedFunc,
|
||||
};
|
||||
|
||||
use std::ffi::{c_char, c_int, c_uint};
|
||||
|
||||
use glib::Bytes;
|
||||
use glib::ffi::{GDestroyNotify, GError, gboolean, gpointer};
|
||||
use glib::prelude::*;
|
||||
use glib::translate::*;
|
||||
|
||||
use gio::MemoryInputStream;
|
||||
use gio::ffi::{GCancellable, GFile, GInputStream};
|
||||
use gio::prelude::*;
|
||||
|
||||
use glib::gstr;
|
||||
|
||||
type RSvgHandle = glib::gobject_ffi::GObject;
|
||||
|
||||
type RsvgSizeFunc = Option<
|
||||
unsafe extern "C" fn(inout_width: *mut c_int, inout_height: *mut c_int, user_data: gpointer),
|
||||
>;
|
||||
|
||||
#[link(name = "rsvg-2")]
|
||||
unsafe extern "C" {
|
||||
fn rsvg_handle_new_from_stream_sync(
|
||||
input_stream: *mut GInputStream,
|
||||
base_file: *mut GFile,
|
||||
flags: u32,
|
||||
cancellable: *mut GCancellable,
|
||||
error: *mut *mut GError,
|
||||
) -> *mut RSvgHandle;
|
||||
|
||||
fn rsvg_handle_get_pixbuf_and_error(
|
||||
handle: *mut RSvgHandle,
|
||||
error: *mut *mut GError,
|
||||
) -> *mut gdk_pixbuf::ffi::GdkPixbuf;
|
||||
|
||||
fn rsvg_handle_set_size_callback(
|
||||
handle: *mut RSvgHandle,
|
||||
size_func: RsvgSizeFunc,
|
||||
user_data: gpointer,
|
||||
destroy_notify: GDestroyNotify,
|
||||
);
|
||||
}
|
||||
|
||||
struct SvgContext {
|
||||
size_func: GdkPixbufModuleSizeFunc,
|
||||
prep_func: GdkPixbufModulePreparedFunc,
|
||||
update_func: GdkPixbufModuleUpdatedFunc,
|
||||
user_data: gpointer,
|
||||
stream: MemoryInputStream,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn begin_load(
|
||||
size_func: GdkPixbufModuleSizeFunc,
|
||||
prep_func: GdkPixbufModulePreparedFunc,
|
||||
update_func: GdkPixbufModuleUpdatedFunc,
|
||||
user_data: gpointer,
|
||||
error: *mut *mut GError,
|
||||
) -> gpointer {
|
||||
if !error.is_null() {
|
||||
*error = null_mut();
|
||||
}
|
||||
|
||||
let stream = MemoryInputStream::new();
|
||||
let ctx = Box::new(SvgContext {
|
||||
size_func,
|
||||
prep_func,
|
||||
update_func,
|
||||
user_data,
|
||||
stream,
|
||||
});
|
||||
|
||||
Box::into_raw(ctx) as gpointer
|
||||
}
|
||||
|
||||
unsafe extern "C" fn load_increment(
|
||||
user_data: gpointer,
|
||||
buffer: *const u8,
|
||||
size: c_uint,
|
||||
error: *mut *mut GError,
|
||||
) -> gboolean {
|
||||
if !error.is_null() {
|
||||
*error = null_mut();
|
||||
}
|
||||
|
||||
let ctx = user_data as *mut SvgContext;
|
||||
|
||||
let data = std::slice::from_raw_parts(buffer, size as usize);
|
||||
(*ctx).stream.add_bytes(&Bytes::from(data));
|
||||
true.into_glib()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn stop_load(user_data: gpointer, error: *mut *mut GError) -> gboolean {
|
||||
let ctx = Box::from_raw(user_data as *mut SvgContext);
|
||||
if !error.is_null() {
|
||||
*error = null_mut();
|
||||
}
|
||||
|
||||
let mut local_error = null_mut::<GError>();
|
||||
|
||||
let handle = {
|
||||
let raw_handle = rsvg_handle_new_from_stream_sync(
|
||||
ctx.stream.upcast_ref::<gio::InputStream>().to_glib_none().0,
|
||||
null_mut(), // base_file
|
||||
0,
|
||||
null_mut(), // cancellable
|
||||
&mut local_error,
|
||||
);
|
||||
if !local_error.is_null() {
|
||||
if !error.is_null() {
|
||||
*error = local_error;
|
||||
}
|
||||
return false.into_glib();
|
||||
}
|
||||
|
||||
glib::Object::from_glib_full(raw_handle)
|
||||
};
|
||||
|
||||
rsvg_handle_set_size_callback(handle.as_ptr(), ctx.size_func, ctx.user_data, None);
|
||||
|
||||
let pixbuf = {
|
||||
let p = rsvg_handle_get_pixbuf_and_error(handle.as_ptr(), &mut local_error);
|
||||
if !local_error.is_null() {
|
||||
if !error.is_null() {
|
||||
*error = local_error;
|
||||
}
|
||||
return false.into_glib();
|
||||
}
|
||||
|
||||
gdk_pixbuf::Pixbuf::from_glib_full(p)
|
||||
};
|
||||
|
||||
let w = pixbuf.width();
|
||||
let h = pixbuf.height();
|
||||
|
||||
if let Some(prep_func) = ctx.prep_func {
|
||||
prep_func(pixbuf.to_glib_none().0, null_mut(), ctx.user_data);
|
||||
}
|
||||
if let Some(update_func) = ctx.update_func {
|
||||
update_func(pixbuf.to_glib_none().0, 0, 0, w, h, ctx.user_data);
|
||||
}
|
||||
|
||||
true.into_glib()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn fill_vtable(module: &mut GdkPixbufModule) {
|
||||
module.begin_load = Some(begin_load);
|
||||
module.stop_load = Some(stop_load);
|
||||
module.load_increment = Some(load_increment);
|
||||
}
|
||||
|
||||
const SIGNATURE: [GdkPixbufModulePattern; 3] = [
|
||||
GdkPixbufModulePattern {
|
||||
prefix: gstr!(" <svg").as_ptr() as *mut c_char,
|
||||
mask: gstr!("* ").as_ptr() as *mut c_char,
|
||||
relevance: 100,
|
||||
},
|
||||
GdkPixbufModulePattern {
|
||||
prefix: gstr!(" <!DOCTYPE svg").as_ptr() as *mut c_char,
|
||||
mask: gstr!("* ").as_ptr() as *mut c_char,
|
||||
relevance: 100,
|
||||
},
|
||||
GdkPixbufModulePattern {
|
||||
prefix: null_mut(),
|
||||
mask: null_mut(),
|
||||
relevance: 0,
|
||||
},
|
||||
];
|
||||
|
||||
const MIME_TYPES: [*const c_char; 7] = [
|
||||
gstr!("image/svg+xml").as_ptr(),
|
||||
gstr!("image/svg").as_ptr(),
|
||||
gstr!("image/svg-xml").as_ptr(),
|
||||
gstr!("image/vnd.adobe.svg+xml").as_ptr(),
|
||||
gstr!("text/xml-svg").as_ptr(),
|
||||
gstr!("image/svg+xml-compressed").as_ptr(),
|
||||
std::ptr::null(),
|
||||
];
|
||||
|
||||
const EXTENSIONS: [*const c_char; 4] = [
|
||||
gstr!("svg").as_ptr(),
|
||||
gstr!("svgz").as_ptr(),
|
||||
gstr!("svg.gz").as_ptr(),
|
||||
std::ptr::null(),
|
||||
];
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn fill_info(info: &mut GdkPixbufFormat) {
|
||||
info.name = gstr!("svg").as_ptr() as *mut c_char;
|
||||
info.signature = SIGNATURE.as_ptr() as *mut GdkPixbufModulePattern;
|
||||
info.description = gstr!("Scalable Vector Graphics").as_ptr() as *mut c_char; //TODO: Gettext this
|
||||
info.mime_types = MIME_TYPES.as_ptr() as *mut *mut c_char;
|
||||
info.extensions = EXTENSIONS.as_ptr() as *mut *mut c_char;
|
||||
info.flags = GDK_PIXBUF_FORMAT_SCALABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
|
||||
info.license = gstr!("LGPL").as_ptr() as *mut c_char;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gdk_pixbuf::ffi::{
|
||||
GDK_PIXBUF_FORMAT_SCALABLE, GDK_PIXBUF_FORMAT_THREADSAFE, GdkPixbufFormat,
|
||||
};
|
||||
use glib::translate::IntoGlib;
|
||||
|
||||
use crate::{EXTENSIONS, MIME_TYPES};
|
||||
use std::ffi::c_char;
|
||||
use std::ptr::null_mut;
|
||||
|
||||
fn pb_format_new() -> GdkPixbufFormat {
|
||||
let mut info = super::GdkPixbufFormat {
|
||||
name: null_mut(),
|
||||
signature: null_mut(),
|
||||
description: null_mut(),
|
||||
mime_types: null_mut(),
|
||||
extensions: null_mut(),
|
||||
flags: 0,
|
||||
license: null_mut(),
|
||||
disabled: false.into_glib(),
|
||||
domain: null_mut(),
|
||||
};
|
||||
|
||||
super::fill_info(&mut info);
|
||||
info
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fill_info() {
|
||||
let info = pb_format_new();
|
||||
|
||||
assert!(!info.name.is_null());
|
||||
assert!(!info.signature.is_null());
|
||||
assert!(!info.description.is_null());
|
||||
assert!(!info.mime_types.is_null());
|
||||
assert!(!info.extensions.is_null());
|
||||
assert_eq!(
|
||||
info.flags,
|
||||
GDK_PIXBUF_FORMAT_SCALABLE | GDK_PIXBUF_FORMAT_THREADSAFE
|
||||
);
|
||||
assert!(!info.license.is_null());
|
||||
}
|
||||
|
||||
fn check_null_terminated_arr_cstrings(arr: &[*const c_char]) {
|
||||
let n_strings = arr
|
||||
.iter()
|
||||
.filter(|e| !e.is_null())
|
||||
.map(|e| {
|
||||
if !e.is_null() {
|
||||
assert!(!unsafe { std::ffi::CStr::from_ptr(*e) }.is_empty())
|
||||
}
|
||||
})
|
||||
.count(); // Count all non_null items
|
||||
|
||||
// Ensure last item is null and is the only null item
|
||||
assert_eq!(n_strings, arr.len() - 1);
|
||||
assert!(arr.last().unwrap().is_null());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extensions_bounds() {
|
||||
check_null_terminated_arr_cstrings(&EXTENSIONS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mime_bounds() {
|
||||
check_null_terminated_arr_cstrings(&MIME_TYPES)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signature() {
|
||||
let info = pb_format_new();
|
||||
unsafe {
|
||||
for i in 0..2 {
|
||||
let ptr = info.signature.offset(i);
|
||||
if i == 2 {
|
||||
assert!((*ptr).prefix.is_null());
|
||||
continue;
|
||||
} else {
|
||||
assert!(!(*ptr).prefix.is_null());
|
||||
if !(*ptr).mask.is_null() {
|
||||
// Mask can be null
|
||||
let prefix = std::ffi::CStr::from_ptr((*ptr).prefix).to_bytes();
|
||||
let mask = std::ffi::CStr::from_ptr((*ptr).mask).to_bytes();
|
||||
assert_eq!(prefix.len(), mask.len());
|
||||
}
|
||||
// Relevance must be 0 to 100
|
||||
assert!((*ptr).relevance >= 0);
|
||||
assert!((*ptr).relevance <= 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SVG_DATA: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="100px"
|
||||
height="150px"
|
||||
viewBox="0 0 26.458333 26.458333"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<rect
|
||||
style="fill:#aa1144;stroke-width:0.0344347"
|
||||
width="26.458332"
|
||||
height="39.6875"
|
||||
x="4.691162e-07"
|
||||
y="-6.6145835"
|
||||
id="rect2" />
|
||||
</svg>
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn minimal_svg() {
|
||||
unsafe extern "C" fn prep_cb(
|
||||
pb: *mut gdk_pixbuf::ffi::GdkPixbuf,
|
||||
pba: *mut gdk_pixbuf::ffi::GdkPixbufAnimation,
|
||||
user_data: *mut std::ffi::c_void,
|
||||
) {
|
||||
assert!(user_data.is_null());
|
||||
assert!(pba.is_null());
|
||||
|
||||
let w = gdk_pixbuf::ffi::gdk_pixbuf_get_width(pb);
|
||||
let h = gdk_pixbuf::ffi::gdk_pixbuf_get_height(pb);
|
||||
let stride = gdk_pixbuf::ffi::gdk_pixbuf_get_rowstride(pb);
|
||||
assert_eq!(w, 100);
|
||||
assert_eq!(h, 150);
|
||||
|
||||
let pixels = gdk_pixbuf::ffi::gdk_pixbuf_get_pixels(pb);
|
||||
|
||||
// Upper left pixel #aa1144ff
|
||||
assert_eq!(*pixels, 0xaa);
|
||||
assert_eq!(*pixels.offset(1), 0x11);
|
||||
assert_eq!(*pixels.offset(2), 0x44);
|
||||
assert_eq!(*pixels.offset(3), 0xff);
|
||||
|
||||
// Bottom left pixel
|
||||
assert_eq!(*pixels.offset((stride * (h - 1)) as isize), 0xaa);
|
||||
assert_eq!(*pixels.offset((stride * (h - 1)) as isize + 1), 0x11);
|
||||
assert_eq!(*pixels.offset((stride * (h - 1)) as isize + 2), 0x44);
|
||||
assert_eq!(*pixels.offset((stride * (h - 1)) as isize + 3), 0xff);
|
||||
|
||||
// Bottom right pixel
|
||||
assert_eq!(
|
||||
*pixels.offset((stride * (h - 1)) as isize + (w as isize - 1) * 4),
|
||||
0xaa
|
||||
);
|
||||
assert_eq!(
|
||||
*pixels.offset((stride * (h - 1)) as isize + (w as isize - 1) * 4 + 1),
|
||||
0x11
|
||||
);
|
||||
assert_eq!(
|
||||
*pixels.offset((stride * (h - 1)) as isize + (w as isize - 1) * 4 + 2),
|
||||
0x44
|
||||
);
|
||||
assert_eq!(
|
||||
*pixels.offset((stride * (h - 1)) as isize + (w as isize - 1) * 4 + 3),
|
||||
0xff
|
||||
);
|
||||
}
|
||||
unsafe {
|
||||
let ctx = crate::begin_load(None, Some(prep_cb), None, null_mut(), null_mut());
|
||||
assert_ne!(ctx, null_mut());
|
||||
|
||||
let inc =
|
||||
crate::load_increment(ctx, SVG_DATA.as_ptr(), SVG_DATA.len() as u32, null_mut());
|
||||
assert_ne!(inc, 0);
|
||||
|
||||
crate::stop_load(ctx, null_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
84
gdk-pixbuf-loader/test.c
Normal file
84
gdk-pixbuf-loader/test.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright © 2011 Christian Persch
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
GOptionContext *context;
|
||||
int width = -1;
|
||||
int height = -1;
|
||||
char **args = NULL;
|
||||
GdkPixbuf *pixbuf = NULL;
|
||||
GError *error = NULL;
|
||||
int ret = 1;
|
||||
|
||||
GOptionEntry options_table[] = {
|
||||
{ "width", 'w', 0, G_OPTION_ARG_INT, &width,
|
||||
"width [optional; defaults to the SVG's width]", "WIDTH" },
|
||||
{ "height", 'h', 0, G_OPTION_ARG_INT, &height,
|
||||
"height [optional; defaults to the SVG's height]", "HEIGHT" },
|
||||
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, "INPUT-FILE OUTPUT-FILE" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
/* Use the locally built rsvg loader, not the system one */
|
||||
g_setenv ("GDK_PIXBUF_MODULE_FILE", "./gdk-pixbuf.loaders", TRUE);
|
||||
|
||||
context = g_option_context_new ("- Pixbuf Test Loader");
|
||||
g_option_context_add_main_entries (context, options_table, NULL);
|
||||
g_option_context_parse (context, &argc, &argv, &error);
|
||||
g_option_context_free (context);
|
||||
if (error)
|
||||
goto done;
|
||||
|
||||
if (args == NULL || g_strv_length (args) != 2) {
|
||||
g_printerr ("Need to specify input and output filenames\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
pixbuf = gdk_pixbuf_new_from_file_at_size (args[0], width, height, &error);
|
||||
if (pixbuf == NULL)
|
||||
goto done;
|
||||
|
||||
if (!gdk_pixbuf_save (pixbuf, args[1], "png", &error, NULL))
|
||||
goto done;
|
||||
|
||||
/* Success! */
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
|
||||
if (error) {
|
||||
g_printerr ("Error: %s\n", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
if (pixbuf)
|
||||
g_object_unref (pixbuf);
|
||||
|
||||
g_strfreev (args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user