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 }