izzi
SVG SUBSET C++ API
Loading...
Searching...
No Matches
a60-svg-composite-and-layer-basics.h
Go to the documentation of this file.
1// svg composite and layer basics -*- mode: C++ -*-
2
3// Copyright (C) 2014-2020, 2025 Benjamin De Kosnik <b.dekosnik@gmail.com>
4
5// This file is part of the alpha60-MiL SVG library. This library is
6// free software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the Free
8// Software Foundation; either version 3, or (at your option) any
9// later version.
10
11// This library is distributed in the hope that it will be useful, but
12// WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// General Public License for more details.
15
16#ifndef MiL_SVG_COMPOSITE_AND_LAYER_BASICS_H
17#define MiL_SVG_COMPOSITE_AND_LAYER_BASICS_H 1
18
19
20namespace svg {
21
22/// Take input size and make a one channel (single-image) SVG form.
24make_svg_1_channel(const space_type deltax, const space_type deltay,
25 const string& outbase)
26{
27 using namespace svg;
28 area<> a = { deltax, deltay };
29 return svg_element(outbase, a);
30}
31
32
33void
35{
36 if (!insert1.empty())
37 {
38 rect_element r1;
39 r1.str(insert1);
40 obj.add_element(r1);
41 }
42}
43
44
45/// Take input size and make a two channel (single-image) SVG form.
47make_svg_2_channel(const space_type deltax, const space_type deltay,
48 const string& outbase)
49{
50 using namespace svg;
51 area<> a = { 2 * deltax, deltay };
52 return svg_element(outbase, a);
53}
54
55
56void
57make_2_channel_insert(svg_element& obj, string insert1, string insert2)
58{
59 if (!insert1.empty())
60 {
61 rect_element r1;
62 r1.str(insert1);
63 obj.add_element(r1);
64 }
65
66 if (!insert2.empty())
67 {
68 rect_element r2;
69 r2.str(insert2);
70 obj.add_element(r2);
71 }
72}
73
74
75/// Paint the edges of a physical page.
76/// Assumes page is square.
77////
78/// @param rlen is height of painted rectangle from edge.
79/// NB: 0.125 is a common bleed == 12 pixels.
80/// First attempt was, common bleed plus 2 pixel "on page" -> 14. Not enough.
81void
82paint_edges_with_char_index(svg_element& obj, const area<> a, const char firstc,
84 const double rlen = 24.0)
85{
86 // Paint edges.
87 const std::locale loc("");
88 const bool alphap = std::isalpha(firstc, loc);
89
90 // Position the first character from (a to z + digit } to x values 0 to 26.
91 // Find this index, with numbers and symbols at the end.
92 const uint maxc = 27;
93 uint cidx(maxc - 1);
94 if (alphap)
95 cidx = (static_cast<uint>(firstc) % 65) - 32;
96
97 const double deltac = double(std::min(a._M_width, a._M_height)) / maxc;
98 double deltax(deltac * cidx);
99 double deltay(deltac * cidx);
100
101 style estyl = svg::k::b_style;
102 estyl.set_colors(klr);
103
104 // Top edge, right to left starting outer to inner
105 point_2t pr1 = { a._M_width - deltax - rlen - deltac, 0 };
106 rect_element r1 = make_rect(pr1, estyl, { deltac, rlen });
107 obj.add_element(r1);
108
109 // Bottom edge, left to right starting innner to outer.
110 point_2t pr2 = { deltax + rlen, a._M_height - rlen };
111 rect_element r2 = make_rect(pr2, estyl, { deltac, rlen });
112 obj.add_element(r2);
113
114 // Right side edge, up from bottom to top.
115 point_2t pr3 = { a._M_width - rlen, a._M_height - deltay - rlen - deltac };
116 rect_element r3 = make_rect(pr3, estyl, { rlen, deltac });
117 obj.add_element(r3);
118}
119
120
121/// Import svg file, convert it to svg_element for insertion.
122/// ifile is a plain SVG file with a 1:1 aspect ratio.
123string
124file_to_svg_insert(const string ifile)
125{
126 string isvg;
127
128 // Read SVG to insert.
129 std::ifstream ifs(ifile);
130 if (ifs.good())
131 {
132 // Strip out any XML version line in the SVG file.
133 // Search for and discard lines with "xml version", iff exists
134 string xmlheader;
135 getline(ifs, xmlheader);
136 if (xmlheader.find("xml version") == string::npos)
137 ifs.seekg(0, ifs.beg);
138
139 std::ostringstream oss;
140 oss << ifs.rdbuf();
141 isvg = oss.str();
142 }
143 else
144 {
145 string m("svg_file_to_svg_insert:: insert nested SVG failed ");
146 m += ifile;
147 m += k::newline;
148 throw std::runtime_error(m);
149 }
150 return isvg;
151}
152
153
154/// Import svg file, convert it to svg_element for insertion.
155string
156element_to_svg_insert(const string isvgpre)
157{
158 string isvg;
159
160 // Read SVG to insert, remove anything previous to <svg"
161 auto svgepos = isvgpre.find("<svg version");
162 if (svgepos != string::npos)
163 {
164 // Strip out any XML version line in the SVG file.
165 // Search for and discard lines with "xml version", iff exists
166 isvg = isvgpre.substr(svgepos);
167 }
168 else
169 {
170 string m("element_to_svg_insert:: insert nested SVG failed of size: ");
171 m += std::to_string(isvgpre.size());
172 m += k::newline;
173 m += "of body:";
174 m += k::newline;
175 m += isvgpre;
176 throw std::runtime_error(m);
177 }
178 return isvg;
179}
180
181
182/// Embed svg in group element.
183/// @param obj is containing svg
184/// @param origin is where glyph placement is inside containing svg element.
185/// @param origsize is original file width/height constant
186/// @param isize is final width/height
187/// @param isvg is the raw svg string to insert, assumes _M_lifetime == false.
188/// @param styl is overide style information: defaults to no_style.
189///
190/// NB This only works is the file has no styles set in svg, group, or
191/// individual element definitions (like circle, path, rectangle,
192/// etc.).
193///
194/// See: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
196insert_svg_at(svg_element& obj, const string isvg,
197 const point_2t origin, const double origsize, const double isize,
198 const double angled = 0, const style& styl = k::no_style)
199{
200 // offset
201 auto [ objx, objy ] = origin;
202 const int x = objx - (isize / 2);
203 const int y = objy - (isize / 2);
204 string xformtranslate(transform::translate(x, y));
205
206 // scaled
207 string xformscale;
208 if (isize != origsize)
209 {
210 const double scalex(isize / origsize);
211 xformscale = transform::scale(scalex);
212 }
213
214 // rotated
215 string xformrotate;
216 if (angled != 0)
217 {
218 xformrotate = transform::rotate(90 - angled, objx, objy);
219 }
220
221 // Order of transformations matters...
222 string ts(xformrotate + k::space + xformtranslate + k::space + xformscale);
223
224 group_element gsvg;
225 gsvg.start_element("inset svg", ts, styl);
226 gsvg.add_raw(isvg);
227 gsvg.finish_element();
228 obj.add_element(gsvg);
229
230 return obj;
231}
232
233
234/// Take @param obj as some kind of inner svg element, and embed it as
235/// a nested svg at a location centered at @param pos on the outer
236/// svg.
237/// Returns a svg_element that can then be add_element from outer svg.
240{
241 // Find centered position.
242 const auto a = obj._M_area;
243 const auto [ width, height ] = a;
244 const auto [ xo, yo ] = p;
245
246 bool outofbounds(false);
247
248 double x(0);
249 if (xo > (width / 2))
250 x = xo - (width / 2);
251 else
252 outofbounds = true;
253
254 double y(0);
255 if (yo > (height / 2))
256 y = yo - (height / 2);
257 else
258 outofbounds = true;
259
260 if (outofbounds)
261 throw std::runtime_error("nest_inner_svg_element_centered::out of bounds");
262
263 svg_element nested_obj("inner-" + obj._M_name, a, false);
264 nested_obj.start_element({x, y}, a);
265 nested_obj.add_element(obj);
266 nested_obj.finish_element();
267 return nested_obj;
268}
269
270
271/// Take @param obj as some kind of inner element_base, and embed it as
272/// a nested svg at a location centered at @param pos on the outer
273/// svg.
274/// Returns a svg_element that can then be add_element from outer svg.
277 const area<> a, const string name,
278 const bool centerp = true)
279{
280 // Find centered position.
281 const auto [ width, height ] = a;
282 const auto [ xo, yo ] = p;
283
284 double x(xo);
285 double y(yo);
286
287 if (centerp)
288 {
289 bool outofbounds(false);
290
291 if (xo > (width / 2))
292 x = xo - (width / 2);
293 else
294 outofbounds = true;
295
296 if (yo > (height / 2))
297 y = yo - (height / 2);
298 else
299 outofbounds = true;
300
301 if (outofbounds)
302 throw std::runtime_error("nest_inner_element::out of bounds");
303 }
304
305 svg_element nested_obj("inner-" + name, a, false);
306 nested_obj.start_element({x, y}, a);
307 nested_obj.add_element(eb);
308 nested_obj.finish_element();
309 return nested_obj;
310}
311
312
313/// Composite frame on bleed.
314/// For printed objects with a center gutter, some intra-page
315/// adjustments are necessary.
316/// @param slxt is odd/even (left/right)
317/// @param bleedin is bleed size for one edge in inches. (1/8)
318/// @param bleedxoffset is distance from spine pushed outward.
319void
321 const svg::select slxt, const double bleedin,
322 const double bleedxoffset = 0)
323{
324 const double bleedpx = get_dpi() * bleedin;
325 const double bleedpxo = get_dpi() * (bleedin + (bleedxoffset / 2));
326
327 if (slxt == svg::select::odd)
328 {
329 // LHS
330 // Individual glyph shapes grid.
331 // Left, center vertical and move to right edge horizontal.
332 const point_2t p = { bleedpx, bleedpxo };
333 obj.start_element(p, obj._M_area);
334 }
335 if (slxt == svg::select::even)
336 {
337 // RHS
338 // Radial metadata dimensions grid.
339 // Right, center vertical and move to left edge horizontal
340 const point_2t p = { bleedxoffset * get_dpi(), bleedpxo };
341 obj.start_element(p, obj._M_area);
342 }
343}
344
345} // namespace svg
346
347#endif
void finish_element()
SVG element end boilerplate.
void start_element()
For groups of elements that have the same name.
void start_element()
SVG element beginning boilerplate for outermost (containing) svg_element. Variable: unit,...
void add_raw(const string &raw)
string str() const
void add_element(const element_base &e)
Abstract base class for all SVG Elements.
const style b_style
rect_element make_rect(const point_2t origin, const style s, const area<> a, const string filterstr="", const string xform="")
Create rect_element at origin.
svg_element insert_svg_at(svg_element &obj, const string isvg, const point_2t origin, const double origsize, const double isize, const double angled=0, const style &styl=k::no_style)
Embed svg in group element.
color
Color enumerated as types.
svg_element make_svg_1_channel(const space_type deltax, const space_type deltay, const string &outbase)
Take input size and make a one channel (single-image) SVG form.
svg_element nest_inner_element(const element_base &eb, const point_2t &p, const area<> a, const string name, const bool centerp=true)
Take.
void paint_edges_with_char_index(svg_element &obj, const area<> a, const char firstc, const svg::color klr=color::duboisgreen2, const double rlen=24.0)
Paint the edges of a physical page. Assumes page is square. /.
double space_type
Base floating point type.
Definition a60-svg.h:63
void composite_bleed_areas(svg_element &obj, const svg::select slxt, const double bleedin, const double bleedxoffset=0)
Composite frame on bleed. For printed objects with a center gutter, some intra-page adjustments are n...
string file_to_svg_insert(const string ifile)
Import svg file, convert it to svg_element for insertion. ifile is a plain SVG file with a 1:1 aspect...
svg_element make_svg_2_channel(const space_type deltax, const space_type deltay, const string &outbase)
Take input size and make a two channel (single-image) SVG form.
svg_element nest_inner_svg_element_centered(const svg_element &obj, const point_2t &p)
Take.
void make_2_channel_insert(svg_element &obj, string insert1, string insert2)
double & get_dpi()
Resolution of output display device.
Definition a60-svg.h:82
unsigned int uint
Definition a60-svg.h:58
void make_1_channel_insert(svg_element &obj, string insert1)
string element_to_svg_insert(const string isvgpre)
Import svg file, convert it to svg_element for insertion.
std::tuple< space_type, space_type > point_2t
Point (x,y) in 2D space, space_type defaults to double.
Definition izzi-points.h:22
Datum consolidating style preferences.
void set_colors(const color_qi &klr)
Convenience function to set all colors at once.
static string translate(int x, int y)
static string scale(double factor)
static string rotate(int deg)