Message ID | 20210804110106.592-1-hofmann@leo-andres.de |
---|---|
State | Accepted |
Commit | fd7a02263035341f85fec624d176fd469290b063 |
Headers |
Return-Path: <development-bounces@lists.ipfire.org> Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) server-digest SHA384 client-signature ECDSA (P-384) client-digest SHA384) (Client CN "mail01.haj.ipfire.org", Issuer "R3" (verified OK)) by web04.haj.ipfire.org (Postfix) with ESMTPS id 4GfphW0fSTz3xGN for <patchwork@web04.haj.ipfire.org>; Wed, 4 Aug 2021 11:01:31 +0000 (UTC) Received: from mail02.haj.ipfire.org (mail02.haj.ipfire.org [172.28.1.201]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) client-signature ECDSA (P-384)) (Client CN "mail02.haj.ipfire.org", Issuer "R3" (verified OK)) by mail01.ipfire.org (Postfix) with ESMTPS id 4GfphT3KH1z171; Wed, 4 Aug 2021 11:01:29 +0000 (UTC) Received: from mail02.haj.ipfire.org (localhost [127.0.0.1]) by mail02.haj.ipfire.org (Postfix) with ESMTP id 4GfphT1gtJz2y0p; Wed, 4 Aug 2021 11:01:29 +0000 (UTC) Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) server-digest SHA384 client-signature ECDSA (P-384) client-digest SHA384) (Client CN "mail01.haj.ipfire.org", Issuer "R3" (verified OK)) by mail02.haj.ipfire.org (Postfix) with ESMTPS id 4GfphR1pg2z2xTN for <development@lists.ipfire.org>; Wed, 4 Aug 2021 11:01:27 +0000 (UTC) Received: from arche.uberspace.de (arche.uberspace.de [185.26.156.147]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mail01.ipfire.org (Postfix) with ESMTPS id 4GfphK55ntz14j for <development@lists.ipfire.org>; Wed, 4 Aug 2021 11:01:21 +0000 (UTC) Received: (qmail 4605 invoked from network); 4 Aug 2021 11:01:19 -0000 Received: from localhost (HELO localhost) (127.0.0.1) by arche.uberspace.de with SMTP; 4 Aug 2021 11:01:19 -0000 From: Leo-Andres Hofmann <hofmann@leo-andres.de> To: development@lists.ipfire.org Subject: [PATCH] rrd graphs: Switch to SVG output format Date: Wed, 4 Aug 2021 13:01:06 +0200 Message-Id: <20210804110106.592-1-hofmann@leo-andres.de> X-Mailer: git-send-email 2.27.0.windows.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit ARC-Authentication-Results: i=1; mail01.ipfire.org; dkim=none; spf=pass (mail01.ipfire.org: domain of hofmann@leo-andres.de designates 185.26.156.147 as permitted sender) smtp.mailfrom=hofmann@leo-andres.de ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=lists.ipfire.org; s=202003rsa; t=1628074882; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=qdhOM2EtHbZCLUiNUYcE6SwLoKHHl+lB3Wtpvhj65Qs=; b=kl5fHhReCGZ6JPsFIBhfk8a1Pg56fGVrM63aEqr3rZ4Yu5eKHlla9P0Lk7Laj5rFND1ERh 76ImA96xqrw1dWY7WYsC1hvGty6hSwvHjQUzSDL5AIu4QpTKNuc5SbsFStNXy8N2CNk4+t ziRuUELh4N1CyNc6nqmtdklVOG0WFuhIbAMlAt/wJlFDWybIy/PfSC0NBENG62cVwXE/p4 BvcmnkCmYAUncHcBo6KjTOFFoivpo70WPae60AEHNZCleYsyXj4SEknksrnWp3Va1/FSdm B88gQtSqCl6HlhgZsMQxcLU84WXNfkJdExnYwtBndktunOtYHhx9gdm1tofuCQ== ARC-Seal: i=1; s=202003rsa; d=lists.ipfire.org; t=1628074882; a=rsa-sha256; cv=none; b=U/Jx+johl+jZ6RHshzAHy5hmmhwgLEZzeweARAZMYnEhsk/X/Mj4il/plhhWoyK1GY4h6v kgD4ut7GyPBjqHNz9/a+oBLRcCpxG2ld5SRvGOl/sqN7eZEW6jaRNAi0V3VrmD9Q4yvC9z Om1zSIbpRDFuxlWSe2bcOuzwIXVCCt3rb8LPfDPEYwhVsmovTsSadPX2zHFZeE/zQkoz1N LPDgRHtdTAO8/YBsjDlmIF+d+Wja9CeiyjBZJdtThM9CRzajXdPZb/TEQdxnnmT+wIzLOX 7HFQ8SwNhhdA2gZsmZPOOZzNNLVBzso0Hi6GfepjlcybuUvu+MYJCw9HIrtkgA== Authentication-Results: mail01.ipfire.org; dkim=none; spf=pass (mail01.ipfire.org: domain of hofmann@leo-andres.de designates 185.26.156.147 as permitted sender) smtp.mailfrom=hofmann@leo-andres.de; dmarc=none X-Rspamd-Server: mail01.haj.ipfire.org X-Spamd-Result: default: False [1.43 / 11.00]; MX_INVALID(0.50)[]; R_MISSING_CHARSET(2.50)[]; IP_REPUTATION_HAM(-0.01)[asn: 205766(0.00), country: DE(-0.01), ip: 185.26.156.147(0.00)]; TO_DN_NONE(0.00)[]; BROKEN_CONTENT_TYPE(1.50)[]; R_SPF_ALLOW(-0.20)[+mx]; ARC_SIGNED(0.00)[lists.ipfire.org:s=202003rsa:i=1]; FROM_EQ_ENVFROM(0.00)[]; R_DKIM_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; ASN(0.00)[asn:205766, ipnet:185.26.156.0/24, country:DE]; RCVD_TLS_LAST(0.00)[]; BAYES_HAM(-3.00)[99.99%]; ARC_NA(0.00)[]; FROM_HAS_DN(0.00)[]; SPF_REPUTATION_HAM(-0.76)[-0.76268748642855]; TO_MATCH_ENVRCPT_ALL(0.00)[]; MIME_GOOD(-0.10)[text/plain]; DMARC_NA(0.00)[leo-andres.de]; RCPT_COUNT_ONE(0.00)[1]; MID_CONTAINS_FROM(1.00)[]; RCVD_COUNT_TWO(0.00)[2] X-Rspamd-Queue-Id: 4GfphK55ntz14j X-BeenThere: development@lists.ipfire.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: IPFire development talk <development.lists.ipfire.org> List-Unsubscribe: <https://lists.ipfire.org/mailman/options/development>, <mailto:development-request@lists.ipfire.org?subject=unsubscribe> List-Archive: <http://lists.ipfire.org/pipermail/development/> List-Post: <mailto:development@lists.ipfire.org> List-Help: <mailto:development-request@lists.ipfire.org?subject=help> List-Subscribe: <https://lists.ipfire.org/mailman/listinfo/development>, <mailto:development-request@lists.ipfire.org?subject=subscribe> Errors-To: development-bounces@lists.ipfire.org Sender: "Development" <development-bounces@lists.ipfire.org> |
Series |
rrd graphs: Switch to SVG output format
|
|
Commit Message
Leo-Andres Hofmann
Aug. 4, 2021, 11:01 a.m. UTC
The vector graphics can be scaled without becoming blurred.
Signed-off-by: Leo-Andres Hofmann <hofmann@leo-andres.de>
---
Hi,
Michael suggested switching to the SVG format for the graphs:
https://lists.ipfire.org/pipermail/development/2021-July/010804.html
I have tested this with firefox and chrome, the graphs and error message look fine to me.
If someone knows a good SVG validator, I would be happy to get the link :)
Regards,
Leo
config/cfgroot/graphs.pl | 2 +-
html/cgi-bin/getrrdimage.cgi | 87 +++++++++++++++++++++---------------
2 files changed, 52 insertions(+), 37 deletions(-)
Comments
No problems found at a quick test with Firefox. Did not check errors, yet. Reviewed-by: Bernhard Bitsch <bbitsch@ipfire.org> Tested-by: Bernhard Bitsch <bbitsch@ipfire.org> Am 04.08.2021 um 13:01 schrieb Leo-Andres Hofmann: > The vector graphics can be scaled without becoming blurred. > > Signed-off-by: Leo-Andres Hofmann <hofmann@leo-andres.de> > --- > > Hi, > > Michael suggested switching to the SVG format for the graphs: > https://lists.ipfire.org/pipermail/development/2021-July/010804.html > > I have tested this with firefox and chrome, the graphs and error message look fine to me. > If someone knows a good SVG validator, I would be happy to get the link :) > > Regards, > Leo > > config/cfgroot/graphs.pl | 2 +- > html/cgi-bin/getrrdimage.cgi | 87 +++++++++++++++++++++--------------- > 2 files changed, 52 insertions(+), 37 deletions(-) > > diff --git a/config/cfgroot/graphs.pl b/config/cfgroot/graphs.pl > index 441d4c483..02341eb45 100644 > --- a/config/cfgroot/graphs.pl > +++ b/config/cfgroot/graphs.pl > @@ -40,7 +40,7 @@ my $ERROR; > > my @GRAPH_ARGS = ( > # Output format > - "--imgformat", "PNG", > + "--imgformat", "SVG", > > # No border > "--border", "0", > diff --git a/html/cgi-bin/getrrdimage.cgi b/html/cgi-bin/getrrdimage.cgi > index c08247c57..92f8b585d 100644 > --- a/html/cgi-bin/getrrdimage.cgi > +++ b/html/cgi-bin/getrrdimage.cgi > @@ -21,8 +21,7 @@ > > use strict; > use URI; > -use GD; > -use GD::Text::Wrap; > +use Text::Wrap; > use experimental 'smartmatch'; > > # debugging > @@ -52,7 +51,7 @@ my $range = lc $query{'range'}; # lower case > # Check parameters > unless(($origin =~ /^\w+?\.cgi$/) && ($graph =~ /^[\w\-.,; ]+?$/) && ($range ~~ @Graphs::time_ranges)) { > # Send HTTP headers > - _start_png_output(); > + _start_svg_output(); > > _print_error("URL parameters missing or malformed."); > exit; > @@ -76,7 +75,7 @@ unless(($origin ~~ @supported_origins) || ($origin eq "getrrdimage.cgi")) { > > ### Create graphs ### > # Send HTTP headers > -_start_png_output(); > +_start_svg_output(); > > # Graphs are first grouped by their origin. > # This is because some graph categories require special parameter handling. > @@ -204,46 +203,62 @@ if($graphstatus) { > > ###--- Internal functions ---### > > -# Send HTTP headers and switch to binary output > +# Send HTTP headers > # (don't print any non-image data to STDOUT afterwards) > -sub _start_png_output { > +sub _start_svg_output { > print "Cache-Control: no-cache, no-store\n"; > - print "Content-Type: image/png\n"; > + print "Content-Type: image/svg+xml\n"; > print "\n"; # End of HTTP headers > - binmode(STDOUT); > } > > -# Print error message to PNG output > +# Print error message to SVG output > sub _print_error { > my ($message) = @_; > - $message = "- Error -\n \n$message"; > - > - # Create new image with the same size as a graph > - my $img = GD::Image->new($Graphs::image_size{'width'}, $Graphs::image_size{'height'}); > - $img->interlaced('true'); > - > - # Basic colors > - my $color_background = $img->colorAllocate(255, 255, 255); > - my $color_border = $img->colorAllocate(255, 0, 0); > - my $color_text = $img->colorAllocate(0, 0, 0); > > - # Background and border > - $img->setThickness(2); > - $img->filledRectangle(0, 0, $img->width, $img->height, $color_background); > - $img->rectangle(10, 10, $img->width - 10, $img->height - 10, $color_border); > - > - # Draw message with line-wrap > - my $textbox = GD::Text::Wrap->new($img, > - text => $message, > - width => ($img->width - 50), > - color => $color_text, > - align => 'center', > - line_space => 5, > - preserve_nl => 1 > + # Prepare image options > + my %img = ( > + 'width' => $Graphs::image_size{'width'}, > + 'height' => $Graphs::image_size{'height'}, > + 'text_center' => int($Graphs::image_size{'width'} / 2), > + 'line_height' => 20, > + 'font_family' => "DejaVu Sans, Helvetica, sans-serif" # Matching the IPFire theme > ); > - $textbox->set_font(gdLargeFont); > - $textbox->draw(25, 25); > > - # Get PNG output > - print $img->png; > + # Line-wrap message to fit image (adjust to font width if necessary) > + local($Text::Wrap::columns) = int($img{'width'} / 10); > + $message = wrap('', '', $message); > + > + # Create new image with fixed background and border > + print <<END > +<?xml version="1.0" encoding="UTF-8"?> > +<svg width="$img{'width'}px" height="$img{'height'}px" viewBox="0 0 $img{'width'} $img{'height'}" version="1.1" xmlns="http://www.w3.org/2000/svg"> > + <!-- Background --> > + <rect width="100%" height="100%" fill="white"/> > + <rect width="100%" height="100%" fill="none" stroke="red" stroke-width="2" transform="scale(0.95)" transform-origin="center"/> > + <!-- Message --> > + <text x="$img{'text_center'}" y="50" font-size="20" font-family="$img{'font_family'}" text-anchor="middle">- $Lang::tr{'error'} -</text> > + <text x="$img{'text_center'}" y="90" font-size="14" font-family="$img{'font_family'}" text-anchor="middle"> > +END > +; > + > + # Print message lines > + my $shift_y = 0; # Shifts text along y-axis > + foreach my $line (split(/\n/, $message)) { > + if($line ne "") { # Don't create empty tspan elements > + print <<END > + <tspan x="$img{'text_center'}" dy="$shift_y">$line</tspan> > +END > +; > + $shift_y = $img{'line_height'}; > + } else { # Create blank lines by summing up unused line height > + $shift_y += $img{'line_height'}; > + } > + } > + > + # Finish SVG output > + print <<END > + </text> > +</svg> > +END > +; > } >
Cool. Thank you. Did you test if the SVG files can be generated quicker than PNG? -Michael > On 4 Aug 2021, at 13:01, Leo-Andres Hofmann <hofmann@leo-andres.de> wrote: > > The vector graphics can be scaled without becoming blurred. > > Signed-off-by: Leo-Andres Hofmann <hofmann@leo-andres.de> > --- > > Hi, > > Michael suggested switching to the SVG format for the graphs: > https://lists.ipfire.org/pipermail/development/2021-July/010804.html > > I have tested this with firefox and chrome, the graphs and error message look fine to me. > If someone knows a good SVG validator, I would be happy to get the link :) > > Regards, > Leo > > config/cfgroot/graphs.pl | 2 +- > html/cgi-bin/getrrdimage.cgi | 87 +++++++++++++++++++++--------------- > 2 files changed, 52 insertions(+), 37 deletions(-) > > diff --git a/config/cfgroot/graphs.pl b/config/cfgroot/graphs.pl > index 441d4c483..02341eb45 100644 > --- a/config/cfgroot/graphs.pl > +++ b/config/cfgroot/graphs.pl > @@ -40,7 +40,7 @@ my $ERROR; > > my @GRAPH_ARGS = ( > # Output format > - "--imgformat", "PNG", > + "--imgformat", "SVG", > > # No border > "--border", "0", > diff --git a/html/cgi-bin/getrrdimage.cgi b/html/cgi-bin/getrrdimage.cgi > index c08247c57..92f8b585d 100644 > --- a/html/cgi-bin/getrrdimage.cgi > +++ b/html/cgi-bin/getrrdimage.cgi > @@ -21,8 +21,7 @@ > > use strict; > use URI; > -use GD; > -use GD::Text::Wrap; > +use Text::Wrap; > use experimental 'smartmatch'; > > # debugging > @@ -52,7 +51,7 @@ my $range = lc $query{'range'}; # lower case > # Check parameters > unless(($origin =~ /^\w+?\.cgi$/) && ($graph =~ /^[\w\-.,; ]+?$/) && ($range ~~ @Graphs::time_ranges)) { > # Send HTTP headers > - _start_png_output(); > + _start_svg_output(); > > _print_error("URL parameters missing or malformed."); > exit; > @@ -76,7 +75,7 @@ unless(($origin ~~ @supported_origins) || ($origin eq "getrrdimage.cgi")) { > > ### Create graphs ### > # Send HTTP headers > -_start_png_output(); > +_start_svg_output(); > > # Graphs are first grouped by their origin. > # This is because some graph categories require special parameter handling. > @@ -204,46 +203,62 @@ if($graphstatus) { > > ###--- Internal functions ---### > > -# Send HTTP headers and switch to binary output > +# Send HTTP headers > # (don't print any non-image data to STDOUT afterwards) > -sub _start_png_output { > +sub _start_svg_output { > print "Cache-Control: no-cache, no-store\n"; > - print "Content-Type: image/png\n"; > + print "Content-Type: image/svg+xml\n"; > print "\n"; # End of HTTP headers > - binmode(STDOUT); > } > > -# Print error message to PNG output > +# Print error message to SVG output > sub _print_error { > my ($message) = @_; > - $message = "- Error -\n \n$message"; > - > - # Create new image with the same size as a graph > - my $img = GD::Image->new($Graphs::image_size{'width'}, $Graphs::image_size{'height'}); > - $img->interlaced('true'); > - > - # Basic colors > - my $color_background = $img->colorAllocate(255, 255, 255); > - my $color_border = $img->colorAllocate(255, 0, 0); > - my $color_text = $img->colorAllocate(0, 0, 0); > > - # Background and border > - $img->setThickness(2); > - $img->filledRectangle(0, 0, $img->width, $img->height, $color_background); > - $img->rectangle(10, 10, $img->width - 10, $img->height - 10, $color_border); > - > - # Draw message with line-wrap > - my $textbox = GD::Text::Wrap->new($img, > - text => $message, > - width => ($img->width - 50), > - color => $color_text, > - align => 'center', > - line_space => 5, > - preserve_nl => 1 > + # Prepare image options > + my %img = ( > + 'width' => $Graphs::image_size{'width'}, > + 'height' => $Graphs::image_size{'height'}, > + 'text_center' => int($Graphs::image_size{'width'} / 2), > + 'line_height' => 20, > + 'font_family' => "DejaVu Sans, Helvetica, sans-serif" # Matching the IPFire theme > ); > - $textbox->set_font(gdLargeFont); > - $textbox->draw(25, 25); > > - # Get PNG output > - print $img->png; > + # Line-wrap message to fit image (adjust to font width if necessary) > + local($Text::Wrap::columns) = int($img{'width'} / 10); > + $message = wrap('', '', $message); > + > + # Create new image with fixed background and border > + print <<END > +<?xml version="1.0" encoding="UTF-8"?> > +<svg width="$img{'width'}px" height="$img{'height'}px" viewBox="0 0 $img{'width'} $img{'height'}" version="1.1" xmlns="http://www.w3.org/2000/svg"> > + <!-- Background --> > + <rect width="100%" height="100%" fill="white"/> > + <rect width="100%" height="100%" fill="none" stroke="red" stroke-width="2" transform="scale(0.95)" transform-origin="center"/> > + <!-- Message --> > + <text x="$img{'text_center'}" y="50" font-size="20" font-family="$img{'font_family'}" text-anchor="middle">- $Lang::tr{'error'} -</text> > + <text x="$img{'text_center'}" y="90" font-size="14" font-family="$img{'font_family'}" text-anchor="middle"> > +END > +; > + > + # Print message lines > + my $shift_y = 0; # Shifts text along y-axis > + foreach my $line (split(/\n/, $message)) { > + if($line ne "") { # Don't create empty tspan elements > + print <<END > + <tspan x="$img{'text_center'}" dy="$shift_y">$line</tspan> > +END > +; > + $shift_y = $img{'line_height'}; > + } else { # Create blank lines by summing up unused line height > + $shift_y += $img{'line_height'}; > + } > + } > + > + # Finish SVG output > + print <<END > + </text> > +</svg> > +END > +; > } > -- > 2.27.0.windows.1 >
Am 04.08.2021 um 16:40 schrieb Michael Tremer: > Cool. Thank you. > > Did you test if the SVG files can be generated quicker than PNG? > Haven't measured in detail, but the time for display of the graph seems equal to the png pictures ( on my small ALIX ). But I didn't check the timings of sampling of the data also. - Bernhard > -Michael > >> On 4 Aug 2021, at 13:01, Leo-Andres Hofmann <hofmann@leo-andres.de> wrote: >> >> The vector graphics can be scaled without becoming blurred. >> >> Signed-off-by: Leo-Andres Hofmann <hofmann@leo-andres.de> >> --- >> >> Hi, >> >> Michael suggested switching to the SVG format for the graphs: >> https://lists.ipfire.org/pipermail/development/2021-July/010804.html >> >> I have tested this with firefox and chrome, the graphs and error message look fine to me. >> If someone knows a good SVG validator, I would be happy to get the link :) >> >> Regards, >> Leo >> >> config/cfgroot/graphs.pl | 2 +- >> html/cgi-bin/getrrdimage.cgi | 87 +++++++++++++++++++++--------------- >> 2 files changed, 52 insertions(+), 37 deletions(-) >> >> diff --git a/config/cfgroot/graphs.pl b/config/cfgroot/graphs.pl >> index 441d4c483..02341eb45 100644 >> --- a/config/cfgroot/graphs.pl >> +++ b/config/cfgroot/graphs.pl >> @@ -40,7 +40,7 @@ my $ERROR; >> >> my @GRAPH_ARGS = ( >> # Output format >> - "--imgformat", "PNG", >> + "--imgformat", "SVG", >> >> # No border >> "--border", "0", >> diff --git a/html/cgi-bin/getrrdimage.cgi b/html/cgi-bin/getrrdimage.cgi >> index c08247c57..92f8b585d 100644 >> --- a/html/cgi-bin/getrrdimage.cgi >> +++ b/html/cgi-bin/getrrdimage.cgi >> @@ -21,8 +21,7 @@ >> >> use strict; >> use URI; >> -use GD; >> -use GD::Text::Wrap; >> +use Text::Wrap; >> use experimental 'smartmatch'; >> >> # debugging >> @@ -52,7 +51,7 @@ my $range = lc $query{'range'}; # lower case >> # Check parameters >> unless(($origin =~ /^\w+?\.cgi$/) && ($graph =~ /^[\w\-.,; ]+?$/) && ($range ~~ @Graphs::time_ranges)) { >> # Send HTTP headers >> - _start_png_output(); >> + _start_svg_output(); >> >> _print_error("URL parameters missing or malformed."); >> exit; >> @@ -76,7 +75,7 @@ unless(($origin ~~ @supported_origins) || ($origin eq "getrrdimage.cgi")) { >> >> ### Create graphs ### >> # Send HTTP headers >> -_start_png_output(); >> +_start_svg_output(); >> >> # Graphs are first grouped by their origin. >> # This is because some graph categories require special parameter handling. >> @@ -204,46 +203,62 @@ if($graphstatus) { >> >> ###--- Internal functions ---### >> >> -# Send HTTP headers and switch to binary output >> +# Send HTTP headers >> # (don't print any non-image data to STDOUT afterwards) >> -sub _start_png_output { >> +sub _start_svg_output { >> print "Cache-Control: no-cache, no-store\n"; >> - print "Content-Type: image/png\n"; >> + print "Content-Type: image/svg+xml\n"; >> print "\n"; # End of HTTP headers >> - binmode(STDOUT); >> } >> >> -# Print error message to PNG output >> +# Print error message to SVG output >> sub _print_error { >> my ($message) = @_; >> - $message = "- Error -\n \n$message"; >> - >> - # Create new image with the same size as a graph >> - my $img = GD::Image->new($Graphs::image_size{'width'}, $Graphs::image_size{'height'}); >> - $img->interlaced('true'); >> - >> - # Basic colors >> - my $color_background = $img->colorAllocate(255, 255, 255); >> - my $color_border = $img->colorAllocate(255, 0, 0); >> - my $color_text = $img->colorAllocate(0, 0, 0); >> >> - # Background and border >> - $img->setThickness(2); >> - $img->filledRectangle(0, 0, $img->width, $img->height, $color_background); >> - $img->rectangle(10, 10, $img->width - 10, $img->height - 10, $color_border); >> - >> - # Draw message with line-wrap >> - my $textbox = GD::Text::Wrap->new($img, >> - text => $message, >> - width => ($img->width - 50), >> - color => $color_text, >> - align => 'center', >> - line_space => 5, >> - preserve_nl => 1 >> + # Prepare image options >> + my %img = ( >> + 'width' => $Graphs::image_size{'width'}, >> + 'height' => $Graphs::image_size{'height'}, >> + 'text_center' => int($Graphs::image_size{'width'} / 2), >> + 'line_height' => 20, >> + 'font_family' => "DejaVu Sans, Helvetica, sans-serif" # Matching the IPFire theme >> ); >> - $textbox->set_font(gdLargeFont); >> - $textbox->draw(25, 25); >> >> - # Get PNG output >> - print $img->png; >> + # Line-wrap message to fit image (adjust to font width if necessary) >> + local($Text::Wrap::columns) = int($img{'width'} / 10); >> + $message = wrap('', '', $message); >> + >> + # Create new image with fixed background and border >> + print <<END >> +<?xml version="1.0" encoding="UTF-8"?> >> +<svg width="$img{'width'}px" height="$img{'height'}px" viewBox="0 0 $img{'width'} $img{'height'}" version="1.1" xmlns="http://www.w3.org/2000/svg"> >> + <!-- Background --> >> + <rect width="100%" height="100%" fill="white"/> >> + <rect width="100%" height="100%" fill="none" stroke="red" stroke-width="2" transform="scale(0.95)" transform-origin="center"/> >> + <!-- Message --> >> + <text x="$img{'text_center'}" y="50" font-size="20" font-family="$img{'font_family'}" text-anchor="middle">- $Lang::tr{'error'} -</text> >> + <text x="$img{'text_center'}" y="90" font-size="14" font-family="$img{'font_family'}" text-anchor="middle"> >> +END >> +; >> + >> + # Print message lines >> + my $shift_y = 0; # Shifts text along y-axis >> + foreach my $line (split(/\n/, $message)) { >> + if($line ne "") { # Don't create empty tspan elements >> + print <<END >> + <tspan x="$img{'text_center'}" dy="$shift_y">$line</tspan> >> +END >> +; >> + $shift_y = $img{'line_height'}; >> + } else { # Create blank lines by summing up unused line height >> + $shift_y += $img{'line_height'}; >> + } >> + } >> + >> + # Finish SVG output >> + print <<END >> + </text> >> +</svg> >> +END >> +; >> } >> -- >> 2.27.0.windows.1 >> >
I used the Firefox developer tools to check the loading times of the images. On average, the SVG image is generated 10ms faster: "HTTP Wait" for PNG: ~126ms, for SVG: ~115ms (My test system: Core 159 on Proxmox VM/Xeon 3,3GHz) Regards, Leo Am 04.08.2021 um 16:40 schrieb Michael Tremer: > Cool. Thank you. > > Did you test if the SVG files can be generated quicker than PNG? > > -Michael > >> On 4 Aug 2021, at 13:01, Leo-Andres Hofmann <hofmann@leo-andres.de> wrote: >> >> The vector graphics can be scaled without becoming blurred. >> >> Signed-off-by: Leo-Andres Hofmann <hofmann@leo-andres.de> >> --- >> >> Hi, >> >> Michael suggested switching to the SVG format for the graphs: >> https://lists.ipfire.org/pipermail/development/2021-July/010804.html >> >> I have tested this with firefox and chrome, the graphs and error message look fine to me. >> If someone knows a good SVG validator, I would be happy to get the link :) >> >> Regards, >> Leo >> >> config/cfgroot/graphs.pl | 2 +- >> html/cgi-bin/getrrdimage.cgi | 87 +++++++++++++++++++++--------------- >> 2 files changed, 52 insertions(+), 37 deletions(-) >> >> diff --git a/config/cfgroot/graphs.pl b/config/cfgroot/graphs.pl >> index 441d4c483..02341eb45 100644 >> --- a/config/cfgroot/graphs.pl >> +++ b/config/cfgroot/graphs.pl >> @@ -40,7 +40,7 @@ my $ERROR; >> >> my @GRAPH_ARGS = ( >> # Output format >> - "--imgformat", "PNG", >> + "--imgformat", "SVG", >> >> # No border >> "--border", "0", >> diff --git a/html/cgi-bin/getrrdimage.cgi b/html/cgi-bin/getrrdimage.cgi >> index c08247c57..92f8b585d 100644 >> --- a/html/cgi-bin/getrrdimage.cgi >> +++ b/html/cgi-bin/getrrdimage.cgi >> @@ -21,8 +21,7 @@ >> >> use strict; >> use URI; >> -use GD; >> -use GD::Text::Wrap; >> +use Text::Wrap; >> use experimental 'smartmatch'; >> >> # debugging >> @@ -52,7 +51,7 @@ my $range = lc $query{'range'}; # lower case >> # Check parameters >> unless(($origin =~ /^\w+?\.cgi$/) && ($graph =~ /^[\w\-.,; ]+?$/) && ($range ~~ @Graphs::time_ranges)) { >> # Send HTTP headers >> - _start_png_output(); >> + _start_svg_output(); >> >> _print_error("URL parameters missing or malformed."); >> exit; >> @@ -76,7 +75,7 @@ unless(($origin ~~ @supported_origins) || ($origin eq "getrrdimage.cgi")) { >> >> ### Create graphs ### >> # Send HTTP headers >> -_start_png_output(); >> +_start_svg_output(); >> >> # Graphs are first grouped by their origin. >> # This is because some graph categories require special parameter handling. >> @@ -204,46 +203,62 @@ if($graphstatus) { >> >> ###--- Internal functions ---### >> >> -# Send HTTP headers and switch to binary output >> +# Send HTTP headers >> # (don't print any non-image data to STDOUT afterwards) >> -sub _start_png_output { >> +sub _start_svg_output { >> print "Cache-Control: no-cache, no-store\n"; >> - print "Content-Type: image/png\n"; >> + print "Content-Type: image/svg+xml\n"; >> print "\n"; # End of HTTP headers >> - binmode(STDOUT); >> } >> >> -# Print error message to PNG output >> +# Print error message to SVG output >> sub _print_error { >> my ($message) = @_; >> - $message = "- Error -\n \n$message"; >> - >> - # Create new image with the same size as a graph >> - my $img = GD::Image->new($Graphs::image_size{'width'}, $Graphs::image_size{'height'}); >> - $img->interlaced('true'); >> - >> - # Basic colors >> - my $color_background = $img->colorAllocate(255, 255, 255); >> - my $color_border = $img->colorAllocate(255, 0, 0); >> - my $color_text = $img->colorAllocate(0, 0, 0); >> >> - # Background and border >> - $img->setThickness(2); >> - $img->filledRectangle(0, 0, $img->width, $img->height, $color_background); >> - $img->rectangle(10, 10, $img->width - 10, $img->height - 10, $color_border); >> - >> - # Draw message with line-wrap >> - my $textbox = GD::Text::Wrap->new($img, >> - text => $message, >> - width => ($img->width - 50), >> - color => $color_text, >> - align => 'center', >> - line_space => 5, >> - preserve_nl => 1 >> + # Prepare image options >> + my %img = ( >> + 'width' => $Graphs::image_size{'width'}, >> + 'height' => $Graphs::image_size{'height'}, >> + 'text_center' => int($Graphs::image_size{'width'} / 2), >> + 'line_height' => 20, >> + 'font_family' => "DejaVu Sans, Helvetica, sans-serif" # Matching the IPFire theme >> ); >> - $textbox->set_font(gdLargeFont); >> - $textbox->draw(25, 25); >> >> - # Get PNG output >> - print $img->png; >> + # Line-wrap message to fit image (adjust to font width if necessary) >> + local($Text::Wrap::columns) = int($img{'width'} / 10); >> + $message = wrap('', '', $message); >> + >> + # Create new image with fixed background and border >> + print <<END >> +<?xml version="1.0" encoding="UTF-8"?> >> +<svg width="$img{'width'}px" height="$img{'height'}px" viewBox="0 0 $img{'width'} $img{'height'}" version="1.1" xmlns="http://www.w3.org/2000/svg"> >> + <!-- Background --> >> + <rect width="100%" height="100%" fill="white"/> >> + <rect width="100%" height="100%" fill="none" stroke="red" stroke-width="2" transform="scale(0.95)" transform-origin="center"/> >> + <!-- Message --> >> + <text x="$img{'text_center'}" y="50" font-size="20" font-family="$img{'font_family'}" text-anchor="middle">- $Lang::tr{'error'} -</text> >> + <text x="$img{'text_center'}" y="90" font-size="14" font-family="$img{'font_family'}" text-anchor="middle"> >> +END >> +; >> + >> + # Print message lines >> + my $shift_y = 0; # Shifts text along y-axis >> + foreach my $line (split(/\n/, $message)) { >> + if($line ne "") { # Don't create empty tspan elements >> + print <<END >> + <tspan x="$img{'text_center'}" dy="$shift_y">$line</tspan> >> +END >> +; >> + $shift_y = $img{'line_height'}; >> + } else { # Create blank lines by summing up unused line height >> + $shift_y += $img{'line_height'}; >> + } >> + } >> + >> + # Finish SVG output >> + print <<END >> + </text> >> +</svg> >> +END >> +; >> } >> -- >> 2.27.0.windows.1 >>
Hello Leo, > On 5 Aug 2021, at 10:40, Leo Hofmann <hofmann@leo-andres.de> wrote: > > I used the Firefox developer tools to check the loading times of the images. > > On average, the SVG image is generated 10ms faster: "HTTP Wait" for PNG: ~126ms, for SVG: ~115ms > (My test system: Core 159 on Proxmox VM/Xeon 3,3GHz) Okay. That seems to be a good improvement then. Around 10%. Cool. -Michael > Regards, > Leo > > Am 04.08.2021 um 16:40 schrieb Michael Tremer: >> Cool. Thank you. >> >> Did you test if the SVG files can be generated quicker than PNG? >> >> -Michael >> >>> On 4 Aug 2021, at 13:01, Leo-Andres Hofmann <hofmann@leo-andres.de> wrote: >>> >>> The vector graphics can be scaled without becoming blurred. >>> >>> Signed-off-by: Leo-Andres Hofmann <hofmann@leo-andres.de> >>> --- >>> >>> Hi, >>> >>> Michael suggested switching to the SVG format for the graphs: >>> https://lists.ipfire.org/pipermail/development/2021-July/010804.html >>> >>> I have tested this with firefox and chrome, the graphs and error message look fine to me. >>> If someone knows a good SVG validator, I would be happy to get the link :) >>> >>> Regards, >>> Leo >>> >>> config/cfgroot/graphs.pl | 2 +- >>> html/cgi-bin/getrrdimage.cgi | 87 +++++++++++++++++++++--------------- >>> 2 files changed, 52 insertions(+), 37 deletions(-) >>> >>> diff --git a/config/cfgroot/graphs.pl b/config/cfgroot/graphs.pl >>> index 441d4c483..02341eb45 100644 >>> --- a/config/cfgroot/graphs.pl >>> +++ b/config/cfgroot/graphs.pl >>> @@ -40,7 +40,7 @@ my $ERROR; >>> >>> my @GRAPH_ARGS = ( >>> # Output format >>> - "--imgformat", "PNG", >>> + "--imgformat", "SVG", >>> >>> # No border >>> "--border", "0", >>> diff --git a/html/cgi-bin/getrrdimage.cgi b/html/cgi-bin/getrrdimage.cgi >>> index c08247c57..92f8b585d 100644 >>> --- a/html/cgi-bin/getrrdimage.cgi >>> +++ b/html/cgi-bin/getrrdimage.cgi >>> @@ -21,8 +21,7 @@ >>> >>> use strict; >>> use URI; >>> -use GD; >>> -use GD::Text::Wrap; >>> +use Text::Wrap; >>> use experimental 'smartmatch'; >>> >>> # debugging >>> @@ -52,7 +51,7 @@ my $range = lc $query{'range'}; # lower case >>> # Check parameters >>> unless(($origin =~ /^\w+?\.cgi$/) && ($graph =~ /^[\w\-.,; ]+?$/) && ($range ~~ @Graphs::time_ranges)) { >>> # Send HTTP headers >>> - _start_png_output(); >>> + _start_svg_output(); >>> >>> _print_error("URL parameters missing or malformed."); >>> exit; >>> @@ -76,7 +75,7 @@ unless(($origin ~~ @supported_origins) || ($origin eq "getrrdimage.cgi")) { >>> >>> ### Create graphs ### >>> # Send HTTP headers >>> -_start_png_output(); >>> +_start_svg_output(); >>> >>> # Graphs are first grouped by their origin. >>> # This is because some graph categories require special parameter handling. >>> @@ -204,46 +203,62 @@ if($graphstatus) { >>> >>> ###--- Internal functions ---### >>> >>> -# Send HTTP headers and switch to binary output >>> +# Send HTTP headers >>> # (don't print any non-image data to STDOUT afterwards) >>> -sub _start_png_output { >>> +sub _start_svg_output { >>> print "Cache-Control: no-cache, no-store\n"; >>> - print "Content-Type: image/png\n"; >>> + print "Content-Type: image/svg+xml\n"; >>> print "\n"; # End of HTTP headers >>> - binmode(STDOUT); >>> } >>> >>> -# Print error message to PNG output >>> +# Print error message to SVG output >>> sub _print_error { >>> my ($message) = @_; >>> - $message = "- Error -\n \n$message"; >>> - >>> - # Create new image with the same size as a graph >>> - my $img = GD::Image->new($Graphs::image_size{'width'}, $Graphs::image_size{'height'}); >>> - $img->interlaced('true'); >>> - >>> - # Basic colors >>> - my $color_background = $img->colorAllocate(255, 255, 255); >>> - my $color_border = $img->colorAllocate(255, 0, 0); >>> - my $color_text = $img->colorAllocate(0, 0, 0); >>> >>> - # Background and border >>> - $img->setThickness(2); >>> - $img->filledRectangle(0, 0, $img->width, $img->height, $color_background); >>> - $img->rectangle(10, 10, $img->width - 10, $img->height - 10, $color_border); >>> - >>> - # Draw message with line-wrap >>> - my $textbox = GD::Text::Wrap->new($img, >>> - text => $message, >>> - width => ($img->width - 50), >>> - color => $color_text, >>> - align => 'center', >>> - line_space => 5, >>> - preserve_nl => 1 >>> + # Prepare image options >>> + my %img = ( >>> + 'width' => $Graphs::image_size{'width'}, >>> + 'height' => $Graphs::image_size{'height'}, >>> + 'text_center' => int($Graphs::image_size{'width'} / 2), >>> + 'line_height' => 20, >>> + 'font_family' => "DejaVu Sans, Helvetica, sans-serif" # Matching the IPFire theme >>> ); >>> - $textbox->set_font(gdLargeFont); >>> - $textbox->draw(25, 25); >>> >>> - # Get PNG output >>> - print $img->png; >>> + # Line-wrap message to fit image (adjust to font width if necessary) >>> + local($Text::Wrap::columns) = int($img{'width'} / 10); >>> + $message = wrap('', '', $message); >>> + >>> + # Create new image with fixed background and border >>> + print <<END >>> +<?xml version="1.0" encoding="UTF-8"?> >>> +<svg width="$img{'width'}px" height="$img{'height'}px" viewBox="0 0 $img{'width'} $img{'height'}" version="1.1" xmlns="http://www.w3.org/2000/svg"> >>> + <!-- Background --> >>> + <rect width="100%" height="100%" fill="white"/> >>> + <rect width="100%" height="100%" fill="none" stroke="red" stroke-width="2" transform="scale(0.95)" transform-origin="center"/> >>> + <!-- Message --> >>> + <text x="$img{'text_center'}" y="50" font-size="20" font-family="$img{'font_family'}" text-anchor="middle">- $Lang::tr{'error'} -</text> >>> + <text x="$img{'text_center'}" y="90" font-size="14" font-family="$img{'font_family'}" text-anchor="middle"> >>> +END >>> +; >>> + >>> + # Print message lines >>> + my $shift_y = 0; # Shifts text along y-axis >>> + foreach my $line (split(/\n/, $message)) { >>> + if($line ne "") { # Don't create empty tspan elements >>> + print <<END >>> + <tspan x="$img{'text_center'}" dy="$shift_y">$line</tspan> >>> +END >>> +; >>> + $shift_y = $img{'line_height'}; >>> + } else { # Create blank lines by summing up unused line height >>> + $shift_y += $img{'line_height'}; >>> + } >>> + } >>> + >>> + # Finish SVG output >>> + print <<END >>> + </text> >>> +</svg> >>> +END >>> +; >>> } >>> -- >>> 2.27.0.windows.1 >>>
diff --git a/config/cfgroot/graphs.pl b/config/cfgroot/graphs.pl index 441d4c483..02341eb45 100644 --- a/config/cfgroot/graphs.pl +++ b/config/cfgroot/graphs.pl @@ -40,7 +40,7 @@ my $ERROR; my @GRAPH_ARGS = ( # Output format - "--imgformat", "PNG", + "--imgformat", "SVG", # No border "--border", "0", diff --git a/html/cgi-bin/getrrdimage.cgi b/html/cgi-bin/getrrdimage.cgi index c08247c57..92f8b585d 100644 --- a/html/cgi-bin/getrrdimage.cgi +++ b/html/cgi-bin/getrrdimage.cgi @@ -21,8 +21,7 @@ use strict; use URI; -use GD; -use GD::Text::Wrap; +use Text::Wrap; use experimental 'smartmatch'; # debugging @@ -52,7 +51,7 @@ my $range = lc $query{'range'}; # lower case # Check parameters unless(($origin =~ /^\w+?\.cgi$/) && ($graph =~ /^[\w\-.,; ]+?$/) && ($range ~~ @Graphs::time_ranges)) { # Send HTTP headers - _start_png_output(); + _start_svg_output(); _print_error("URL parameters missing or malformed."); exit; @@ -76,7 +75,7 @@ unless(($origin ~~ @supported_origins) || ($origin eq "getrrdimage.cgi")) { ### Create graphs ### # Send HTTP headers -_start_png_output(); +_start_svg_output(); # Graphs are first grouped by their origin. # This is because some graph categories require special parameter handling. @@ -204,46 +203,62 @@ if($graphstatus) { ###--- Internal functions ---### -# Send HTTP headers and switch to binary output +# Send HTTP headers # (don't print any non-image data to STDOUT afterwards) -sub _start_png_output { +sub _start_svg_output { print "Cache-Control: no-cache, no-store\n"; - print "Content-Type: image/png\n"; + print "Content-Type: image/svg+xml\n"; print "\n"; # End of HTTP headers - binmode(STDOUT); } -# Print error message to PNG output +# Print error message to SVG output sub _print_error { my ($message) = @_; - $message = "- Error -\n \n$message"; - - # Create new image with the same size as a graph - my $img = GD::Image->new($Graphs::image_size{'width'}, $Graphs::image_size{'height'}); - $img->interlaced('true'); - - # Basic colors - my $color_background = $img->colorAllocate(255, 255, 255); - my $color_border = $img->colorAllocate(255, 0, 0); - my $color_text = $img->colorAllocate(0, 0, 0); - # Background and border - $img->setThickness(2); - $img->filledRectangle(0, 0, $img->width, $img->height, $color_background); - $img->rectangle(10, 10, $img->width - 10, $img->height - 10, $color_border); - - # Draw message with line-wrap - my $textbox = GD::Text::Wrap->new($img, - text => $message, - width => ($img->width - 50), - color => $color_text, - align => 'center', - line_space => 5, - preserve_nl => 1 + # Prepare image options + my %img = ( + 'width' => $Graphs::image_size{'width'}, + 'height' => $Graphs::image_size{'height'}, + 'text_center' => int($Graphs::image_size{'width'} / 2), + 'line_height' => 20, + 'font_family' => "DejaVu Sans, Helvetica, sans-serif" # Matching the IPFire theme ); - $textbox->set_font(gdLargeFont); - $textbox->draw(25, 25); - # Get PNG output - print $img->png; + # Line-wrap message to fit image (adjust to font width if necessary) + local($Text::Wrap::columns) = int($img{'width'} / 10); + $message = wrap('', '', $message); + + # Create new image with fixed background and border + print <<END +<?xml version="1.0" encoding="UTF-8"?> +<svg width="$img{'width'}px" height="$img{'height'}px" viewBox="0 0 $img{'width'} $img{'height'}" version="1.1" xmlns="http://www.w3.org/2000/svg"> + <!-- Background --> + <rect width="100%" height="100%" fill="white"/> + <rect width="100%" height="100%" fill="none" stroke="red" stroke-width="2" transform="scale(0.95)" transform-origin="center"/> + <!-- Message --> + <text x="$img{'text_center'}" y="50" font-size="20" font-family="$img{'font_family'}" text-anchor="middle">- $Lang::tr{'error'} -</text> + <text x="$img{'text_center'}" y="90" font-size="14" font-family="$img{'font_family'}" text-anchor="middle"> +END +; + + # Print message lines + my $shift_y = 0; # Shifts text along y-axis + foreach my $line (split(/\n/, $message)) { + if($line ne "") { # Don't create empty tspan elements + print <<END + <tspan x="$img{'text_center'}" dy="$shift_y">$line</tspan> +END +; + $shift_y = $img{'line_height'}; + } else { # Create blank lines by summing up unused line height + $shift_y += $img{'line_height'}; + } + } + + # Finish SVG output + print <<END + </text> +</svg> +END +; }