1 module skia.SKFont;
2 
3 import skia.SkiaApi;
4 import skia.SKPaint;
5 import skia.Definitions;
6 import skia.Exceptions;
7 import skia.MathTypes;
8 import skia.SKPath;
9 import skia.SKTypeface;
10 import skia.Dictionary;
11 import skia.SKObject;
12 import skia.IDisposable;
13 import std.range;
14 import skia.Exceptions;
15 import skia.SKMatrix;
16 import skia.SKPathMeasure;
17 
18 import std.experimental.logger;
19 import std.algorithm;
20 
21 /**
22  * 
23  */
24 class SKFont : SKObject {
25 	enum float DefaultSize = 12f;
26 	enum float DefaultScaleX = 1f;
27 	enum float DefaultSkewX = 0f;
28 
29 	this(void* handle, bool owns) {
30 		super(handle, owns);
31 	}
32 
33 	this() {
34 		this(SkiaApi.sk_font_new(), true);
35 		if (Handle is null)
36 			throw new InvalidOperationException("Unable to create a new SKFont instance.");
37 	}
38 
39 	this(SKTypeface typeface, float size = DefaultSize,
40 			float scaleX = DefaultScaleX, float skewX = DefaultSkewX) {
41 		void* handle = null;
42 		if (typeface !is null)
43 			handle = typeface.Handle();
44 		this(SkiaApi.sk_font_new_with_values(cast(sk_typeface_t*) handle, size,
45 				scaleX, skewX), true);
46 		if (Handle is null)
47 			throw new InvalidOperationException("Unable to create a new SKFont instance.");
48 	}
49 
50 	protected override void DisposeNative() {
51 		return SkiaApi.sk_font_delete(cast(sk_font_t*) Handle);
52 	}
53 
54 	bool ForceAutoHinting() {
55 		return SkiaApi.sk_font_is_force_auto_hinting(cast(sk_font_t*) Handle);
56 	}
57 
58 	void ForceAutoHinting(bool value) {
59 		SkiaApi.sk_font_set_force_auto_hinting(cast(sk_font_t*) Handle, value);
60 	}
61 
62 	bool EmbeddedBitmaps() {
63 		return SkiaApi.sk_font_is_embedded_bitmaps(cast(sk_font_t*) Handle);
64 	}
65 
66 	void EmbeddedBitmaps(bool value) {
67 		SkiaApi.sk_font_set_embedded_bitmaps(cast(sk_font_t*) Handle, value);
68 	}
69 
70 	bool Subpixel() {
71 		return SkiaApi.sk_font_is_subpixel(cast(sk_font_t*) Handle);
72 	}
73 
74 	void Subpixel(bool value) {
75 		SkiaApi.sk_font_set_subpixel(cast(sk_font_t*) Handle, value);
76 	}
77 
78 	bool LinearMetrics() {
79 		return SkiaApi.sk_font_is_linear_metrics(cast(sk_font_t*) Handle);
80 	}
81 
82 	void LinearMetrics(bool value) {
83 		SkiaApi.sk_font_set_linear_metrics(cast(sk_font_t*) Handle, value);
84 	}
85 
86 	bool Embolden() {
87 		return SkiaApi.sk_font_is_embolden(cast(sk_font_t*) Handle);
88 	}
89 
90 	void Embolden(bool value) {
91 		SkiaApi.sk_font_set_embolden(cast(sk_font_t*) Handle, value);
92 	}
93 
94 	bool BaselineSnap() {
95 		return SkiaApi.sk_font_is_baseline_snap(cast(sk_font_t*) Handle);
96 	}
97 
98 	void BaselineSnap(bool value) {
99 		SkiaApi.sk_font_set_baseline_snap(cast(sk_font_t*) Handle, value);
100 	}
101 
102 	SKFontEdging Edging() {
103 		return SkiaApi.sk_font_get_edging(cast(sk_font_t*) Handle);
104 	}
105 
106 	void Edging(SKFontEdging value) {
107 		SkiaApi.sk_font_set_edging(cast(sk_font_t*) Handle, value);
108 	}
109 
110 	SKFontHinting Hinting() {
111 		return SkiaApi.sk_font_get_hinting(cast(sk_font_t*) Handle);
112 	}
113 
114 	void Hinting(SKFontHinting value) {
115 		SkiaApi.sk_font_set_hinting(cast(sk_font_t*) Handle, value);
116 	}
117 
118 	SKTypeface Typeface() {
119 		return SKTypeface.GetObject(SkiaApi.sk_font_get_typeface(cast(sk_font_t*) Handle));
120 	}
121 
122 	void Typeface(SKTypeface value) {
123 		SkiaApi.sk_font_set_typeface(cast(sk_font_t*) Handle,
124 				cast(sk_typeface_t*)(value is null ? null : value.Handle));
125 	}
126 
127 	float Size() {
128 		return SkiaApi.sk_font_get_size(cast(sk_font_t*) Handle);
129 	}
130 
131 	void Size(float value) {
132 		SkiaApi.sk_font_set_size(cast(sk_font_t*) Handle, value);
133 	}
134 
135 	float ScaleX() {
136 		return SkiaApi.sk_font_get_scale_x(cast(sk_font_t*) Handle);
137 	}
138 
139 	void ScaleX(float value) {
140 		SkiaApi.sk_font_set_scale_x(cast(sk_font_t*) Handle, value);
141 	}
142 
143 	float SkewX() {
144 		return SkiaApi.sk_font_get_skew_x(cast(sk_font_t*) Handle);
145 	}
146 
147 	void SkewX(float value) {
148 		SkiaApi.sk_font_set_skew_x(cast(sk_font_t*) Handle, value);
149 	}
150 
151 	// FontSpacing
152 
153 	float Spacing() {
154 		return SkiaApi.sk_font_get_metrics(cast(sk_font_t*) Handle, null);
155 	}
156 
157 	// FontMetrics
158 
159 	SKFontMetrics Metrics() {
160 
161 		SKFontMetrics metrics;
162 		GetFontMetrics(metrics);
163 		return metrics;
164 
165 	}
166 
167 	float GetFontMetrics(out SKFontMetrics metrics) {
168 		SKFontMetrics* m = &metrics;
169 		return SkiaApi.sk_font_get_metrics(cast(sk_font_t*) Handle, m);
170 	}
171 
172 	// GetGlyph
173 
174 	ushort GetGlyph(int codepoint) {
175 		return SkiaApi.sk_font_unichar_to_glyph(cast(sk_font_t*) Handle, codepoint);
176 	}
177 
178 	// GetGlyphs
179 
180 	ushort[] GetGlyphs(const(int)[] codepoints) {
181 		auto glyphs = new ushort[codepoints.length];
182 		GetGlyphs (codepoints, glyphs);
183 		return glyphs;
184 	}
185 
186 	void GetGlyphs(const(int)[] codepoints, ushort[] glyphs) {
187 		if (codepoints.empty())
188 			return;
189 
190 		if (glyphs.length != codepoints.length)
191 			throw new ArgumentException("The length of glyphs must be the same as the length of codepoints.",
192 					glyphs.stringof);
193 
194 		int* up = cast(int*)codepoints.ptr;
195 		ushort* gp = glyphs.ptr;
196 		SkiaApi.sk_font_unichars_to_glyphs(cast(sk_font_t*) Handle, up,
197 				cast(int) codepoints.length, gp);
198 	}
199 
200 	// GetGlyphs
201 
202 	ushort[] GetGlyphs(string text) {
203 		return GetGlyphs(cast(const(char)[]) text);
204 	}
205 
206 	ushort[] GetGlyphs(const(char)[] text) {
207 		void* t = cast(void*) text.ptr;
208 		return GetGlyphs(t, cast(int) text.length, SKTextEncoding.Utf8);
209 	}
210 
211 	ushort[] GetGlyphs(const(byte)[] text, SKTextEncoding encoding) {
212 		void* t = cast(void*) text.ptr;
213 		return GetGlyphs(t, cast(int) text.length, encoding);
214 	}
215 
216 	// ushort[] GetGlyphs (void* text, int length, SKTextEncoding encoding)
217 	// {
218 	//     return GetGlyphs (cast(void*)text, length, encoding);
219 	// }
220 
221 	void GetGlyphs(string text, ushort[] glyphs) {
222 		return GetGlyphs(cast(const(char)[]) text, glyphs);
223 	}
224 
225 	void GetGlyphs(const(char)[] text, ushort[] glyphs) {
226 		void* t = cast(void*) text.ptr;
227 		GetGlyphs(t, cast(int) text.length, SKTextEncoding.Utf8, glyphs);
228 	}
229 
230 	void GetGlyphs(const(byte)[] text, SKTextEncoding encoding, ushort[] glyphs) {
231 		void* t = cast(void*) text.ptr;
232 		GetGlyphs(t, cast(int) text.length, encoding, glyphs);
233 	}
234 
235 	// void GetGlyphs (void* text, int length, SKTextEncoding encoding, ushort[] glyphs)
236 	// {
237 	//     return GetGlyphs (cast(void*)text, length, encoding, glyphs);
238 	// }
239 
240 	ushort[] GetGlyphs(void* text, int length, SKTextEncoding encoding) {
241 		if (!ValidateTextArgs(text, length, encoding))
242 			return new ushort[0];
243 
244 		int n = CountGlyphs(text, length, encoding);
245 		tracef("n: %d, length: %d, encoding: %s", n, length, encoding);
246 		if (n <= 0)
247 			return [];
248 
249 		auto glyphs = new ushort[n];
250 		GetGlyphs(text, length, encoding, glyphs);
251 		return glyphs;
252 	}
253 
254 	void GetGlyphs(void* text, int length, SKTextEncoding encoding, ushort[] glyphs) {
255 		if (!ValidateTextArgs(text, length, encoding))
256 			return;
257 
258 		ushort* gp = glyphs.ptr;
259 		SkiaApi.sk_font_text_to_glyphs(cast(sk_font_t*) Handle, text,
260 				cast(int) length, encoding, gp, cast(int) glyphs.length);
261 	}
262 
263 	// ContainsGlyph
264 
265 	bool ContainsGlyph(int codepoint) {
266 		return GetGlyph(codepoint) != 0;
267 	}
268 
269 	// ContainsGlyphs
270 
271 	bool ContainsGlyphs(const(int)[] codepoints) {
272 		return ContainsGlyphs(GetGlyphs(codepoints));
273 	}
274 
275 	bool ContainsGlyphs(string text) {
276 		return ContainsGlyphs(GetGlyphs(text));
277 	}
278 
279 	bool ContainsGlyphs(const(char)[] text) {
280 		return ContainsGlyphs(GetGlyphs(text));
281 	}
282 
283 	bool ContainsGlyphs(const(byte)[] text, SKTextEncoding encoding) {
284 		return ContainsGlyphs(GetGlyphs(text, encoding));
285 	}
286 
287 	bool ContainsGlyphs(void* text, int length, SKTextEncoding encoding) {
288 		return ContainsGlyphs(GetGlyphs(text, length, encoding));
289 	}
290 
291 	private bool ContainsGlyphs(ushort[] glyphs) {
292 		// return Array.IndexOf (glyphs, cast(ushort)0) == -1;
293 		return false;
294 	}
295 
296 	// CountGlyphs
297 
298 	int CountGlyphs(string text) {
299 		return CountGlyphs(cast(const(char)[]) text);
300 	}
301 
302 	int CountGlyphs(const(char)[] text) {
303 		void* t = cast(void*) text.ptr;
304 		return CountGlyphs(t, cast(int) text.length, SKTextEncoding.Utf8);
305 	}
306 
307 	int CountGlyphs(const(byte)[] text, SKTextEncoding encoding) {
308 		void* t = cast(void*) text.ptr;
309 		return CountGlyphs(t, cast(int) text.length, encoding);
310 	}
311 
312 
313 	int CountGlyphs(void* text, int length, SKTextEncoding encoding) {
314 		if (!ValidateTextArgs(text, length, encoding))
315 			return 0;
316 
317 		// int len = SkiaApi.sk_font_text_to_glyphs(cast(sk_font_t*) Handle, text,
318 		// 		cast(size_t) length, encoding, null, 0);
319 
320 		// tracef("len: %d", len);
321 		// return len;
322 
323 		return SkiaApi.sk_font_text_to_glyphs(cast(sk_font_t*) Handle, text,
324 				cast(size_t) length, encoding, null, 0);
325 	}
326 
327 	// MeasureText (text)
328 
329 	float MeasureText (string text, SKPaint paint = null)
330 	{
331 		void* t = cast(void*) text.ptr;
332 		return MeasureText(t, cast(int) text.length, SKTextEncoding.Utf8, null, paint);
333 	}
334 
335 	// float MeasureText(const(char)[] text, SKPaint paint = null) {
336 	// 	void* t = cast(void*) text.ptr;
337 	// 	return MeasureText(t, cast(int) text.length, SKTextEncoding.Utf8, null, paint);
338 	// }
339 
340 	float MeasureText(const(byte)[] text, SKTextEncoding encoding, SKPaint paint = null) {
341 		void* t = cast(void*) text.ptr;
342 		return MeasureText(t, cast(int) text.length, encoding, null, paint);
343 	}
344 
345 	float MeasureText(void* text, int length, SKTextEncoding encoding, SKPaint paint = null) {
346 		return MeasureText(cast(void*) text, length, encoding, null, paint);
347 	}
348 
349 	float MeasureText(string text, out SKRect bounds, SKPaint paint = null) {
350 		void* t = cast(void*) text.ptr;
351 		SKRect* b = &bounds;
352 		return MeasureText(t, cast(int) text.length, SKTextEncoding.Utf8, b, paint);
353 	}
354 
355 	float MeasureText(const(byte)[] text, SKTextEncoding encoding,
356 			out SKRect bounds, SKPaint paint = null) {
357 		void* t = cast(void*) text.ptr;
358 		SKRect* b = &bounds;
359 		return MeasureText(t, cast(int) text.length, encoding, b, paint);
360 	}
361 
362 	float MeasureText(void* text, int length, SKTextEncoding encoding,
363 			out SKRect bounds, SKPaint paint = null) {
364 		SKRect* b = &bounds;
365 		return MeasureText(cast(void*) text, length, encoding, b, paint);
366 	}
367 
368 	float MeasureText(void* text, int length, SKTextEncoding encoding,
369 			SKRect* bounds, SKPaint paint) {
370 		if (!ValidateTextArgs(text, length, encoding))
371 			return 0;
372 
373 		float measuredWidth;
374 		void* paintHandle = null;
375 		if(paint !is null) {
376 			paintHandle = paint.Handle;
377 		}
378 
379 		SkiaApi.sk_font_measure_text_no_return(cast(sk_font_t*) Handle, text,
380 				length, encoding, bounds, cast(sk_paint_t*)paintHandle, &measuredWidth);
381 		return measuredWidth;
382 	}
383 
384 	// MeasureText (glyphs)
385 
386 	float MeasureText(ushort[] glyphs, SKPaint paint = null) {
387 		ushort* gp = glyphs.ptr;
388 		return MeasureText(gp, cast(int) glyphs.length * 2, SKTextEncoding.GlyphId, null, paint);
389 	}
390 
391 	float MeasureText(ushort[] glyphs, out SKRect bounds, SKPaint paint = null) {
392 		ushort* gp = glyphs.ptr;
393 		SKRect* b = &bounds;
394 		return MeasureText(gp, cast(int) glyphs.length * 2, SKTextEncoding.GlyphId, b, paint);
395 	}
396 
397 	// BreakText
398 
399 	int BreakText(string text, float maxWidth, out float measuredWidth, SKPaint paint = null) {
400 		return BreakText(cast(const(char)[]) text, maxWidth, measuredWidth, paint);
401 	}
402 
403 	int BreakText(const(char)[] text, float maxWidth, out float measuredWidth, SKPaint paint = null) {
404 		void* t = cast(void*) text.ptr;
405 		float* mw = &measuredWidth;
406 		{
407 			auto bytesRead = BreakText(t, cast(int) text.length,
408 					SKTextEncoding.Utf8, maxWidth, mw, paint);
409 			return bytesRead / 2;
410 		}
411 	}
412 
413 	int BreakText(const(byte)[] text, SKTextEncoding encoding, float maxWidth,
414 			out float measuredWidth, SKPaint paint = null) {
415 		void* t = cast(void*) text.ptr;
416 		float* mw = &measuredWidth;
417 		return BreakText(t, cast(int) text.length, encoding, maxWidth, mw, paint);
418 	}
419 
420 	int BreakText(void* text, int length, SKTextEncoding encoding, float maxWidth,
421 			out float measuredWidth, SKPaint paint = null) {
422 		float* mw = &measuredWidth;
423 		return BreakText(cast(void*) text, length, encoding, maxWidth, mw, paint);
424 	}
425 
426 	int BreakText(void* text, int length, SKTextEncoding encoding, float maxWidth,
427 			float* measuredWidth, SKPaint paint) {
428 		if (!ValidateTextArgs(text, length, encoding))
429 			return 0;
430 
431 		return cast(int) SkiaApi.sk_font_break_text(cast(sk_font_t*) Handle, text,
432 				length, encoding, maxWidth, measuredWidth,
433 				cast(sk_paint_t*)(paint.Handle ? paint.Handle : null));
434 	}
435 
436 	// GetGlyphPositions (text)
437 
438 	SKPoint[] GetGlyphPositions(string text, SKPoint origin = SKPoint.Empty) {
439 		return GetGlyphPositions(cast(const(char)[]) text, origin);
440 	}
441 
442 	SKPoint[] GetGlyphPositions(const(char)[] text, SKPoint origin = SKPoint.Empty) {
443 		void* t = cast(void*) text.ptr;
444 		// return GetGlyphPositions(t, cast(int) text.length * 2, SKTextEncoding.Utf16, origin);
445 		return GetGlyphPositions(t, cast(int) text.length, SKTextEncoding.Utf8, origin);
446 	}
447 
448 	SKPoint[] GetGlyphPositions(const(byte)[] text, SKTextEncoding encoding,
449 			SKPoint origin = SKPoint.Empty) {
450 		void* t = cast(void*) text.ptr;
451 		return GetGlyphPositions(t, cast(int) text.length, encoding, origin);
452 	}
453 
454 	// SKPoint[] GetGlyphPositions (void* text, int length, SKTextEncoding encoding, SKPoint origin = SKPoint.Empty)
455 	// {
456 	//     return GetGlyphPositions (cast(void*)text, length, encoding, origin);
457 	// }
458 
459 	void GetGlyphPositions(string text, SKPoint[] offsets, SKPoint origin = SKPoint.Empty) {
460 		return GetGlyphPositions(cast(const(char)[]) text, offsets, origin);
461 	}
462 
463 	void GetGlyphPositions(const(char)[] text, SKPoint[] offsets, SKPoint origin = SKPoint.Empty) {
464 		void* t = cast(void*) text.ptr;
465 		// GetGlyphPositions(t, cast(int) text.length * 2, SKTextEncoding.Utf16, offsets, origin);
466 		GetGlyphPositions(t, cast(int) text.length, SKTextEncoding.Utf8, offsets, origin);
467 	}
468 
469 	void GetGlyphPositions(const(byte)[] text, SKTextEncoding encoding,
470 			SKPoint[] offsets, SKPoint origin = SKPoint.Empty) {
471 		void* t = cast(void*) text.ptr;
472 		GetGlyphPositions(t, cast(int) text.length, encoding, offsets, origin);
473 	}
474 
475 	SKPoint[] GetGlyphPositions(void* text, int length, SKTextEncoding encoding, SKPoint origin) {
476 		if (!ValidateTextArgs (text, cast(int)length, encoding))
477 			return [];
478 
479 		auto n = CountGlyphs (text, cast(int)length, encoding);
480 		if (n <= 0)
481 			return [];
482 
483 		SKPoint[] positions = new SKPoint[n];
484 		GetGlyphPositions (text, length, encoding, positions, origin);
485 		return positions;
486 	}
487 
488 	void GetGlyphPositions (void* text, int length, SKTextEncoding encoding, SKPoint[] offsets, SKPoint origin)
489 	{
490 		warning("TODO");
491 
492 		if (!ValidateTextArgs (text, length, encoding))
493 			return;
494 
495 		auto n = offsets.length;
496 		if (n <= 0)
497 			return;
498 
499 		// auto glyphs =  Utils.RentArray<ushort> (n);
500 		// scope(exit) {
501 		//     glyphs.Dispose();
502 		// }
503 
504 		// GetGlyphs (text, length, encoding, glyphs);
505 		// GetGlyphPositions (glyphs, offsets, origin);
506 	}
507 
508 	// GetGlyphPositions (glyphs)
509 
510 	SKPoint[] GetGlyphPositions(const(ushort)[] glyphs, SKPoint origin = SKPoint.Empty) {
511 		SKPoint[] positions = new SKPoint[glyphs.length];
512 		GetGlyphPositions (glyphs, positions, origin);
513 		return positions;
514 	}
515 
516 	void GetGlyphPositions(const(ushort)[] glyphs, SKPoint[] positions, SKPoint origin = SKPoint.Empty) {
517 		if (glyphs.length != positions.length)
518 			throw new ArgumentException("The length of glyphs must be the same as the length of positions.",
519 					positions.stringof);
520 
521 		ushort* gp = cast(ushort*)glyphs.ptr;
522 		SKPoint* pp = positions.ptr;
523 		SkiaApi.sk_font_get_pos(cast(sk_font_t*) Handle, gp, cast(int) glyphs.length, pp, &origin);
524 	}
525 
526 	// GetGlyphOffsets (text)
527 
528 	float[] GetGlyphOffsets(string text, float origin = 0f) {
529 		return GetGlyphOffsets(cast(const(char)[]) text, origin);
530 	}
531 
532 	float[] GetGlyphOffsets(const(char)[] text, float origin = 0f) {
533 		void* t = cast(void*) text.ptr;
534 		return GetGlyphOffsets(t, cast(int) text.length, SKTextEncoding.Utf8, origin);
535 	}
536 
537 	float[] GetGlyphOffsets(byte[] text, SKTextEncoding encoding, float origin = 0f) {
538 		void* t = text.ptr;
539 		return GetGlyphOffsets(t, cast(int) text.length, encoding, origin);
540 	}
541 
542 	// float[] GetGlyphOffsets (void* text, int length, SKTextEncoding encoding, float origin = 0f)
543 	// {
544 	//     return GetGlyphOffsets (cast(void*)text, length, encoding, origin);
545 	// }
546 
547 	void GetGlyphOffsets(string text, float[] offsets, float origin = 0f) {
548 		GetGlyphOffsets(cast(char[]) text, offsets, origin);
549 	}
550 
551 	void GetGlyphOffsets(char[] text, float[] offsets, float origin = 0f) {
552 		void* t = text.ptr;
553 		GetGlyphOffsets(t, cast(int) text.length, SKTextEncoding.Utf8, offsets, origin);
554 	}
555 
556 	void GetGlyphOffsets(byte[] text, SKTextEncoding encoding, float[] offsets, float origin = 0f) {
557 		void* t = text.ptr;
558 		GetGlyphOffsets(t, cast(int) text.length, encoding, offsets, origin);
559 	}
560 
561 	void GetGlyphOffsets(void* text, int length, SKTextEncoding encoding,
562 			float[] offsets, float origin = 0f) {
563 		return GetGlyphOffsets(cast(void*) text, length, encoding, offsets, origin);
564 	}
565 
566 	float[] GetGlyphOffsets(void* text, int length, SKTextEncoding encoding, float origin) {
567 		if (!ValidateTextArgs(text, length, encoding))
568 			return new float[0];
569 
570 		auto n = CountGlyphs(text, length, encoding);
571 		if (n <= 0)
572 			return new float[0];
573 
574 		auto offsets = new float[n];
575 		GetGlyphOffsets(text, length, encoding, offsets, origin);
576 		return offsets;
577 	}
578 
579 	// void GetGlyphOffsets (void* text, int length, SKTextEncoding encoding, float[] offsets, float origin)
580 	// {
581 	// 	if (!ValidateTextArgs (text, length, encoding))
582 	// 		return;
583 
584 	// 	auto n = offsets.length;
585 	// 	if (n <= 0)
586 	// 		return;
587 
588 	// 	auto glyphs =  Utils.RentArray<ushort> (n);
589 	// 	scope(exit) {
590 	// 	    glyphs.Dispose();
591 	// 	}
592 
593 	// 	GetGlyphs (text, length, encoding, glyphs);
594 	// 	GetGlyphOffsets (glyphs, offsets, origin);
595 	// }
596 
597 	// GetGlyphOffsets (glyphs)
598 
599 	float[] GetGlyphOffsets(ushort[] glyphs, float origin = 0f) {
600 		auto offsets = new float[glyphs.length];
601 		GetGlyphOffsets(glyphs, offsets, origin);
602 		return offsets;
603 	}
604 
605 	void GetGlyphOffsets(ushort[] glyphs, float[] offsets, float origin = 0f) {
606 		if (glyphs.length != offsets.length)
607 			throw new ArgumentException("The length of glyphs must be the same as the length of offsets.",
608 					offsets.stringof);
609 
610 		ushort* gp = glyphs.ptr;
611 		float* pp = offsets.ptr;
612 		SkiaApi.sk_font_get_xpos(cast(sk_font_t*) Handle, gp, cast(int) glyphs.length, pp, origin);
613 	}
614 
615 	// GetGlyphWidths (text)
616 
617 	float[] GetGlyphWidths(string text, SKPaint paint = null) {
618 		return GetGlyphWidths(cast(char[]) text, paint);
619 	}
620 
621 	float[] GetGlyphWidths(char[] text, SKPaint paint = null) {
622 		void* t = text.ptr;
623 		return GetGlyphWidths(t, cast(int) text.length, SKTextEncoding.Utf8, paint);
624 	}
625 
626 	float[] GetGlyphWidths(byte[] text, SKTextEncoding encoding, SKPaint paint = null) {
627 		void* t = text.ptr;
628 		return GetGlyphWidths(t, cast(int) text.length, encoding, paint);
629 	}
630 
631 	// float[] GetGlyphWidths (void* text, int length, SKTextEncoding encoding, SKPaint paint = null)
632 	// {
633 	//     return GetGlyphWidths (cast(void*)text, length, encoding, paint);
634 	// }
635 
636 	float[] GetGlyphWidths(string text, out SKRect[] bounds, SKPaint paint = null) {
637 		return GetGlyphWidths(cast(char[]) text, bounds, paint);
638 	}
639 
640 	float[] GetGlyphWidths(char[] text, out SKRect[] bounds, SKPaint paint = null) {
641 		void* t = text.ptr;
642 		return GetGlyphWidths(t, cast(int) text.length, SKTextEncoding.Utf8, bounds, paint);
643 	}
644 
645 	float[] GetGlyphWidths(byte[] text, SKTextEncoding encoding,
646 			out SKRect[] bounds, SKPaint paint = null) {
647 		void* t = text.ptr;
648 		return GetGlyphWidths(t, cast(int) text.length, encoding, bounds, paint);
649 	}
650 
651 	// float[] GetGlyphWidths (void* text, int length, SKTextEncoding encoding, out SKRect[] bounds, SKPaint paint = null)
652 	// {
653 	//     return GetGlyphWidths (cast(void*)text, length, encoding,  bounds, paint);
654 	// }
655 
656 	void GetGlyphWidths(string text, float[] widths, SKRect[] bounds, SKPaint paint = null) {
657 		return GetGlyphWidths(cast(char[]) text, widths, bounds, paint);
658 	}
659 
660 	void GetGlyphWidths(const(char)[] text, float[] widths, SKRect[] bounds, SKPaint paint = null) {
661 		void* t = cast(void*) text.ptr;
662 		GetGlyphWidths(t, cast(int) text.length, SKTextEncoding.Utf8, widths, bounds, paint);
663 	}
664 
665 	void GetGlyphWidths(const(byte)[] text, SKTextEncoding encoding,
666 			float[] widths, SKRect[] bounds, SKPaint paint = null) {
667 		void* t = cast(void*) text.ptr;
668 		GetGlyphWidths(t, cast(int) text.length, encoding, widths, bounds, paint);
669 	}
670 
671 	// void GetGlyphWidths (void* text, int length, SKTextEncoding encoding, float[] widths, SKRect[] bounds, SKPaint paint = null)
672 	// {
673 	//     return GetGlyphWidths (cast(void*)text, length, encoding, widths, bounds, paint);
674 	// }
675 
676 	float[] GetGlyphWidths(void* text, int length, SKTextEncoding encoding, SKPaint paint) {
677 		if (!ValidateTextArgs(text, length, encoding))
678 			return new float[0];
679 
680 		auto n = CountGlyphs(text, length, encoding);
681 		if (n <= 0)
682 			return new float[0];
683 
684 		auto widths = new float[n];
685 		GetGlyphWidths(text, length, encoding, widths, null, paint);
686 		return widths;
687 	}
688 
689 	float[] GetGlyphWidths(void* text, int length, SKTextEncoding encoding,
690 			out SKRect[] bounds, SKPaint paint) {
691 		if (!ValidateTextArgs(text, length, encoding)) {
692 			bounds = new SKRect[0];
693 			return new float[0];
694 		}
695 
696 		auto n = CountGlyphs(text, length, encoding);
697 		if (n <= 0) {
698 			bounds = new SKRect[0];
699 			return new float[0];
700 		}
701 
702 		bounds = new SKRect[n];
703 		auto widths = new float[n];
704 		GetGlyphWidths(text, length, encoding, widths, bounds, paint);
705 		return widths;
706 	}
707 
708 	void GetGlyphWidths(void* text, int length, SKTextEncoding encoding,
709 			float[] widths, SKRect[] bounds, SKPaint paint) {
710 
711 		warning("TODO");
712 		if (!ValidateTextArgs (text, length, encoding))
713 			return;
714 
715 		auto n = max(widths.length, bounds.length);
716 		if (n <= 0)
717 			return;
718 
719 		// // make sure that the destination spans are either empty/null or the same length
720 		// if (widths.length != 0 && widths.length != n)
721 		// 	throw new ArgumentException ("The length of widths must be equal to the length of bounds or empty.", widths.stringof);
722 		// if (bounds.length != 0 && bounds.length != n)
723 		// 	throw new ArgumentException ("The length of bounds must be equal to the length of widths or empty.", bounds.stringof);
724 
725 		// auto glyphs =  Utils.RentArray<ushort> (n);
726 		// scope(exit) {
727 		//     glyphs.Dispose();
728 		// }
729 
730 		// GetGlyphs (text, length, encoding, glyphs);
731 		// GetGlyphWidths (glyphs, widths, bounds, paint);
732 	}
733 
734 	// GetGlyphWidths (glyphs)
735 
736 	float[] GetGlyphWidths(const(ushort)[] glyphs, SKPaint paint = null) {
737 		auto widths = new float[glyphs.length];
738 		GetGlyphWidths(glyphs, widths, null, paint);
739 		return widths;
740 	}
741 
742 	float[] GetGlyphWidths(const(ushort)[] glyphs, out SKRect[] bounds, SKPaint paint = null) {
743 		auto widths = new float[glyphs.length];
744 		bounds = new SKRect[glyphs.length];
745 		GetGlyphWidths(glyphs, widths, bounds, paint);
746 		return widths;
747 	}
748 
749 	void GetGlyphWidths(const(ushort)[] glyphs, float[] widths, SKRect[] bounds, SKPaint paint = null) {
750 		ushort* gp = cast(ushort*) glyphs;
751 		float* wp = widths.ptr;
752 		SKRect* bp = bounds.ptr;
753 		{
754 			auto w = widths.length > 0 ? wp : null;
755 			auto b = bounds.length > 0 ? bp : null;
756 			SkiaApi.sk_font_get_widths_bounds(cast(sk_font_t*) Handle, gp,
757 					cast(int) glyphs.length, w, b,
758 					cast(sk_paint_t*)(paint.Handle ? paint.Handle : null));
759 		}
760 	}
761 
762 	// GetGlyphPath
763 
764 	SKPath GetGlyphPath(ushort glyph) {
765 		auto path = new SKPath();
766 		if (!SkiaApi.sk_font_get_path(cast(sk_font_t*) Handle, glyph,
767 				cast(sk_path_t*) path.Handle)) {
768 			path.Dispose();
769 			path = null;
770 		}
771 		return path;
772 	}
773 
774 	// GetTextPath (text)
775 
776 	SKPath GetTextPath(string text, SKPoint origin = SKPoint.Empty) {
777 		return GetTextPath(cast(const(char)[]) text, origin);
778 	}
779 
780 	SKPath GetTextPath(const(char)[] text, SKPoint origin = SKPoint.Empty) {
781 		void* t = cast(void*) text.ptr;
782 		return GetTextPath(t, cast(int) text.length, SKTextEncoding.Utf8, origin);
783 	}
784 
785 	SKPath GetTextPath(const(byte)[] text, SKTextEncoding encoding, SKPoint origin = SKPoint.Empty) {
786 		void* t = cast(void*) text.ptr;
787 		return GetTextPath(t, cast(int) text.length, encoding, origin);
788 	}
789 
790 	SKPath GetTextPath(void* text, int length, SKTextEncoding encoding, SKPoint origin) {
791 		auto path = new SKPath();
792 		if (!ValidateTextArgs(text, length, encoding))
793 			return path;
794 
795 		SkiaApi.sk_text_utils_get_path(text, length, encoding,
796 				origin.X, origin.Y, cast(sk_font_t*) Handle, cast(sk_path_t*) path.Handle);
797 		return path;
798 	}
799 
800 	// GetTextPath (positioned)
801 
802 	SKPath GetTextPath(string text, const(SKPoint)[] positions) {
803 		return GetTextPath(cast(const(char)[]) text, positions);
804 	}
805 
806 	SKPath GetTextPath(const(char)[] text, const(SKPoint)[] positions) {
807 		void* t = cast(void*) text.ptr;
808 		return GetTextPath(t, cast(int) text.length, SKTextEncoding.Utf8, positions);
809 	}
810 
811 	SKPath GetTextPath(const(byte)[] text, SKTextEncoding encoding, const(SKPoint)[] positions) {
812 		void* t = cast(void*) text.ptr;
813 		return GetTextPath(t, cast(int) text.length, encoding, positions);
814 	}
815 
816 	// SKPath GetTextPath (void* text, int length, SKTextEncoding encoding, const(SKPoint)[] positions)
817 	// {
818 	//     return GetTextPath (cast(void*)text, length, encoding, positions);
819 	// }
820 
821 	SKPath GetTextPath(void* text, int length, SKTextEncoding encoding, const(SKPoint)[] positions) {
822 		if (!ValidateTextArgs(text, length, encoding))
823 			return new SKPath();
824 
825 		auto path = new SKPath();
826 		SKPoint* p = cast(SKPoint*) positions;
827 		SkiaApi.sk_text_utils_get_pos_path(text, length, encoding,
828 				p, cast(sk_font_t*) Handle, cast(sk_path_t*) path.Handle);
829 		return path;
830 	}
831 
832 	// GetGlyphPaths
833 
834 	// void GetGlyphPaths (const(ushort)[] glyphs, SKGlyphPathDelegate glyphPathDelegate)
835 	// {
836 	// 	auto proxy = DelegateProxies.Create (glyphPathDelegate, DelegateProxies.SKGlyphPathDelegateProxy, out var gch, out var ctx);
837 	// 	try {
838 	// 		ushort* g = glyphs;
839 	// 		SkiaApi.sk_font_get_paths (Handle, g, glyphs.length, proxy, cast(void*)ctx);
840 	// 	} finally {
841 	// 		gch.Free ();
842 	// 	}
843 	// }
844 
845 	// GetTextPathOnPath (text)
846 
847 	SKPath GetTextPathOnPath(string text, SKPath path,
848 			SKTextAlign textAlign = SKTextAlign.Left, SKPoint origin = SKPoint.Empty) {
849 		return GetTextPathOnPath(cast(const(char)[]) text, path, textAlign, origin);
850 	}
851 
852 	SKPath GetTextPathOnPath(const(char)[] text, SKPath path,
853 			SKTextAlign textAlign = SKTextAlign.Left, SKPoint origin = SKPoint.Empty) {
854 		void* t = cast(void*) text.ptr;
855 		return GetTextPathOnPath(t, cast(int) text.length,
856 				SKTextEncoding.Utf8, path, textAlign, origin);
857 	}
858 
859 	SKPath GetTextPathOnPath(const(byte)[] text, SKTextEncoding encoding, SKPath path,
860 			SKTextAlign textAlign = SKTextAlign.Left, SKPoint origin = SKPoint.Empty) {
861 		void* t = cast(void*) text.ptr;
862 		return GetTextPathOnPath(t, cast(int) text.length, encoding, path, textAlign, origin);
863 	}
864 
865 	SKPath GetTextPathOnPath(void* text, int length, SKTextEncoding encoding,
866 			SKPath path, SKTextAlign textAlign = SKTextAlign.Left, SKPoint origin = SKPoint.Empty) {
867 		return GetTextPathOnPath(cast(void*) text, length, encoding, path, textAlign, origin);
868 	}
869 
870 	// SKPath GetTextPathOnPath (void* text, int length, SKTextEncoding encoding, SKPath path, SKTextAlign textAlign = SKTextAlign.Left, SKPoint origin = SKPoint.Empty)
871 	// {
872 	// 	if (!ValidateTextArgs (text, length, encoding))
873 	// 		return new SKPath ();
874 
875 	// 	auto n = CountGlyphs (text, length, encoding);
876 	// 	if (n <= 0)
877 	// 		return new SKPath ();
878 
879 	// 	auto glyphs =  Utils.RentArray<ushort> (n);
880 	// 	scope(exit) {
881 	// 	    glyphs.Dispose();
882 	// 	}
883 
884 	// 	GetGlyphs (text, length, encoding, glyphs);
885 
886 	// 	return GetTextPathOnPath (glyphs, path, textAlign, origin);
887 	// }
888 
889 	// GetTextPathOnPath (glyphs)
890 
891 	// SKPath GetTextPathOnPath (const(ushort)[] glyphs, SKPath path, SKTextAlign textAlign = SKTextAlign.Left, SKPoint origin = SKPoint.Empty)
892 	// {
893 	// 	if (path is null)
894 	// 		throw new ArgumentNullException (path.stringof);
895 
896 	// 	if (glyphs.length == 0)
897 	// 		return new SKPath ();
898 
899 	// 	auto glyphWidths =  Utils.RentArray<float> (glyphs.length);
900 	// 	scope(exit) {
901 	// 	    glyphWidths.Dispose();
902 	// 	}
903 
904 	// 	auto glyphOffsets =  Utils.RentArray<SKPoint> (glyphs.length);
905 	// 	scope(exit) {
906 	// 	    glyphOffsets.Dispose();
907 	// 	}
908 
909 	// 	GetGlyphWidths (glyphs, glyphWidths, SKRect[].Empty);
910 	// 	GetGlyphPositions (glyphs, glyphOffsets, origin);
911 
912 	// 	return GetTextPathOnPath (glyphs, glyphWidths, glyphOffsets, path, textAlign);
913 	// }
914 
915 	SKPath GetTextPathOnPath(const(ushort)[] glyphs, const(float)[] glyphWidths,
916 			SKPoint[] glyphPositions, SKPath path, SKTextAlign textAlign = SKTextAlign.Left) {
917 		if (glyphs.length != glyphWidths.length)
918 			throw new ArgumentException("The number of glyphs and glyph widths must be the same.");
919 		if (glyphs.length != glyphPositions.length)
920 			throw new ArgumentException("The number of glyphs and glyph offsets must be the same.");
921 		if (path is null)
922 			throw new ArgumentNullException(path.stringof);
923 
924 		if (glyphs.length == 0)
925 			return new SKPath();
926 
927 		auto glyphPathCache = new GlyphPathCache(this);
928 		scope (exit) {
929 			glyphPathCache.Dispose();
930 		}
931 
932 		auto pathMeasure = new SKPathMeasure(path);
933 		scope (exit) {
934 			pathMeasure.Dispose();
935 		}
936 
937 		auto contourLength = pathMeasure.Length;
938 
939 		auto textLength = glyphPositions[glyphs.length - 1].X + glyphWidths[glyphs.length - 1];
940 		auto alignment = cast(int) textAlign * 0.5f;
941 		auto startOffset = glyphPositions[0].X + (contourLength - textLength) * alignment;
942 
943 		auto textPath = new SKPath();
944 
945 		// TODO: deal with multiple contours?
946 		for (int index = 0; index < glyphPositions.length; index++) {
947 			auto glyphOffset = glyphPositions[index];
948 			auto gw = glyphWidths[index];
949 			auto x0 = startOffset + glyphOffset.X;
950 			auto x1 = x0 + gw;
951 
952 			if (x1 >= 0 && x0 <= contourLength) {
953 				auto glyphId = glyphs[index];
954 				auto glyphPath = glyphPathCache.GetPath(glyphId);
955 				if (glyphPath !is null) {
956 					auto transformation = SKMatrix.CreateTranslation(x0, glyphOffset.Y);
957 					MorphPath(textPath, glyphPath, pathMeasure, transformation);
958 				}
959 			}
960 		}
961 
962 		return textPath;
963 	}
964 
965 	static void MorphPath(SKPath dst, SKPath src, SKPathMeasure meas, in SKMatrix matrix) {
966 		// TODO:
967 		// Need differentially more subdivisions when the follow-path is curvy. Not sure how to determine
968 		// that, but we need it. I guess a cheap answer is let the caller tell us, but that seems like a
969 		// cop-out. Another answer is to get Skia's Rob Johnson to figure it out.
970 
971 		auto it = src.CreateIterator(false);
972 		scope (exit) {
973 			it.Dispose();
974 		}
975 
976 		SKPathVerb verb;
977 
978 		SKPoint[] srcP = new SKPoint[4];
979 		SKPoint[] dstP = new SKPoint[4];
980 
981 		while ((verb = it.Next(srcP)) != SKPathVerb.Done) {
982 			switch (verb) {
983 			case SKPathVerb.Move:
984 				MorphPoints(dstP, srcP, 1, meas, matrix);
985 				dst.MoveTo(dstP[0]);
986 				break;
987 			case SKPathVerb.Line:
988 				// turn lines into quads to look bendy
989 				srcP[0].X = (srcP[0].X + srcP[1].X) * 0.5f;
990 				srcP[0].Y = (srcP[0].Y + srcP[1].Y) * 0.5f;
991 				MorphPoints(dstP, srcP, 2, meas, matrix);
992 				dst.QuadTo(dstP[0], dstP[1]);
993 				break;
994 			case SKPathVerb.Quad:
995 				MorphPoints(dstP, srcP[1 .. 1 + 2], 2, meas, matrix);
996 				dst.QuadTo(dstP[0], dstP[1]);
997 				break;
998 			case SKPathVerb.Conic:
999 				MorphPoints(dstP, srcP[1 .. 1 + 2], 2, meas, matrix);
1000 				dst.ConicTo(dstP[0], dstP[1], it.ConicWeight());
1001 				break;
1002 			case SKPathVerb.Cubic:
1003 				MorphPoints(dstP, srcP[1 .. 1 + 3], 3, meas, matrix);
1004 				dst.CubicTo(dstP[0], dstP[1], dstP[2]);
1005 				break;
1006 			case SKPathVerb.Close:
1007 				dst.Close();
1008 				break;
1009 			default:
1010 				// Debug.Fail ("Unknown verb when iterating points in glyph path.");
1011 				break;
1012 			}
1013 		}
1014 	}
1015 
1016 	static void MorphPoints(SKPoint[] dst, SKPoint[] src, int count,
1017 			SKPathMeasure meas, in SKMatrix matrix) {
1018 		for (int i = 0; i < count; i++) {
1019 			SKPoint s = matrix.MapPoint(src[i].X, src[i].Y);
1020 			SKPoint p;
1021 			SKPoint t;
1022 			if (!meas.GetPositionAndTangent(s.X, p, t)) {
1023 				// set to 0 if the measure failed, so that we just set dst == pos
1024 				t = SKPoint.Empty;
1025 			}
1026 
1027 			// y-offset the point in the direction of the normal vector on the path.
1028 			dst[i] = SKPoint(p.X - t.Y * s.Y, p.Y + t.X * s.Y);
1029 		}
1030 	}
1031 
1032 	// Utils
1033 
1034 	private bool ValidateTextArgs(void* text, int length, SKTextEncoding encoding) {
1035 		if (length == 0)
1036 			return false;
1037 
1038 		if (text is null)
1039 			throw new ArgumentNullException(text.stringof);
1040 
1041 		return true;
1042 	}
1043 
1044 	//
1045 
1046 	static SKFont GetObject(void* handle, bool owns = true) {
1047 		return GetOrAddObject!(SKFont)(handle, delegate SKFont(h, o) {
1048 			return new SKFont(h, o);
1049 		});
1050 	}
1051 
1052 }
1053 
1054 //
1055 
1056 class GlyphPathCache : Dictionary!(ushort, SKPath), IDisposable {
1057 	SKFont font;
1058 
1059 	SKFont Font() {
1060 		return font;
1061 	}
1062 
1063 	this(SKFont font) {
1064 		this.font = font;
1065 	}
1066 
1067 	SKPath GetPath(ushort glyph) {
1068 		SKPath path;
1069 		if (!TryGetValue(glyph, path)) {
1070 			path = Font.GetGlyphPath(glyph);
1071 			this[glyph] = path;
1072 		}
1073 
1074 		return path;
1075 	}
1076 
1077 	void Dispose() {
1078 		foreach (SKPath path; Values) {
1079 			if (path !is null)
1080 				path.Dispose();
1081 		}
1082 		Clear();
1083 	}
1084 }