The OpenD Programming Language

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);