img: Add back support for SVG images, bypassing ImageMagick and simply passing the SVG through to the browser

SVG scaling by img directives has subtly changed; where before size=wxh
would preserve aspect ratio, this cannot be done when passing them through
and so specifying both a width and height can change the SVG's aspect
ratio.

(This patch looks significantly more complex than it was, because a large
block of code had to be indented.)

[smcv: drop trailing whitespace, fix some spelling]
master
Simon McVittie 2016-05-06 06:57:12 +01:00
parent 7ff6221ac9
commit 984ba82f1b
3 changed files with 108 additions and 91 deletions

View File

@ -23,8 +23,8 @@ sub getsetup () {
},
img_allowed_formats => {
type => "string",
default => [qw(jpeg png gif)],
description => "Image formats to process (jpeg, png, gif, pdf, svg or 'everything' to accept all)",
default => [qw(jpeg png gif svg)],
description => "Image formats to process (jpeg, png, gif, svg, pdf or 'everything' to accept all)",
# ImageMagick has had arbitrary code execution flaws,
# and the whole delegates mechanism is scary from
# that perspective
@ -36,7 +36,7 @@ sub getsetup () {
sub allowed {
my $format = shift;
my $allowed = $config{img_allowed_formats};
$allowed = ['jpeg', 'png'] unless defined $allowed && @$allowed;
$allowed = ['jpeg', 'png', 'gif', 'svg'] unless defined $allowed && @$allowed;
foreach my $a (@$allowed) {
return 1 if $a eq $format || $a eq 'everything';
@ -131,17 +131,7 @@ sub preprocess (@) {
error sprintf(gettext("%s image processing disabled in img_allowed_formats configuration"), $format ? $format : "\"$extension\"") unless allowed($format ? $format : "everything");
# Try harder to protect ImageMagick from itself
if ($format eq 'svg') {
my $content;
read($in, $content, 5) or error sprintf(gettext("failed to read %s: %s"), $file, $!);
# This is an over-simplification, but ?xml is the check that
# ImageMagick uses. We also accept <svg for the simplest
# possible SVGs.
if ($content !~ m/^(.\?xml|<svg)/is) {
error sprintf(gettext("\"%s\" does not seem to be a valid %s file"), $file, $format);
}
}
elsif ($magic) {
if (defined $magic) {
my $content;
read($in, $content, length $magic) or error sprintf(gettext("failed to read %s: %s"), $file, $!);
if ($magic ne $content) {
@ -149,94 +139,112 @@ sub preprocess (@) {
}
}
my $issvg = $base=~s/\.svg$/.png/i;
my $ispdf = $base=~s/\.pdf$/.png/i;
my $pagenumber = exists($params{pagenumber}) ? int($params{pagenumber}) : 0;
if ($pagenumber != 0) {
$base = "p$pagenumber-$base";
}
eval q{use Image::Magick};
error gettext("Image::Magick is not installed") if $@;
my $im = Image::Magick->new();
my $imglink;
my $imgdatalink;
my $r = $im->Read("$format:$srcfile\[$pagenumber]");
error sprintf(gettext("failed to read %s: %s"), $file, $r) if $r;
if (! defined $im->Get("width") || ! defined $im->Get("height")) {
error sprintf(gettext("failed to get dimensions of %s"), $file);
}
my ($dwidth, $dheight);
if ($params{size} eq 'full') {
$dwidth = $im->Get("width");
$dheight = $im->Get("height");
} else {
my ($w, $h) = ($params{size} =~ /^(\d*)x(\d*)$/);
error sprintf(gettext('wrong size format "%s" (should be WxH)'), $params{size})
unless (defined $w && defined $h &&
(length $w || length $h));
if ($im->Get("width") == 0 || $im->Get("height") == 0) {
($dwidth, $dheight)=(0, 0);
} elsif (! length $w || (length $h && $im->Get("height")*$w > $h * $im->Get("width"))) {
# using height because only height is given or ...
# because original image is more portrait than $w/$h
# ... slimness of $im > $h/w
# ... $im->Get("height")/$im->Get("width") > $h/$w
# ... $im->Get("height")*$w > $h * $im->Get("width")
$dheight=$h;
$dwidth=$h / $im->Get("height") * $im->Get("width");
} else { # (! length $h) or $w is what determines the resized size
$dwidth=$w;
$dheight=$w / $im->Get("width") * $im->Get("height");
}
my ($w, $h);
if ($params{size} ne 'full') {
($w, $h) = ($params{size} =~ /^(\d*)x(\d*)$/);
}
if ($dwidth < $im->Get("width") || $ispdf) {
# resize down, or resize to pixels at all
if ($format eq 'svg') {
# svg images are not scaled using ImageMagick because the
# pipeline is complex. Instead, the image size is simply
# set to the provided values.
#
# Aspect ratio will be preserved automatically when
# only a width or only a height is specified.
# When both are specified, aspect ratio will not be
# preserved.
$imglink = $file;
$dwidth = $w if length $w;
$dheight = $h if length $h;
}
else {
eval q{use Image::Magick};
error gettext("Image::Magick is not installed") if $@;
my $im = Image::Magick->new();
my $r = $im->Read("$format:$srcfile\[$pagenumber]");
error sprintf(gettext("failed to read %s: %s"), $file, $r) if $r;
my $outfile = "$config{destdir}/$dir/$params{size}-$base";
$imglink = "$dir/$params{size}-$base";
will_render($params{page}, $imglink);
if (-e $outfile && (-M $srcfile >= -M $outfile)) {
$im = Image::Magick->new;
$r = $im->Read($outfile);
error sprintf(gettext("failed to read %s: %s"), $outfile, $r) if $r;
if (! defined $im->Get("width") || ! defined $im->Get("height")) {
error sprintf(gettext("failed to get dimensions of %s"), $file);
}
else {
$r = $im->Resize(geometry => "${dwidth}x${dheight}");
error sprintf(gettext("failed to resize: %s"), $r) if $r;
$im->set(($issvg || $ispdf) ? (magick => 'png') : ());
my @blob = $im->ImageToBlob();
# don't actually write resized file in preview mode;
# rely on width and height settings
if (! $params{preview}) {
writefile($imglink, $config{destdir}, $blob[0], 1);
if (! length $w && ! length $h) {
$dwidth = $im->Get("width");
$dheight = $im->Get("height");
} else {
error sprintf(gettext('wrong size format "%s" (should be WxH)'), $params{size})
unless (defined $w && defined $h &&
(length $w || length $h));
if ($im->Get("width") == 0 || $im->Get("height") == 0) {
($dwidth, $dheight)=(0, 0);
} elsif (! length $w || (length $h && $im->Get("height")*$w > $h * $im->Get("width"))) {
# using height because only height is given or ...
# because original image is more portrait than $w/$h
# ... slimness of $im > $h/w
# ... $im->Get("height")/$im->Get("width") > $h/$w
# ... $im->Get("height")*$w > $h * $im->Get("width")
$dheight=$h;
$dwidth=$h / $im->Get("height") * $im->Get("width");
} else { # (! length $h) or $w is what determines the resized size
$dwidth=$w;
$dheight=$w / $im->Get("width") * $im->Get("height");
}
}
if ($dwidth < $im->Get("width") || $ispdf) {
# resize down, or resize to pixels at all
my $outfile = "$config{destdir}/$dir/$params{size}-$base";
$imglink = "$dir/$params{size}-$base";
will_render($params{page}, $imglink);
if (-e $outfile && (-M $srcfile >= -M $outfile)) {
$im = Image::Magick->new;
$r = $im->Read($outfile);
error sprintf(gettext("failed to read %s: %s"), $outfile, $r) if $r;
}
else {
eval q{use MIME::Base64};
error($@) if $@;
$imgdatalink = "data:image/".$im->Get("magick").";base64,".encode_base64($blob[0]);
$r = $im->Resize(geometry => "${dwidth}x${dheight}");
error sprintf(gettext("failed to resize: %s"), $r) if $r;
$im->set($ispdf ? (magick => 'png') : ());
my @blob = $im->ImageToBlob();
# don't actually write resized file in preview mode;
# rely on width and height settings
if (! $params{preview}) {
writefile($imglink, $config{destdir}, $blob[0], 1);
}
else {
eval q{use MIME::Base64};
error($@) if $@;
$imgdatalink = "data:image/".$im->Get("magick").";base64,".encode_base64($blob[0]);
}
}
# always get the true size of the resized image (it could be
# that imagemagick did its calculations differently)
$dwidth = $im->Get("width");
$dheight = $im->Get("height");
} else {
$imglink = $file;
}
# always get the true size of the resized image (it could be
# that imagemagick did its calculations differently)
$dwidth = $im->Get("width");
$dheight = $im->Get("height");
} else {
$imglink = $file;
}
if (! defined($dwidth) || ! defined($dheight)) {
error sprintf(gettext("failed to determine size of image %s"), $file)
if (! defined($dwidth) || ! defined($dheight)) {
error sprintf(gettext("failed to determine size of image %s"), $file)
}
}
my ($fileurl, $imgurl);
@ -255,10 +263,10 @@ sub preprocess (@) {
}
}
my $imgtag='<img src="'.$imgurl.
'" width="'.$dwidth.
'" height="'.$dheight.'"'.
$attrs.
my $imgtag='<img src="'.$imgurl.'"';
$imgtag.=' width="'.$dwidth.'"' if defined $dwidth;
$imgtag.=' height="'.$dheight.'"' if defined $dheight;
$imgtag.= $attrs.
(exists $params{align} && ! exists $params{caption} ? ' align="'.$params{align}.'"' : '').
' />';

7
debian/changelog vendored
View File

@ -16,6 +16,13 @@ ikiwiki (3.20160122) UNRELEASED; urgency=medium
* d/control: add Vcs-Browser
[ Joey Hess ]
* img: Add back support for SVG images, bypassing ImageMagick and
simply passing the SVG through to the browser, which is supported by all
commonly used browsers these days.
SVG scaling by img directives has subtly changed; where before
size=wxh would preserve aspect ratio, this cannot be done when passing
them through and so specifying both a width and height can change
the SVG's aspect ratio.
* loginselector: When only openid and emailauth are enabled, but
passwordauth is not, avoid showing a "Other" box which opens an
empty form.

View File

@ -12,11 +12,13 @@ providing a link to a full-size version.
The image file will be searched for using the same rules as used to find
the file pointed to by a [[ikiwiki/WikiLink]].
The `size` parameter is optional, defaulting to full size. Note that the
original image's aspect ratio is always preserved, even if this means
making the image smaller than the specified size. You can also specify only
the width or the height, and the other value will be calculated based on
it: "200x", "x200"
The `size` parameter is optional, defaulting to full size.
You can specify only the width or the height, and the other value will
be calculated based on it: "200x", "x200".
If you specify both the width and height, the original image's aspect
ratio will be preserved, even if this means making the image smaller
than the specified size. (However, this is not done for svg images.)
You can also pass `alt`, `title`, `class`, `align`, `id`, `hspace`, and
`vspace` parameters.