1 module skia.SKTextBlob;
2 
3 import skia.Exceptions;
4 import skia.SkiaApi;
5 import skia.SKObject;
6 import skia.Util;
7 import skia.SKFont;
8 import skia.Definitions;
9 import skia.MathTypes;
10 import skia.SKPath;
11 import skia.SKPaint;
12 import skia.SKRunBuffer;
13 import skia.SKPathMeasure;
14 
15 import std.typecons;
16 import std.experimental.logger;
17 
18 /**
19  * 
20  */
21 class SKTextBlob : SKObject, ISKNonVirtualReferenceCounted, ISKSkipObjectRegistration {
22 	this(void* x, bool owns) {
23 		super(x, owns);
24 	}
25 
26 	void ReferenceNative() {
27 		return SkiaApi.sk_textblob_ref(cast(sk_textblob_t*) Handle);
28 	}
29 
30 	void UnreferenceNative() {
31 		return SkiaApi.sk_textblob_unref(cast(sk_textblob_t*) Handle);
32 	}
33 
34 	SKRect Bounds() {
35 		SKRect bounds;
36 		SkiaApi.sk_textblob_get_bounds(cast(sk_textblob_t*) Handle, &bounds);
37 		return bounds;
38 	}
39 
40 	uint UniqueId() {
41 		return SkiaApi.sk_textblob_get_unique_id(cast(sk_textblob_t*) Handle);
42 	}
43 
44 	// Create
45 
46 	static SKTextBlob Create(string text, SKFont font, SKPoint origin = SKPoint.Empty) {
47 		return Create(cast(const(byte)[]) text, SKTextEncoding.Utf8, font, origin);
48 	}
49 
50 	// static SKTextBlob Create (const(char)[] text, SKFont font, SKPoint origin = SKPoint.Empty)
51 	// {
52 	// 	void* t = text.ptr;
53 	// 	return Create (t, text.length * 2, SKTextEncoding.Utf16, font, origin);
54 	// }
55 
56 	// static SKTextBlob Create (void* text, int length, SKTextEncoding encoding, SKFont font, SKPoint origin = SKPoint.Empty)
57 	// {
58 	// 	return Create (text.AsReadOnlySpan (length), encoding, font, origin);
59 	// }
60 
61 	static SKTextBlob Create(const(byte)[] text, SKTextEncoding encoding,
62 			SKFont font, SKPoint origin = SKPoint.Empty) {
63 		void* t = cast(void*) text.ptr;
64 		return Create(t, cast(int) text.length, encoding, font, origin);
65 	}
66 
67 	static SKTextBlob Create(void* text, int length, SKTextEncoding encoding,
68 			SKFont font, SKPoint origin = SKPoint.Empty) {
69 		if (font is null)
70 			throw new ArgumentNullException(font.stringof);
71 
72 		auto count = font.CountGlyphs(text, length, encoding);
73 		if (count <= 0)
74 			return null;
75 
76 		SKTextBlobBuilder builder = new SKTextBlobBuilder();
77 		scope (exit) {
78 			builder.Dispose();
79 		}
80 
81 		SKPositionedRunBuffer buffer = builder.AllocatePositionedRun(font, count);
82 		font.GetGlyphs(text, length, encoding, buffer.GetGlyphSpan());
83 		font.GetGlyphPositions(buffer.GetGlyphSpan(), buffer.GetPositionSpan(), origin);
84 
85 		return builder.Build();
86 	}
87 
88 	// CreateHorizontal
89 
90 	static SKTextBlob CreateHorizontal(string text, SKFont font, const(float)[] positions, float y) {
91 		return CreateHorizontal(cast(const(char)[]) text, font, positions, y);
92 	}
93 
94 	static SKTextBlob CreateHorizontal(const(char)[] text, SKFont font,
95 			const(float)[] positions, float y) {
96 		void* t = cast(void*) text.ptr;
97 		return CreateHorizontal(t, cast(int) text.length, SKTextEncoding.Utf8, font, positions, y);
98 	}
99 
100 	// static SKTextBlob CreateHorizontal (void* text, int length, SKTextEncoding encoding, SKFont font, const(float)[] positions, float y)
101 	// {
102 	// 	return CreateHorizontal (text.AsReadOnlySpan (length), encoding, font, positions, y);
103 	// }
104 
105 	static SKTextBlob CreateHorizontal(const(byte)[] text,
106 			SKTextEncoding encoding, SKFont font, const(float)[] positions, float y) {
107 		void* t = cast(void*) text.ptr;
108 		return CreateHorizontal(t, cast(int) text.length, encoding, font, positions, y);
109 	}
110 
111 	static SKTextBlob CreateHorizontal(void* text, int length,
112 			SKTextEncoding encoding, SKFont font, const(float)[] positions, float y) {
113 		if (font is null)
114 			throw new ArgumentNullException(font.stringof);
115 
116 		auto count = font.CountGlyphs(text, length, encoding);
117 		if (count <= 0)
118 			return null;
119 
120 		auto builder = new SKTextBlobBuilder();
121 		scope (exit) {
122 			builder.Dispose();
123 		}
124 
125 		auto buffer = builder.AllocateHorizontalRun(font, count, y);
126 		font.GetGlyphs(text, length, encoding, buffer.GetGlyphSpan());
127 		// positions.CopyTo (buffer.GetPositionSpan ());
128 		return builder.Build();
129 	}
130 
131 	// CreatePositioned
132 
133 	static SKTextBlob CreatePositioned(string text, SKFont font, const(SKPoint)[] positions) {
134 		return CreatePositioned(cast(const(char)[]) text, font, positions);
135 	}
136 
137 	static SKTextBlob CreatePositioned(const(char)[] text, SKFont font, const(SKPoint)[] positions) {
138 		void* t = cast(void*) text.ptr;
139 		return CreatePositioned(t, cast(int) text.length, SKTextEncoding.Utf8, font, positions);
140 	}
141 
142 	// static SKTextBlob CreatePositioned (void* text, int length, SKTextEncoding encoding, SKFont font, const(SKPoint)[] positions)
143 	// {
144 	// 	return CreatePositioned (text.AsReadOnlySpan (length), encoding, font, positions);
145 	// }
146 
147 	static SKTextBlob CreatePositioned(const(byte)[] text,
148 			SKTextEncoding encoding, SKFont font, const(SKPoint)[] positions) {
149 		void* t = cast(void*) text.ptr;
150 		return CreatePositioned(t, cast(int) text.length, encoding, font, positions);
151 	}
152 
153 	static SKTextBlob CreatePositioned(void* text, int length,
154 			SKTextEncoding encoding, SKFont font, const(SKPoint)[] positions) {
155 		if (font is null)
156 			throw new ArgumentNullException(font.stringof);
157 
158 		auto count = font.CountGlyphs(text, length, encoding);
159 		if (count <= 0)
160 			return null;
161 
162 		auto builder = new SKTextBlobBuilder();
163 		scope (exit) {
164 			builder.Dispose();
165 		}
166 
167 		auto buffer = builder.AllocatePositionedRun(font, count);
168 		font.GetGlyphs(text, length, encoding, buffer.GetGlyphSpan());
169 		// positions.CopyTo (buffer.GetPositionSpan ());
170 		return builder.Build();
171 	}
172 
173 	// CreateRotationScale
174 
175 	static SKTextBlob CreateRotationScale(string text, SKFont font,
176 			const(SKRotationScaleMatrix)[] positions) {
177 		return CreateRotationScale(cast(const(char)[]) text, font, positions);
178 	}
179 
180 	static SKTextBlob CreateRotationScale(const(char)[] text, SKFont font,
181 			const(SKRotationScaleMatrix)[] positions) {
182 		void* t = cast(void*) text.ptr;
183 		// return CreateRotationScale (t, cast(int)text.length * 2, SKTextEncoding.Utf16, font, positions);
184 		return CreateRotationScale(t, cast(int) text.length, SKTextEncoding.Utf8, font, positions);
185 	}
186 
187 	// static SKTextBlob CreateRotationScale (void* text, int length, SKTextEncoding encoding, SKFont font, const(SKRotationScaleMatrix)[] positions)
188 	// {
189 	// 	return CreateRotationScale (text.AsReadOnlySpan (length), encoding, font, positions);
190 	// }
191 
192 	static SKTextBlob CreateRotationScale(const(byte)[] text,
193 			SKTextEncoding encoding, SKFont font, const(SKRotationScaleMatrix)[] positions) {
194 		void* t = cast(void*) text.ptr;
195 		return CreateRotationScale(t, cast(int) text.length, encoding, font, positions);
196 	}
197 
198 	static SKTextBlob CreateRotationScale(void* text, int length,
199 			SKTextEncoding encoding, SKFont font, const(SKRotationScaleMatrix)[] positions) {
200 		if (font is null)
201 			throw new ArgumentNullException(font.stringof);
202 
203 		auto count = font.CountGlyphs(text, length, encoding);
204 		if (count <= 0)
205 			return null;
206 
207 		auto builder = new SKTextBlobBuilder();
208 		scope (exit) {
209 			builder.Dispose();
210 		}
211 
212 		auto buffer = builder.AllocateRotationScaleRun(font, count);
213 		font.GetGlyphs(text, length, encoding, buffer.GetGlyphSpan());
214 		// positions.CopyTo (buffer.GetRotationScaleSpan ());
215 		return builder.Build();
216 	}
217 
218 	// CreatePathPositioned
219 
220 	static SKTextBlob CreatePathPositioned(string text, SKFont font, SKPath path,
221 			SKTextAlign textAlign = SKTextAlign.Left, SKPoint origin = SKPoint.Empty) {
222 		return CreatePathPositioned(cast(const(char)[]) text, font, path, textAlign, origin);
223 	}
224 
225 	static SKTextBlob CreatePathPositioned(const(char)[] text, SKFont font, SKPath path,
226 			SKTextAlign textAlign = SKTextAlign.Left, SKPoint origin = SKPoint.Empty) {
227 		void* t = cast(void*) text.ptr;
228 		return CreatePathPositioned(t, cast(int) text.length,
229 				SKTextEncoding.Utf8, font, path, textAlign, origin);
230 	}
231 
232 	// static SKTextBlob CreatePathPositioned (void* text, int length, SKTextEncoding encoding, SKFont font, SKPath path, SKTextAlign textAlign = SKTextAlign.Left, SKPoint origin = SKPoint.Empty)
233 	// {
234 	// 	return CreatePathPositioned (text.AsReadOnlySpan (length), encoding, font, path, textAlign, origin);
235 	// }
236 
237 	static SKTextBlob CreatePathPositioned(const(byte)[] text, SKTextEncoding encoding, SKFont font,
238 			SKPath path, SKTextAlign textAlign = SKTextAlign.Left, SKPoint origin = SKPoint.Empty) {
239 		void* t = cast(void*) text.ptr;
240 		return CreatePathPositioned(t, cast(int) text.length, encoding, font,
241 				path, textAlign, origin);
242 	}
243 
244 	static SKTextBlob CreatePathPositioned(void* text, int length, SKTextEncoding encoding, SKFont font,
245 			SKPath path, SKTextAlign textAlign = SKTextAlign.Left, SKPoint origin = SKPoint.Empty) {
246 		if (font is null)
247 			throw new ArgumentNullException(font.stringof);
248 
249 		auto count = font.CountGlyphs(text, length, encoding);
250 		if (count <= 0)
251 			return null;
252 
253 		// we use temporary arrays because we might only use part of the text
254 		// Utils.RentArray!(ushort) glyphs = Utils.RentArray!(ushort) (count);
255 		ushort[] glyphs = new ushort[count];
256 		// auto glyphWidths =  Utils.RentArray!(float) (glyphs.length);
257 		float[] glyphWidths = new float[glyphs.length];
258 		// scope(exit) {
259 		//     glyphWidths.Dispose();
260 		// }
261 
262 		// auto glyphOffsets =  Utils.RentArray!SKPoint (glyphs.length);
263 		SKPoint[] glyphOffsets = new SKPoint[glyphs.length];
264 		// scope(exit) {
265 		//     glyphOffsets.Dispose();
266 		// }
267 
268 		font.GetGlyphs(text, length, encoding, glyphs);
269 		font.GetGlyphWidths(glyphs, glyphWidths, null);
270 		font.GetGlyphPositions(glyphs, glyphOffsets, origin);
271 
272 		auto builder = new SKTextBlobBuilder();
273 		scope (exit) {
274 			builder.Dispose();
275 		}
276 
277 		builder.AddPathPositionedRun(glyphs, font, glyphWidths, glyphOffsets, path, textAlign);
278 		return builder.Build();
279 	}
280 
281 	// GetIntercepts
282 
283 	float[] GetIntercepts(float upperBounds, float lowerBounds, SKPaint paint = null) {
284 		int n = CountIntercepts(upperBounds, lowerBounds, paint);
285 		auto intervals = new float[n];
286 		GetIntercepts(upperBounds, lowerBounds, intervals, paint);
287 		return intervals;
288 	}
289 
290 	void GetIntercepts(float upperBounds, float lowerBounds, float[] intervals, SKPaint paint = null) {
291 		auto bounds = new float[2];
292 		bounds[0] = upperBounds;
293 		bounds[1] = lowerBounds;
294 		float* i = intervals.ptr;
295 		void* handle = null;
296 		if (paint !is null && paint.Handle !is null) {
297 			handle = paint.Handle;
298 		}
299 		SkiaApi.sk_textblob_get_intercepts(cast(sk_textblob_t*) Handle,
300 				bounds.ptr, i, cast(sk_paint_t*) handle);
301 	}
302 
303 	// CountIntercepts
304 
305 	int CountIntercepts(float upperBounds, float lowerBounds, SKPaint paint = null) {
306 		auto bounds = new float[2];
307 		bounds[0] = upperBounds;
308 		bounds[1] = lowerBounds;
309 		void* handle = null;
310 		if (paint !is null && paint.Handle !is null) {
311 			handle = paint.Handle;
312 		}
313 		return SkiaApi.sk_textblob_get_intercepts(cast(sk_textblob_t*) Handle,
314 				bounds.ptr, null, cast(sk_paint_t*) handle);
315 	}
316 
317 	//
318 
319 	static SKTextBlob GetObject(void* handle) {
320 		return handle is null ? null : new SKTextBlob(handle, true);
321 	}
322 }
323 
324 /**
325  * 
326  */
327 class SKTextBlobBuilder : SKObject, ISKSkipObjectRegistration {
328 	this(void* x, bool owns) {
329 		super(x, owns);
330 	}
331 
332 	this() {
333 		this(SkiaApi.sk_textblob_builder_new(), true);
334 	}
335 
336 	// protected override void Dispose(bool disposing) {
337 	// 	return super.Dispose(disposing);
338 	// }
339 
340 	protected override void DisposeNative() {
341 		return SkiaApi.sk_textblob_builder_delete(cast(sk_textblob_builder_t*) Handle);
342 	}
343 
344 	// override void Dispose() {
345 	// 	return super.Dispose();
346 	// }
347 
348 	// Build
349 
350 	SKTextBlob Build() {
351 		auto blob = SKTextBlob.GetObject(
352 				SkiaApi.sk_textblob_builder_make(cast(sk_textblob_builder_t*) Handle));
353 		// GC.KeepAlive (this);
354 		return blob;
355 	}
356 
357 	// AddRun
358 
359 	void AddRun(const(ushort)[] glyphs, SKFont font, SKPoint origin = SKPoint.Empty) {
360 		if (font is null)
361 			throw new ArgumentNullException(font.stringof);
362 
363 		auto buffer = AllocatePositionedRun(font, cast(int) glyphs.length);
364 		// glyphs.CopyTo (buffer.GetGlyphSpan ());
365 		font.GetGlyphPositions(buffer.GetGlyphSpan(), buffer.GetPositionSpan(), origin);
366 	}
367 
368 	// AddHorizontalRun
369 
370 	void AddHorizontalRun(const(ushort)[] glyphs, SKFont font, const(float)[] positions, float y) {
371 		if (font is null)
372 			throw new ArgumentNullException(font.stringof);
373 
374 		auto buffer = AllocateHorizontalRun(font, cast(int) glyphs.length, y);
375 		// glyphs.CopyTo (buffer.GetGlyphSpan ());
376 		// positions.CopyTo (buffer.GetPositionSpan ());
377 	}
378 
379 	// AddPositionedRun
380 
381 	void AddPositionedRun(const(ushort)[] glyphs, SKFont font, const(SKPoint)[] positions) {
382 		if (font is null)
383 			throw new ArgumentNullException(font.stringof);
384 
385 		auto buffer = AllocatePositionedRun(font, cast(int) glyphs.length);
386 		// glyphs.CopyTo (buffer.GetGlyphSpan ());
387 		// positions.CopyTo (buffer.GetPositionSpan ());
388 	}
389 
390 	// AddRotationScaleRun 
391 
392 	void AddRotationScaleRun(const(ushort)[] glyphs, SKFont font,
393 			const(SKRotationScaleMatrix)[] positions) {
394 		if (font is null)
395 			throw new ArgumentNullException(font.stringof);
396 
397 		auto buffer = AllocateRotationScaleRun(font, cast(int) glyphs.length);
398 		// glyphs.CopyTo (buffer.GetGlyphSpan ());
399 		// positions.CopyTo (buffer.GetRotationScaleSpan ());
400 	}
401 
402 	// AddPathPositionedRun
403 
404 	void AddPathPositionedRun(const(ushort)[] glyphs, SKFont font, const(float)[] glyphWidths,
405 			SKPoint[] glyphOffsets, SKPath path, SKTextAlign textAlign = SKTextAlign.Left) {
406 		auto pathMeasure = new SKPathMeasure(path);
407 		scope (exit) {
408 			pathMeasure.Dispose();
409 		}
410 
411 		auto contourLength = pathMeasure.Length();
412 
413 		auto textLength = glyphOffsets[glyphs.length - 1].X + glyphWidths[glyphs.length - 1];
414 		auto alignment = cast(int) textAlign * 0.5f;
415 		auto startOffset = glyphOffsets[0].X + (contourLength - textLength) * alignment;
416 
417 		auto firstGlyphIndex = 0;
418 		auto pathGlyphCount = 0;
419 
420 		// auto glyphTransforms =  Utils.RentArray<SKRotationScaleMatrix> (glyphs.length);
421 		// scope(exit) {
422 		//     glyphTransforms.Dispose();
423 		// }
424 		SKRotationScaleMatrix[] glyphTransforms = new SKRotationScaleMatrix[glyphs.length];
425 
426 		// TODO: deal with multiple contours?
427 		for (int index = 0; index < glyphOffsets.length; index++) {
428 			auto glyphOffset = glyphOffsets[index];
429 			auto halfWidth = glyphWidths[index] * 0.5f;
430 			auto pathOffset = startOffset + glyphOffset.X + halfWidth;
431 
432 			// TODO: clip glyphs on both ends of paths
433 			SKPoint position;
434 			SKPoint tangent;
435 
436 			if (pathOffset >= 0 && pathOffset < contourLength
437 					&& pathMeasure.GetPositionAndTangent(pathOffset, position, tangent)) {
438 				if (pathGlyphCount == 0)
439 					firstGlyphIndex = index;
440 
441 				auto tx = tangent.X;
442 				auto ty = tangent.Y;
443 
444 				auto px = position.X;
445 				auto py = position.Y;
446 
447 				// horizontally offset the position using the tangent vector
448 				px -= tx * halfWidth;
449 				py -= ty * halfWidth;
450 
451 				// vertically offset the position using the normal vector  (-ty, tx)
452 				auto dy = glyphOffset.Y;
453 				px -= dy * ty;
454 				py += dy * tx;
455 
456 				glyphTransforms[pathGlyphCount++] = SKRotationScaleMatrix(tx, ty, px, py);
457 			}
458 		}
459 
460 		auto glyphSubset = glyphs[firstGlyphIndex .. firstGlyphIndex + pathGlyphCount];
461 		auto positions = glyphTransforms[0 .. pathGlyphCount];
462 
463 		AddRotationScaleRun(glyphSubset, font, positions);
464 	}
465 
466 	// AllocateRun
467 
468 	SKRunBuffer AllocateRun(SKFont font, int count, float x, float y, Nullable!SKRect bounds) {
469 		if (font is null)
470 			throw new ArgumentNullException(font.stringof);
471 
472 		SKRunBufferInternal runbuffer;
473 
474 		if (!bounds.isNull) {
475 			SKRect b = bounds.get();
476 			SkiaApi.sk_textblob_builder_alloc_run(cast(sk_textblob_builder_t*) Handle,
477 					cast(sk_font_t*) font.Handle, count, x, y, &b, &runbuffer);
478 		} else {
479 			SkiaApi.sk_textblob_builder_alloc_run(cast(sk_textblob_builder_t*) Handle,
480 					cast(sk_font_t*) font.Handle, count, x, y, null, &runbuffer);
481 		}
482 
483 		return new SKRunBuffer(runbuffer, count);
484 	}
485 
486 	// AllocateHorizontalRun
487 	SKHorizontalRunBuffer AllocateHorizontalRun(SKFont font, int count, float y) {
488 		// return AllocateHorizontalRun ( font,  count,  y, SKRect());
489 		return null;
490 	}
491 
492 	SKHorizontalRunBuffer AllocateHorizontalRun(SKFont font, int count, float y,
493 			Nullable!SKRect bounds) {
494 		if (font is null)
495 			throw new ArgumentNullException(font.stringof);
496 
497 		SKRunBufferInternal runbuffer;
498 
499 		if (!bounds.isNull) {
500 			SKRect b = bounds.get();
501 			SkiaApi.sk_textblob_builder_alloc_run_pos_h(cast(sk_textblob_builder_t*) Handle,
502 					cast(sk_font_t*) font.Handle, count, y, &b, &runbuffer);
503 		} else {
504 			SkiaApi.sk_textblob_builder_alloc_run_pos_h(cast(sk_textblob_builder_t*) Handle,
505 					cast(sk_font_t*) font.Handle, count, y, null, &runbuffer);
506 		}
507 
508 		return new SKHorizontalRunBuffer(runbuffer, count);
509 	}
510 
511 	// AllocatePositionedRun
512 
513 	SKPositionedRunBuffer AllocatePositionedRun(SKFont font, int count,
514 			Nullable!SKRect bounds = Nullable!SKRect.init) {
515 		if (font is null)
516 			throw new ArgumentNullException(font.stringof);
517 
518 		SKRunBufferInternal runbuffer;
519 
520 		if (bounds.isNull) {
521 			SkiaApi.sk_textblob_builder_alloc_run_pos(cast(sk_textblob_builder_t*) Handle,
522 					cast(sk_font_t*) font.Handle, count, null, &runbuffer);
523 		} else {
524 			warning("22");
525 			SKRect b = bounds.get();
526 			SkiaApi.sk_textblob_builder_alloc_run_pos(cast(sk_textblob_builder_t*) Handle,
527 					cast(sk_font_t*) font.Handle, count, &b, &runbuffer);
528 		}
529 
530 		return new SKPositionedRunBuffer(runbuffer, count);
531 	}
532 
533 	// AllocateRotationScaleRun
534 
535 	SKRotationScaleRunBuffer AllocateRotationScaleRun(SKFont font, int count) {
536 		if (font is null)
537 			throw new ArgumentNullException(font.stringof);
538 
539 		SKRunBufferInternal runbuffer;
540 		SkiaApi.sk_textblob_builder_alloc_run_rsxform(cast(sk_textblob_builder_t*) Handle,
541 				cast(sk_font_t*) font.Handle, count, &runbuffer);
542 
543 		return new SKRotationScaleRunBuffer(runbuffer, count);
544 	}
545 
546 	// OBSOLETE OVERLOADS
547 
548 	// AddRun
549 
550 	void AddRun(SKPaint font, float x, float y, ushort[] glyphs, string text, uint[] clusters) {
551 		auto utf8Text = StringUtilities.GetEncodedText(text, SKTextEncoding.Utf8);
552 		AddRun(font, x, y, glyphs, utf8Text, clusters);
553 	}
554 
555 	void AddRun(SKPaint font, float x, float y, ushort[] glyphs, string text,
556 			uint[] clusters, SKRect bounds) {
557 		auto utf8Text = StringUtilities.GetEncodedText(text, SKTextEncoding.Utf8);
558 		AddRun(font, x, y, glyphs, utf8Text, clusters, bounds);
559 	}
560 
561 	void AddRun(SKPaint font, float x, float y, ushort[] glyphs) {
562 		// return AddRun (font, x, y, glyphs, const(byte)[].Empty, null, null);
563 
564 		// return AddRun (font, x, y, glyphs, (const(byte)[]).init, null, null);
565 	}
566 
567 	void AddRun(SKPaint font, float x, float y, ushort[] glyphs, SKRect bounds) {
568 		AddRun(font, x, y, glyphs, (const(byte)[]).init, null, bounds);
569 	}
570 
571 	void AddRun(SKPaint font, float x, float y, ushort[] glyphs, byte[] text, uint[] clusters) {
572 		//  AddRun (font, x, y, glyphs, text, clusters, null);
573 	}
574 
575 	void AddRun(SKPaint font, float x, float y, ushort[] glyphs, byte[] text,
576 			uint[] clusters, SKRect bounds) {
577 		AddRun(font, x, y, glyphs, text, clusters, bounds);
578 	}
579 
580 	// AddRun (spans)
581 
582 	void AddRun(SKPaint font, float x, float y, const(ushort)[] glyphs) {
583 		//  AddRun (font, x, y, glyphs, (const(byte)[]).init, null, null);
584 	}
585 
586 	// void AddRun (SKPaint font, float x, float y, const(ushort)[] glyphs1 Nullable!SKRect bounds)
587 	// {
588 	// 	return AddRun (font, x, y, glyphs, (const(byte)[]).init, null, bounds);
589 	// }
590 
591 	void AddRun(SKPaint font, float x, float y, const(ushort)[] glyphs,
592 			const(byte)[] text, const(uint)[] clusters) {
593 		//  AddRun (font, x, y, glyphs, text, clusters, null);
594 	}
595 
596 	// void AddRun (SKPaint font, float x, float y, const(ushort)[] glyphs, const(byte)[] text, const(uint)[] clusters1 Nullable!SKRect bounds)
597 	// {
598 	// 	if (font is null)
599 	// 		throw new ArgumentNullException (font.stringof);
600 	// 	if (glyphs.empty())
601 	// 		throw new ArgumentNullException (glyphs.stringof);
602 
603 	// 	if (!text.empty()) {
604 	// 		if (clusters.empty())
605 	// 			throw new ArgumentNullException (clusters.stringof);
606 	// 		if (glyphs.length != clusters.length)
607 	// 			throw new ArgumentException ("The number of glyphs and clusters must be the same.");
608 	// 	}
609 
610 	// 	auto run = AllocateRun (font, glyphs.length, x, y, text.empty() ? 0 : text.length, bounds);
611 	// 	run.SetGlyphs (glyphs);
612 
613 	// 	if (!text.empty()) {
614 	// 		run.SetText (text);
615 	// 		run.SetClusters (clusters);
616 	// 	}
617 	// }
618 
619 	// AddHorizontalRun
620 
621 	void AddHorizontalRun(SKPaint font, float y, ushort[] glyphs,
622 			float[] positions, string text, uint[] clusters) {
623 		auto utf8Text = StringUtilities.GetEncodedText(text, SKTextEncoding.Utf8);
624 		AddHorizontalRun(font, y, glyphs, positions, utf8Text, clusters);
625 	}
626 
627 	void AddHorizontalRun(SKPaint font, float y, ushort[] glyphs,
628 			float[] positions, string text, uint[] clusters, SKRect bounds) {
629 		auto utf8Text = StringUtilities.GetEncodedText(text, SKTextEncoding.Utf8);
630 		AddHorizontalRun(font, y, glyphs, positions, utf8Text, clusters, bounds);
631 	}
632 
633 	void AddHorizontalRun(SKPaint font, float y, ushort[] glyphs, float[] positions) {
634 		//  AddHorizontalRun (font, y, glyphs, positions, (const(byte)[]).init, null, null);
635 	}
636 
637 	void AddHorizontalRun(SKPaint font, float y, ushort[] glyphs, float[] positions, SKRect bounds) {
638 		AddHorizontalRun(font, y, glyphs, positions, (const(byte)[]).init, null, bounds);
639 	}
640 
641 	void AddHorizontalRun(SKPaint font, float y, ushort[] glyphs,
642 			float[] positions, byte[] text, uint[] clusters) {
643 		//  AddHorizontalRun (font, y, glyphs, positions, text, clusters, null);
644 	}
645 
646 	void AddHorizontalRun(SKPaint font, float y, ushort[] glyphs,
647 			float[] positions, byte[] text, uint[] clusters, SKRect bounds) {
648 		AddHorizontalRun(font, y, glyphs, positions, text, clusters, bounds);
649 	}
650 
651 	// AddHorizontalRun (spans)
652 
653 	void AddHorizontalRun(SKPaint font, float y, const(ushort)[] glyphs, const(float)[] positions) {
654 		//  AddHorizontalRun (font, y, glyphs, positions, (const(byte)[]).init, null, null);
655 	}
656 
657 	// void AddHorizontalRun (SKPaint font, float y, const(ushort)[] glyphs, const(float)[] positions1, Nullable!SKRect bounds)
658 	// {
659 	// 	return AddHorizontalRun (font, y, glyphs, positions, const(byte)[].Empty, null, bounds);
660 	// }
661 
662 	void AddHorizontalRun(SKPaint font, float y, const(ushort)[] glyphs,
663 			const(float)[] positions, const(byte)[] text, const(uint)[] clusters) {
664 		//  AddHorizontalRun (font, y, glyphs, positions, text, clusters, null);
665 	}
666 
667 	// void AddHorizontalRun (SKPaint font, float y, const(ushort)[] glyphs, const(float)[] positions, const(byte)[] text, const(uint)[] clusters1 Nullable!SKRect bounds)
668 	// {
669 	// 	if (font is null)
670 	// 		throw new ArgumentNullException (font.stringof);
671 	// 	if (glyphs.empty())
672 	// 		throw new ArgumentNullException (glyphs.stringof);
673 	// 	if (positions.empty())
674 	// 		throw new ArgumentNullException (positions.stringof);
675 	// 	if (glyphs.length != positions.length)
676 	// 		throw new ArgumentException ("The number of glyphs and positions must be the same.");
677 
678 	// 	if (!text.empty()) {
679 	// 		if (clusters.empty())
680 	// 			throw new ArgumentNullException (clusters.stringof);
681 	// 		if (glyphs.length != clusters.length)
682 	// 			throw new ArgumentException ("The number of glyphs and clusters must be the same.");
683 	// 	}
684 
685 	// 	auto run = AllocateHorizontalRun (font, glyphs.length, y, text.empty() ? 0 : text.length, bounds);
686 	// 	run.SetGlyphs (glyphs);
687 	// 	run.SetPositions (positions);
688 
689 	// 	if (!text.empty()) {
690 	// 		run.SetText (text);
691 	// 		run.SetClusters (clusters);
692 	// 	}
693 	// }
694 
695 	// AddPositionedRun
696 
697 	void AddPositionedRun(SKPaint font, ushort[] glyphs, SKPoint[] positions,
698 			string text, uint[] clusters) {
699 		auto utf8Text = StringUtilities.GetEncodedText(text, SKTextEncoding.Utf8);
700 		AddPositionedRun(font, glyphs, positions, utf8Text, clusters);
701 	}
702 
703 	void AddPositionedRun(SKPaint font, ushort[] glyphs, SKPoint[] positions,
704 			string text, uint[] clusters, SKRect bounds) {
705 		auto utf8Text = StringUtilities.GetEncodedText(text, SKTextEncoding.Utf8);
706 		AddPositionedRun(font, glyphs, positions, utf8Text, clusters, bounds);
707 	}
708 
709 	void AddPositionedRun(SKPaint font, ushort[] glyphs, SKPoint[] positions) {
710 		//  AddPositionedRun (font, glyphs, positions, (const(byte)[]).init, null, null);
711 	}
712 
713 	void AddPositionedRun(SKPaint font, ushort[] glyphs, SKPoint[] positions, SKRect bounds) {
714 		AddPositionedRun(font, glyphs, positions, (const(byte)[]).init, null, bounds);
715 	}
716 
717 	void AddPositionedRun(SKPaint font, ushort[] glyphs, SKPoint[] positions,
718 			byte[] text, uint[] clusters) {
719 		//  AddPositionedRun (font, glyphs, positions, text, clusters, null);
720 	}
721 
722 	void AddPositionedRun(SKPaint font, ushort[] glyphs, SKPoint[] positions,
723 			byte[] text, uint[] clusters, SKRect bounds) {
724 		AddPositionedRun(font, glyphs, positions, text, clusters, bounds);
725 	}
726 
727 	// AddPositionedRun (spans)
728 
729 	void AddPositionedRun(SKPaint font, const(ushort)[] glyphs, const(SKPoint)[] positions) {
730 		//  AddPositionedRun (font, glyphs, positions, (const(byte)[]).init, null, null);
731 	}
732 
733 	// void AddPositionedRun (SKPaint font, const(ushort)[] glyphs, const(SKPoint)[] positions1 Nullable!SKRect bounds)
734 	// {
735 	// 	return AddPositionedRun (font, glyphs, positions, const(byte)[].Empty, null, bounds);
736 	// }
737 
738 	void AddPositionedRun(SKPaint font, const(ushort)[] glyphs,
739 			const(SKPoint)[] positions, const(byte)[] text, const(uint)[] clusters) {
740 		//  AddPositionedRun (font, glyphs, positions, text, clusters, null);
741 	}
742 
743 	// void AddPositionedRun (SKPaint font, const(ushort)[] glyphs, const(SKPoint)[] positions, const(byte)[] text, const(uint)[] clusters1 Nullable!SKRect bounds)
744 	// {
745 	// 	if (font is null)
746 	// 		throw new ArgumentNullException (font.stringof);
747 	// 	if (glyphs.empty())
748 	// 		throw new ArgumentNullException (glyphs.stringof);
749 	// 	if (positions.empty())
750 	// 		throw new ArgumentNullException (positions.stringof);
751 	// 	if (glyphs.length != positions.length)
752 	// 		throw new ArgumentException ("The number of glyphs and positions must be the same.");
753 
754 	// 	if (!text.empty()) {
755 	// 		if (clusters.empty())
756 	// 			throw new ArgumentNullException (clusters.stringof);
757 	// 		if (glyphs.length != clusters.length)
758 	// 			throw new ArgumentException ("The number of glyphs and clusters must be the same.");
759 	// 	}
760 
761 	// 	auto run = AllocatePositionedRun (font, glyphs.length, text.empty() ? 0 : text.length, bounds);
762 	// 	run.SetGlyphs (glyphs);
763 	// 	run.SetPositions (positions);
764 
765 	// 	if (!text.empty()) {
766 	// 		run.SetText (text);
767 	// 		run.SetClusters (clusters);
768 	// 	}
769 	// }
770 
771 	// AllocateRun
772 
773 	SKRunBuffer AllocateRun(SKPaint font, int count, float x, float y) {
774 		// return AllocateRun (font, count, x, y, 0, null);
775 		return null;
776 	}
777 
778 	// SKRunBuffer AllocateRun (SKPaint font, int count, float x, float y1 Nullable!SKRect bounds)
779 	// {
780 	// 	return AllocateRun (font, count, x, y, 0, bounds);
781 	// }
782 
783 	// SKRunBuffer AllocateRun (SKPaint font, int count, float x, float y, int textByteCount)
784 	// {
785 	// 	return AllocateRun (font, count, x, y, textByteCount);
786 	// }
787 
788 	// SKRunBuffer AllocateRun (SKPaint font, int count, float x, float y, int textByteCount1 Nullable!SKRect bounds)
789 	// {
790 	// 	if (font is null)
791 	// 		throw new ArgumentNullException (font.stringof);
792 
793 	// 	auto originalEncoding = font.TextEncoding;
794 	// 	try {
795 	// 		font.TextEncoding = SKTextEncoding.GlyphId;
796 
797 	// 		SKRunBufferInternal runbuffer;
798 	// 		if (bounds is SKRect b) {
799 	// 			SkiaApi.sk_textblob_builder_alloc_run_text (Handle, font.GetFont ().Handle, count, x, y, textByteCount, &b, &runbuffer);
800 	// 		} else {
801 	// 			SkiaApi.sk_textblob_builder_alloc_run_text (Handle, font.GetFont ().Handle, count, x, y, textByteCount, null, &runbuffer);
802 	// 		}
803 
804 	// 		return new SKRunBuffer (runbuffer, count, textByteCount);
805 
806 	// 	} finally {
807 	// 		font.TextEncoding = originalEncoding;
808 	// 	}
809 	// }
810 
811 	// AllocateHorizontalRun
812 
813 	SKHorizontalRunBuffer AllocateHorizontalRun(SKPaint font, int count, float y) {
814 		return AllocateHorizontalRun(font, count, y, 0);
815 	}
816 
817 	// SKHorizontalRunBuffer AllocateHorizontalRun (SKPaint font, int count, float y1 Nullable!SKRect bounds)
818 	// {
819 	// 	return AllocateHorizontalRun (font, count, y, 0, bounds);
820 	// }
821 
822 	SKHorizontalRunBuffer AllocateHorizontalRun(SKPaint font, int count, float y, int textByteCount) {
823 		return AllocateHorizontalRun(font, count, y, textByteCount);
824 	}
825 
826 	// SKHorizontalRunBuffer AllocateHorizontalRun (SKPaint font, int count, float y, int textByteCount1 Nullable!SKRect bounds)
827 	// {
828 	// 	if (font is null)
829 	// 		throw new ArgumentNullException (font.stringof);
830 
831 	// 	auto originalEncoding = font.TextEncoding;
832 	// 	try {
833 	// 		font.TextEncoding = SKTextEncoding.GlyphId;
834 
835 	// 		SKRunBufferInternal runbuffer;
836 	// 		SKRect b = cast(SKRect)bounds;
837 	// 		if (bounds is SKRect b) {
838 	// 			SkiaApi.sk_textblob_builder_alloc_run_text_pos_h (Handle, font.GetFont ().Handle, count, y, textByteCount, &b, &runbuffer);
839 	// 		} else {
840 	// 			SkiaApi.sk_textblob_builder_alloc_run_text_pos_h (Handle, font.GetFont ().Handle, count, y, textByteCount, null, &runbuffer);
841 	// 		}
842 
843 	// 		return new SKHorizontalRunBuffer (runbuffer, count, textByteCount);
844 	// 	} finally {
845 	// 		font.TextEncoding = originalEncoding;
846 	// 	}
847 	// }
848 
849 	// AllocatePositionedRun
850 
851 	// SKPositionedRunBuffer AllocatePositionedRun (SKPaint font, int count)
852 	// {
853 	// 	return AllocatePositionedRun (font, count, 0);
854 	// }
855 
856 	// SKPositionedRunBuffer AllocatePositionedRun (SKPaint font, int count1 Nullable!SKRect bounds)
857 	// {
858 	// 	return AllocatePositionedRun (font, count, 0, bounds);
859 	// }
860 
861 	// SKPositionedRunBuffer AllocatePositionedRun (SKPaint font, int count, int textByteCount)
862 	// {
863 	// 	return AllocatePositionedRun (font, count, textByteCount);
864 	// }
865 
866 	// SKPositionedRunBuffer AllocatePositionedRun (SKPaint font, int count, int textByteCount1 Nullable!SKRect bounds)
867 	// {
868 	// 	if (font is null)
869 	// 		throw new ArgumentNullException (font.stringof);
870 
871 	// 	auto originalEncoding = font.TextEncoding;
872 	// 	try {
873 	// 		font.TextEncoding = SKTextEncoding.GlyphId;
874 
875 	// 		SKRunBufferInternal runbuffer;
876 	// 		if (bounds is SKRect b) {
877 	// 			SkiaApi.sk_textblob_builder_alloc_run_text_pos (Handle, font.GetFont ().Handle, count, textByteCount, &b, &runbuffer);
878 	// 		} else {
879 	// 			SkiaApi.sk_textblob_builder_alloc_run_text_pos (Handle, font.GetFont ().Handle, count, textByteCount, null, &runbuffer);
880 	// 		}
881 
882 	// 		return new SKPositionedRunBuffer (runbuffer, count, textByteCount);
883 	// 	} finally {
884 	// 		font.TextEncoding = originalEncoding;
885 	// 	}
886 	// }
887 }