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 class PseudoRasterBarCodeSvgDrawer : BaseBarCodeSvgDrawer 102 { 103 override DrawData buildPath(BarCode bc) 104 { 105 auto cW = W, cH = H; 106 107 if (fixSizeMode) 108 { 109 cW /= bc.width; 110 cH /= bc.height; 111 } 112 113 string[] paths; 114 115 long start = -1; 116 float len = 0; 117 118 foreach (size_t y; 0..bc.height) 119 foreach (size_t x; 0..bc.width) 120 { 121 if (bc[x,y]) 122 paths ~= "M%s,%sh%sv%sh-%sz" 123 .format(x*cW+borderX, y*cH+borderY, cW, cH, cW); 124 } 125 126 long w = cast(long)(bc.width * cW + borderX * 2); 127 long h = cast(long)(bc.height * cH + borderY * 2); 128 129 return DrawData(paths.join(" "), w, h); 130 } 131 } 132 133 string flineOffset(string txt) @property @safe 134 { 135 import std..string; 136 import std.algorithm; 137 string[] res; 138 ptrdiff_t offset = -1; 139 foreach (ln; txt.splitLines.map!(a=>a.stripRight)) 140 { 141 // skip empty lines 142 auto sln = ln.strip; 143 if (sln.length == 0) 144 { 145 if (res.length) res ~= ""; 146 continue; 147 } 148 149 if (offset == -1) 150 offset = ln.length - sln.length; 151 152 res ~= ln[min(offset, ln.length - sln.length)..$]; 153 } 154 return res.join("\n"); 155 } 156 157 @safe 158 unittest 159 { 160 enum txt = (" \n some \n text \n "~ 161 " \n here \n\n end ").flineOffset; 162 enum exp = "some\ntext\n\n here\n\nend"; 163 164 static assert(txt == exp); 165 } 166 167 @safe 168 unittest 169 { 170 enum txt = ` 171 some text 172 with 173 wrong formated 174 lines`.flineOffset; 175 enum exp = "some text\n" ~ 176 "with\n" ~ 177 "wrong formated\n" ~ 178 " lines"; 179 static assert(txt == exp); 180 }