OpenCPN Partial API docs
Loading...
Searching...
No Matches
svg_utils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2018 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/ *
16 ***************************************************************************/
17
24#ifdef __ANDROID__
25#include "androidUTIL.h"
26#include "qdebug.h"
27#endif
28
29#include <wx/filename.h>
30#include <wx/dir.h>
31
32#ifdef ocpnUSE_SVG
33#ifndef ocpnUSE_wxBitmapBundle
34#include "wxSVG/svg.h"
35#else
36#include <wx/bmpbndl.h>
37#endif
38#endif // ocpnUSE_SVG
39
40#include "model/svg_utils.h"
41
42#include "model/base_platform.h"
43#include "model/routeman.h"
44#include "pugixml.hpp"
45
46#define SVG_IN_TO_PT 72
47#define SVG_IN_TO_PX 96
48#define SVG_PT_TO_IN 1 / 72
49#define SVG_PX_TO_IN 1 / 96
50#define SVG_PT_TO_PX 96 / 72
51#define SVG_MM_TO_PX 3.7795275591
52#define SVG_PX_TO_MM 0.2645833333
53#define SVG_MM_TO_PT 2.8346456693
54#define SVG_PT_TO_MM 0.3527777778
55#define SVG_CM_TO_PX 37.795275591
56#define SVG_CM_TO_PT 28.346456693
57#define SVG_MM_TO_IN 25.4
58
59wxBitmap LoadSVG(const wxString filename, const unsigned int width,
60 const unsigned int height, wxBitmap* default_bitmap,
61 bool use_cache) {
62#ifdef ocpnUSE_SVG
63#ifndef ocpnUSE_wxBitmapBundle
64#ifdef __ANDROID__
65 return loadAndroidSVG(filename, width, height);
66#else
67 wxSVGDocument svgDoc;
68 if (svgDoc.Load(filename))
69 return wxBitmap(svgDoc.Render(width, height, NULL, true, true));
70 else
71 return wxBitmap(width, height);
72#endif
73#else
74#ifdef __ANDROID__
75 return loadAndroidSVG(filename, width, height);
76#else
77 wxSize s(width, height);
78 if (wxFileExists(filename)) {
79 wxBitmap bmp;
80 std::string key;
81 if (use_cache && SVGBitmapCache::GetInstance().HasKey(
82 key = SVGBitmapCache::GetInstance().MakeKey(
83 filename, width, height))) {
84 bmp = SVGBitmapCache::GetInstance().Get(key);
85 } else {
86 bmp = wxBitmapBundle::FromSVGFile(filename, s).GetBitmap(s);
87 if (use_cache) {
88 SVGBitmapCache::GetInstance().Add(key, bmp);
89 }
90 }
91 if (bmp.IsOk()) {
92 return bmp;
93 }
94 }
95 if (default_bitmap) {
96 return *default_bitmap;
97 } else {
98 return wxNullBitmap; // Or wxBitmap(width, height);?
99 }
100#endif
101#endif
102#else
103 return wxBitmap(width, height);
104#endif // ocpnUSE_SVG
105}
106
107/* returns 1 if str ends with suffix */
108int str_ends_with(const char* str, const char* suffix) {
109 if (str == NULL || suffix == NULL) return 0;
110
111 size_t str_len = strlen(str);
112 size_t suffix_len = strlen(suffix);
113
114 if (suffix_len > str_len) return 0;
115
116 return 0 == strncmp(str + str_len - suffix_len, suffix, suffix_len);
117}
118
119// Convert the provided value to the standard 96 DPI pixels
120// if such a conversion is doable. If not doable or not necessary (px, em, ex,
121// %), numerical part of the value is returned In case of an error, 0 is
122// returned
123unsigned int get_px_length(const char* val) {
124 int num;
125 try {
126 num = std::stoi(val);
127 } catch (std::invalid_argument&) {
128 return 0;
129 } catch (std::out_of_range&) {
130 return 0;
131 }
132 if (num < 0) {
133 return 0;
134 }
135
136 if (str_ends_with(val, "mm")) {
137 return (unsigned int)((float)num * SVG_MM_TO_PX);
138 } else if (str_ends_with(val, "cm")) {
139 return (unsigned int)((float)num * SVG_CM_TO_PX);
140 } else if (str_ends_with(val, "in")) {
141 return (unsigned int)((float)num * SVG_CM_TO_PX);
142 } else if (str_ends_with(val, "pt")) {
143 return (unsigned int)((float)num * SVG_PT_TO_PX);
144 }
145 return num;
146}
147
148bool SVGDocumentPixelSize(const wxString filename, unsigned int& width,
149 unsigned int& height) {
150 width = 0;
151 height = 0;
152 float viewBoxWidth = 0, viewBoxHeight = 0;
153 pugi::xml_document svgDoc;
154 if (svgDoc.load_file(filename.fn_str())) {
155 pugi::xml_node svgNode = svgDoc.child("svg");
156
157 // Check for viewBox attribute.
158 if (const char* viewBox = svgNode.attribute("viewBox").value()) {
159 std::istringstream iss(viewBox);
160 float minX, minY;
161 iss >> minX >> minY >> viewBoxWidth >> viewBoxHeight;
162 }
163
164 // Check width/height attributes
165 for (pugi::xml_attribute attr = svgNode.first_attribute(); attr;
166 attr = attr.next_attribute()) {
167 const char* pca = attr.name();
168 if (!strcmp(pca, "width")) {
169 width = get_px_length(attr.as_string());
170 } else if (!strcmp(pca, "height")) {
171 height = get_px_length(attr.as_string());
172 }
173 }
174 // SVG sizing rules: https://svgwg.org/specs/integration/#svg-css-sizing
175 if (width == 0 && height == 0) {
176 if (viewBoxWidth > 0 && viewBoxHeight > 0) {
177 // Use viewBox dimensions
178 width = viewBoxWidth;
179 height = viewBoxHeight;
180 } else {
181 // Default sizes per spec
182 width = 300;
183 height = 150;
184 }
185 } else if (width == 0) {
186 if (viewBoxWidth > 0 && viewBoxHeight > 0) {
187 width = height * (viewBoxWidth / viewBoxHeight);
188 } else {
189 width = 300;
190 }
191 } else if (height == 0) {
192 if (viewBoxWidth > 0 && viewBoxHeight > 0) {
193 height = width * (viewBoxHeight / viewBoxWidth);
194 } else {
195 height = 150;
196 }
197 }
198 }
199 return false;
200}
201
202unsigned int SVGPixelsToDisplay(unsigned int svg_px) {
203 return g_BasePlatform->GetDisplayDPmm() * SVG_MM_TO_IN / SVG_IN_TO_PX *
204 svg_px * g_ChartScaleFactorExp;
205}
206
207wxBitmap LoadSvgStdIcon(const std::string& svg_file, const wxWindow* w,
208 bool touch) {
209 auto path = fs::path(g_BasePlatform->GetSharedDataDir().ToStdString()) /
210 "uidata" / "MUI_flat" / svg_file;
211 int size = g_BasePlatform->GetSvgStdIconSize(w, touch);
212 return LoadSVG(path.string(), size, size);
213}
214
215SVGBitmapCache::SVGBitmapCache() {
216 wxFileName iconcachedir;
217 iconcachedir.SetName("iconCacheSVG");
218 iconcachedir.SetPath(g_BasePlatform->GetPrivateDataDir());
219 // Create the cache dir here if necessary
220 if (!wxDir::Exists(iconcachedir.GetFullPath())) {
221 wxFileName::Mkdir(iconcachedir.GetFullPath());
222 }
223 cache_directory = iconcachedir.GetFullPath();
224}
225
226std::string SVGBitmapCache::MakeKey(wxString file_path, const int width,
227 const int height) {
228 std::replace(file_path.begin(), file_path.end(), ':', '_');
229 std::replace(file_path.begin(), file_path.end(), '/', '_');
230 std::replace(file_path.begin(), file_path.end(), '\\', '_');
231 std::replace(file_path.begin(), file_path.end(), '>', '_');
232 std::replace(file_path.begin(), file_path.end(), '<', '_');
233 std::replace(file_path.begin(), file_path.end(), '"', '_');
234 std::replace(file_path.begin(), file_path.end(), '|', '_');
235 std::replace(file_path.begin(), file_path.end(), '?', '_');
236 std::replace(file_path.begin(), file_path.end(), '*', '_');
237
238 std::ostringstream ss;
239 ss << file_path << "_" << width << "x" << height;
240 return ss.str();
241}
242
243void SVGBitmapCache::Add(const wxString key, const wxBitmap bmp) {
244 if (!bmp.IsOk()) {
245 return;
246 }
247 sync.lock();
248 items.emplace(key, bmp);
249 wxFileName fn;
250 fn.SetName(key);
251 fn.SetPath(cache_directory);
252 bmp.SaveFile(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
253 sync.unlock();
254}
255
256wxBitmap SVGBitmapCache::Get(const wxString key) {
257 wxBitmap bmp = wxNullBitmap;
258 sync.lock();
259 std::unordered_map<std::string, wxBitmap>::const_iterator i =
260 items.find(key.ToStdString());
261 if (i != items.end()) {
262 bmp = i->second;
263 } else {
264 wxFileName fn;
265 fn.SetName(key);
266 fn.SetPath(cache_directory);
267 if (fn.FileExists()) {
268 bmp.LoadFile(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
269 if (bmp.IsOk()) {
270 items.emplace(key, bmp);
271 } else {
272 bmp = wxNullBitmap;
273 }
274 }
275 }
276 sync.unlock();
277 return bmp;
278}
279
280bool SVGBitmapCache::HasKey(const wxString key) {
281 bool res = false;
282 sync.lock();
283 if (items.find(key.ToStdString()) != items.end()) {
284 res = true;
285 } else {
286 wxFileName fn;
287 fn.SetName(key);
288 fn.SetPath(cache_directory);
289 if (fn.FileExists()) {
290 // We proactively also load it here if it exists
291 wxBitmap bmp;
292 bmp.LoadFile(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
293 if (bmp.IsOk()) {
294 items.emplace(key, bmp);
295 res = true;
296 }
297 }
298 }
299 sync.unlock();
300 return res;
301}
BasePlatform * g_BasePlatform
points to g_platform, handles brain-dead MS linker.
Basic platform specific support utilities without GUI deps.
wxString & GetPrivateDataDir()
Return dir path for opencpn.log, etc., respecting -c cli option.
int GetSvgStdIconSize(const wxWindow *w, bool touch) override
Return icon size roughly corresponding to height of a char in w, tweaked to be "big enough" for touch...
float g_ChartScaleFactorExp
Global instance.
Definition routeman.cpp:68
Route Manager.
wxBitmap LoadSVG(const wxString filename, const unsigned int width, const unsigned int height, wxBitmap *default_bitmap, bool use_cache)
Load SVG file and return it's bitmap representation of requested size In case file can't be loaded an...
Definition svg_utils.cpp:59
wxBitmap LoadSvgStdIcon(const std::string &svg_file, const wxWindow *w, bool touch)
Load an svg icon from standard path, roughly scaled to the height of a char.
bool SVGDocumentPixelSize(const wxString filename, unsigned int &width, unsigned int &height)
Return the size of the SVG document in standard 96 DPI pixels https://developer.mozilla....
unsigned int SVGPixelsToDisplay(unsigned int svg_px)
Recalculate the length in standard 96 DPI pixels to actual display pixels.
SVG utilities.