1 ///
2 module barcode.svgdraw;
3 
4 import std..string : format, join;
5 import std.conv : text;
6 
7 import barcode.types;
8 
9 ///
10 interface BarCodeSvgDrawer
11 {
12     ///
13     string draw(BarCode bc);
14 }
15 
16 ///
17 class BaseBarCodeSvgDrawer : BarCodeSvgDrawer
18 {
19     ///
20     bool fixSizeMode = false;
21     ///
22     bool withBackground = true;
23 
24     ///
25     float borderX=50.0f, borderY=50.0f;
26     ///
27     float W=10.0f, H=10.0f;
28 
29     string bgColor = "#FFFFFF";
30     string fgColor = "#000000";
31 
32     ///
33     struct DrawData
34     {
35         ///
36         string svgpath;
37         ///
38         long w, h;
39     }
40 
41     DrawData buildPath(BarCode bc)
42     {
43         auto cW = W, cH = H;
44 
45         if (fixSizeMode)
46         {
47             cW /= bc.width;
48             cH /= bc.height;
49         }
50 
51         string[] paths;
52 
53         long start = -1;
54         float len = 0;
55 
56         foreach (size_t y; 0..bc.height)
57             foreach (size_t x; 0..bc.width)
58             {
59                 if (bc[x,y])
60                 {
61                     if (start == -1)
62                         start = x;
63                     len += cW;
64                 }
65 
66                 if ((!bc[x,y] || x == bc.width-1) && start != -1)
67                 {
68                     paths ~= "M%s,%sh%sv%sh-%sz"
69                         .format(start*cW+borderX,
70                                 y*cH+borderY, len, cH, len);
71                     start = -1;
72                     len = 0;
73                 }
74             }
75 
76         long w = cast(long)(bc.width * cW + borderX * 2);
77         long h = cast(long)(bc.height * cH + borderY * 2);
78 
79         return DrawData(paths.join(" "), w, h);
80     }
81 
82     ///
83     string draw(BarCode bc)
84     {
85         string bgStr;
86         if (withBackground)
87             bgStr = `<rect width="100%" height="100%" fill="`~bgColor~`" stroke-width="0"/>`;
88 
89         auto dd = buildPath(bc);
90 
91         return text(`
92             <?xml version="1.0" encoding="UTF-8"?>
93             <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
94             <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 `, dd.w, " ", dd.h, `">
95                 `, bgStr, `
96                 <path d="`, dd.svgpath, `" fill="`, fgColor, `" stroke-width="0"/>
97             </svg>`).flineOffset;
98     }
99 }
100 
101 string flineOffset(string txt) @property
102 {
103     import std..string;
104     string[] res;
105     ptrdiff_t offset = -1;
106     foreach (ln; txt.splitLines)
107     {
108         if (ln.strip.length == 0)
109         {
110             if(res.length) res ~= "";
111             continue;
112         }
113         if (offset == -1)
114             offset = ln.length - ln.stripLeft.length;
115         res ~= ln.stripRight[offset..$];
116     }
117     return res.join("\n");
118 }
119 
120 unittest
121 {
122     import std..string;
123     enum txt = "    \n            some  \n            text   \n  "~
124                "   \n                here   \n\n            end  ".flineOffset;
125     enum exp = "some\ntext\n\n    here\n\nend";
126     static assert(txt == exp);
127 }