1 /** 2 Generic 2D vector renderer. 3 4 Copyright: Guillaume Piolat 2018. 5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 module printed.canvas.irenderer; 8 9 public import printed.canvas.image; 10 11 /// Describes the `printed` 2D renderer. 12 /// 13 /// This is the law, specific implementation MUST obey this interface and 14 /// not the underlying implementation in PDF/SVG/whatever (and if necessary revise this spec). 15 /// 16 /// We are heavily influenced by the HTML5 Canvas 2D context API, for its familiarity. 17 /// See_also: https://www.w3.org/TR/2dcontext/ 18 interface IRenderingContext2D 19 { 20 // GRAPHICAL CONTEXT 21 22 /// Number of units in a page. 23 /// Return: Page width in millimeters. 24 float pageWidth(); 25 26 /// Number of units in a page. 27 /// Return: Page height in millimeters. 28 float pageHeight(); 29 30 /// Push state on state stack. 31 /// What this states contains: 32 /// - transformation matrices 33 void save(); 34 35 /// Pop state stack and restore state 36 void restore(); 37 38 /// Start a new page, finish the previous one. 39 /// This invalidates any transformation. 40 /// The origin (0, 0) becomes again the top-left point of each page. 41 void newPage(); 42 43 44 // TRANSFORMATIONS (default: transform is the identity matrix) 45 // The origin (0, 0) of the default is the top-left point of each page. 46 47 /// Changes the transformation matrix to apply a scaling transformation with the given characteristics. 48 void scale(float x, float y); 49 50 /// Changes the transformation matrix to apply a translation transformation with the given characteristics. 51 void translate(float dx, float dy); 52 53 /// Changes the transformation matrix to apply a rotation transformation with the given characteristics. 54 /// The angle is in radians, the direction is clockwise. 55 /// Params: 56 /// angle The rotation angle, in radians. 57 void rotate(float angle); 58 59 60 // IMAGES 61 62 /// Draws an image at the given position. 63 void drawImage(Image image, float x, float y); 64 65 /// Draws an image at the given position, with the given width and height. 66 /// Both `width` and `height` must be provided. 67 void drawImage(Image image, float x, float y, float width, float height); 68 69 // COLOURS 70 71 /// Changes the current fill brush (Default = "black"). 72 /// Params: 73 /// color Any HTML color string, or a `Brush` if you want to save the parsing cost. 74 void fillStyle(Brush brush); 75 ///ditto 76 void fillStyle(const(char)[] color); // equivalent to `fillStyle(brush(color));` 77 78 /// Changes the current stroke brush (Default = "black"). 79 /// Params: 80 /// color Any HTML color string. 81 void strokeStyle(Brush brush); 82 ///ditto 83 void strokeStyle(const(char)[] color); // equivalent to `strokeStyle(brush(color));` 84 85 // DASHED LINES 86 87 /// Sets the current line dash pattern (as used when stroking). The 88 /// argument is an array of distances for which to alternately have the 89 /// line on and the line off. 90 /// 91 /// Params: 92 /// segments = array of distances for which to alternately have the line on and 93 /// the line off. 94 void setLineDash(float[] segments = []); 95 96 /// Returns a copy of the current line dash pattern. The array returned 97 /// will always have an even number of entries (i.e. the pattern is 98 /// normalized). 99 float[] getLineDash(); 100 101 /// Returns the phase offset (in the same units as the line dash pattern). 102 /// Can be set, to change the phase offset. Values that are not finite 103 /// values are ignored. 104 void lineDashOffset(float offset); 105 /// ditto 106 float lineDashOffset(); 107 108 // BASIC SHAPES 109 110 /// 111 void fillRect(float x, float y, float width, float height); 112 void strokeRect(float x, float y, float width, float height); 113 114 /// Draw filled text. 115 void fillText(const(char)[] text, float x, float y); 116 117 // PATHS 118 /// The context always has a current default path. 119 /// There is only one current path, it is not part of the drawing state. 120 121 /// Resets the current path, and move the cursor to (x, y). 122 void beginPath(float x, float y); 123 124 /// Change the width of a line. 125 /// The whole path has the same line width. 126 void lineWidth(float width); 127 128 /// Add a subpath forming a line. Its exact width is set when the path is drawn with `fill`, `stroke` or `fillAndStroke`. 129 void lineTo(float dx, float dy); 130 131 /// Fills the subpaths of the current path or the given path with the current fill style. 132 /// Uses the last set fill style, line width for the whole path. 133 void fill(); 134 135 /// Strokes the subpaths of the current path or the given path with the current stroke style. 136 /// Uses the last set fill style, line width for the whole path. 137 void stroke(); 138 139 /// Both fills and strokes the subpaths of the current path, in a more efficient way than calling 140 /// `fill` and `stroke` separately. 141 /// Uses the last set fill style, line width for the whole path. 142 void fillAndStroke(); 143 144 /// Close the path, returning to the initial first point. 145 /// TODO: specify exactly what it does. 146 void closePath(); 147 148 149 // FONTS 150 // The specific font will be lazily choosen across all available fonts, 151 // with a matching algorithm. 152 // See_also: `findBestMatchingFont`. 153 154 /// Changes font face (default = Helvetica) 155 void fontFace(string fontFace); 156 157 /// Changes font weight (Default = FontWeight.normal) 158 void fontWeight(FontWeight fontWeight); 159 160 /// Changes font style (default = FontStyle.normal) 161 void fontStyle(FontStyle fontStyle); 162 163 /// Changes font size in points (default = 11pt) 164 /// Warning: not millimeters. 165 void fontSize(float sizeInPt); 166 167 /// Changes text alignment (default = TextAlign.start) 168 void textAlign(TextAlign alignment); 169 170 /// Changes text baseline (default = TextBaseline.alphabetic) 171 void textBaseline(TextBaseline baseline); 172 173 /// Returns a `TextMetrics` struct that contains information about the measured text 174 /// (such as its width, for example). 175 TextMetrics measureText(const(char)[] text); 176 } 177 178 enum TextAlign 179 { 180 /// Align to the start edge of the text (left side in left-to-right text, right side in right-to-left text). 181 /// This is the default. 182 start, 183 184 /// Align to the end edge of the text (right side in left-to-right text, left side in right-to-left text). 185 end, 186 187 /// Align to the left. 188 left, 189 190 /// Align to the right. 191 right, 192 193 /// Align to the center. 194 center 195 } 196 197 /// Text reference baseline. 198 enum TextBaseline // Note: MUST be kept in sync with FontBaseline 199 { 200 top, 201 hanging, 202 middle, 203 alphabetic, // default 204 bottom 205 } 206 207 /// Font weight. 208 enum FontWeight : int 209 { 210 thinest = 0, // Note: thinest doesn't exist in PostScript 211 thin = 100, 212 extraLight = 200, 213 light = 300, 214 normal = 400, 215 medium = 500, 216 semiBold = 600, 217 bold = 700, 218 extraBold = 800, 219 black = 900 220 } 221 222 /// Font style 223 enum FontStyle 224 { 225 normal, 226 italic, 227 oblique 228 } 229 230 /// Make a brush suitable for `fillStyle` and `strokeStyle`. 231 Brush brush(int r, int g, int b, int a) 232 { 233 return Brush(r, g, b, a); 234 } 235 236 ///ditto 237 Brush brush(int r, int g, int b) 238 { 239 return Brush(r, g, b); 240 } 241 242 ///ditto 243 Brush brush(const(char)[] htmlColor) 244 { 245 return Brush(htmlColor); 246 } 247 248 struct Brush 249 { 250 ubyte[4] rgba; 251 252 this(int r, int g, int b) 253 { 254 rgba[0] = cast(ubyte)r; 255 rgba[1] = cast(ubyte)g; 256 rgba[2] = cast(ubyte)b; 257 rgba[3] = 255; 258 } 259 260 this(int r, int g, int b, int a) 261 { 262 rgba[0] = cast(ubyte)r; 263 rgba[1] = cast(ubyte)g; 264 rgba[2] = cast(ubyte)b; 265 rgba[3] = cast(ubyte)a; 266 } 267 268 this(const(char)[] htmlColor) 269 { 270 import printed.htmlcolors; 271 rgba = parseHTMLColor(htmlColor); 272 } 273 274 bool isOpaque() 275 { 276 return rgba[3] == 255; 277 } 278 279 ubyte[4] toRGBAColor() 280 { 281 return rgba; 282 } 283 284 string toSVGColor() 285 { 286 import std.string; 287 // TODO: optimize 288 return format("rgba(%d,%d,%d,%f)", rgba[0], rgba[1], rgba[2], rgba[3] / 255.0f); 289 } 290 } 291 292 293 // Utility functions 294 295 /// Convert points to millimeters. 296 float convertPointsToMillimeters(float pt) pure nothrow @nogc @safe 297 { 298 return pt * 0.35277777778f; 299 } 300 301 /// Convert millimeters to points. 302 float convertMillimetersToPoints(float pt) pure nothrow @nogc @safe 303 { 304 return pt * 2.83464567f; 305 } 306 307 /// The `TextMetrics` interface represents the dimensions of a piece of text, 308 /// as created by the `measureText()` method. 309 /// Reference: https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics 310 struct TextMetrics 311 { 312 /// Suggested horizontal advance to the next block of text. 313 /// This is the so-called "text's advance width". 314 float width; 315 316 /// The distance parallel to the baseline from the alignment point given by 317 /// TextAlign attribute to the left side of the bounding rectangle of the 318 /// given text. Positive numbers indicating a distance going left from the 319 /// given alignment point. 320 /// TODO: currently imprecise. 321 float actualBoundingBoxLeft; 322 323 /// The distance parallel to the baseline from the alignment point given by 324 /// TextAlign attribute to the right side of the bounding rectangle of the 325 /// given text. Positive numbers indicating a distance going right from the 326 /// given alignment point. 327 /// TODO: currently can't be relied upon. 328 float actualBoundingBoxRight; 329 330 /// Distance between left side and right side of the given text. 331 /// TODO: currently can't be relied upon. 332 float actualBoundingBoxWidth; 333 334 /// The distance from the horizontal line indicated by the textBaseline 335 /// attribute to the ascent metric of the font. 336 /// Positive numbers indicating a distance going up from the given baseline. 337 /// Note: doesn't depend upon specific input text in `measureText`. 338 float fontBoundingBoxAscent; 339 340 /// The distance from the horizontal line indicated by the textBaseline 341 /// attribute to the descent metric of the font. 342 /// Positive numbers indicating a distance going down from the given baseline. 343 /// Note: doesn't depend upon specific input text in `measureText`. 344 float fontBoundingBoxDescent; 345 346 /// Distance between ascent and descent baselines in the font. 347 /// Note: doesn't depend upon specific input text in `measureText`. 348 float fontBoundingBoxHeight; 349 350 /// Suggested offset to the next line, baseline to baseline. 351 /// Note: doesn't depend upon specific input text in `measureText`. 352 float lineGap; 353 } 354 355 /// Allows to customize rendering, for format-specific features. 356 struct RenderOptions 357 { 358 // `true` for reproducibility. 359 // `false` for not embedding fonts and have smaller file sizes. 360 // Only supported by SVG/HTML. 361 bool embedFonts; 362 } 363 364 /// The default render options. 365 __gshared immutable RenderOptions defaultRenderOptions = RenderOptions(true);