comparison dwt/graphics/TextLayout.d @ 213:36f5cb12e1a2

Update to SWT 3.4M7
author Frank Benoit <benoit@tionex.de>
date Sat, 17 May 2008 17:34:28 +0200
parents ab60f3309436
children a8fed3e56433
comparison
equal deleted inserted replaced
212:ab60f3309436 213:36f5cb12e1a2
14 14
15 import dwt.DWT; 15 import dwt.DWT;
16 import dwt.DWTException; 16 import dwt.DWTException;
17 import dwt.internal.Compatibility; 17 import dwt.internal.Compatibility;
18 import dwt.internal.gdip.Gdip; 18 import dwt.internal.gdip.Gdip;
19
19 import dwt.internal.win32.OS; 20 import dwt.internal.win32.OS;
20 21
21 import dwt.graphics.Color; 22 import dwt.graphics.Color;
22 import dwt.graphics.Device; 23 import dwt.graphics.Device;
23 import dwt.graphics.Font; 24 import dwt.graphics.Font;
49 * </p> 50 * </p>
50 * 51 *
51 * @since 3.0 52 * @since 3.0
52 */ 53 */
53 public final class TextLayout : Resource { 54 public final class TextLayout : Resource {
55 alias Resource.init_ init_;
56
54 Font font; 57 Font font;
55 String text, segmentsText; 58 String text, segmentsText;
56 int lineSpacing; 59 int lineSpacing;
57 int ascent, descent; 60 int ascent, descent;
58 int alignment; 61 int alignment;
61 int indent; 64 int indent;
62 bool justify; 65 bool justify;
63 int[] tabs; 66 int[] tabs;
64 int[] segments; 67 int[] segments;
65 StyleItem[] styles; 68 StyleItem[] styles;
69 int stylesCount;
66 70
67 StyleItem[] allRuns; 71 StyleItem[] allRuns;
68 StyleItem[][] runs; 72 StyleItem[][] runs;
69 int[] lineOffset, lineY, lineWidth; 73 int[] lineOffset, lineY, lineWidth;
70 void* mLangFontLink2; 74 void* mLangFontLink2;
87 static_this_completed = true; 91 static_this_completed = true;
88 } 92 }
89 } 93 }
90 } 94 }
91 95
96 /* IME has a copy of these constants */
97 static const int UNDERLINE_IME_DOT = 1 << 16;
98 static const int UNDERLINE_IME_DASH = 2 << 16;
99 static const int UNDERLINE_IME_THICK = 3 << 16;
100
92 class StyleItem { 101 class StyleItem {
93 TextStyle style; 102 TextStyle style;
94 int start, length; 103 int start, length;
95 bool lineBreak, softBreak, tab; 104 bool lineBreak, softBreak, tab;
96 105
110 int width; 119 int width;
111 int ascent; 120 int ascent;
112 int descent; 121 int descent;
113 int leading; 122 int leading;
114 int x; 123 int x;
124 int underlinePos, underlineThickness;
125 int strikeoutPos, strikeoutThickness;
115 126
116 /* Justify info (malloc during computeRuns) */ 127 /* Justify info (malloc during computeRuns) */
117 int* justify; 128 int* justify;
118 129
119 /* ScriptBreak */ 130 /* ScriptBreak */
189 * 200 *
190 * @see #dispose() 201 * @see #dispose()
191 */ 202 */
192 public this (Device device) { 203 public this (Device device) {
193 static_this(); 204 static_this();
194 if (device is null) device = Device.getDevice(); 205 super(device);
195 if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
196 this.device = device;
197 wrapWidth = ascent = descent = -1; 206 wrapWidth = ascent = descent = -1;
198 lineSpacing = 0; 207 lineSpacing = 0;
199 orientation = DWT.LEFT_TO_RIGHT; 208 orientation = DWT.LEFT_TO_RIGHT;
200 styles = new StyleItem[2]; 209 styles = new StyleItem[2];
201 styles[0] = new StyleItem(); 210 styles[0] = new StyleItem();
202 styles[1] = new StyleItem(); 211 styles[1] = new StyleItem();
212 stylesCount = 2;
203 text = ""; //$NON-NLS-1$ 213 text = ""; //$NON-NLS-1$
204 void* ppv; 214 void* ppv;
205 OS.OleInitialize(null); 215 OS.OleInitialize(null);
206 if (OS.CoCreateInstance(CLSID_CMultiLanguage.ptr, null, OS.CLSCTX_INPROC_SERVER, IID_IMLangFontLink2.ptr, cast(void*)&ppv) is OS.S_OK) { 216 if (OS.CoCreateInstance(CLSID_CMultiLanguage.ptr, null, OS.CLSCTX_INPROC_SERVER, IID_IMLangFontLink2.ptr, cast(void*)&ppv) is OS.S_OK) {
207 mLangFontLink2 = ppv; 217 mLangFontLink2 = ppv;
208 } 218 }
209 if (device.tracking) device.new_Object(this); 219 init_();
210 } 220 }
211 221
212 void breakRun(StyleItem run) { 222 void breakRun(StyleItem run) {
213 if (run.psla !is null) return; 223 if (run.psla !is null) return;
214 wchar[] chars = StrToWCHARs( 0, segmentsText[ run.start .. run.start + run.length ] ); 224 wchar[] chars = StrToWCHARs( 0, segmentsText[ run.start .. run.start + run.length ] );
347 start = run.length - 1; 357 start = run.length - 1;
348 } 358 }
349 if (start is 0 && i !is lineStart && !run.tab) { 359 if (start is 0 && i !is lineStart && !run.tab) {
350 run = allRuns[--i]; 360 run = allRuns[--i];
351 } else if (start <= 0 && i is lineStart) { 361 } else if (start <= 0 && i is lineStart) {
352 i = firstIndice; 362 if (lineWidth is wrapWidth && firstIndice > 0) {
353 run = allRuns[i]; 363 i = firstIndice - 1;
354 start = Math.max(1, firstStart); 364 run = allRuns[i];
365 start = run.length;
366 } else {
367 i = firstIndice;
368 run = allRuns[i];
369 start = Math.max(1, firstStart);
370 }
355 } 371 }
356 breakRun(run); 372 breakRun(run);
357 while (start < run.length) { 373 while (start < run.length) {
358 logAttr = run.psla + start; 374 logAttr = run.psla + start;
359 //OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof); 375 //OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof);
469 } 485 }
470 if (srcHdc !is null) OS.DeleteDC(srcHdc); 486 if (srcHdc !is null) OS.DeleteDC(srcHdc);
471 if (gc is null) device.internal_dispose_GC(hDC, null); 487 if (gc is null) device.internal_dispose_GC(hDC, null);
472 } 488 }
473 489
474 /** 490 void destroy () {
475 * Disposes of the operating system resources associated with
476 * the text layout. Applications must dispose of all allocated text layouts.
477 */
478 override public void dispose () {
479 if (device is null) return;
480 freeRuns(); 491 freeRuns();
481 font = null; 492 font = null;
482 text = null; 493 text = null;
483 segmentsText = null; 494 segmentsText = null;
484 tabs = null; 495 tabs = null;
491 /* Release() */ 502 /* Release() */
492 OS.VtblCall(2, mLangFontLink2); 503 OS.VtblCall(2, mLangFontLink2);
493 mLangFontLink2 = null; 504 mLangFontLink2 = null;
494 } 505 }
495 OS.OleUninitialize(); 506 OS.OleUninitialize();
496 if (device.tracking) device.dispose_Object(this);
497 device = null;
498 } 507 }
499 508
500 /** 509 /**
501 * Draws the receiver's text using the specified GC at the specified 510 * Draws the receiver's text using the specified GC at the specified
502 * point. 511 * point.
593 Gdip.Matrix_Invert(identity_); 602 Gdip.Matrix_Invert(identity_);
594 Gdip.Matrix_Multiply(matrix, identity_, Gdip.MatrixOrderAppend); 603 Gdip.Matrix_Multiply(matrix, identity_, Gdip.MatrixOrderAppend);
595 Gdip.Matrix_delete(identity_); 604 Gdip.Matrix_delete(identity_);
596 if (!Gdip.Matrix_IsIdentity(matrix)) { 605 if (!Gdip.Matrix_IsIdentity(matrix)) {
597 lpXform = new float[6]; 606 lpXform = new float[6];
598 Gdip.Matrix_GetElements(matrix, lpXform); 607 Gdip.Matrix_GetElements(matrix, lpXform.ptr);
599 } 608 }
600 Gdip.Matrix_delete(matrix); 609 Gdip.Matrix_delete(matrix);
601 if ((data.style & DWT.MIRRORED) !is 0 && lpXform !is null) { 610 if ((data.style & DWT.MIRRORED) !is 0 && lpXform !is null) {
602 gdip = true; 611 gdip = true;
603 lpXform = null; 612 lpXform = null;
640 if (selectionBackground is null) selectionBackground = device.getSystemColor(DWT.COLOR_LIST_SELECTION); 649 if (selectionBackground is null) selectionBackground = device.getSystemColor(DWT.COLOR_LIST_SELECTION);
641 selectionStart = translateOffset(selectionStart); 650 selectionStart = translateOffset(selectionStart);
642 selectionEnd = translateOffset(selectionEnd); 651 selectionEnd = translateOffset(selectionEnd);
643 } 652 }
644 RECT rect; 653 RECT rect;
645 void* selBrush; 654 Gdip.Brush selBrush;
646 void* selPen; 655 Gdip.Pen selPen;
647 void* selBrushFg; 656 Gdip.Brush selBrushFg;
648 657
649 if (hasSelection || (flags & DWT.LAST_LINE_SELECTION) !is 0) { 658 if (hasSelection || (flags & DWT.LAST_LINE_SELECTION) !is 0) {
650 if (gdip) { 659 if (gdip) {
651 auto bg = selectionBackground.handle; 660 auto bg = selectionBackground.handle;
652 auto argb = ((alpha & 0xFF) << 24) | ((bg >> 16) & 0xFF) | (bg & 0xFF00) | ((bg & 0xFF) << 16); 661 auto argb = ((alpha & 0xFF) << 24) | ((bg >> 16) & 0xFF) | (bg & 0xFF00) | ((bg & 0xFF) << 16);
653 auto color = Gdip.Color_new(argb); 662 auto color = Gdip.Color_new(argb);
654 selBrush = Gdip.SolidBrush_new(color); 663 selBrush = cast(Gdip.Brush)Gdip.SolidBrush_new(color);
655 Gdip.Color_delete(color); 664 Gdip.Color_delete(color);
656 auto fg = selectionForeground.handle; 665 auto fg = selectionForeground.handle;
657 argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16); 666 argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16);
658 color = Gdip.Color_new(argb); 667 color = Gdip.Color_new(argb);
659 selBrushFg = Gdip.SolidBrush_new(color); 668 selBrushFg = cast(Gdip.Brush)Gdip.SolidBrush_new(color);
660 selPen = Gdip.Pen_new( cast(Gdip.Brush)selBrushFg, 1); 669 selPen = cast(Gdip.Pen) Gdip.Pen_new( cast(Gdip.Brush)selBrushFg, 1);
661 Gdip.Color_delete(color); 670 Gdip.Color_delete(color);
662 } else { 671 } else {
663 selBrush = OS.CreateSolidBrush(selectionBackground.handle); 672 selBrush = cast(Gdip.Brush)OS.CreateSolidBrush(selectionBackground.handle);
664 selPen = OS.CreatePen(OS.PS_SOLID, 1, selectionForeground.handle); 673 selPen = cast(Gdip.Pen)OS.CreatePen(OS.PS_SOLID, 1, selectionForeground.handle);
665 } 674 }
666 } 675 }
667 int offset = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? -1 : 0; 676 int offset = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? -1 : 0;
668 OS.SetBkMode(hdc, OS.TRANSPARENT); 677 OS.SetBkMode(hdc, OS.TRANSPARENT);
669 for (int line=0; line<runs.length; line++) { 678 for (int line=0; line<runs.length; line++) {
670 int drawX = x + getLineIndent(line); 679 int drawX = x + getLineIndent(line);
671 int drawY = y + lineY[line]; 680 int drawY = y + lineY[line];
672 StyleItem[] lineRuns = runs[line]; 681 StyleItem[] lineRuns = runs[line];
673 int lineHeight = lineY[line+1] - lineY[line]; 682 int lineHeight = lineY[line+1] - lineY[line] - lineSpacing;
674 if (flags !is 0 && (hasSelection || (flags & DWT.LAST_LINE_SELECTION) !is 0)) { 683 if (flags !is 0 && (hasSelection || (flags & DWT.LAST_LINE_SELECTION) !is 0)) {
675 bool extents = false; 684 bool extents = false;
676 if (line is runs.length - 1 && (flags & DWT.LAST_LINE_SELECTION) !is 0) { 685 if (line is runs.length - 1 && (flags & DWT.LAST_LINE_SELECTION) !is 0) {
677 extents = true; 686 extents = true;
678 } else { 687 } else {
689 if (extents) { 698 if (extents) {
690 int width; 699 int width;
691 if ((flags & DWT.FULL_SELECTION) !is 0) { 700 if ((flags & DWT.FULL_SELECTION) !is 0) {
692 width = OS.IsWin95 ? 0x7FFF : 0x6FFFFFF; 701 width = OS.IsWin95 ? 0x7FFF : 0x6FFFFFF;
693 } else { 702 } else {
694 width = (lineHeight - lineSpacing) / 3; 703 width = lineHeight / 3;
695 } 704 }
696 if (gdip) { 705 if (gdip) {
697 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, drawX + lineWidth[line], drawY, width, lineHeight - lineSpacing); 706 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, drawX + lineWidth[line], drawY, width, lineHeight);
698 } else { 707 } else {
699 OS.SelectObject(hdc, selBrush); 708 OS.SelectObject(hdc, selBrush);
700 OS.PatBlt(hdc, drawX + lineWidth[line], drawY, width, lineHeight - lineSpacing, OS.PATCOPY); 709 OS.PatBlt(hdc, drawX + lineWidth[line], drawY, width, lineHeight, OS.PATCOPY);
701 } 710 }
702 } 711 }
703 } 712 }
704 if (drawX > clip.x + clip.width) continue; 713 if (drawX > clip.x + clip.width) continue;
705 if (drawX + lineWidth[line] < clip.x) continue; 714 if (drawX + lineWidth[line] < clip.x) continue;
706 int baseline = Math.max(0, this.ascent); 715 int baseline = Math.max(0, this.ascent);
716 int lineUnderlinePos = 0;
707 for (int i = 0; i < lineRuns.length; i++) { 717 for (int i = 0; i < lineRuns.length; i++) {
708 baseline = Math.max(baseline, lineRuns[i].ascent); 718 baseline = Math.max(baseline, lineRuns[i].ascent);
719 lineUnderlinePos = Math.min(lineUnderlinePos, lineRuns[i].underlinePos);
709 } 720 }
710 int alignmentX = drawX; 721 int alignmentX = drawX;
711 for (int i = 0; i < lineRuns.length; i++) { 722 for (int i = 0; i < lineRuns.length; i++) {
712 StyleItem run = lineRuns[i]; 723 StyleItem run = lineRuns[i];
713 if (run.length is 0) continue; 724 if (run.length is 0) continue;
716 if (!run.lineBreak || run.softBreak) { 727 if (!run.lineBreak || run.softBreak) {
717 int end = run.start + run.length - 1; 728 int end = run.start + run.length - 1;
718 bool fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end; 729 bool fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end;
719 if (fullSelection) { 730 if (fullSelection) {
720 if (gdip) { 731 if (gdip) {
721 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, drawX, drawY, run.width, lineHeight - lineSpacing); 732 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, drawX, drawY, run.width, lineHeight);
722 } else { 733 } else {
723 OS.SelectObject(hdc, selBrush); 734 OS.SelectObject(hdc, selBrush);
724 OS.PatBlt(hdc, drawX, drawY, run.width, lineHeight - lineSpacing, OS.PATCOPY); 735 OS.PatBlt(hdc, drawX, drawY, run.width, lineHeight, OS.PATCOPY);
725 } 736 }
726 } else { 737 } else {
727 if (run.style !is null && run.style.background !is null) { 738 if (run.style !is null && run.style.background !is null) {
728 auto bg = run.style.background.handle; 739 auto bg = run.style.background.handle;
729 int drawRunY = drawY + (baseline - run.ascent);
730 if (gdip) { 740 if (gdip) {
731 int argb = ((alpha & 0xFF) << 24) | ((bg >> 16) & 0xFF) | (bg & 0xFF00) | ((bg & 0xFF) << 16); 741 int argb = ((alpha & 0xFF) << 24) | ((bg >> 16) & 0xFF) | (bg & 0xFF00) | ((bg & 0xFF) << 16);
732 auto color = Gdip.Color_new(argb); 742 auto color = Gdip.Color_new(argb);
733 auto brush = Gdip.SolidBrush_new(color); 743 auto brush = Gdip.SolidBrush_new(color);
734 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)brush, drawX, drawRunY, run.width, run.ascent + run.descent); 744 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)brush, drawX, drawY, run.width, lineHeight);
735 Gdip.Color_delete(color); 745 Gdip.Color_delete(color);
736 Gdip.SolidBrush_delete(brush); 746 Gdip.SolidBrush_delete(brush);
737 } else { 747 } else {
738 auto hBrush = OS.CreateSolidBrush (bg); 748 auto hBrush = OS.CreateSolidBrush (bg);
739 auto oldBrush = OS.SelectObject(hdc, hBrush); 749 auto oldBrush = OS.SelectObject(hdc, hBrush);
740 OS.PatBlt(hdc, drawX, drawRunY, run.width, run.ascent + run.descent, OS.PATCOPY); 750 OS.PatBlt(hdc, drawX, drawY, run.width, lineHeight, OS.PATCOPY);
741 OS.SelectObject(hdc, oldBrush); 751 OS.SelectObject(hdc, oldBrush);
742 OS.DeleteObject(hBrush); 752 OS.DeleteObject(hBrush);
743 } 753 }
744 } 754 }
745 bool partialSelection = hasSelection && !(selectionStart > end || run.start > selectionEnd); 755 bool partialSelection = hasSelection && !(selectionStart > end || run.start > selectionEnd);
755 rect.left = drawX + runX; 765 rect.left = drawX + runX;
756 rect.top = drawY; 766 rect.top = drawY;
757 OS.ScriptCPtoX(selEnd, true, cChars, gGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piX); 767 OS.ScriptCPtoX(selEnd, true, cChars, gGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piX);
758 runX = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? run.width - piX : piX; 768 runX = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? run.width - piX : piX;
759 rect.right = drawX + runX; 769 rect.right = drawX + runX;
760 rect.bottom = drawY + lineHeight - lineSpacing; 770 rect.bottom = drawY + lineHeight;
761 if (gdip) { 771 if (gdip) {
772 if (rect.left > rect.right) {
773 int tmp = rect.left;
774 rect.left = rect.right;
775 rect.right = tmp;
776 }
762 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); 777 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
763 } else { 778 } else {
764 OS.SelectObject(hdc, selBrush); 779 OS.SelectObject(hdc, selBrush);
765 OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY); 780 OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
766 } 781 }
768 } 783 }
769 } 784 }
770 } 785 }
771 drawX += run.width; 786 drawX += run.width;
772 } 787 }
788 RECT* borderClip = null;
773 drawX = alignmentX; 789 drawX = alignmentX;
774 for (int i = 0; i < lineRuns.length; i++) { 790 for (int i = 0; i < lineRuns.length; i++) {
775 StyleItem run = lineRuns[i]; 791 StyleItem run = lineRuns[i];
776 if (run.length is 0) continue; 792 if (run.length is 0) continue;
777 if (drawX > clip.x + clip.width) break; 793 if (drawX > clip.x + clip.width) break;
817 default: 833 default:
818 } 834 }
819 if ((type & OS.PT_CLOSEFIGURE) !is 0) newType |= Gdip.PathPointTypeCloseSubpath; 835 if ((type & OS.PT_CLOSEFIGURE) !is 0) newType |= Gdip.PathPointTypeCloseSubpath;
820 types[typeIndex] = cast(byte)newType; 836 types[typeIndex] = cast(byte)newType;
821 } 837 }
822 auto path = Gdip.GraphicsPath_new(cast(Gdip.Point[])points, types, count, Gdip.FillModeAlternate); 838 auto path = Gdip.GraphicsPath_new(cast(Gdip.Point*)points.ptr, types.ptr, count, Gdip.FillModeAlternate);
823 if (path is null) DWT.error(DWT.ERROR_NO_HANDLES); 839 if (path is null) DWT.error(DWT.ERROR_NO_HANDLES);
824 auto brush = foregroundBrush; 840 auto brush = foregroundBrush;
825 if (fullSelection) { 841 if (fullSelection) {
826 brush = cast(Gdip.Brush)selBrushFg; 842 brush = cast(Gdip.Brush)selBrushFg;
827 } else { 843 } else {
863 Gdip.Graphics_FillPath(gdipGraphics, brush, path); 879 Gdip.Graphics_FillPath(gdipGraphics, brush, path);
864 if ((data.style & DWT.MIRRORED) !is 0) { 880 if ((data.style & DWT.MIRRORED) !is 0) {
865 Gdip.Graphics_Restore(gdipGraphics, gstate2); 881 Gdip.Graphics_Restore(gdipGraphics, gstate2);
866 } 882 }
867 Gdip.Graphics_SetSmoothingMode(gdipGraphics, antialias); 883 Gdip.Graphics_SetSmoothingMode(gdipGraphics, antialias);
868 if (run.style !is null && (run.style.underline || run.style.strikeout)) { 884 drawLines(gdip, gdipGraphics, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, brush, null, alpha);
869 auto newPen = hasSelection ? cast(Gdip.Pen)selPen : Gdip.Pen_new(brush, 1);
870 Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone);
871 if (run.style.underline) {
872 int underlineY = drawY + baseline + 1 - run.style.rise;
873 Gdip.Graphics_DrawLine(gdipGraphics, newPen, drawX, underlineY, drawX + run.width, underlineY);
874 }
875 if (run.style.strikeout) {
876 int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2;
877 Gdip.Graphics_DrawLine(gdipGraphics, newPen, drawX, strikeoutY, drawX + run.width, strikeoutY);
878 }
879 if (cast(void*)newPen !is selPen) Gdip.Pen_delete(newPen);
880 Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf);
881 }
882 if (partialSelection) { 885 if (partialSelection) {
883 Gdip.Graphics_Restore(gdipGraphics, gstate); 886 Gdip.Graphics_Restore(gdipGraphics, gstate);
884 gstate = Gdip.Graphics_Save(gdipGraphics); 887 gstate = Gdip.Graphics_Save(gdipGraphics);
885 Gdip.Graphics_SetClip(gdipGraphics, &gdipRect, Gdip.CombineModeIntersect); 888 Gdip.Graphics_SetClip(gdipGraphics, &gdipRect, Gdip.CombineModeIntersect);
886 Gdip.Graphics_SetSmoothingMode(gdipGraphics, textAntialias); 889 Gdip.Graphics_SetSmoothingMode(gdipGraphics, textAntialias);
887 Gdip.Graphics_FillPath(gdipGraphics, cast(Gdip.Brush)selBrushFg, path); 890 if ((data.style & DWT.MIRRORED) !is 0) {
891 gstate2 = Gdip.Graphics_Save(gdipGraphics);
892 Gdip.Graphics_ScaleTransform(gdipGraphics, -1, 1, Gdip.MatrixOrderPrepend);
893 Gdip.Graphics_TranslateTransform(gdipGraphics, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend);
894 }
895 Gdip.Graphics_FillPath(gdipGraphics, selBrushFg, path);
896 if ((data.style & DWT.MIRRORED) !is 0) {
897 Gdip.Graphics_Restore(gdipGraphics, gstate2);
898 }
888 Gdip.Graphics_SetSmoothingMode(gdipGraphics, antialias); 899 Gdip.Graphics_SetSmoothingMode(gdipGraphics, antialias);
889 if (run.style !is null && (run.style.underline || run.style.strikeout)) { 900 drawLines(gdip, gdipGraphics, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, selBrushFg, &rect, alpha);
890 Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone);
891 if (run.style.underline) {
892 int underlineY = drawY + baseline + 1 - run.style.rise;
893 Gdip.Graphics_DrawLine(gdipGraphics, cast(Gdip.Pen)selPen, rect.left, underlineY, rect.right, underlineY);
894 }
895 if (run.style.strikeout) {
896 int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2;
897 Gdip.Graphics_DrawLine(gdipGraphics, cast(Gdip.Pen)selPen, rect.left, strikeoutY, rect.right, strikeoutY);
898 }
899 Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf);
900 }
901 Gdip.Graphics_Restore(gdipGraphics, gstate); 901 Gdip.Graphics_Restore(gdipGraphics, gstate);
902 } 902 }
903 borderClip = drawBorder(gdip, gdipGraphics, x, drawY, lineHeight, foregroundBrush, selBrushFg, fullSelection, borderClip, partialSelection ? &rect : null, alpha, lineRuns, i, selectionStart, selectionEnd);
903 Gdip.GraphicsPath_delete(path); 904 Gdip.GraphicsPath_delete(path);
904 if ( brush !is cast(Gdip.Brush)selBrushFg && brush !is cast(Gdip.Brush)foregroundBrush) 905 if ( brush !is cast(Gdip.Brush)selBrushFg && brush !is cast(Gdip.Brush)foregroundBrush)
905 Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush); 906 Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush);
906 } else { 907 } else {
907 int fg = foreground; 908 auto fg = foreground;
908 if (fullSelection) { 909 if (fullSelection) {
909 fg = selectionForeground.handle; 910 fg = selectionForeground.handle;
910 } else { 911 } else {
911 if (run.style !is null && run.style.foreground !is null) fg = run.style.foreground.handle; 912 if (run.style !is null && run.style.foreground !is null) fg = run.style.foreground.handle;
912 } 913 }
913 OS.SetTextColor(hdc, fg); 914 OS.SetTextColor(hdc, fg);
914 OS.ScriptTextOut(hdc, run.psc, drawX + offset, drawRunY, 0, null, &run.analysis , null, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets); 915 OS.ScriptTextOut(hdc, run.psc, drawX + offset, drawRunY, 0, null, &run.analysis , null, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets);
915 if (run.style !is null && (run.style.underline || run.style.strikeout)) { 916 drawLines(gdip, hdc, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, cast(void*)fg, null, alpha);
916 auto newPen = hasSelection && fg is selectionForeground.handle ? cast(HPEN)selPen : OS.CreatePen(OS.PS_SOLID, 1, fg);
917 auto oldPen = OS.SelectObject(hdc, newPen);
918 if (run.style.underline) {
919 int underlineY = drawY + baseline + 1 - run.style.rise;
920 OS.MoveToEx(hdc, drawX, underlineY, null);
921 OS.LineTo(hdc, drawX + run.width, underlineY);
922 }
923 if (run.style.strikeout) {
924 int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2;
925 OS.MoveToEx(hdc, drawX, strikeoutY, null);
926 OS.LineTo(hdc, drawX + run.width, strikeoutY);
927 }
928 OS.SelectObject(hdc, oldPen);
929 if (!hasSelection || fg !is selectionForeground.handle) OS.DeleteObject(newPen);
930 }
931 if (partialSelection && fg !is selectionForeground.handle) { 917 if (partialSelection && fg !is selectionForeground.handle) {
932 OS.SetTextColor(hdc, selectionForeground.handle); 918 OS.SetTextColor(hdc, selectionForeground.handle);
933 OS.ScriptTextOut(hdc, run.psc, drawX + offset, drawRunY, OS.ETO_CLIPPED, &rect, &run.analysis , null, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets); 919 OS.ScriptTextOut(hdc, run.psc, drawX + offset, drawRunY, OS.ETO_CLIPPED, &rect, &run.analysis , null, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets);
934 if (run.style !is null && (run.style.underline || run.style.strikeout)) { 920 drawLines(gdip, hdc, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, cast(void*)selectionForeground.handle, &rect, alpha);
935 auto oldPen = OS.SelectObject(hdc, selPen);
936 if (run.style.underline) {
937 int underlineY = drawY + baseline + 1 - run.style.rise;
938 OS.MoveToEx(hdc, rect.left, underlineY, null);
939 OS.LineTo(hdc, rect.right, underlineY);
940 }
941 if (run.style.strikeout) {
942 int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2;
943 OS.MoveToEx(hdc, rect.left, strikeoutY, null);
944 OS.LineTo(hdc, rect.right, strikeoutY);
945 }
946 OS.SelectObject(hdc, oldPen);
947 }
948 } 921 }
922 int selForeground = selectionForeground !is null ? selectionForeground.handle : 0;
923 borderClip = drawBorder(gdip, hdc, x, drawY, lineHeight, cast(void*)foreground, cast(void*)selForeground, fullSelection, borderClip, partialSelection ? &rect : null, alpha, lineRuns, i, selectionStart, selectionEnd);
949 } 924 }
950 } 925 }
951 } 926 }
952 drawX += run.width; 927 drawX += run.width;
953 } 928 }
954 } 929 }
955 if (gdip) { 930 if (gdip) {
956 if (selBrush !is null) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)selBrush); 931 if (selBrush !is null) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)selBrush);
957 if (selBrushFg !is null) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)selBrushFg); 932 if (selBrushFg !is null) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)selBrushFg);
958 if (selPen !is null) Gdip.Pen_delete(cast(Gdip.Pen)selPen); 933 if (selPen !is null) Gdip.Pen_delete(selPen);
959 } else { 934 } else {
960 OS.RestoreDC(hdc, state); 935 OS.RestoreDC(hdc, state);
961 if (gdipGraphics !is null) Gdip.Graphics_ReleaseHDC(gdipGraphics, hdc); 936 if (gdipGraphics !is null) Gdip.Graphics_ReleaseHDC(gdipGraphics, hdc);
962 if (selBrush !is null) OS.DeleteObject (selBrush); 937 if (selBrush !is null) OS.DeleteObject (selBrush);
963 if (selPen !is null) OS.DeleteObject (selPen); 938 if (selPen !is null) OS.DeleteObject (selPen);
964 } 939 }
965 } 940 }
966 941
942 void drawLines(bool advance, void* graphics, int x, int lineBaseline, int lineUnderlinePos, int lineBottom, StyleItem[] line, int index, void* color, RECT* clipRect, int alpha) {
943 StyleItem run = line[index];
944 TextStyle style = run.style;
945 if (style is null) return;
946 if (!style.underline && !style.strikeout) return;
947 int runX = x + run.x;
948 int underlineY = lineBaseline - lineUnderlinePos;
949 int strikeoutY = lineBaseline - run.strikeoutPos;
950 if (advance) {
951 Gdip.Graphics_SetPixelOffsetMode(cast(Gdip.Graphics)graphics, Gdip.PixelOffsetModeNone);
952 auto brush = color;
953 if (style.underline) {
954 if (style.underlineColor !is null) {
955 int fg = style.underlineColor.handle;
956 int argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16);
957 auto gdiColor = Gdip.Color_new(argb);
958 brush = Gdip.SolidBrush_new(gdiColor);
959 Gdip.Color_delete(gdiColor);
960 }
961 switch (style.underlineStyle) {
962 case DWT.UNDERLINE_SQUIGGLE:
963 case DWT.UNDERLINE_ERROR: {
964 int squigglyThickness = 1;
965 int squigglyHeight = 2 * squigglyThickness;
966 int squigglyY = Math.min(underlineY - squigglyHeight / 2, lineBottom - squigglyHeight - 1);
967 int squigglyX = runX;
968 for (int i = index; i > 0 && style.isAdherentUnderline(line[i - 1].style); i--) {
969 squigglyX = x + line[i - 1].x;
970 }
971 int gstate = 0;
972 if (clipRect is null) {
973 gstate = Gdip.Graphics_Save(cast(Gdip.Graphics)graphics);
974 Gdip.Rect gdipRect;
975 gdipRect.X = runX;
976 gdipRect.Y = squigglyY;
977 gdipRect.Width = run.width + 1;
978 gdipRect.Height = squigglyY + squigglyHeight + 1;
979 Gdip.Graphics_SetClip(cast(Gdip.Graphics)graphics, &gdipRect, Gdip.CombineModeIntersect);
980 }
981 int[] points = computePolyline(squigglyX, squigglyY, runX + run.width, squigglyY + squigglyHeight);
982 auto pen = Gdip.Pen_new(cast(Gdip.Brush)brush, squigglyThickness);
983 Gdip.Graphics_DrawLines(cast(Gdip.Graphics)graphics, pen, cast(Gdip.Point*)points.ptr, points.length / 2);
984 Gdip.Pen_delete(pen);
985 if (gstate !is 0) Gdip.Graphics_Restore(cast(Gdip.Graphics)graphics, gstate);
986 break;
987 }
988 case DWT.UNDERLINE_SINGLE:
989 Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX, underlineY, run.width, run.underlineThickness);
990 break;
991 case DWT.UNDERLINE_DOUBLE:
992 Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX, underlineY, run.width, run.underlineThickness);
993 Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX, underlineY + run.underlineThickness * 2, run.width, run.underlineThickness);
994 break;
995 case UNDERLINE_IME_THICK:
996 Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX - run.underlineThickness, underlineY, run.width, run.underlineThickness * 2);
997 break;
998 case UNDERLINE_IME_DOT:
999 case UNDERLINE_IME_DASH: {
1000 auto pen = Gdip.Pen_new(cast(Gdip.Brush)brush, 1);
1001 int dashStyle = style.underlineStyle is UNDERLINE_IME_DOT ? Gdip.DashStyleDot : Gdip.DashStyleDash;
1002 Gdip.Pen_SetDashStyle(pen, dashStyle);
1003 Gdip.Graphics_DrawLine(cast(Gdip.Graphics)graphics, pen, runX, underlineY, runX + run.width, underlineY);
1004 Gdip.Pen_delete(pen);
1005 break;
1006 }
1007 }
1008 if (brush !is color) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush);
1009 }
1010 if (style.strikeout) {
1011 if (style.strikeoutColor !is null) {
1012 int fg = style.strikeoutColor.handle;
1013 int argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16);
1014 auto gdiColor = Gdip.Color_new(argb);
1015 brush = Gdip.SolidBrush_new(gdiColor);
1016 Gdip.Color_delete(gdiColor);
1017 }
1018 Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX, strikeoutY, run.width, run.strikeoutThickness);
1019 if (brush !is color) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush);
1020 }
1021 Gdip.Graphics_SetPixelOffsetMode(cast(Gdip.Graphics)graphics, Gdip.PixelOffsetModeHalf);
1022 } else {
1023 uint colorRefUnderline = cast(uint)color;
1024 uint colorRefStrikeout = cast(uint)color;
1025 int /*long*/ brushUnderline = 0;
1026 int /*long*/ brushStrikeout = 0;
1027 RECT rect;
1028 if (style.underline) {
1029 if (style.underlineColor !is null) {
1030 colorRefUnderline = style.underlineColor.handle;
1031 }
1032 switch (style.underlineStyle) {
1033 case DWT.UNDERLINE_SQUIGGLE:
1034 case DWT.UNDERLINE_ERROR: {
1035 int squigglyThickness = 1;
1036 int squigglyHeight = 2 * squigglyThickness;
1037 int squigglyY = Math.min(underlineY - squigglyHeight / 2, lineBottom - squigglyHeight - 1);
1038 int squigglyX = runX;
1039 for (int i = index; i > 0 && style.isAdherentUnderline(line[i - 1].style); i--) {
1040 squigglyX = x + line[i - 1].x;
1041 }
1042 int state = OS.SaveDC(graphics);
1043 if (clipRect !is null) {
1044 OS.IntersectClipRect(graphics, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
1045 } else {
1046 OS.IntersectClipRect(graphics, runX, squigglyY, runX + run.width + 1, squigglyY + squigglyHeight + 1);
1047 }
1048 int[] points = computePolyline(squigglyX, squigglyY, runX + run.width, squigglyY + squigglyHeight);
1049 auto pen = OS.CreatePen(OS.PS_SOLID, squigglyThickness, colorRefUnderline);
1050 auto oldPen = OS.SelectObject(graphics, pen);
1051 OS.Polyline(graphics, cast(POINT*)points.ptr, points.length / 2);
1052 int length_ = points.length;
1053 if (length_ >= 2 && squigglyThickness <= 1) {
1054 OS.SetPixel (graphics, points[length_ - 2], points[length_ - 1], colorRefUnderline);
1055 }
1056 OS.RestoreDC(graphics, state);
1057 OS.SelectObject(graphics, oldPen);
1058 OS.DeleteObject(pen);
1059 break;
1060 }
1061 case DWT.UNDERLINE_SINGLE:
1062 brushUnderline = cast(uint) OS.CreateSolidBrush(colorRefUnderline);
1063 OS.SetRect(&rect, runX, underlineY, runX + run.width, underlineY + run.underlineThickness);
1064 if (clipRect !is null) {
1065 rect.left = Math.max(rect.left, clipRect.left);
1066 rect.right = Math.min(rect.right, clipRect.right);
1067 }
1068 OS.FillRect(graphics, &rect, cast(void*)brushUnderline);
1069 break;
1070 case DWT.UNDERLINE_DOUBLE:
1071 brushUnderline = cast(uint)OS.CreateSolidBrush(colorRefUnderline);
1072 OS.SetRect(&rect, runX, underlineY, runX + run.width, underlineY + run.underlineThickness);
1073 if (clipRect !is null) {
1074 rect.left = Math.max(rect.left, clipRect.left);
1075 rect.right = Math.min(rect.right, clipRect.right);
1076 }
1077 OS.FillRect(graphics, &rect, cast(void*)brushUnderline);
1078 OS.SetRect(&rect, runX, underlineY + run.underlineThickness * 2, runX + run.width, underlineY + run.underlineThickness * 3);
1079 if (clipRect !is null) {
1080 rect.left = Math.max(rect.left, clipRect.left);
1081 rect.right = Math.min(rect.right, clipRect.right);
1082 }
1083 OS.FillRect(graphics, &rect, cast(void*)brushUnderline);
1084 break;
1085 case UNDERLINE_IME_THICK:
1086 brushUnderline = cast(uint)OS.CreateSolidBrush(colorRefUnderline);
1087 OS.SetRect(&rect, runX, underlineY - run.underlineThickness, runX + run.width, underlineY + run.underlineThickness);
1088 if (clipRect !is null) {
1089 rect.left = Math.max(rect.left, clipRect.left);
1090 rect.right = Math.min(rect.right, clipRect.right);
1091 }
1092 OS.FillRect(graphics, &rect, cast(void*)brushUnderline);
1093 break;
1094 case UNDERLINE_IME_DASH:
1095 case UNDERLINE_IME_DOT: {
1096 underlineY = lineBaseline + run.descent;
1097 int penStyle = style.underlineStyle is UNDERLINE_IME_DASH ? OS.PS_DASH : OS.PS_DOT;
1098 auto pen = OS.CreatePen(penStyle, 1, colorRefUnderline);
1099 auto oldPen = OS.SelectObject(graphics, pen);
1100 OS.SetRect(&rect, runX, underlineY, runX + run.width, underlineY + run.underlineThickness);
1101 if (clipRect !is null) {
1102 rect.left = Math.max(rect.left, clipRect.left);
1103 rect.right = Math.min(rect.right, clipRect.right);
1104 }
1105 OS.MoveToEx(graphics, rect.left, rect.top, null);
1106 OS.LineTo(graphics, rect.right, rect.top);
1107 OS.SelectObject(graphics, oldPen);
1108 OS.DeleteObject(pen);
1109 break;
1110 }
1111 }
1112 }
1113 if (style.strikeout) {
1114 if (style.strikeoutColor !is null) {
1115 colorRefStrikeout = style.strikeoutColor.handle;
1116 }
1117 if (brushUnderline !is 0 && colorRefStrikeout is colorRefUnderline) {
1118 brushStrikeout = brushUnderline;
1119 } else {
1120 brushStrikeout = cast(int) OS.CreateSolidBrush(colorRefStrikeout);
1121 }
1122 OS.SetRect(&rect, runX, strikeoutY, runX + run.width, strikeoutY + run.strikeoutThickness);
1123 if (clipRect !is null) {
1124 rect.left = Math.max(rect.left, clipRect.left);
1125 rect.right = Math.min(rect.right, clipRect.right);
1126 }
1127 OS.FillRect(graphics, &rect, cast(void*)brushStrikeout);
1128 }
1129 if (brushUnderline !is 0) OS.DeleteObject(cast(void*)brushUnderline);
1130 if (brushStrikeout !is 0 && brushStrikeout !is brushUnderline) OS.DeleteObject(cast(void*)brushStrikeout);
1131 }
1132 }
1133
1134 RECT* drawBorder(bool advance, void* graphics, int x, int y, int lineHeight, void* color, void* selectionColor, bool fullSelection, RECT* clipRect, RECT* rect, int alpha, StyleItem[] line, int index, int selectionStart, int selectionEnd) {
1135 StyleItem run = line[index];
1136 TextStyle style = run.style;
1137 if (style is null) return null;
1138 if (style.borderStyle is DWT.NONE) return null;
1139 if (rect !is null) {
1140 if (clipRect is null) {
1141 clipRect = new RECT ();
1142 OS.SetRect(clipRect, -1, rect.top, -1, rect.bottom);
1143 }
1144 bool isRTL = (orientation & DWT.RIGHT_TO_LEFT) !is 0;
1145 if (run.start <= selectionStart && selectionStart <= run.start + run.length) {
1146 if (run.analysis.fRTL ^ isRTL) {
1147 clipRect.right = rect.left;
1148 } else {
1149 clipRect.left = rect.left;
1150 }
1151 }
1152 if (run.start <= selectionEnd && selectionEnd <= run.start + run.length) {
1153 if (run.analysis.fRTL ^ isRTL) {
1154 clipRect.left = rect.right;
1155 } else {
1156 clipRect.right = rect.right;
1157 }
1158 }
1159 }
1160 if (index + 1 >= line.length || !style.isAdherentBorder(line[index + 1].style)) {
1161 int left = run.x;
1162 for (int i = index; i > 0 && style.isAdherentBorder(line[i - 1].style); i--) {
1163 left = line[i - 1].x;
1164 }
1165 if (advance) {
1166 auto brush = color;
1167 int customColor = -1;
1168 if (style.borderColor !is null) {
1169 customColor = style.borderColor.handle;
1170 } else {
1171 if (style.foreground !is null) {
1172 customColor = style.foreground.handle;
1173 }
1174 if (fullSelection && clipRect is null) {
1175 customColor = -1;
1176 brush = selectionColor;
1177 }
1178 }
1179 if (customColor !is -1) {
1180 int argb = ((alpha & 0xFF) << 24) | ((customColor >> 16) & 0xFF) | (customColor & 0xFF00) | ((customColor & 0xFF) << 16);
1181 auto gdiColor = Gdip.Color_new(argb);
1182 brush = Gdip.SolidBrush_new(gdiColor);
1183 Gdip.Color_delete(gdiColor);
1184 }
1185 int lineWidth = 1;
1186 int lineStyle = Gdip.DashStyleSolid;
1187 switch (style.borderStyle) {
1188 case DWT.BORDER_SOLID: break;
1189 case DWT.BORDER_DASH: lineStyle = Gdip.DashStyleDash; break;
1190 case DWT.BORDER_DOT: lineStyle = Gdip.DashStyleDot; break;
1191 }
1192 auto pen = Gdip.Pen_new(cast(Gdip.Brush)brush, lineWidth);
1193 Gdip.Pen_SetDashStyle(pen, lineStyle);
1194 float gdipXOffset = 0.5f, gdipYOffset = 0.5f;
1195 Gdip.Graphics_TranslateTransform(cast(Gdip.Graphics)graphics, gdipXOffset, gdipYOffset, Gdip.MatrixOrderPrepend);
1196 if (style.borderColor is null && clipRect !is null) {
1197 int gstate = Gdip.Graphics_Save(cast(Gdip.Graphics)graphics);
1198 if (clipRect.left is -1) clipRect.left = 0;
1199 if (clipRect.right is -1) clipRect.right = 0x7ffff;
1200 Gdip.Rect gdipRect;
1201 gdipRect.X = clipRect.left;
1202 gdipRect.Y = clipRect.top;
1203 gdipRect.Width = clipRect.right - clipRect.left;
1204 gdipRect.Height = clipRect.bottom - clipRect.top;
1205 Gdip.Graphics_SetClip(cast(Gdip.Graphics)graphics, &gdipRect, Gdip.CombineModeExclude);
1206 Gdip.Graphics_DrawRectangle(cast(Gdip.Graphics)graphics, pen, x + left, y, run.x + run.width - left - 1, lineHeight - 1);
1207 Gdip.Graphics_Restore(cast(Gdip.Graphics)graphics, gstate);
1208 gstate = Gdip.Graphics_Save(cast(Gdip.Graphics)graphics);
1209 Gdip.Graphics_SetClip(cast(Gdip.Graphics)graphics, &gdipRect, Gdip.CombineModeIntersect);
1210 auto selPen = Gdip.Pen_new(cast(Gdip.Brush)selectionColor, lineWidth);
1211 Gdip.Pen_SetDashStyle(pen, lineStyle);
1212 Gdip.Graphics_DrawRectangle(cast(Gdip.Graphics)graphics, selPen, x + left, y, run.x + run.width - left - 1, lineHeight - 1);
1213 Gdip.Pen_delete(selPen);
1214 Gdip.Graphics_Restore(cast(Gdip.Graphics)graphics, gstate);
1215 } else {
1216 Gdip.Graphics_DrawRectangle(cast(Gdip.Graphics)graphics, pen, x + left, y, run.x + run.width - left - 1, lineHeight - 1);
1217 }
1218 Gdip.Graphics_TranslateTransform(cast(Gdip.Graphics)graphics, -gdipXOffset, -gdipYOffset, Gdip.MatrixOrderPrepend);
1219 Gdip.Pen_delete(pen);
1220 if (customColor !is -1) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush);
1221 } else {
1222 if (style.borderColor !is null) {
1223 color = cast(void*)style.borderColor.handle;
1224 } else {
1225 if (style.foreground !is null) {
1226 color = cast(void*)style.foreground.handle;
1227 }
1228 if (fullSelection && clipRect is null) {
1229 color = selectionColor;
1230 }
1231 }
1232 int lineWidth = 1;
1233 int lineStyle = OS.PS_SOLID;
1234 switch (style.borderStyle) {
1235 case DWT.BORDER_SOLID: break;
1236 case DWT.BORDER_DASH: lineStyle = OS.PS_DASH; break;
1237 case DWT.BORDER_DOT: lineStyle = OS.PS_DOT; break;
1238 }
1239 LOGBRUSH logBrush;
1240 logBrush.lbStyle = OS.BS_SOLID;
1241 logBrush.lbColor = cast(uint)color;
1242 auto newPen = OS.ExtCreatePen(lineStyle | OS.PS_GEOMETRIC, Math.max(1, lineWidth), &logBrush, 0, null);
1243 auto oldPen = OS.SelectObject(graphics, newPen);
1244 auto oldBrush = OS.SelectObject(graphics, OS.GetStockObject(OS.NULL_BRUSH));
1245 OS.Rectangle(graphics, x + left, y, x + run.x + run.width, y + lineHeight);
1246 if (style.borderColor is null && clipRect !is null && color !is selectionColor) {
1247 int state = OS.SaveDC(graphics);
1248 if (clipRect.left is -1) clipRect.left = 0;
1249 if (clipRect.right is -1) clipRect.right = 0x7ffff;
1250 OS.IntersectClipRect(graphics, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
1251 logBrush.lbColor = cast(uint)selectionColor;
1252 auto selPen = OS.ExtCreatePen (lineStyle | OS.PS_GEOMETRIC, Math.max(1, lineWidth), &logBrush, 0, null);
1253 OS.SelectObject(graphics, selPen);
1254 OS.Rectangle(graphics, x + left, y, x + run.x + run.width, y + lineHeight);
1255 OS.RestoreDC(graphics, state);
1256 OS.SelectObject(graphics, newPen);
1257 OS.DeleteObject(selPen);
1258 }
1259 OS.SelectObject(graphics, oldBrush);
1260 OS.SelectObject(graphics, oldPen);
1261 OS.DeleteObject(newPen);
1262 }
1263 return null;
1264 }
1265 return clipRect;
1266 }
1267
1268 int[] computePolyline(int left, int top, int right, int bottom) {
1269 int height = bottom - top; // can be any number
1270 int width = 2 * height; // must be even
1271 int peaks = Compatibility.ceil(right - left, width);
1272 if (peaks is 0 && right - left > 2) {
1273 peaks = 1;
1274 }
1275 int length_ = ((2 * peaks) + 1) * 2;
1276 if (length_ < 0) return new int[0];
1277
1278 int[] coordinates = new int[length_];
1279 for (int i = 0; i < peaks; i++) {
1280 int index = 4 * i;
1281 coordinates[index] = left + (width * i);
1282 coordinates[index+1] = bottom;
1283 coordinates[index+2] = coordinates[index] + width / 2;
1284 coordinates[index+3] = top;
1285 }
1286 coordinates[length_-2] = left + (width * peaks);
1287 coordinates[length_-1] = bottom;
1288 return coordinates;
1289 }
1290
967 void freeRuns () { 1291 void freeRuns () {
968 if (allRuns is null) return; 1292 if (allRuns is null) return;
969 for (int i=0; i<allRuns.length; i++) { 1293 for (int i=0; i<allRuns.length; i++) {
970 StyleItem run = allRuns[i]; 1294 StyleItem run = allRuns[i];
971 run.free(); 1295 run.free();
1009 checkLayout(); 1333 checkLayout();
1010 return ascent; 1334 return ascent;
1011 } 1335 }
1012 1336
1013 /** 1337 /**
1014 * Returns the bounds of the receiver. 1338 * Returns the bounds of the receiver. The width returned is either the
1339 * width of the longest line or the width set using {@link TextLayout#setWidth(int)}.
1340 * To obtain the text bounds of a line use {@link TextLayout#getLineBounds(int)}.
1015 * 1341 *
1016 * @return the bounds of the receiver 1342 * @return the bounds of the receiver
1017 * 1343 *
1018 * @exception DWTException <ul> 1344 * @exception DWTException <ul>
1019 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 1345 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1020 * </ul> 1346 * </ul>
1347 *
1348 * @see #setWidth(int)
1349 * @see #getLineBounds(int)
1021 */ 1350 */
1022 public Rectangle getBounds () { 1351 public Rectangle getBounds () {
1023 checkLayout(); 1352 checkLayout();
1024 computeRuns(null); 1353 computeRuns(null);
1025 int width = 0; 1354 int width = 0;
1048 * </ul> 1377 * </ul>
1049 */ 1378 */
1050 public Rectangle getBounds (int start, int end) { 1379 public Rectangle getBounds (int start, int end) {
1051 checkLayout(); 1380 checkLayout();
1052 computeRuns(null); 1381 computeRuns(null);
1053 int length = text.length; 1382 int length_ = text.length;
1054 if (length is 0) return new Rectangle(0, 0, 0, 0); 1383 if (length_ is 0) return new Rectangle(0, 0, 0, 0);
1055 if (start > end) return new Rectangle(0, 0, 0, 0); 1384 if (start > end) return new Rectangle(0, 0, 0, 0);
1056 start = Math.min(Math.max(0, start), length - 1); 1385 start = Math.min(Math.max(0, start), length_ - 1);
1057 end = Math.min(Math.max(0, end), length - 1); 1386 end = Math.min(Math.max(0, end), length_ - 1);
1058 start = translateOffset(start); 1387 start = translateOffset(start);
1059 end = translateOffset(end); 1388 end = translateOffset(end);
1060 int left = 0x7fffffff, right = 0; 1389 int left = 0x7fffffff, right = 0;
1061 int top = 0x7fffffff, bottom = 0; 1390 int top = 0x7fffffff, bottom = 0;
1062 bool isRTL = (orientation & DWT.RIGHT_TO_LEFT) !is 0; 1391 bool isRTL = (orientation & DWT.RIGHT_TO_LEFT) !is 0;
1185 return item.style.font.handle; 1514 return item.style.font.handle;
1186 } 1515 }
1187 if (this.font !is null) { 1516 if (this.font !is null) {
1188 return this.font.handle; 1517 return this.font.handle;
1189 } 1518 }
1190 return device.systemFont; 1519 return device.systemFont.handle;
1191 } 1520 }
1192 1521
1193 /** 1522 /**
1194 * Returns the embedding level for the specified character offset. The 1523 * Returns the embedding level for the specified character offset. The
1195 * embedding level is usually used to determine the directionality of a 1524 * embedding level is usually used to determine the directionality of a
1205 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 1534 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1206 */ 1535 */
1207 public int getLevel (int offset) { 1536 public int getLevel (int offset) {
1208 checkLayout(); 1537 checkLayout();
1209 computeRuns(null); 1538 computeRuns(null);
1210 int length = text.length; 1539 int length_ = text.length;
1211 if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); 1540 if (!(0 <= offset && offset <= length_)) DWT.error(DWT.ERROR_INVALID_RANGE);
1212 offset = translateOffset(offset); 1541 offset = translateOffset(offset);
1213 for (int i=1; i<allRuns.length; i++) { 1542 for (int i=1; i<allRuns.length; i++) {
1214 if (allRuns[i].start > offset) { 1543 if (allRuns[i].start > offset) {
1215 return allRuns[i - 1].analysis.s.uBidiLevel; 1544 return allRuns[i - 1].analysis.s.uBidiLevel;
1216 } 1545 }
1304 * </ul> 1633 * </ul>
1305 */ 1634 */
1306 public int getLineIndex (int offset) { 1635 public int getLineIndex (int offset) {
1307 checkLayout(); 1636 checkLayout();
1308 computeRuns(null); 1637 computeRuns(null);
1309 int length = text.length; 1638 int length_ = text.length;
1310 if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); 1639 if (!(0 <= offset && offset <= length_)) DWT.error(DWT.ERROR_INVALID_RANGE);
1311 offset = translateOffset(offset); 1640 offset = translateOffset(offset);
1312 for (int line=0; line<runs.length; line++) { 1641 for (int line=0; line<runs.length; line++) {
1313 if (lineOffset[line + 1] > offset) { 1642 if (lineOffset[line + 1] > offset) {
1314 return line; 1643 return line;
1315 } 1644 }
1335 computeRuns(null); 1664 computeRuns(null);
1336 if (!(0 <= lineIndex && lineIndex < runs.length)) DWT.error(DWT.ERROR_INVALID_RANGE); 1665 if (!(0 <= lineIndex && lineIndex < runs.length)) DWT.error(DWT.ERROR_INVALID_RANGE);
1337 auto hDC = device.internal_new_GC(null); 1666 auto hDC = device.internal_new_GC(null);
1338 auto srcHdc = OS.CreateCompatibleDC(hDC); 1667 auto srcHdc = OS.CreateCompatibleDC(hDC);
1339 TEXTMETRIC lptm; 1668 TEXTMETRIC lptm;
1340 OS.SelectObject(srcHdc, font !is null ? font.handle : device.systemFont); 1669 OS.SelectObject(srcHdc, font !is null ? font.handle : device.systemFont.handle);
1341 OS.GetTextMetrics(srcHdc, &lptm); 1670 OS.GetTextMetrics(srcHdc, &lptm);
1342 OS.DeleteDC(srcHdc); 1671 OS.DeleteDC(srcHdc);
1343 device.internal_dispose_GC(hDC, null); 1672 device.internal_dispose_GC(hDC, null);
1344 1673
1345 int ascent = Math.max(lptm.tmAscent, this.ascent); 1674 int ascent = Math.max(lptm.tmAscent, this.ascent);
1402 * @see #getOffset(int, int, int[]) 1731 * @see #getOffset(int, int, int[])
1403 */ 1732 */
1404 public Point getLocation (int offset, bool trailing) { 1733 public Point getLocation (int offset, bool trailing) {
1405 checkLayout(); 1734 checkLayout();
1406 computeRuns(null); 1735 computeRuns(null);
1407 int length = text.length; 1736 int length_ = text.length;
1408 if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); 1737 if (!(0 <= offset && offset <= length_)) DWT.error(DWT.ERROR_INVALID_RANGE);
1409 length = segmentsText.length; 1738 length_ = segmentsText.length;
1410 offset = translateOffset(offset); 1739 offset = translateOffset(offset);
1411 int line; 1740 int line;
1412 for (line=0; line<runs.length; line++) { 1741 for (line=0; line<runs.length; line++) {
1413 if (lineOffset[line + 1] > offset) break; 1742 if (lineOffset[line + 1] > offset) break;
1414 } 1743 }
1415 line = Math.min(line, runs.length - 1); 1744 line = Math.min(line, runs.length - 1);
1416 StyleItem[] lineRuns = runs[line]; 1745 if (offset is length_) {
1417 Point result = null; 1746 return new Point(getLineIndent(line) + lineWidth[line], lineY[line]);
1418 if (offset is length) { 1747 }
1419 result = new Point(lineWidth[line], lineY[line]); 1748 int low = -1;
1420 } else { 1749 int high = allRuns.length;
1421 int width = 0; 1750 while (high - low > 1) {
1422 for (int i=0; i<lineRuns.length; i++) { 1751 int index = ((high + low) / 2);
1423 StyleItem run = lineRuns[i]; 1752 StyleItem run = allRuns[index];
1424 int end = run.start + run.length; 1753 if (run.start > offset) {
1425 if (run.start <= offset && offset < end) { 1754 high = index;
1426 if (run.style !is null && run.style.metrics !is null) { 1755 } else if (run.start + run.length <= offset) {
1427 GlyphMetrics metrics = run.style.metrics; 1756 low = index;
1428 width += metrics.width * (offset - run.start + (trailing ? 1 : 0)); 1757 } else {
1429 result = new Point(width, lineY[line]); 1758 int width;
1430 } else if (run.tab) { 1759 if (run.style !is null && run.style.metrics !is null) {
1431 if (trailing || (offset is length)) width += run.width; 1760 GlyphMetrics metrics = run.style.metrics;
1432 result = new Point(width, lineY[line]); 1761 width = metrics.width * (offset - run.start + (trailing ? 1 : 0));
1433 } else { 1762 } else if (run.tab) {
1434 int runOffset = offset - run.start; 1763 width = (trailing || (offset is length_)) ? run.width : 0;
1435 int cChars = run.length; 1764 } else {
1436 int gGlyphs = run.glyphCount; 1765 int runOffset = offset - run.start;
1437 int piX; 1766 int cChars = run.length;
1438 int* advances = run.justify !is null ? run.justify : run.advances; 1767 int gGlyphs = run.glyphCount;
1439 OS.ScriptCPtoX(runOffset, trailing, cChars, gGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piX); 1768 int piX;
1440 if ((orientation & DWT.RIGHT_TO_LEFT) !is 0) { 1769 int* advances = run.justify !is null ? run.justify : run.advances;
1441 result = new Point(width + (run.width - piX), lineY[line]); 1770 OS.ScriptCPtoX(runOffset, trailing, cChars, gGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piX);
1442 } else { 1771 width = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? run.width - piX : piX;
1443 result = new Point(width + piX, lineY[line]); 1772 }
1444 } 1773 return new Point(run.x + width, lineY[line]);
1445 } 1774 }
1446 break; 1775 }
1447 } 1776 return new Point(0, 0);
1448 width += run.width;
1449 }
1450 }
1451 if (result is null) result = new Point(0, 0);
1452 result.x += getLineIndent(line);
1453 return result;
1454 } 1777 }
1455 1778
1456 /** 1779 /**
1457 * Returns the next offset for the specified offset and movement 1780 * Returns the next offset for the specified offset and movement
1458 * type. The movement is one of <code>DWT.MOVEMENT_CHAR</code>, 1781 * type. The movement is one of <code>DWT.MOVEMENT_CHAR</code>,
1477 return _getOffset (offset, movement, true); 1800 return _getOffset (offset, movement, true);
1478 } 1801 }
1479 1802
1480 int _getOffset(int offset, int movement, bool forward) { 1803 int _getOffset(int offset, int movement, bool forward) {
1481 computeRuns(null); 1804 computeRuns(null);
1482 int length = text.length; 1805 int length_ = text.length;
1483 if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); 1806 if (!(0 <= offset && offset <= length_)) DWT.error(DWT.ERROR_INVALID_RANGE);
1484 if (forward && offset is length) return length; 1807 if (forward && offset is length_) return length_;
1485 if (!forward && offset is 0) return 0; 1808 if (!forward && offset is 0) return 0;
1486 int step = forward ? 1 : -1; 1809 int step = forward ? 1 : -1;
1487 if ((movement & DWT.MOVEMENT_CHAR) !is 0) return offset + step; 1810 if ((movement & DWT.MOVEMENT_CHAR) !is 0) return offset + step;
1488 length = segmentsText.length; 1811 length_ = segmentsText.length;
1489 offset = translateOffset(offset); 1812 offset = translateOffset(offset);
1490 SCRIPT_LOGATTR* logAttr; 1813 SCRIPT_LOGATTR* logAttr;
1491 SCRIPT_PROPERTIES* properties; 1814 SCRIPT_PROPERTIES* properties;
1492 int i = forward ? 0 : allRuns.length - 1; 1815 int i = forward ? 0 : allRuns.length - 1;
1493 offset = validadeOffset(offset, step); 1816 offset = validadeOffset(offset, step);
1543 } 1866 }
1544 offset = validadeOffset(offset, step); 1867 offset = validadeOffset(offset, step);
1545 } 1868 }
1546 } 1869 }
1547 i += step; 1870 i += step;
1548 } while (0 <= i && i < allRuns.length - 1 && 0 <= offset && offset < length); 1871 } while (0 <= i && i < allRuns.length - 1 && 0 <= offset && offset < length_);
1549 return forward ? text.length : 0; 1872 return forward ? text.length : 0;
1550 } 1873 }
1551 1874
1552 /** 1875 /**
1553 * Returns the character offset for the specified point. 1876 * Returns the character offset for the specified point.
1609 int lineCount = runs.length; 1932 int lineCount = runs.length;
1610 for (line=0; line<lineCount; line++) { 1933 for (line=0; line<lineCount; line++) {
1611 if (lineY[line + 1] > y) break; 1934 if (lineY[line + 1] > y) break;
1612 } 1935 }
1613 line = Math.min(line, runs.length - 1); 1936 line = Math.min(line, runs.length - 1);
1614 x -= getLineIndent(line);
1615 StyleItem[] lineRuns = runs[line]; 1937 StyleItem[] lineRuns = runs[line];
1616 if (x >= lineWidth[line]) x = lineWidth[line] - 1; 1938 int lineIndent = getLineIndent(line);
1617 if (x < 0) x = 0; 1939 if (x >= lineIndent + lineWidth[line]) x = lineIndent + lineWidth[line] - 1;
1618 int width = 0; 1940 if (x < lineIndent) x = lineIndent;
1619 for (int i = 0; i < lineRuns.length; i++) { 1941 int low = -1;
1620 StyleItem run = lineRuns[i]; 1942 int high = lineRuns.length;
1621 if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start); 1943 while (high - low > 1) {
1622 if (width + run.width > x) { 1944 int index = ((high + low) / 2);
1623 int xRun = x - width; 1945 StyleItem run = lineRuns[index];
1946 if (run.x > x) {
1947 high = index;
1948 } else if (run.x + run.width <= x) {
1949 low = index;
1950 } else {
1951 if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start);
1952 int xRun = x - run.x;
1624 if (run.style !is null && run.style.metrics !is null) { 1953 if (run.style !is null && run.style.metrics !is null) {
1625 GlyphMetrics metrics = run.style.metrics; 1954 GlyphMetrics metrics = run.style.metrics;
1626 if (metrics.width > 0) { 1955 if (metrics.width > 0) {
1627 if (trailing !is null) { 1956 if (trailing !is null) {
1628 trailing[0] = (xRun % metrics.width < metrics.width / 2) ? 0 : 1; 1957 trailing[0] = (xRun % metrics.width < metrics.width / 2) ? 0 : 1;
1629 } 1958 }
1630 return untranslateOffset(run.start + xRun / metrics.width); 1959 return untranslateOffset(run.start + xRun / metrics.width);
1631 } 1960 }
1632 } 1961 }
1633 if (run.tab) { 1962 if (run.tab) {
1634 if (trailing !is null) trailing[0] = x < (width + run.width / 2) ? 0 : 1; 1963 if (trailing !is null) trailing[0] = x < (run.x + run.width / 2) ? 0 : 1;
1635 return untranslateOffset(run.start); 1964 return untranslateOffset(run.start);
1636 } 1965 }
1637 int cChars = run.length; 1966 int cChars = run.length;
1638 int cGlyphs = run.glyphCount; 1967 int cGlyphs = run.glyphCount;
1639 int piCP; 1968 int piCP;
1644 int* advances = run.justify !is null ? run.justify : run.advances; 1973 int* advances = run.justify !is null ? run.justify : run.advances;
1645 OS.ScriptXtoCP(xRun, cChars, cGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piCP, &piTrailing); 1974 OS.ScriptXtoCP(xRun, cChars, cGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piCP, &piTrailing);
1646 if (trailing !is null) trailing[0] = piTrailing; 1975 if (trailing !is null) trailing[0] = piTrailing;
1647 return untranslateOffset(run.start + piCP); 1976 return untranslateOffset(run.start + piCP);
1648 } 1977 }
1649 width += run.width;
1650 } 1978 }
1651 if (trailing !is null) trailing[0] = 0; 1979 if (trailing !is null) trailing[0] = 0;
1652 return untranslateOffset(lineOffset[line + 1]); 1980 return untranslateOffset(lineOffset[line + 1]);
1653 } 1981 }
1654 1982
1704 * 2032 *
1705 * @since 3.2 2033 * @since 3.2
1706 */ 2034 */
1707 public int[] getRanges () { 2035 public int[] getRanges () {
1708 checkLayout(); 2036 checkLayout();
1709 int[] result = new int[styles.length * 2]; 2037 int[] result = new int[stylesCount * 2];
1710 int count = 0; 2038 int count = 0;
1711 for (int i=0; i<styles.length - 1; i++) { 2039 for (int i=0; i<stylesCount - 1; i++) {
1712 if (styles[i].style !is null) { 2040 if (styles[i].style !is null) {
1713 result[count++] = styles[i].start; 2041 result[count++] = styles[i].start;
1714 result[count++] = styles[i + 1].start - 1; 2042 result[count++] = styles[i + 1].start - 1;
1715 } 2043 }
1716 } 2044 }
1738 2066
1739 String getSegmentsText() { 2067 String getSegmentsText() {
1740 if (segments is null) return text; 2068 if (segments is null) return text;
1741 int nSegments = segments.length; 2069 int nSegments = segments.length;
1742 if (nSegments <= 1) return text; 2070 if (nSegments <= 1) return text;
1743 int length = text.length; 2071 int length_ = text.length;
1744 if (length is 0) return text; 2072 if (length_ is 0) return text;
1745 if (nSegments is 2) { 2073 if (nSegments is 2) {
1746 if (segments[0] is 0 && segments[1] is length) return text; 2074 if (segments[0] is 0 && segments[1] is length_) return text;
1747 } 2075 }
1748 char[] oldChars = new char[length]; 2076 char[] oldChars = new char[length_];
1749 text.getChars(0, length, oldChars, 0); 2077 text.getChars(0, length_, oldChars, 0);
1750 char[] newChars = new char[length + nSegments]; 2078 char[] newChars = new char[length_ + nSegments];
1751 int charCount = 0, segmentCount = 0; 2079 int charCount = 0, segmentCount = 0;
1752 wchar separator = orientation is DWT.RIGHT_TO_LEFT ? RTL_MARK : LTR_MARK; 2080 wchar separator = orientation is DWT.RIGHT_TO_LEFT ? RTL_MARK : LTR_MARK;
1753 while (charCount < length) { 2081 while (charCount < length_) {
1754 if (segmentCount < nSegments && charCount is segments[segmentCount]) { 2082 if (segmentCount < nSegments && charCount is segments[segmentCount]) {
1755 newChars[charCount + segmentCount++] = separator; 2083 newChars[charCount + segmentCount++] = separator;
1756 } else { 2084 } else {
1757 newChars[charCount + segmentCount] = oldChars[charCount++]; 2085 newChars[charCount + segmentCount] = oldChars[charCount++];
1758 } 2086 }
1791 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> 2119 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
1792 * </ul> 2120 * </ul>
1793 */ 2121 */
1794 public TextStyle getStyle (int offset) { 2122 public TextStyle getStyle (int offset) {
1795 checkLayout(); 2123 checkLayout();
1796 int length = text.length; 2124 int length_ = text.length;
1797 if (!(0 <= offset && offset < length)) DWT.error(DWT.ERROR_INVALID_RANGE); 2125 if (!(0 <= offset && offset < length_)) DWT.error(DWT.ERROR_INVALID_RANGE);
1798 for (int i=1; i<styles.length; i++) { 2126 for (int i=1; i<stylesCount; i++) {
1799 if (styles[i].start > offset) { 2127 if (styles[i].start > offset) {
1800 return styles[i - 1].style; 2128 return styles[i - 1].style;
1801 } 2129 }
1802 } 2130 }
1803 return null; 2131 return null;
1816 * 2144 *
1817 * @since 3.2 2145 * @since 3.2
1818 */ 2146 */
1819 public TextStyle[] getStyles () { 2147 public TextStyle[] getStyles () {
1820 checkLayout(); 2148 checkLayout();
1821 TextStyle[] result = new TextStyle[styles.length]; 2149 TextStyle[] result = new TextStyle[stylesCount];
1822 int count = 0; 2150 int count = 0;
1823 for (int i=0; i<styles.length; i++) { 2151 for (int i=0; i<stylesCount; i++) {
1824 if (styles[i].style !is null) { 2152 if (styles[i].style !is null) {
1825 result[count++] = styles[i].style; 2153 result[count++] = styles[i].style;
1826 } 2154 }
1827 } 2155 }
1828 if (count !is result.length) { 2156 if (count !is result.length) {
1894 /* 2222 /*
1895 * Itemize the receiver text 2223 * Itemize the receiver text
1896 */ 2224 */
1897 StyleItem[] itemize () { 2225 StyleItem[] itemize () {
1898 segmentsText = getSegmentsText(); 2226 segmentsText = getSegmentsText();
1899 int length = segmentsText.length; 2227 int length_ = segmentsText.length;
1900 SCRIPT_CONTROL scriptControl; 2228 SCRIPT_CONTROL scriptControl;
1901 SCRIPT_STATE scriptState; 2229 SCRIPT_STATE scriptState;
1902 final int MAX_ITEM = length + 1; 2230 final int MAX_ITEM = length_ + 1;
1903 2231
1904 if ((orientation & DWT.RIGHT_TO_LEFT) !is 0) { 2232 if ((orientation & DWT.RIGHT_TO_LEFT) !is 0) {
1905 scriptState.uBidiLevel = 1; 2233 scriptState.uBidiLevel = 1;
1906 scriptState.fArabicNumContext = true; 2234 scriptState.fArabicNumContext = true;
1907 SCRIPT_DIGITSUBSTITUTE psds; 2235 SCRIPT_DIGITSUBSTITUTE psds;
1924 2252
1925 /* 2253 /*
1926 * Merge styles ranges and script items 2254 * Merge styles ranges and script items
1927 */ 2255 */
1928 StyleItem[] merge (SCRIPT_ITEM* items, int itemCount) { 2256 StyleItem[] merge (SCRIPT_ITEM* items, int itemCount) {
2257 if (styles.length > stylesCount) {
2258 StyleItem[] newStyles = new StyleItem[stylesCount];
2259 System.arraycopy(styles, 0, newStyles, 0, stylesCount);
2260 styles = newStyles;
2261 }
1929 int count = 0, start = 0, end = segmentsText.length, itemIndex = 0, styleIndex = 0; 2262 int count = 0, start = 0, end = segmentsText.length, itemIndex = 0, styleIndex = 0;
1930 StyleItem[] runs = new StyleItem[itemCount + styles.length]; 2263 StyleItem[] runs = new StyleItem[itemCount + stylesCount];
1931 SCRIPT_ITEM* scriptItem; 2264 SCRIPT_ITEM* scriptItem = new SCRIPT_ITEM();
1932 bool linkBefore = false; 2265 bool linkBefore = false;
1933 while (start < end) { 2266 while (start < end) {
1934 StyleItem item = new StyleItem(); 2267 StyleItem item = new StyleItem();
1935 item.start = start; 2268 item.start = start;
1936 item.style = styles[styleIndex].style; 2269 item.style = styles[styleIndex].style;
1937 runs[count++] = item; 2270 runs[count++] = item;
1938 scriptItem = items + itemIndex; 2271 *scriptItem = items[itemIndex];
1939 item.analysis = scriptItem.a; 2272 item.analysis = scriptItem.a;
1940 if (linkBefore) { 2273 if (linkBefore) {
1941 item.analysis.fLinkBefore = true; 2274 item.analysis.fLinkBefore = true;
1942 linkBefore = false; 2275 linkBefore = false;
1943 } 2276 }
1944 //scriptItem.a = new SCRIPT_ANALYSIS(); 2277 //scriptItem.a = new SCRIPT_ANALYSIS();
1945 scriptItem = items + (itemIndex + 1); 2278 *scriptItem = items[ itemIndex + 1];
1946 int itemLimit = scriptItem.iCharPos; 2279 int itemLimit = scriptItem.iCharPos;
1947 int styleLimit = translateOffset(styles[styleIndex + 1].start); 2280 int styleLimit = translateOffset(styles[styleIndex + 1].start);
1948 if (styleLimit <= itemLimit) { 2281 if (styleLimit <= itemLimit) {
1949 styleIndex++; 2282 styleIndex++;
1950 start = styleLimit; 2283 start = styleLimit;
1951 if (start < itemLimit && 0 < start && start < end) { 2284 if (start < itemLimit && 0 < start && start < end) {
1952 char pChar = segmentsText.charAt(start - 1); 2285 char pChar = segmentsText.charAt(start - 1);
1953 char tChar = segmentsText.charAt(start); 2286 char tChar = segmentsText.charAt(start);
1954 if (!Compatibility.isWhitespace(pChar) && !Compatibility.isWhitespace(tChar)) { 2287 if (Compatibility.isLetter(pChar) && Compatibility.isLetter(tChar)) {
1955 item.analysis.fLinkAfter = true; 2288 item.analysis.fLinkAfter = true;
1956 linkBefore = true; 2289 linkBefore = true;
1957 } 2290 }
1958 } 2291 }
1959 } 2292 }
1963 } 2296 }
1964 item.length = start - item.start; 2297 item.length = start - item.start;
1965 } 2298 }
1966 StyleItem item = new StyleItem(); 2299 StyleItem item = new StyleItem();
1967 item.start = end; 2300 item.start = end;
1968 scriptItem = items + itemCount; 2301 *scriptItem = items[ itemCount ];
1969 item.analysis = scriptItem.a; 2302 item.analysis = scriptItem.a;
1970 runs[count++] = item; 2303 runs[count++] = item;
1971 if (runs.length !is count) { 2304 if (runs.length !is count) {
1972 StyleItem[] result = new StyleItem[count]; 2305 StyleItem[] result = new StyleItem[count];
1973 System.arraycopy(runs, 0, result, 0, count); 2306 System.arraycopy(runs, 0, result, 0, count);
2112 * </ul> 2445 * </ul>
2113 */ 2446 */
2114 public void setFont (Font font) { 2447 public void setFont (Font font) {
2115 checkLayout(); 2448 checkLayout();
2116 if (font !is null && font.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT); 2449 if (font !is null && font.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
2117 if (this.font is font) return; 2450 Font oldFont = this.font;
2118 if (font !is null && font ==/*eq*/ this.font) return; 2451 if (oldFont is font) return;
2452 this.font = font;
2453 if (oldFont !is null && oldFont.opEquals(font)) return;
2119 freeRuns(); 2454 freeRuns();
2120 this.font = font;
2121 } 2455 }
2122 2456
2123 /** 2457 /**
2124 * Sets the indent of the receiver. This indent it applied of the first line of 2458 * Sets the indent of the receiver. This indent it applied of the first line of
2125 * each paragraph. 2459 * each paragraph.
2254 if (length_ is 0) return; 2588 if (length_ is 0) return;
2255 if (start > end) return; 2589 if (start > end) return;
2256 start = Math.min(Math.max(0, start), length_ - 1); 2590 start = Math.min(Math.max(0, start), length_ - 1);
2257 end = Math.min(Math.max(0, end), length_ - 1); 2591 end = Math.min(Math.max(0, end), length_ - 1);
2258 int low = -1; 2592 int low = -1;
2259 int high = styles.length; 2593 int high = stylesCount;
2260 while (high - low > 1) { 2594 while (high - low > 1) {
2261 int index = (high + low) / 2; 2595 int index = (high + low) / 2;
2262 if (styles[index + 1].start > start) { 2596 if (styles[index + 1].start > start) {
2263 high = index; 2597 high = index;
2264 } else { 2598 } else {
2265 low = index; 2599 low = index;
2266 } 2600 }
2267 } 2601 }
2268 if (0 <= high && high < styles.length) { 2602 if (0 <= high && high < stylesCount) {
2269 StyleItem item = styles[high]; 2603 StyleItem item = styles[high];
2270 if (item.start is start && styles[high + 1].start - 1 is end) { 2604 if (item.start is start && styles[high + 1].start - 1 is end) {
2271 if (style is null) { 2605 if (style is null) {
2272 if (item.style is null) return; 2606 if (item.style is null) return;
2273 } else { 2607 } else {
2276 } 2610 }
2277 } 2611 }
2278 freeRuns(); 2612 freeRuns();
2279 int modifyStart = high; 2613 int modifyStart = high;
2280 int modifyEnd = modifyStart; 2614 int modifyEnd = modifyStart;
2281 while (modifyEnd < styles.length) { 2615 while (modifyEnd < stylesCount) {
2282 if (styles[modifyEnd + 1].start > end) break; 2616 if (styles[modifyEnd + 1].start > end) break;
2283 modifyEnd++; 2617 modifyEnd++;
2284 } 2618 }
2285 if (modifyStart is modifyEnd) { 2619 if (modifyStart is modifyEnd) {
2286 int styleStart = styles[modifyStart].start; 2620 int styleStart = styles[modifyStart].start;
2288 if (styleStart is start && styleEnd is end) { 2622 if (styleStart is start && styleEnd is end) {
2289 styles[modifyStart].style = style; 2623 styles[modifyStart].style = style;
2290 return; 2624 return;
2291 } 2625 }
2292 if (styleStart !is start && styleEnd !is end) { 2626 if (styleStart !is start && styleEnd !is end) {
2293 StyleItem[] newStyles = new StyleItem[styles.length + 2]; 2627 int newLength = stylesCount + 2;
2294 System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1); 2628 if (newLength > styles.length) {
2629 int newSize = Math.min(newLength + 1024, Math.max(64, newLength * 2));
2630 StyleItem[] newStyles = new StyleItem[newSize];
2631 System.arraycopy(styles, 0, newStyles, 0, stylesCount);
2632 styles = newStyles;
2633 }
2634 System.arraycopy(styles, modifyEnd + 1, styles, modifyEnd + 3, stylesCount - modifyEnd - 1);
2295 StyleItem item = new StyleItem(); 2635 StyleItem item = new StyleItem();
2296 item.start = start; 2636 item.start = start;
2297 item.style = style; 2637 item.style = style;
2298 newStyles[modifyStart + 1] = item; 2638 styles[modifyStart + 1] = item;
2299 item = new StyleItem(); 2639 item = new StyleItem();
2300 item.start = end + 1; 2640 item.start = end + 1;
2301 item.style = styles[modifyStart].style; 2641 item.style = styles[modifyStart].style;
2302 newStyles[modifyStart + 2] = item; 2642 styles[modifyStart + 2] = item;
2303 System.arraycopy(styles, modifyEnd + 1, newStyles, modifyEnd + 3, styles.length - modifyEnd - 1); 2643 stylesCount = newLength;
2304 styles = newStyles;
2305 return; 2644 return;
2306 } 2645 }
2307 } 2646 }
2308 if (start is styles[modifyStart].start) modifyStart--; 2647 if (start is styles[modifyStart].start) modifyStart--;
2309 if (end is styles[modifyEnd + 1].start - 1) modifyEnd++; 2648 if (end is styles[modifyEnd + 1].start - 1) modifyEnd++;
2310 int newLength = styles.length + 1 - (modifyEnd - modifyStart - 1); 2649 int newLength = stylesCount + 1 - (modifyEnd - modifyStart - 1);
2311 StyleItem[] newStyles = new StyleItem[newLength]; 2650 if (newLength > styles.length) {
2312 System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1); 2651 int newSize = Math.min(newLength + 1024, Math.max(64, newLength * 2));
2652 StyleItem[] newStyles = new StyleItem[newSize];
2653 System.arraycopy(styles, 0, newStyles, 0, stylesCount);
2654 styles = newStyles;
2655 }
2656 System.arraycopy(styles, modifyEnd, styles, modifyStart + 2, stylesCount - modifyEnd);
2313 StyleItem item = new StyleItem(); 2657 StyleItem item = new StyleItem();
2314 item.start = start; 2658 item.start = start;
2315 item.style = style; 2659 item.style = style;
2316 newStyles[modifyStart + 1] = item; 2660 styles[modifyStart + 1] = item;
2317 styles[modifyEnd].start = end + 1; 2661 styles[modifyStart + 2].start = end + 1;
2318 System.arraycopy(styles, modifyEnd, newStyles, modifyStart + 2, styles.length - modifyEnd); 2662 stylesCount = newLength;
2319 styles = newStyles;
2320 } 2663 }
2321 2664
2322 /** 2665 /**
2323 * Sets the receiver's tab list. Each value in the tab list specifies 2666 * Sets the receiver's tab list. Each value in the tab list specifies
2324 * the space in pixels from the origin of the text layout to the respective 2667 * the space in pixels from the origin of the text layout to the respective
2367 this.text = text; 2710 this.text = text;
2368 styles = new StyleItem[2]; 2711 styles = new StyleItem[2];
2369 styles[0] = new StyleItem(); 2712 styles[0] = new StyleItem();
2370 styles[1] = new StyleItem(); 2713 styles[1] = new StyleItem();
2371 styles[1].start = text.length; 2714 styles[1].start = text.length;
2715 stylesCount = 2;
2372 } 2716 }
2373 2717
2374 /** 2718 /**
2375 * Sets the line width of the receiver, which determines how 2719 * Sets the line width of the receiver, which determines how
2376 * text should be wrapped and aligned. The default value is 2720 * text should be wrapped and aligned. The default value is
2417 } 2761 }
2418 run.glyphCount = 0; 2762 run.glyphCount = 0;
2419 return false; 2763 return false;
2420 } 2764 }
2421 2765
2766 struct CallbackDataEnumFontFamExProc {
2767 int delegate ( ENUMLOGFONTEX* lpelfe, NEWTEXTMETRICEX* lpntme, int FontType, int lParam) EnumFontFamExProc;
2768 int lParam;
2769 }
2770 extern(Windows) private static int EnumFontFamExProcFunc( ENUMLOGFONTEX* lpelfe, NEWTEXTMETRICEX* lpntme, int FontType, int lParam) {
2771 auto cb = cast(CallbackDataEnumFontFamExProc*)cast(void*)lParam;
2772 return cb.EnumFontFamExProc( lpelfe, lpntme, FontType, cb.lParam );
2773 }
2774
2422 /* 2775 /*
2423 * Generate glyphs for one Run. 2776 * Generate glyphs for one Run.
2424 */ 2777 */
2425 void shape (HDC hdc, StyleItem run) { 2778 void shape (HDC hdc, StyleItem run) {
2426 int[] buffer = new int[1]; 2779 final int[] buffer = new int[1];
2427 char[] chars = new char[run.length]; 2780 final char[] chars = new char[run.length];
2428 segmentsText.getChars(run.start, run.start + run.length, chars, 0); 2781 segmentsText.getChars(run.start, run.start + run.length, chars, 0);
2429 wchar[] wchars = StrToWCHARs( chars ); 2782 wchar[] wchars = StrToWCHARs( chars );
2430 int maxGlyphs = (chars.length * 3 / 2) + 16; 2783 int maxGlyphs = (chars.length * 3 / 2) + 16;
2431 auto hHeap = OS.GetProcessHeap(); 2784 auto hHeap = OS.GetProcessHeap();
2432 run.glyphs = cast(ushort*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2); 2785 run.glyphs = cast(ushort*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2);
2433 if (run.glyphs is null) DWT.error(DWT.ERROR_NO_HANDLES); 2786 if (run.glyphs is null) DWT.error(DWT.ERROR_NO_HANDLES);
2434 run.clusters = cast(WORD*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2); 2787 run.clusters = cast(WORD*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2);
2435 if (run.clusters is null) DWT.error(DWT.ERROR_NO_HANDLES); 2788 if (run.clusters is null) DWT.error(DWT.ERROR_NO_HANDLES);
2436 run.visAttrs = cast(SCRIPT_VISATTR*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * SCRIPT_VISATTR_SIZEOF); 2789 run.visAttrs = cast(SCRIPT_VISATTR*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * SCRIPT_VISATTR_SIZEOF);
2437 if (run.visAttrs is null) DWT.error(DWT.ERROR_NO_HANDLES); 2790 if (run.visAttrs is null) DWT.error(DWT.ERROR_NO_HANDLES);
2438 run.psc = cast(SCRIPT_CACHE*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, 4); 2791 run.psc = cast(SCRIPT_CACHE*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, (void*).sizeof);
2439 if (run.psc is null) DWT.error(DWT.ERROR_NO_HANDLES); 2792 if (run.psc is null) DWT.error(DWT.ERROR_NO_HANDLES);
2440 if (!shape(hdc, run, chars, buffer, maxGlyphs)) { 2793 bool shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs);
2794 final short script = run.analysis.eScript;
2795
2796 if (!shapeSucceed) {
2797 /*
2798 * Shape failed.
2799 * Try to shape with fNoGlyphIndex when the run is in the
2800 * Private Use Area. This allows for end-user-defined character (EUDC).
2801 */
2802 auto properties = device.scripts[script];
2803 if (properties.fPrivateUseArea) {
2804 run.analysis.fNoGlyphIndex = true;
2805 shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs);
2806 }
2807 }
2808
2809 if (!shapeSucceed) {
2810 /*
2811 * Shape Failed.
2812 * Try to use MLANG to find a suitable font to shape the run.
2813 */
2441 if (mLangFontLink2 !is null) { 2814 if (mLangFontLink2 !is null) {
2442 int dwCodePages; 2815 int dwCodePages;
2443 int cchCodePages; 2816 int cchCodePages;
2444 /* GetStrCodePages() */ 2817 /* GetStrCodePages() */
2445 OS.VtblCall(4, mLangFontLink2, cast(int)wchars.ptr, wchars.length, 0, cast(int)&dwCodePages, cast(int)&cchCodePages); 2818 OS.VtblCall(4, mLangFontLink2, cast(int)wchars.ptr, wchars.length, 0, cast(int)&dwCodePages, cast(int)&cchCodePages);
2446 HFONT hNewFont; 2819 HFONT hNewFont;
2447 /* MapFont() */ 2820 /* MapFont() */
2448 if (OS.VtblCall(10, mLangFontLink2, cast(int)hdc, dwCodePages, cast(int)wchars[0], cast(int)hNewFont) is OS.S_OK) { 2821 if (OS.VtblCall(10, mLangFontLink2, cast(int)hdc, dwCodePages, cast(int)wchars[0], cast(int)hNewFont) is OS.S_OK) {
2449 auto hFont = OS.SelectObject(hdc, hNewFont); 2822 auto hFont = OS.SelectObject(hdc, hNewFont);
2450 if (shape(hdc, run, chars, buffer, maxGlyphs)) { 2823 shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs);
2824 if (shapeSucceed) {
2451 run.fallbackFont = hNewFont; 2825 run.fallbackFont = hNewFont;
2452 } else { 2826 } else {
2453 /* ReleaseFont() */ 2827 /* ReleaseFont() */
2454 OS.VtblCall(8, mLangFontLink2, cast(int)hNewFont); 2828 OS.VtblCall(8, mLangFontLink2, cast(int)hNewFont);
2455 OS.SelectObject(hdc, hFont); 2829 OS.SelectObject(hdc, hFont);
2456 SCRIPT_PROPERTIES* properties;
2457 properties = device.scripts[run.analysis.eScript];
2458 if (properties.fPrivateUseArea) {
2459 run.analysis.fNoGlyphIndex = true;
2460 }
2461 OS.ScriptShape(hdc, run.psc, wchars.ptr, wchars.length, maxGlyphs, &run.analysis, run.glyphs, run.clusters, run.visAttrs, buffer.ptr);
2462 run.glyphCount = buffer[0];
2463 } 2830 }
2464 } 2831 }
2465 } 2832 }
2466 } 2833 }
2467 ABC abc; 2834
2835 if (!shapeSucceed) {
2836 /*
2837 * Shape Failed.
2838 * Try to shape the run using the LOGFONT in the cache.
2839 */
2840 auto hFont = OS.GetCurrentObject(hdc, OS.OBJ_FONT);
2841 LOGFONT logFont;
2842 OS.GetObject(hFont, LOGFONT.sizeof, &logFont);
2843
2844 LOGFONT* cachedLogFont = device.logFontsCache !is null ? device.logFontsCache[script] : null;
2845 if (cachedLogFont !is null) {
2846 cachedLogFont.lfHeight = logFont.lfHeight;
2847 cachedLogFont.lfWeight = logFont.lfWeight;
2848 cachedLogFont.lfItalic = logFont.lfItalic;
2849 cachedLogFont.lfWidth = logFont.lfWidth;
2850 auto newFont = OS.CreateFontIndirect(cachedLogFont);
2851 OS.SelectObject(hdc, newFont);
2852 shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs);
2853 if (shapeSucceed) {
2854 run.fallbackFont = newFont;
2855 } else {
2856 OS.SelectObject(hdc, hFont);
2857 OS.DeleteObject(newFont);
2858 }
2859 }
2860 if (!shapeSucceed) {
2861 /*
2862 * Shape Failed.
2863 * Use EnumFontFamExProc to iterate over every font in the system that supports
2864 * the charset of the run and try to shape it.
2865 */
2866 if (device.logFontsCache is null) device.logFontsCache = new LOGFONT*[device.scripts.length];
2867 LOGFONT newLogFont;
2868
2869 int EnumFontFamExProc( ENUMLOGFONTEX* lpelfe, NEWTEXTMETRICEX* lpntme, int FontType, int lParam) {
2870 OS.MoveMemory(&newLogFont, cast(void*)lpelfe, LOGFONT.sizeof);
2871 if (FontType is OS.RASTER_FONTTYPE) return 1;
2872 newLogFont.lfHeight = logFont.lfHeight;
2873 newLogFont.lfWeight = logFont.lfWeight;
2874 newLogFont.lfItalic = logFont.lfItalic;
2875 newLogFont.lfWidth = logFont.lfWidth;
2876 auto newFont = OS.CreateFontIndirect(&newLogFont);
2877 OS.SelectObject(hdc, newFont);
2878 if (shape(hdc, run, chars, buffer, maxGlyphs)) {
2879 run.fallbackFont = newFont;
2880 LOGFONT* cacheLogFont = new LOGFONT();
2881 OS.MoveMemory(cacheLogFont, lpelfe, LOGFONT.sizeof);
2882 device.logFontsCache[script] = cacheLogFont;
2883 return 0;
2884 }
2885 OS.SelectObject(hdc, hFont);
2886 OS.DeleteObject(newFont);
2887 return 1;
2888 }
2889 CallbackDataEnumFontFamExProc cb;
2890 cb.EnumFontFamExProc = &EnumFontFamExProc;
2891 cb.lParam = 0;
2892 auto properties = device.scripts[script];
2893 int charSet = properties.fAmbiguousCharSet ? OS.DEFAULT_CHARSET : properties.bCharSet;
2894 newLogFont.lfCharSet = cast(byte)charSet;
2895 OS.EnumFontFamiliesEx(hdc, &newLogFont, &EnumFontFamExProcFunc, cast(int) &cb, 0);
2896 shapeSucceed = run.fallbackFont !is null;
2897 }
2898 }
2899
2900 if (!shapeSucceed) {
2901 /*
2902 * Shape Failed.
2903 * Give up and shape the run with the default font.
2904 * Missing glyphs typically will be represent as black boxes in the text.
2905 */
2906 auto wchars_ = StrToWCHARs(chars);
2907 OS.ScriptShape(hdc, run.psc, wchars_.ptr, wchars_.length, maxGlyphs, &run.analysis, run.glyphs, run.clusters, run.visAttrs, buffer.ptr);
2908 run.glyphCount = buffer[0];
2909 }
2910 int[3] abc;
2468 run.advances = cast(int*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * 4); 2911 run.advances = cast(int*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * 4);
2469 if (run.advances is null) DWT.error(DWT.ERROR_NO_HANDLES); 2912 if (run.advances is null) DWT.error(DWT.ERROR_NO_HANDLES);
2470 run.goffsets = cast(GOFFSET*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * GOFFSET_SIZEOF); 2913 run.goffsets = cast(GOFFSET*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * GOFFSET_SIZEOF);
2471 if (run.goffsets is null) DWT.error(DWT.ERROR_NO_HANDLES); 2914 if (run.goffsets is null) DWT.error(DWT.ERROR_NO_HANDLES);
2472 OS.ScriptPlace(hdc, run.psc, run.glyphs, run.glyphCount, run.visAttrs, &run.analysis, run.advances, run.goffsets, &abc); 2915 OS.ScriptPlace(hdc, run.psc, run.glyphs, run.glyphCount, run.visAttrs, &run.analysis, run.advances, run.goffsets, cast(ABC*)abc.ptr);
2473 if (run.style !is null && run.style.metrics !is null) { 2916 run.width = abc[0] + abc[1] + abc[2];
2474 GlyphMetrics metrics = run.style.metrics; 2917 TextStyle style = run.style;
2475 /* 2918 if (style !is null) {
2476 * Bug in Windows, on a Japanese machine, Uniscribe returns glyphcount 2919 OUTLINETEXTMETRIC* lotm = null;
2477 * equals zero for FFFC (possibly other unicode code points), the fix 2920 if (style.underline || style.strikeout) {
2478 * is to make sure the glyph is at least one pixel wide. 2921 lotm = new OUTLINETEXTMETRIC();
2479 */ 2922 if (OS.GetOutlineTextMetrics(hdc, OUTLINETEXTMETRIC.sizeof, lotm) is 0) {
2480 run.width = metrics.width * Math.max (1, run.glyphCount); 2923 lotm = null;
2481 run.ascent = metrics.ascent; 2924 }
2482 run.descent = metrics.descent; 2925 }
2483 run.leading = 0; 2926 if (style.metrics !is null) {
2927 GlyphMetrics metrics = style.metrics;
2928 /*
2929 * Bug in Windows, on a Japanese machine, Uniscribe returns glyphcount
2930 * equals zero for FFFC (possibly other unicode code points), the fix
2931 * is to make sure the glyph is at least one pixel wide.
2932 */
2933 run.width = metrics.width * Math.max (1, run.glyphCount);
2934 run.ascent = metrics.ascent;
2935 run.descent = metrics.descent;
2936 run.leading = 0;
2937 } else {
2938 TEXTMETRIC lptm;
2939 if (lotm !is null) {
2940 lptm = lotm.otmTextMetrics;
2941 } else {
2942 lptm = TEXTMETRIC.init;
2943 OS.GetTextMetrics(hdc, &lptm);
2944 }
2945 run.ascent = lptm.tmAscent;
2946 run.descent = lptm.tmDescent;
2947 run.leading = lptm.tmInternalLeading;
2948 }
2949 if (lotm !is null) {
2950 run.underlinePos = lotm.otmsUnderscorePosition;
2951 run.underlineThickness = Math.max(1, lotm.otmsUnderscoreSize);
2952 run.strikeoutPos = lotm.otmsStrikeoutPosition;
2953 run.strikeoutThickness = Math.max(1, lotm.otmsStrikeoutSize);
2954 } else {
2955 run.underlinePos = 1;
2956 run.underlineThickness = 1;
2957 run.strikeoutPos = run.ascent / 2;
2958 run.strikeoutThickness = 1;
2959 }
2960 run.ascent += style.rise;
2961 run.descent -= style.rise;
2484 } else { 2962 } else {
2485 run.width = abc.abcA + abc.abcB + abc.abcC;
2486 TEXTMETRIC lptm; 2963 TEXTMETRIC lptm;
2487 OS.GetTextMetrics(hdc, &lptm); 2964 OS.GetTextMetrics(hdc, &lptm);
2488 run.ascent = lptm.tmAscent; 2965 run.ascent = lptm.tmAscent;
2489 run.descent = lptm.tmDescent; 2966 run.descent = lptm.tmDescent;
2490 run.leading = lptm.tmInternalLeading; 2967 run.leading = lptm.tmInternalLeading;
2491 }
2492 if (run.style !is null) {
2493 run.ascent += run.style.rise;
2494 run.descent -= +run.style.rise;
2495 } 2968 }
2496 } 2969 }
2497 2970
2498 int validadeOffset(int offset, int step) { 2971 int validadeOffset(int offset, int step) {
2499 offset += step; 2972 offset += step;