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.
72     /// Params:
73     ///    color Any HTML color string.
74     void fillStyle(Brush brush);
75 
76     /// Changes the current stroke brush.
77     /// Params:
78     ///    color Any HTML color string.
79     void strokeStyle(Brush brush);
80 
81 
82     // BASIC SHAPES
83 
84     ///
85     void fillRect(float x, float y, float width, float height);
86     void strokeRect(float x, float y, float width, float height);
87 
88     /// Draw filled text. 
89     void fillText(string text, float x, float y);
90 
91     // PATHS
92     /// The context always has a current default path. 
93     /// There is only one current path, it is not part of the drawing state.
94 
95     /// Resets the current path, and move the cursor to (x, y).
96     void beginPath(float x, float y);
97 
98     /// Change the width of a line.
99     /// The whole path has the same line width.
100     void lineWidth(float width);
101 
102     /// Add a subpath forming a line. Its exact width is set when the path is drawn with `fill`, `stroke` or `fillAndStroke`.
103     void lineTo(float dx, float dy);
104 
105     /// Fills the subpaths of the current path or the given path with the current fill style.
106     /// Uses the last set fill style, line width for the whole path.
107     void fill();
108 
109     /// Strokes the subpaths of the current path or the given path with the current stroke style.
110     /// Uses the last set fill style, line width for the whole path.
111     void stroke();
112 
113     /// Both fills and strokes the subpaths of the current path, in a more efficient way than calling
114     /// `fill` and `stroke` separately.
115     /// Uses the last set fill style, line width for the whole path.
116     void fillAndStroke();
117 
118     /// Close the path, returning to the initial first point.
119     /// TODO: specify exactly what it does.
120     void closePath();
121 
122 
123     // FONTS
124     // The specific font will be lazily choosen across all available fonts, 
125     // with a matching algorithm.
126     // See_also: `findBestMatchingFont`.
127     
128     /// Changes font face (default = Helvetica)
129     void fontFace(string fontFace);
130 
131     /// Changes font weight (Default = FontWeight.normal)
132     void fontWeight(FontWeight fontWeight);
133 
134     /// Changes font style (default = FontStyle.normal)
135     void fontStyle(FontStyle fontStyle);
136 
137     /// Changes font size in points (default = 11pt)
138     /// Warning: not millimeters.
139     void fontSize(float sizeInPt);
140 
141     /// Changes text alignment (default = TextAlign.start)
142     void textAlign(TextAlign alignment);
143 
144     /// Changes text baseline (default = TextBaseline.alphabetic)
145     void textBaseline(TextBaseline baseline);
146 
147     /// Returns a `TextMetrics` struct that contains information about the measured text 
148     /// (such as its width, for example).
149     TextMetrics measureText(string text);
150 }
151 
152 enum TextAlign
153 {
154     /// Align to the start edge of the text (left side in left-to-right text, right side in right-to-left text).
155     /// This is the default.
156     start,
157 
158     /// Align to the end edge of the text (right side in left-to-right text, left side in right-to-left text).
159     end,
160 
161     /// Align to the left.
162     left,
163 
164     /// Align to the right.
165     right,
166 
167     /// Align to the center.
168     center
169 }
170 
171 enum TextBaseline
172 {
173     top,
174     hanging,
175     middle,
176     alphabetic, // default
177     bottom
178 }
179 
180 /// Font weight
181 enum FontWeight : int
182 {
183     thinest = 0, // Note: thinest doesn't exist in PostScript
184     thin = 100,
185     extraLight = 200,
186     light = 300,
187     normal = 400,
188     medium = 500,
189     semiBold = 600,
190     bold = 700,
191     extraBold = 800,
192     black = 900
193 }
194 
195 /// Font style
196 enum FontStyle
197 {
198     normal,
199     italic,
200     oblique
201 }
202 
203 /// Make a brush suitable for `fillStyle` and `strokeStyle`.
204 Brush brush(int r, int g, int b, int a)
205 {
206     return Brush(r, g, b, a);
207 }
208 
209 ///ditto
210 Brush brush(int r, int g, int b)
211 {
212     return Brush(r, g, b);
213 }
214 
215 ///ditto
216 Brush brush(string htmlColor)
217 {
218     return Brush(htmlColor);
219 }
220 
221 struct Brush
222 {
223     ubyte[4] rgba;
224 
225     this(int r, int g, int b)
226     {
227         rgba[0] = cast(ubyte)r;
228         rgba[1] = cast(ubyte)g;
229         rgba[2] = cast(ubyte)b;
230         rgba[3] = 255;
231     }
232 
233     this(int r, int g, int b, int a)
234     {
235         rgba[0] = cast(ubyte)r;
236         rgba[1] = cast(ubyte)g;
237         rgba[2] = cast(ubyte)b;
238         rgba[3] = cast(ubyte)a;
239     }
240 
241     this(string htmlColor)
242     {
243         import printed.htmlcolors;
244         rgba = parseHTMLColor(htmlColor);
245     }
246 
247     bool isOpaque()
248     {
249         return rgba[3] == 255;
250     }
251 
252     ubyte[4] toRGBAColor()
253     {
254         return rgba;
255     }
256 
257     string toSVGColor()
258     {
259         import std..string;
260         // TODO: optimize
261         return format("rgba(%d,%d,%d,%f)", rgba[0], rgba[1], rgba[2], rgba[3] / 255.0f);
262     }
263 }
264 
265 
266 // Utility functions
267 
268 float convertPointsToMillimeters(float pt) pure nothrow @nogc @safe
269 {
270     return pt * 0.35277777778f;
271 }
272 
273 float convertMillimetersToPoints(float pt) pure nothrow @nogc @safe
274 {
275     return pt * 2.83464567f;
276 }
277 
278 
279 /// The `TextMetrics` interface represents the dimensions of a piece of text, 
280 /// as created by the `measureText()` method.
281 struct TextMetrics
282 {
283     float width;   /// Suggested horizontal advance to the next block of text.
284     float lineGap; /// Suggested offset to the next line, baseline to baseline.
285 }
286 
287