Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ IMPORTANT NOTE: This version only works on CACTI 1.x++!

--- develop ---

* feature#298: Fit or stretch background images within the map canvas

* issue#289: Load viewer helpers explicitly before rendering maps

* issue#288: Preserve dynamic editor labels and cycle countdowns
Expand Down
28 changes: 25 additions & 3 deletions lib/WeatherMap.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -3911,6 +3911,11 @@ function DrawMap($filename = '', $thumbnailfile = '', $thumbnailmax = 250, $with

wm_debug('Finished Post-Processing Plugins...');

$background_sizing = $this->get_hint('background_sizing', 'image');
if (!in_array($background_sizing, ['fit', 'stretch', 'image'], true)) {
$background_sizing = 'image';
}

wm_debug('=====================================');
wm_debug('Start of Map Drawing');

Expand All @@ -3936,8 +3941,10 @@ function DrawMap($filename = '', $thumbnailfile = '', $thumbnailmax = 250, $with
if (!$bgimage) {
wm_warn('Failed to open background image. One possible reason: Is your BACKGROUND really a PNG?');
} else {
$this->width = imagesx($bgimage);
$this->height = imagesy($bgimage);
if ($background_sizing === 'image') {
$this->width = imagesx($bgimage);
$this->height = imagesy($bgimage);
}
}
} else {
wm_warn('Your background image file could not be read. Check the filename, and permissions, for ' . $this->background);
Expand Down Expand Up @@ -3975,7 +3982,22 @@ function DrawMap($filename = '', $thumbnailfile = '', $thumbnailmax = 250, $with
wimagefilledrectangle($image, 0, 0, $this->width, $this->height, $this->colours['DEFAULT']['BG']['gdref1']);

if ($bgimage) {
imagecopy($image, $bgimage, 0, 0, 0, 0, $this->width, $this->height);
if ($background_sizing === 'image') {
imagecopy($image, $bgimage, 0, 0, 0, 0, $this->width, $this->height);
} else {
$source_width = imagesx($bgimage);
$source_height = imagesy($bgimage);
$dest_width = $this->width;
$dest_height = $this->height;
if ($background_sizing === 'fit') {
$ratio = min($dest_width / $source_width, $dest_height / $source_height);
$dest_width = max(1, (int) round($source_width * $ratio));
$dest_height = max(1, (int) round($source_height * $ratio));
}
$dest_x = (int) floor(($this->width - $dest_width) / 2);
$dest_y = (int) floor(($this->height - $dest_height) / 2);
imagecopyresampled($image, $bgimage, $dest_x, $dest_y, 0, 0, $dest_width, $dest_height, $source_width, $source_height);
}
unset($bgimage);
}

Expand Down
5 changes: 5 additions & 0 deletions lib/editor.actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,11 @@ function setMapProperties($mapfile) {
$map->background = wm_editor_sanitize_file(stripslashes(get_nfilter_request_var('map_bgfile')), ['png', 'jpg', 'gif', 'jpeg']);
}

$background_sizing = get_nfilter_request_var('map_background_sizing');
if (is_string($background_sizing) && in_array($background_sizing, ['fit', 'stretch', 'image'], true)) {
$map->add_hint('background_sizing', $background_sizing);
}

db_execute_prepared('UPDATE weathermap_maps
SET titlecache = ?
WHERE configfile = ?',
Expand Down
16 changes: 16 additions & 0 deletions locales/po/cacti.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,22 @@ msgstr ""
msgid "Background Image Filename"
msgstr ""

#: weathermap-cacti-plugin-editor.php
msgid "Background sizing"
msgstr ""

#: weathermap-cacti-plugin-editor.php
msgid "Fit to map"
msgstr ""

#: weathermap-cacti-plugin-editor.php
msgid "Stretch to map"
msgstr ""

#: weathermap-cacti-plugin-editor.php
msgid "Use image size"
msgstr ""

#: weathermap-cacti-plugin-editor.php
msgid "Timestamp Text"
msgstr ""
Expand Down
104 changes: 104 additions & 0 deletions tests/Support/BackgroundSizingEngineRegression.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php
/*
+-------------------------------------------------------------------------+
| Copyright (C) 2004-2026 The Cacti Group, Howard Jones |
| |
| 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 |
| of the License, 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. |
+-------------------------------------------------------------------------+
| Cacti: The Complete RRDtool-based Graphing Solution |
+-------------------------------------------------------------------------+
| This code is designed, written, and maintained by the Cacti Group. See |
| about.php and/or the AUTHORS file for specific developer information. |
+-------------------------------------------------------------------------+
| http://www.cacti.net/ |
+-------------------------------------------------------------------------+
*/

require dirname(__DIR__) . '/bootstrap-unit.php';
$plugin = dirname(__DIR__,2);
chdir($plugin);
require $plugin . '/setup.php';
require $plugin . '/lib/WeatherMap.class.php';
require $plugin . '/lib/editor.inc.php';
require $plugin . '/lib/editor.actions.php';

if (!defined('MESSAGE_LEVEL_INFO')) {
define('MESSAGE_LEVEL_INFO',0);
}
function wm_engine_assert($value,$message) {
if (!$value) {
throw new RuntimeException($message);
}
}
$directory = sys_get_temp_dir() . '/wm-engine-' . bin2hex(random_bytes(8));
mkdir($directory);

try {
$bg = imagecreatetruecolor(200,100);
imagefill($bg,0,0,imagecolorallocate($bg,255,0,0));
imagepng($bg,$directory . '/background.png');

foreach (['fit'=>[120, 120], 'stretch'=>[120, 120], 'image'=>[200, 100], 'invalid'=>[200, 100]] as $mode=>$size) {
$map = new WeatherMap();
$map->width = 120;
$map->height = 120;
$map->background = $directory . '/background.png';
$map->add_hint('background_sizing', 'image');
$map->WriteConfig($directory . '/' . $mode . '.conf');
$GLOBALS['__test_nfilter_request'] = [
'map_title' => 'Background test', 'map_legend' => 'Traffic', 'map_stamp' => '',
'map_htmlfile' => '', 'map_pngfile' => '', 'map_width' => '120', 'map_height' => '120',
'map_bgfile' => $directory . '/background.png', 'map_background_sizing' => $mode,
'map_linkdefaultwidth' => '7', 'map_linkdefaultbwin' => '100M', 'map_linkdefaultbwout' => '100M',
];
setMapProperties($directory . '/' . $mode . '.conf');
$saved = new WeatherMap();
$saved->ReadConfig($directory . '/' . $mode . '.conf');
wm_engine_assert($saved->get_hint('background_sizing') === ($mode === 'invalid' ? 'image' : $mode), $mode . ' editor persistence');
if ($mode === 'invalid') {
$saved->add_hint('background_sizing', 'invalid');
}
$saved->DrawMap($directory . '/' . $mode . '.png');
$image = imagecreatefrompng($directory . '/' . $mode . '.png');
wm_engine_assert([imagesx($image), imagesy($image)] === $size,$mode . ' canvas');
$pixel = imagecolorsforindex($image,imagecolorat($image,60,1));
wm_engine_assert(($pixel['red'] === 255 && $pixel['green'] === 0) === ($mode !== 'fit'),$mode . ' fit proportions');
imagedestroy($image);
}

foreach (['fit' => [120, 120], 'stretch' => [120, 120], 'image' => [200, 100], 'invalid' => [200, 100]] as $mode => $size) {
$map = new WeatherMap();
$map->width = 120;
$map->height = 120;
$map->background = $directory . '/background.png';
$map->add_hint('background_sizing', 'stretch');
$map->postprocessclasses = ['SizingOverride'];
$map->plugins['post']['SizingOverride'] = new class($mode) {
public function __construct(private string $mode) {}
public function run($map) {
$map->add_hint('background_sizing', $this->mode);
}
};
$file = $directory . '/post-' . $mode . '.png';
$map->DrawMap($file);
$image = imagecreatefrompng($file);
wm_engine_assert([imagesx($image), imagesy($image)] === $size, $mode . ' post-processor canvas');
$pixel = imagecolorsforindex($image, imagecolorat($image, 60, 1));
wm_engine_assert(($pixel['red'] === 255 && $pixel['green'] === 0) === ($mode !== 'fit'), $mode . ' post-processor proportions');
imagedestroy($image);
}

print "PASS\n";
} finally {
foreach (glob($directory . '/*') as $file) {
unlink($file);
}rmdir($directory);
}
43 changes: 43 additions & 0 deletions tests/Unit/BackgroundSizingEngineTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
/*
+-------------------------------------------------------------------------+
| Copyright (C) 2004-2026 The Cacti Group, Howard Jones |
| |
| 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 |
| of the License, 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. |
+-------------------------------------------------------------------------+
| Cacti: The Complete RRDtool-based Graphing Solution |
+-------------------------------------------------------------------------+
| This code is designed, written, and maintained by the Cacti Group. See |
| about.php and/or the AUTHORS file for specific developer information. |
+-------------------------------------------------------------------------+
| http://www.cacti.net/ |
+-------------------------------------------------------------------------+
*/

use PHPUnit\Framework\Attributes\RunInSeparateProcess;
use PHPUnit\Framework\Attributes\PreserveGlobalState;
use PHPUnit\Framework\TestCase;

final class BackgroundSizingEngineTest extends TestCase {
#[RunInSeparateProcess]
#[PreserveGlobalState(false)]
public function testPersistsAndRendersBackgroundSizing(): void {
ob_start();

try {
require dirname(__DIR__) . '/Support/BackgroundSizingEngineRegression.php';
$result = ob_get_contents();
} finally {
ob_end_clean();
}
self::assertSame("PASS\n", $result);
}
}
20 changes: 20 additions & 0 deletions tests/bootstrap-unit.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,26 @@ function cacti_log($m, $p = false, $t = '', $l = 0) {
}
}

if (!function_exists('cacti_count')) {
function cacti_count($value) {
if (isset($GLOBALS['__test_cacti_count']) && is_callable($GLOBALS['__test_cacti_count'])) {
return ($GLOBALS['__test_cacti_count'])($value);
}

return is_countable($value) ? count($value) : 0;
}
}

if (!function_exists('clean_up_name')) {
function clean_up_name($value) {
if (isset($GLOBALS['__test_clean_up_name']) && is_callable($GLOBALS['__test_clean_up_name'])) {
return ($GLOBALS['__test_clean_up_name'])($value);
}

return preg_replace('/[^A-Za-z0-9_\\-.]/', '_', $value);
}
}

if (!function_exists('cacti_sizeof')) {
function cacti_sizeof($a) {
return is_array($a) ? count($a) : 0;
Expand Down
12 changes: 12 additions & 0 deletions weathermap-cacti-plugin-editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,18 @@
</select>
</td>
</tr>
<tr>
<td><label for='map_background_sizing'><?php print __('Background sizing', 'weathermap'); ?></label></td>
<td><select id='map_background_sizing' name='map_background_sizing'>
<?php
$background_sizing = $map->get_hint('background_sizing', 'image');
if (!in_array($background_sizing, ['fit', 'stretch', 'image'], true)) { $background_sizing = 'image'; }
foreach (['fit' => __('Fit to map', 'weathermap'), 'stretch' => __('Stretch to map', 'weathermap'), 'image' => __('Use image size', 'weathermap')] as $value => $label) {
print '<option value="' . $value . '"' . ($background_sizing === $value ? ' selected' : '') . '>' . html_escape($label) . '</option>';
}
?>
</select></td>
</tr>
<tr>
<td><?php print __('Timestamp Text', 'weathermap'); ?></td>
<td><input id='map_stamp' name='map_stamp' type='text' class='ui-state-default ui-corner-all' size='40' value='<?php print html_escape($map->stamptext) ?>' /></td>
Expand Down