diff options
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java')
-rw-r--r-- | bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java | 495 |
1 files changed, 296 insertions, 199 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java index 663d970214..1c71538df3 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2018 IBM Corporation and others. + * Copyright (c) 2000, 2020 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -18,6 +18,7 @@ import java.util.*; import org.eclipse.swt.*; import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.gdip.*; +import org.eclipse.swt.internal.ole.win32.*; import org.eclipse.swt.internal.win32.*; /** @@ -60,21 +61,40 @@ public final class TextLayout extends Resource { StyleItem[] allRuns; StyleItem[][] runs; int[] lineOffset, lineY, lineWidth; - long /*int*/ mLangFontLink2; + IMLangFontLink2 mLangFontLink2; int verticalIndentInPoints; static final char LTR_MARK = '\u200E', RTL_MARK = '\u200F'; static final int SCRIPT_VISATTR_SIZEOF = 2; static final int GOFFSET_SIZEOF = 8; - static final byte[] CLSID_CMultiLanguage = new byte[16]; - static final byte[] IID_IMLangFontLink2 = new byte[16]; - static { - OS.IIDFromString("{275c23e2-3747-11d0-9fea-00aa003f8646}\0".toCharArray(), CLSID_CMultiLanguage); - OS.IIDFromString("{DCCFC162-2B38-11d2-B7EC-00C04F8F5D9A}\0".toCharArray(), IID_IMLangFontLink2); - } - static final int MERGE_MAX = 512; static final int TOO_MANY_RUNS = 1024; + /** + * Runs over a certain length (32000 characters / 65536 Glyphs / 32768 pixels - + * these numbers come from WinAPI docs + analysis done in Bug 23406 Comment 31) + * will fail to render in ScriptTextOut, ScriptShape, ScriptPlace so such + * long runs need to be split into shorter runs. Because it is expensive to + * keep testing (with Script*) to maximize the length we use this heuristic + * to minimize the length. However splitting the runs into too short pieces + * affects performance, so this is a balance. + */ + static final int MAX_RUN_LENGTH = 32000; + /** + * When splitting a run (see {@link #MAX_RUN_LENGTH}) the run needs to split + * in a way that does not affect the display of the glyphs, so it is important + * to not split the run in the middle of a glyph. We use the same info to find + * where we can wrap text to find where we can break the runs (ScriptBreak's info). + * This setting limits how far back from {@link #MAX_RUN_LENGTH} the code + * will search for a break before forcing a break at {@link #MAX_RUN_LENGTH}. + */ + static final int MAX_SEARCH_RUN_BREAK = 1000; + { + // While developing the splitting long runs it can be useful to + // make these constants smaller, but these invariants must + // preserved even in such cases. + assert MAX_RUN_LENGTH > 1; + assert MAX_SEARCH_RUN_BREAK < MAX_RUN_LENGTH; + } /* IME has a copy of these constants */ static final int UNDERLINE_IME_DOT = 1 << 16; @@ -88,17 +108,17 @@ public final class TextLayout extends Resource { /*Script cache and analysis */ SCRIPT_ANALYSIS analysis; - long /*int*/ psc = 0; + long psc = 0; /*Shape info (malloc when the run is shaped) */ - long /*int*/ glyphs; + long glyphs; int glyphCount; - long /*int*/ clusters; - long /*int*/ visAttrs; + long clusters; + long visAttrs; /*Place info (malloc when the run is placed) */ - long /*int*/ advances; - long /*int*/ goffsets; + long advances; + long goffsets; int width; int ascentInPoints; int descentInPoints; @@ -108,15 +128,16 @@ public final class TextLayout extends Resource { int strikeoutPos, strikeoutThickness; /* Justify info (malloc during computeRuns) */ - long /*int*/ justify; + long justify; /* ScriptBreak */ - long /*int*/ psla; + int pslaAllocSize; + long psla; - long /*int*/ fallbackFont; + long fallbackFont; void free() { - long /*int*/ hHeap = OS.GetProcessHeap(); + long hHeap = OS.GetProcessHeap(); if (psc != 0) { OS.ScriptFreeCache (psc); OS.HeapFree(hHeap, 0, psc); @@ -190,10 +211,10 @@ public TextLayout (Device device) { styles[1] = new StyleItem(); stylesCount = 2; text = ""; //$NON-NLS-1$ - long /*int*/[] ppv = new long /*int*/[1]; + long[] ppv = new long[1]; OS.OleInitialize(0); - if (OS.CoCreateInstance(CLSID_CMultiLanguage, 0, OS.CLSCTX_INPROC_SERVER, IID_IMLangFontLink2, ppv) == OS.S_OK) { - mLangFontLink2 = ppv[0]; + if (COM.CoCreateInstance(COM.CLSID_CMultiLanguage, 0, COM.CLSCTX_INPROC_SERVER, COM.IID_IMLangFontLink2, ppv) == OS.S_OK) { + mLangFontLink2 = new IMLangFontLink2(ppv[0]); } init(); } @@ -227,7 +248,8 @@ void breakRun(StyleItem run) { if (run.psla != 0) return; char[] chars = new char[run.length]; segmentsText.getChars(run.start, run.start + run.length, chars, 0); - long /*int*/ hHeap = OS.GetProcessHeap(); + long hHeap = OS.GetProcessHeap(); + run.pslaAllocSize = SCRIPT_LOGATTR.sizeof * chars.length; run.psla = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, SCRIPT_LOGATTR.sizeof * chars.length); if (run.psla == 0) SWT.error(SWT.ERROR_NO_HANDLES); OS.ScriptBreak(chars, chars.length, run.analysis, run.psla); @@ -243,8 +265,8 @@ void checkLayout () { */ void computeRuns (GC gc) { if (runs != null) return; - long /*int*/ hDC = gc != null ? gc.handle : device.internal_new_GC(null); - long /*int*/ srcHdc = OS.CreateCompatibleDC(hDC); + long hDC = gc != null ? gc.handle : device.internal_new_GC(null); + long srcHdc = OS.CreateCompatibleDC(hDC); allRuns = itemize(); for (int i=0; i<allRuns.length - 1; i++) { StyleItem run = allRuns[i]; @@ -272,6 +294,11 @@ void computeRuns (GC gc) { run.width = tabX - lineWidth; } } + + /* + * This block adjusts the indentation after merged tabs stops. + * The extra tabs are removed in merge. + */ int length = run.length; if (length > 1) { int stop = j + length - 1; @@ -434,10 +461,9 @@ void computeRuns (GC gc) { } } lineWidth += lineIndent; - long /*int*/ hHeap = OS.GetProcessHeap(); + long hHeap = OS.GetProcessHeap(); int newLineWidth = 0; - for (int j = 0; j < runs[line].length; j++) { - StyleItem item = runs[line][j]; + for (StyleItem item : runs[line]) { int iDx = item.width * wrapWidth / lineWidth; if (iDx != item.width) { item.justify = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, item.glyphCount * 4); @@ -461,9 +487,9 @@ void computeRuns (GC gc) { } lineWidth = getLineIndent(line); - for (int j = 0; j < runs[line].length; j++) { - runs[line][j].x = lineWidth; - lineWidth += runs[line][j].width; + for (StyleItem run1 : runs[line]) { + run1.x = lineWidth; + lineWidth += run1.width; } line++; lineY[line] = lineY[line - 1] + ascentInPoints + descentInPoints + lineSpacingInPoints; @@ -491,10 +517,9 @@ void destroy () { lineWidth = null; segments = null; segmentsChars = null; - if (mLangFontLink2 != 0) { - /* Release() */ - OS.VtblCall(2, mLangFontLink2); - mLangFontLink2 = 0; + if (mLangFontLink2 != null) { + mLangFontLink2.Release(); + mLangFontLink2 = null; } OS.OleUninitialize(); } @@ -546,12 +571,12 @@ int[] computePolyline(int left, int top, int right, int bottom) { return coordinates; } -long /*int*/ createGdipBrush(int pixel, int alpha) { +long createGdipBrush(int pixel, int alpha) { int argb = ((alpha & 0xFF) << 24) | ((pixel >> 16) & 0xFF) | (pixel & 0xFF00) | ((pixel & 0xFF) << 16); return Gdip.SolidBrush_new(argb); } -long /*int*/ createGdipBrush(Color color, int alpha) { +long createGdipBrush(Color color, int alpha) { return createGdipBrush(color.handle, alpha); } @@ -648,16 +673,16 @@ void drawInPixels (GC gc, int x, int y, int selectionStart, int selectionEnd, Co int length = text.length(); if (length == 0 && flags == 0) return; y += getScaledVerticalIndent(); - long /*int*/ hdc = gc.handle; + long hdc = gc.handle; Rectangle clip = gc.getClippingInPixels(); GCData data = gc.data; - long /*int*/ gdipGraphics = data.gdipGraphics; + long gdipGraphics = data.gdipGraphics; int foreground = data.foreground; int linkColor = OS.GetSysColor (OS.COLOR_HOTLIGHT); int alpha = data.alpha; boolean gdip = gdipGraphics != 0; - long /*int*/ gdipForeground = 0; - long /*int*/ gdipLinkColor = 0; + long gdipForeground = 0; + long gdipLinkColor = 0; int state = 0; if (gdip) { gc.checkGC(GC.FOREGROUND); @@ -669,8 +694,8 @@ void drawInPixels (GC gc, int x, int y, int selectionStart, int selectionEnd, Co } } boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; - long /*int*/ gdipSelBackground = 0, gdipSelForeground = 0, gdipFont = 0, lastHFont = 0; - long /*int*/ selBackground = 0; + long gdipSelBackground = 0, gdipSelForeground = 0, gdipFont = 0, lastHFont = 0; + long selBackground = 0; int selForeground = 0; if (hasSelection || ((flags & SWT.LAST_LINE_SELECTION) != 0 && (flags & (SWT.FULL_SELECTION | SWT.DELIMITER_SELECTION)) != 0)) { int fgSel = selectionForeground != null ? selectionForeground.handle : OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT); @@ -731,8 +756,7 @@ void drawInPixels (GC gc, int x, int y, int selectionStart, int selectionEnd, Co //Draw the background of the runs in the line int alignmentX = drawX; - for (int i = 0; i < lineRuns.length; i++) { - StyleItem run = lineRuns[i]; + for (StyleItem run : lineRuns) { if (run.length == 0) continue; if (drawX > clip.x + clip.width) break; if (drawX + run.width >= clip.x) { @@ -751,9 +775,9 @@ void drawInPixels (GC gc, int x, int y, int selectionStart, int selectionEnd, Co //Draw the text, underline, strikeout, and border of the runs in the line int baselineInPixels = Math.max(0, this.ascentInPixels); int lineUnderlinePos = 0; - for (int i = 0; i < lineRuns.length; i++) { - baselineInPixels = Math.max(baselineInPixels, DPIUtil.autoScaleUp(getDevice(), lineRuns[i].ascentInPoints)); - lineUnderlinePos = Math.min(lineUnderlinePos, lineRuns[i].underlinePos); + for (StyleItem run : lineRuns) { + baselineInPixels = Math.max(baselineInPixels, DPIUtil.autoScaleUp(getDevice(), run.ascentInPoints)); + lineUnderlinePos = Math.min(lineUnderlinePos, run.underlinePos); } RECT borderClip = null, underlineClip = null, strikeoutClip = null, pRect = null; drawX = alignmentX; @@ -768,11 +792,11 @@ void drawInPixels (GC gc, int x, int y, int selectionStart, int selectionEnd, Co if (!skipTab && (!run.lineBreak || run.softBreak) && !(style != null && style.metrics != null)) { OS.SetRect(rect, drawX, drawY, drawX + run.width, drawY + lineHeight); if (gdip) { - long /*int*/ hFont = getItemFont(run); + long hFont = getItemFont(run); if (hFont != lastHFont) { lastHFont = hFont; if (gdipFont != 0) Gdip.Font_delete(gdipFont); - long /*int*/ oldFont = OS.SelectObject(hdc, hFont); + long oldFont = OS.SelectObject(hdc, hFont); gdipFont = Gdip.Font_new(hdc, hFont); OS.SelectObject(hdc, oldFont); if (gdipFont == 0) SWT.error(SWT.ERROR_NO_HANDLES); @@ -781,7 +805,7 @@ void drawInPixels (GC gc, int x, int y, int selectionStart, int selectionEnd, Co gdipFont = 0; } } - long /*int*/ gdipFg = gdipForeground; + long gdipFg = gdipForeground; if (style != null && style.underline && style.underlineStyle == SWT.UNDERLINE_LINK) { if (gdipLinkColor == 0) gdipLinkColor = createGdipBrush(linkColor, alpha); gdipFg = gdipLinkColor; @@ -815,14 +839,14 @@ void drawInPixels (GC gc, int x, int y, int selectionStart, int selectionEnd, Co if (selBackground != 0) OS.DeleteObject (selBackground); } -RECT drawBorder(long /*int*/ hdc, int x, int y, int lineHeight, StyleItem[] line, int index, int color, int selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, Rectangle drawClip) { +RECT drawBorder(long hdc, int x, int y, int lineHeight, StyleItem[] line, int index, int color, int selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, Rectangle drawClip) { StyleItem run = line[index]; TextStyle style = run.style; if (style == null) return null; if (style.borderStyle == SWT.NONE) return null; clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd); boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width); - if (index + 1 >= line.length || lastRunVisible || !style.isAdherentBorder(line[index + 1].style)) { + if (index + 1 >= line.length || lastRunVisible || line[index + 1].lineBreak || !style.isAdherentBorder(line[index + 1].style)) { int left = run.x; int start = run.start; int end = run.start + run.length - 1; @@ -862,12 +886,12 @@ RECT drawBorder(long /*int*/ hdc, int x, int y, int lineHeight, StyleItem[] line break; } } - long /*int*/ oldBrush = OS.SelectObject(hdc, OS.GetStockObject(OS.NULL_BRUSH)); + long oldBrush = OS.SelectObject(hdc, OS.GetStockObject(OS.NULL_BRUSH)); LOGBRUSH logBrush = new LOGBRUSH(); logBrush.lbStyle = OS.BS_SOLID; - logBrush.lbColor = /*64*/(int)color; - long /*int*/ newPen = OS.ExtCreatePen(lineStyle | OS.PS_GEOMETRIC, lineWidth, logBrush, 0, null); - long /*int*/ oldPen = OS.SelectObject(hdc, newPen); + logBrush.lbColor = color; + long newPen = OS.ExtCreatePen(lineStyle | OS.PS_GEOMETRIC, lineWidth, logBrush, 0, null); + long oldPen = OS.SelectObject(hdc, newPen); RECT drawRect = new RECT(); OS.SetRect(drawRect, x + left, y, x + run.x + run.width, y + lineHeight); if (drawClip != null) { @@ -888,8 +912,8 @@ RECT drawBorder(long /*int*/ hdc, int x, int y, int lineHeight, StyleItem[] line if (clipRect.left == -1) clipRect.left = 0; if (clipRect.right == -1) clipRect.right = 0x7ffff; OS.IntersectClipRect(hdc, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); - logBrush.lbColor = /*64*/(int)selectionColor; - long /*int*/ selPen = OS.ExtCreatePen (lineStyle | OS.PS_GEOMETRIC, lineWidth, logBrush, 0, null); + logBrush.lbColor = selectionColor; + long selPen = OS.ExtCreatePen (lineStyle | OS.PS_GEOMETRIC, lineWidth, logBrush, 0, null); oldPen = OS.SelectObject(hdc, selPen); OS.Rectangle(hdc, drawRect.left, drawRect.top, drawRect.right, drawRect.bottom); OS.RestoreDC(hdc, state); @@ -902,14 +926,14 @@ RECT drawBorder(long /*int*/ hdc, int x, int y, int lineHeight, StyleItem[] line return clipRect; } -RECT drawBorderGDIP(long /*int*/ graphics, int x, int y, int lineHeight, StyleItem[] line, int index, long /*int*/ color, long /*int*/ selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, int alpha, Rectangle drawClip) { +RECT drawBorderGDIP(long graphics, int x, int y, int lineHeight, StyleItem[] line, int index, long color, long selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, int alpha, Rectangle drawClip) { StyleItem run = line[index]; TextStyle style = run.style; if (style == null) return null; if (style.borderStyle == SWT.NONE) return null; clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd); boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width); - if (index + 1 >= line.length || lastRunVisible || !style.isAdherentBorder(line[index + 1].style)) { + if (index + 1 >= line.length || lastRunVisible || line[index + 1].lineBreak || !style.isAdherentBorder(line[index + 1].style)) { int left = run.x; int start = run.start; int end = run.start + run.length - 1; @@ -920,7 +944,7 @@ RECT drawBorderGDIP(long /*int*/ graphics, int x, int y, int lineHeight, StyleIt } boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd; - long /*int*/ brush = color; + long brush = color; if (style.borderColor != null) { brush = createGdipBrush(style.borderColor, alpha); clipRect = null; @@ -941,7 +965,7 @@ RECT drawBorderGDIP(long /*int*/ graphics, int x, int y, int lineHeight, StyleIt case SWT.BORDER_DASH: lineStyle = Gdip.DashStyleDash; break; case SWT.BORDER_DOT: lineStyle = Gdip.DashStyleDot; break; } - long /*int*/ pen = Gdip.Pen_new(brush, lineWidth); + long pen = Gdip.Pen_new(brush, lineWidth); Gdip.Pen_SetDashStyle(pen, lineStyle); Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeNone); int smoothingMode = Gdip.Graphics_GetSmoothingMode(graphics); @@ -960,7 +984,7 @@ RECT drawBorderGDIP(long /*int*/ graphics, int x, int y, int lineHeight, StyleIt Gdip.Graphics_Restore(graphics, gstate); gstate = Gdip.Graphics_Save(graphics); Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect); - long /*int*/ selPen = Gdip.Pen_new(selectionColor, lineWidth); + long selPen = Gdip.Pen_new(selectionColor, lineWidth); Gdip.Pen_SetDashStyle(selPen, lineStyle); Gdip.Graphics_DrawRectangle(graphics, selPen, x + left, y, run.x + run.width - left - 1, lineHeight - 1); Gdip.Pen_delete(selPen); @@ -977,7 +1001,7 @@ RECT drawBorderGDIP(long /*int*/ graphics, int x, int y, int lineHeight, StyleIt return clipRect; } -void drawRunBackground(StyleItem run, long /*int*/ hdc, RECT rect, int selectionStart, int selectionEnd, long /*int*/ selBrush, boolean hasSelection) { +void drawRunBackground(StyleItem run, long hdc, RECT rect, int selectionStart, int selectionEnd, long selBrush, boolean hasSelection) { int end = run.start + run.length - 1; boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end; if (fullSelection) { @@ -986,8 +1010,8 @@ void drawRunBackground(StyleItem run, long /*int*/ hdc, RECT rect, int selection } else { if (run.style != null && run.style.background != null) { int bg = run.style.background.handle; - long /*int*/ hBrush = OS.CreateSolidBrush (bg); - long /*int*/ oldBrush = OS.SelectObject(hdc, hBrush); + long hBrush = OS.CreateSolidBrush (bg); + long oldBrush = OS.SelectObject(hdc, hBrush); OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY); OS.SelectObject(hdc, oldBrush); OS.DeleteObject(hBrush); @@ -1001,14 +1025,14 @@ void drawRunBackground(StyleItem run, long /*int*/ hdc, RECT rect, int selection } } -void drawRunBackgroundGDIP(StyleItem run, long /*int*/ graphics, RECT rect, int selectionStart, int selectionEnd, int alpha, long /*int*/ selBrush, boolean hasSelection) { +void drawRunBackgroundGDIP(StyleItem run, long graphics, RECT rect, int selectionStart, int selectionEnd, int alpha, long selBrush, boolean hasSelection) { int end = run.start + run.length - 1; boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end; if (fullSelection) { Gdip.Graphics_FillRectangle(graphics, selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); } else { if (run.style != null && run.style.background != null) { - long /*int*/ brush = createGdipBrush(run.style.background, alpha); + long brush = createGdipBrush(run.style.background, alpha); Gdip.Graphics_FillRectangle(graphics, brush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); Gdip.SolidBrush_delete(brush); } @@ -1025,7 +1049,7 @@ void drawRunBackgroundGDIP(StyleItem run, long /*int*/ graphics, RECT rect, int } } -RECT drawRunText(long /*int*/ hdc, StyleItem run, RECT rect, int baselineInPixels, int color, int selectionColor, int selectionStart, int selectionEnd) { +RECT drawRunText(long hdc, StyleItem run, RECT rect, int baselineInPixels, int color, int selectionColor, int selectionStart, int selectionEnd) { int end = run.start + run.length - 1; boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end; @@ -1033,7 +1057,7 @@ RECT drawRunText(long /*int*/ hdc, StyleItem run, RECT rect, int baselineInPixel int offset = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? -1 : 0; int x = rect.left + offset; int y = rect.top + (baselineInPixels - DPIUtil.autoScaleUp(getDevice(), run.ascentInPoints)); - long /*int*/ hFont = getItemFont(run); + long hFont = getItemFont(run); OS.SelectObject(hdc, hFont); if (fullSelection) { color = selectionColor; @@ -1052,7 +1076,7 @@ RECT drawRunText(long /*int*/ hdc, StyleItem run, RECT rect, int baselineInPixel return fullSelection || partialSelection ? rect : null; } -RECT drawRunTextGDIP(long /*int*/ graphics, StyleItem run, RECT rect, long /*int*/ gdipFont, int baselineInPixels, long /*int*/ color, long /*int*/ selectionColor, int selectionStart, int selectionEnd, int alpha) { +RECT drawRunTextGDIP(long graphics, StyleItem run, RECT rect, long gdipFont, int baselineInPixels, long color, long selectionColor, int selectionStart, int selectionEnd, int alpha) { int end = run.start + run.length - 1; boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; boolean fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end; @@ -1060,7 +1084,7 @@ RECT drawRunTextGDIP(long /*int*/ graphics, StyleItem run, RECT rect, long /*int int drawY = rect.top + baselineInPixels; if (run.style != null && run.style.rise != 0) drawY -= DPIUtil.autoScaleUp(getDevice(), run.style.rise); int drawX = rect.left; - long /*int*/ brush = color; + long brush = color; if (fullSelection) { brush = selectionColor; } else { @@ -1137,10 +1161,10 @@ RECT drawRunTextGDIP(long /*int*/ graphics, StyleItem run, RECT rect, long /*int return fullSelection || partialSelection ? rect : null; } -RECT drawRunTextGDIPRaster(long /*int*/ graphics, StyleItem run, RECT rect, int baselineInPixels, int color, int selectionColor, int selectionStart, int selectionEnd) { - long /*int*/ clipRgn = 0; +RECT drawRunTextGDIPRaster(long graphics, StyleItem run, RECT rect, int baselineInPixels, int color, int selectionColor, int selectionStart, int selectionEnd) { + long clipRgn = 0; Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeNone); - long /*int*/ rgn = Gdip.Region_new(); + long rgn = Gdip.Region_new(); if (rgn == 0) SWT.error(SWT.ERROR_NO_HANDLES); Gdip.Graphics_GetClip(graphics, rgn); if (!Gdip.Region_IsInfinite(rgn, graphics)) { @@ -1149,7 +1173,7 @@ RECT drawRunTextGDIPRaster(long /*int*/ graphics, StyleItem run, RECT rect, int Gdip.Region_delete(rgn); Gdip.Graphics_SetPixelOffsetMode(graphics, Gdip.PixelOffsetModeHalf); float[] lpXform = null; - long /*int*/ matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0); + long matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0); if (matrix == 0) SWT.error(SWT.ERROR_NO_HANDLES); Gdip.Graphics_GetTransform(graphics, matrix); if (!Gdip.Matrix_IsIdentity(matrix)) { @@ -1157,7 +1181,7 @@ RECT drawRunTextGDIPRaster(long /*int*/ graphics, StyleItem run, RECT rect, int Gdip.Matrix_GetElements(matrix, lpXform); } Gdip.Matrix_delete(matrix); - long /*int*/ hdc = Gdip.Graphics_GetHDC(graphics); + long hdc = Gdip.Graphics_GetHDC(graphics); int state = OS.SaveDC(hdc); if (lpXform != null) { OS.SetGraphicsMode(hdc, OS.GM_ADVANCED); @@ -1177,14 +1201,14 @@ RECT drawRunTextGDIPRaster(long /*int*/ graphics, StyleItem run, RECT rect, int return pRect; } -RECT drawStrikeout(long /*int*/ hdc, int x, int baselineInPixels, StyleItem[] line, int index, int color, int selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, Rectangle drawClip) { +RECT drawStrikeout(long hdc, int x, int baselineInPixels, StyleItem[] line, int index, int color, int selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, Rectangle drawClip) { StyleItem run = line[index]; TextStyle style = run.style; if (style == null) return null; if (!style.strikeout) return null; clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd); boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width); - if (index + 1 >= line.length || lastRunVisible || !style.isAdherentStrikeout(line[index + 1].style)) { + if (index + 1 >= line.length || lastRunVisible || line[index + 1].lineBreak || !style.isAdherentStrikeout(line[index + 1].style)) { int left = run.x; int start = run.start; int end = run.start + run.length - 1; @@ -1211,11 +1235,11 @@ RECT drawStrikeout(long /*int*/ hdc, int x, int baselineInPixels, StyleItem[] li RECT rect = new RECT(); int riseInPixels = DPIUtil.autoScaleUp(getDevice(), style.rise); OS.SetRect(rect, x + left, baselineInPixels - run.strikeoutPos - riseInPixels, x + run.x + run.width, baselineInPixels - run.strikeoutPos + run.strikeoutThickness - riseInPixels); - long /*int*/ brush = OS.CreateSolidBrush(color); + long brush = OS.CreateSolidBrush(color); OS.FillRect(hdc, rect, brush); OS.DeleteObject(brush); if (clipRect != null) { - long /*int*/ selBrush = OS.CreateSolidBrush(selectionColor); + long selBrush = OS.CreateSolidBrush(selectionColor); if (clipRect.left == -1) clipRect.left = 0; if (clipRect.right == -1) clipRect.right = 0x7ffff; OS.SetRect(clipRect, Math.max(rect.left, clipRect.left), rect.top, Math.min(rect.right, clipRect.right), rect.bottom); @@ -1227,14 +1251,14 @@ RECT drawStrikeout(long /*int*/ hdc, int x, int baselineInPixels, StyleItem[] li return clipRect; } -RECT drawStrikeoutGDIP(long /*int*/ graphics, int x, int baselineInPixels, StyleItem[] line, int index, long /*int*/ color, long /*int*/ selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, int alpha, Rectangle drawClip) { +RECT drawStrikeoutGDIP(long graphics, int x, int baselineInPixels, StyleItem[] line, int index, long color, long selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, int alpha, Rectangle drawClip) { StyleItem run = line[index]; TextStyle style = run.style; if (style == null) return null; if (!style.strikeout) return null; clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd); boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width); - if (index + 1 >= line.length || lastRunVisible || !style.isAdherentStrikeout(line[index + 1].style)) { + if (index + 1 >= line.length || lastRunVisible || line[index + 1].lineBreak || !style.isAdherentStrikeout(line[index + 1].style)) { int left = run.x; int start = run.start; int end = run.start + run.length - 1; @@ -1245,7 +1269,7 @@ RECT drawStrikeoutGDIP(long /*int*/ graphics, int x, int baselineInPixels, Style } boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd; - long /*int*/ brush = color; + long brush = color; if (style.strikeoutColor != null) { brush = createGdipBrush(style.strikeoutColor, alpha); clipRect = null; @@ -1285,14 +1309,14 @@ RECT drawStrikeoutGDIP(long /*int*/ graphics, int x, int baselineInPixels, Style return clipRect; } -RECT drawUnderline(long /*int*/ hdc, int x, int baselineInPixels, int lineUnderlinePos, int lineBottom, StyleItem[] line, int index, int color, int selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, Rectangle drawClip) { +RECT drawUnderline(long hdc, int x, int baselineInPixels, int lineUnderlinePos, int lineBottom, StyleItem[] line, int index, int color, int selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, Rectangle drawClip) { StyleItem run = line[index]; TextStyle style = run.style; if (style == null) return null; if (!style.underline) return null; clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd); boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width); - if (index + 1 >= line.length || lastRunVisible || !style.isAdherentUnderline(line[index + 1].style)) { + if (index + 1 >= line.length || lastRunVisible || line[index + 1].lineBreak || !style.isAdherentUnderline(line[index + 1].style)) { int left = run.x; int start = run.start; int end = run.start + run.length - 1; @@ -1331,8 +1355,8 @@ RECT drawUnderline(long /*int*/ hdc, int x, int baselineInPixels, int lineUnderl int squigglyHeight = 2 * squigglyThickness; int squigglyY = Math.min(rect.top - squigglyHeight / 2, lineBottom - squigglyHeight - 1); int[] points = computePolyline(rect.left, squigglyY, rect.right, squigglyY + squigglyHeight); - long /*int*/ pen = OS.CreatePen(OS.PS_SOLID, squigglyThickness, color); - long /*int*/ oldPen = OS.SelectObject(hdc, pen); + long pen = OS.CreatePen(OS.PS_SOLID, squigglyThickness, color); + long oldPen = OS.SelectObject(hdc, pen); int state = OS.SaveDC(hdc); OS.IntersectClipRect(hdc, rect.left, squigglyY, rect.right + 1, squigglyY + squigglyHeight + 1); OS.Polyline(hdc, points, points.length / 2); @@ -1371,7 +1395,7 @@ RECT drawUnderline(long /*int*/ hdc, int x, int baselineInPixels, int lineUnderl OS.OffsetRect(rect, 0, lineBottom - bottom); if (clipRect != null) OS.OffsetRect(clipRect, 0, lineBottom - bottom); } - long /*int*/ brush = OS.CreateSolidBrush(color); + long brush = OS.CreateSolidBrush(color); OS.FillRect(hdc, rect, brush); if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) { OS.SetRect(rect, rect.left, rect.top + run.underlineThickness * 2, rect.right, rect.bottom + run.underlineThickness * 2); @@ -1379,7 +1403,7 @@ RECT drawUnderline(long /*int*/ hdc, int x, int baselineInPixels, int lineUnderl } OS.DeleteObject(brush); if (clipRect != null) { - long /*int*/ selBrush = OS.CreateSolidBrush(selectionColor); + long selBrush = OS.CreateSolidBrush(selectionColor); OS.FillRect(hdc, clipRect, selBrush); if (style.underlineStyle == SWT.UNDERLINE_DOUBLE) { OS.SetRect(clipRect, clipRect.left, rect.top, clipRect.right, rect.bottom); @@ -1391,8 +1415,8 @@ RECT drawUnderline(long /*int*/ hdc, int x, int baselineInPixels, int lineUnderl case UNDERLINE_IME_DASH: case UNDERLINE_IME_DOT: { int penStyle = style.underlineStyle == UNDERLINE_IME_DASH ? OS.PS_DASH : OS.PS_DOT; - long /*int*/ pen = OS.CreatePen(penStyle, 1, color); - long /*int*/ oldPen = OS.SelectObject(hdc, pen); + long pen = OS.CreatePen(penStyle, 1, color); + long oldPen = OS.SelectObject(hdc, pen); int descentInPixels = DPIUtil.autoScaleUp(getDevice(), run.descentInPoints); OS.SetRect(rect, rect.left, baselineInPixels + descentInPixels, rect.right, baselineInPixels + descentInPixels + run.underlineThickness); OS.MoveToEx(hdc, rect.left, rect.top, 0); @@ -1416,14 +1440,14 @@ RECT drawUnderline(long /*int*/ hdc, int x, int baselineInPixels, int lineUnderl return clipRect; } -RECT drawUnderlineGDIP (long /*int*/ graphics, int x, int baselineInPixels, int lineUnderlinePos, int lineBottom, StyleItem[] line, int index, long /*int*/ color, long /*int*/ selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, int alpha, Rectangle drawClip) { +RECT drawUnderlineGDIP (long graphics, int x, int baselineInPixels, int lineUnderlinePos, int lineBottom, StyleItem[] line, int index, long color, long selectionColor, RECT clipRect, RECT pRect, int selectionStart, int selectionEnd, int alpha, Rectangle drawClip) { StyleItem run = line[index]; TextStyle style = run.style; if (style == null) return null; if (!style.underline) return null; clipRect = addClipRect(run, clipRect, pRect, selectionStart, selectionEnd); boolean lastRunVisible = drawClip != null && (x + run.x + run.width) > (drawClip.x + drawClip.width); - if (index + 1 >= line.length || lastRunVisible || !style.isAdherentUnderline(line[index + 1].style)) { + if (index + 1 >= line.length || lastRunVisible || line[index + 1].lineBreak || !style.isAdherentUnderline(line[index + 1].style)) { int left = run.x; int start = run.start; int end = run.start + run.length - 1; @@ -1434,7 +1458,7 @@ RECT drawUnderlineGDIP (long /*int*/ graphics, int x, int baselineInPixels, int } boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; boolean fullSelection = hasSelection && selectionStart <= start && end <= selectionEnd; - long /*int*/ brush = color; + long brush = color; if (style.underlineColor != null) { brush = createGdipBrush(style.underlineColor, alpha); clipRect = null; @@ -1473,7 +1497,7 @@ RECT drawUnderlineGDIP (long /*int*/ graphics, int x, int baselineInPixels, int int squigglyHeight = 2 * squigglyThickness; int squigglyY = Math.min(rect.top - squigglyHeight / 2, lineBottom - squigglyHeight - 1); int[] points = computePolyline(rect.left, squigglyY, rect.right, squigglyY + squigglyHeight); - long /*int*/ pen = Gdip.Pen_new(brush, squigglyThickness); + long pen = Gdip.Pen_new(brush, squigglyThickness); gstate = Gdip.Graphics_Save(graphics); if (gdipRect != null) { Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeExclude); @@ -1487,7 +1511,7 @@ RECT drawUnderlineGDIP (long /*int*/ graphics, int x, int baselineInPixels, int } Gdip.Graphics_DrawLines(graphics, pen, points, points.length / 2); if (gdipRect != null) { - long /*int*/ selPen = Gdip.Pen_new(selectionColor, squigglyThickness); + long selPen = Gdip.Pen_new(selectionColor, squigglyThickness); Gdip.Graphics_Restore(graphics, gstate); gstate = Gdip.Graphics_Save(graphics); Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect); @@ -1538,7 +1562,7 @@ RECT drawUnderlineGDIP (long /*int*/ graphics, int x, int baselineInPixels, int break; case UNDERLINE_IME_DOT: case UNDERLINE_IME_DASH: { - long /*int*/ pen = Gdip.Pen_new(brush, 1); + long pen = Gdip.Pen_new(brush, 1); int dashStyle = style.underlineStyle == UNDERLINE_IME_DOT ? Gdip.DashStyleDot : Gdip.DashStyleDash; Gdip.Pen_SetDashStyle(pen, dashStyle); if (gdipRect != null) { @@ -1551,7 +1575,7 @@ RECT drawUnderlineGDIP (long /*int*/ graphics, int x, int baselineInPixels, int Gdip.Graphics_Restore(graphics, gstate); gstate = Gdip.Graphics_Save(graphics); Gdip.Graphics_SetClip(graphics, gdipRect, Gdip.CombineModeIntersect); - long /*int*/ selPen = Gdip.Pen_new(brush, 1); + long selPen = Gdip.Pen_new(brush, 1); Gdip.Pen_SetDashStyle(selPen, dashStyle); Gdip.Graphics_DrawLine(graphics, selPen, rect.left, baselineInPixels + descentInPixels, run.width - run.length, baselineInPixels + descentInPixels); Gdip.Graphics_Restore(graphics, gstate); @@ -1571,8 +1595,7 @@ RECT drawUnderlineGDIP (long /*int*/ graphics, int x, int baselineInPixels, int void freeRuns () { if (allRuns == null) return; - for (int i=0; i<allRuns.length; i++) { - StyleItem run = allRuns[i]; + for (StyleItem run : allRuns) { run.free(); } allRuns = null; @@ -1707,10 +1730,8 @@ Rectangle getBoundsInPixels (int start, int end) { GlyphMetrics metrics = run.style.metrics; cx = metrics.getWidthInPixels() * (start - run.start); } else if (!run.tab) { - int[] piX = new int[1]; - long /*int*/ advances = run.justify != 0 ? run.justify : run.advances; - OS.ScriptCPtoX(start - run.start, false, run.length, run.glyphCount, run.clusters, run.visAttrs, advances, run.analysis, piX); - cx = isRTL ? run.width - piX[0] : piX[0]; + int iX = ScriptCPtoX(start - run.start, false, run); + cx = isRTL ? run.width - iX : iX; } if (run.analysis.fRTL ^ isRTL) { runTrail = run.x + cx; @@ -1724,10 +1745,8 @@ Rectangle getBoundsInPixels (int start, int end) { GlyphMetrics metrics = run.style.metrics; cx = metrics.getWidthInPixels() * (end - run.start + 1); } else if (!run.tab) { - int[] piX = new int[1]; - long /*int*/ advances = run.justify != 0 ? run.justify : run.advances; - OS.ScriptCPtoX(end - run.start, true, run.length, run.glyphCount, run.clusters, run.visAttrs, advances, run.analysis, piX); - cx = isRTL ? run.width - piX[0] : piX[0]; + int iX = ScriptCPtoX(end - run.start, true, run); + cx = isRTL ? run.width - iX : iX; } if (run.analysis.fRTL ^ isRTL) { runLead = run.x + cx; @@ -1817,7 +1836,7 @@ public boolean getJustify () { return justify; } -long /*int*/ getItemFont (StyleItem item) { +long getItemFont (StyleItem item) { if (item.fallbackFont != 0) return item.fallbackFont; if (item.style != null && item.style.font != null) { return item.style.font.handle; @@ -1975,8 +1994,8 @@ public FontMetrics getLineMetrics (int lineIndex) { checkLayout(); computeRuns(null); if (!(0 <= lineIndex && lineIndex < runs.length)) SWT.error(SWT.ERROR_INVALID_RANGE); - long /*int*/ hDC = device.internal_new_GC(null); - long /*int*/ srcHdc = OS.CreateCompatibleDC(hDC); + long hDC = device.internal_new_GC(null); + long srcHdc = OS.CreateCompatibleDC(hDC); TEXTMETRIC lptm = new TEXTMETRIC(); OS.SelectObject(srcHdc, font != null ? font.handle : device.systemFont.handle); OS.GetTextMetrics(srcHdc, lptm); @@ -1987,9 +2006,7 @@ public FontMetrics getLineMetrics (int lineIndex) { int descentInPoints = DPIUtil.autoScaleDown(getDevice(), Math.max(lptm.tmDescent, this.descentInPixels)); int leadingInPoints = DPIUtil.autoScaleDown(getDevice(), lptm.tmInternalLeading); if (text.length() != 0) { - StyleItem[] lineRuns = runs[lineIndex]; - for (int i = 0; i<lineRuns.length; i++) { - StyleItem run = lineRuns[i]; + for (StyleItem run : runs[lineIndex]) { if (run.ascentInPoints > ascentInPoints) { ascentInPoints = run.ascentInPoints; leadingInPoints = run.leadingInPoints; @@ -2100,12 +2117,8 @@ Point getLocationInPixels (int offset, boolean trailing) { width = (trailing || (offset == length)) ? run.width : 0; } else { int runOffset = offset - run.start; - int cChars = run.length; - int gGlyphs = run.glyphCount; - int[] piX = new int[1]; - long /*int*/ advances = run.justify != 0 ? run.justify : run.advances; - OS.ScriptCPtoX(runOffset, trailing, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX); - width = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0]; + final int iX = ScriptCPtoX(runOffset, trailing, run); + width = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - iX : iX; } return new Point(run.x + width, DPIUtil.autoScaleUp(getDevice(), lineY[line]) + getScaledVerticalIndent()); } @@ -2114,6 +2127,24 @@ Point getLocationInPixels (int offset, boolean trailing) { } /** + * Wrapper around + * {@link OS#ScriptCPtoX(int, boolean, int, int, long, long, long, SCRIPT_ANALYSIS, int[])} + * to handle common arguments consistently. + * + * @param characterPosition the first argument of OS.ScriptCPtoX + * @param trailing the first argument of OS.ScriptCPtoX + * @param run used to define remaining arguments of OS.ScriptCPtoX + * @return x position of the caret. + */ +private int ScriptCPtoX(int characterPosition, boolean trailing, StyleItem run) { + int[] piX = new int[1]; + long advances = run.justify != 0 ? run.justify : run.advances; + OS.ScriptCPtoX(characterPosition, trailing, run.length, run.glyphCount, run.clusters, run.visAttrs, advances, + run.analysis, piX); + return piX[0]; +} + +/** * Returns the next offset for the specified offset and movement * type. The movement is one of <code>SWT.MOVEMENT_CHAR</code>, * <code>SWT.MOVEMENT_CLUSTER</code>, <code>SWT.MOVEMENT_WORD</code>, @@ -2320,7 +2351,7 @@ int getOffsetInPixels (int x, int y, int[] trailing) { if ((orientation & SWT.RIGHT_TO_LEFT) != 0) { xRun = run.width - xRun; } - long /*int*/ advances = run.justify != 0 ? run.justify : run.advances; + long advances = run.justify != 0 ? run.justify : run.advances; OS.ScriptXtoCP(xRun, cChars, cGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piCP, piTrailing); int offset = run.start + piCP[0]; int length = segmentsText.length(); @@ -2372,16 +2403,12 @@ void getPartialSelection(StyleItem run, int selectionStart, int selectionEnd, RE int end = run.start + run.length - 1; int selStart = Math.max(selectionStart, run.start) - run.start; int selEnd = Math.min(selectionEnd, end) - run.start; - int cChars = run.length; - int gGlyphs = run.glyphCount; - int[] piX = new int[1]; int x = rect.left; - long /*int*/ advances = run.justify != 0 ? run.justify : run.advances; - OS.ScriptCPtoX(selStart, false, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX); - int runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0]; + int iX = ScriptCPtoX(selStart, false, run); + int runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - iX : iX; rect.left = x + runX; - OS.ScriptCPtoX(selEnd, true, cChars, gGlyphs, run.clusters, run.visAttrs, advances, run.analysis, piX); - runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - piX[0] : piX[0]; + iX = ScriptCPtoX(selEnd, true, run); + runX = (orientation & SWT.RIGHT_TO_LEFT) != 0 ? run.width - iX : iX; rect.right = x + runX; } @@ -2718,27 +2745,45 @@ StyleItem[] itemize () { scriptState.fArabicNumContext = true; } - /* - * In the version of Usp10.h that SWT is compiled the fReserved field is declared - * as a bitfield size 8. In newer versions of the Uniscribe, the first bit of fReserved - * was used to implement the fMergeNeutralItems feature which can be used to increase - * performance by reducing the number of SCRIPT_ITEM returned by ScriptItemize. - * - * Note: This code is wrong on a big endian machine. - * - * Note: This code is intentionally commented because it causes bug#377472. - */ -// scriptControl.fReserved = 0x1; - - OS.ScriptApplyDigitSubstitution(null, scriptControl, scriptState); + OS.ScriptApplyDigitSubstitution(0, scriptControl, scriptState); - long /*int*/ hHeap = OS.GetProcessHeap(); - long /*int*/ pItems = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, MAX_ITEM * SCRIPT_ITEM.sizeof); + long hHeap = OS.GetProcessHeap(); + // This buffer needs to be one entry bigger than the cMaxItems param to ScriptItemize + // see https://docs.microsoft.com/en-us/windows/win32/api/usp10/nf-usp10-scriptitemize + // and https://bugzilla.mozilla.org/show_bug.cgi?id=366643 which was a similar bug + // in Mozilla. The MSDN docs have been updated since the Mozilla bug to make this clear + long pItems = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, (1 + MAX_ITEM) * SCRIPT_ITEM.sizeof); if (pItems == 0) SWT.error(SWT.ERROR_NO_HANDLES); int[] pcItems = new int[1]; char[] chars = new char[length]; segmentsText.getChars(0, length, chars, 0); + // enable font ligatures + scriptControl.fMergeNeutralItems = true; + /* + * With font ligatures enabled: CJK characters are not rendered properly when + * used in Java comments, workaround is to avoid ligatures between ascii and + * non-ascii chars. For more details refer bug 565526 + */ + for (int i = 0, latestNeutralIndex = -2, latestUnicodeIndex = -2; i < length; i++) { + char c = chars[i]; + + if (c >= ' ' && c <= '~' && !Character.isAlphabetic(c)) { + latestNeutralIndex = i; + } else if (c > 255) { + latestUnicodeIndex = i; + } else { + continue; + } + + // If the latest neutral and unicode characters are adjacent + if (Math.abs(latestNeutralIndex - latestUnicodeIndex) == 1) { + // Change the neutral into a non-neutral alphabet character + chars[latestNeutralIndex] = 'A'; + } + } + OS.ScriptItemize(chars, length, MAX_ITEM, scriptControl, scriptState, pItems, pcItems); + // if (hr == E_OUTOFMEMORY) //TODO handle it StyleItem[] runs = merge(pItems, pcItems[0]); @@ -2749,14 +2794,19 @@ StyleItem[] itemize () { /* * Merge styles ranges and script items */ -StyleItem[] merge (long /*int*/ items, int itemCount) { +StyleItem[] merge (long items, int itemCount) { if (styles.length > stylesCount) { StyleItem[] newStyles = new StyleItem[stylesCount]; System.arraycopy(styles, 0, newStyles, 0, stylesCount); styles = newStyles; } - int count = 0, start = 0, end = segmentsText.length(), itemIndex = 0, styleIndex = 0; - StyleItem[] runs = new StyleItem[itemCount + stylesCount]; + final int end = segmentsText.length(); + int start = 0, itemIndex = 0, styleIndex = 0; + /* + * Maximum size of runs is each itemized item + each style needing its own run + + * enough space for splitting runs that are too long. + */ + List<StyleItem> runs = new ArrayList<>(itemCount + stylesCount + (end + MAX_RUN_LENGTH - 1) / MAX_RUN_LENGTH); SCRIPT_ITEM scriptItem = new SCRIPT_ITEM(); int itemLimit = -1; int nextItemIndex = 0; @@ -2767,7 +2817,7 @@ StyleItem[] merge (long /*int*/ items, int itemCount) { StyleItem item = new StyleItem(); item.start = start; item.style = styles[styleIndex].style; - runs[count++] = item; + runs.add(item); OS.MoveMemory(scriptItem, items + itemIndex * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof); item.analysis = scriptItem.a; scriptItem.a = new SCRIPT_ANALYSIS(); @@ -2794,6 +2844,13 @@ StyleItem[] merge (long /*int*/ items, int itemCount) { OS.MoveMemory(scriptItem, items + nextItemIndex * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof); itemLimit = scriptItem.iCharPos; } + + /* + * This block merges a bunch of tabs or non-complex scripts into a single item + * run. This is done so that less item runs are needed and is used when there + * could be a performance penalty because of too many runs. + * The tabs need to be "restored", see computeRuns + */ if (nextItemIndex < itemCount && merge) { if (!item.lineBreak) { OS.MoveMemory(sp, device.scripts[item.analysis.eScript], SCRIPT_PROPERTIES.sizeof); @@ -2814,20 +2871,28 @@ StyleItem[] merge (long /*int*/ items, int itemCount) { } } + boolean mayNeedSplit = true; int styleLimit = translateOffset(styles[styleIndex + 1].start); if (styleLimit <= itemLimit) { - styleIndex++; - start = styleLimit; - if (start < itemLimit && 0 < start && start < end) { - char pChar = segmentsText.charAt(start - 1); - char tChar = segmentsText.charAt(start); - if (Character.isLetter(pChar) && Character.isLetter(tChar)) { - item.analysis.fLinkAfter = true; - linkBefore = true; + int runLen = styleLimit - start; + if (runLen < MAX_RUN_LENGTH) { + mayNeedSplit = false; + styleIndex++; + start = styleLimit; + if (start < itemLimit && 0 < start && start < end) { + char pChar = segmentsText.charAt(start - 1); + char tChar = segmentsText.charAt(start); + if (Character.isLetter(pChar) && Character.isLetter(tChar)) { + item.analysis.fLinkAfter = true; + linkBefore = true; + } } } } - if (itemLimit <= styleLimit) { + int runLen = itemLimit - start; + if (mayNeedSplit && runLen > MAX_RUN_LENGTH) { + start += splitLongRun(item); + } else if (itemLimit <= styleLimit) { itemIndex = nextItemIndex; start = itemLimit; itemLimit = -1; @@ -2838,13 +2903,43 @@ StyleItem[] merge (long /*int*/ items, int itemCount) { item.start = end; OS.MoveMemory(scriptItem, items + itemCount * SCRIPT_ITEM.sizeof, SCRIPT_ITEM.sizeof); item.analysis = scriptItem.a; - runs[count++] = item; - if (runs.length != count) { - StyleItem[] result = new StyleItem[count]; - System.arraycopy(runs, 0, result, 0, count); - return result; + runs.add(item); + return runs.toArray(StyleItem[]::new); +} + +/** + * Use OS.ScriptBreak to identify where in the run it is safe to split a character. + * @param run the run to split + * @return how many characters into the run is the best place to split + */ +int splitLongRun(StyleItem run) { + run.length = MAX_RUN_LENGTH; + breakRun(run); + SCRIPT_LOGATTR logAttr = new SCRIPT_LOGATTR(); + int best = MAX_RUN_LENGTH; + for (int i = MAX_RUN_LENGTH - 1; i >= MAX_RUN_LENGTH - MAX_SEARCH_RUN_BREAK; i--) { + int memoryIndex = i * SCRIPT_LOGATTR.sizeof; + if (memoryIndex + SCRIPT_LOGATTR.sizeof > run.pslaAllocSize) { + throw new IndexOutOfBoundsException(); + } + OS.MoveMemory(logAttr, run.psla + memoryIndex, SCRIPT_LOGATTR.sizeof); + if (logAttr.fSoftBreak || logAttr.fWhiteSpace || logAttr.fWordStop) { + best = i; + break; + } } - return runs; + + /* + * In the improbable case that the entire run has nowhere to split we need to + * make sure that at least we don't split a surrogate pair. This can happen + * if ScriptBreak above identifies nowhere that can be split, and the last + * character is the first part of a surrogate pair. + */ + if (Character.isHighSurrogate(segmentsText.charAt(run.start + best - 1))) { + best--; + } + + return best; } /* @@ -3414,7 +3509,7 @@ void setWrapIndentInPixels (int wrapIndent) { this.wrapIndent = wrapIndent; } -boolean shape (long /*int*/ hdc, StyleItem run, char[] chars, int[] glyphCount, int maxGlyphs, SCRIPT_PROPERTIES sp) { +boolean shape (long hdc, StyleItem run, char[] chars, int[] glyphCount, int maxGlyphs, SCRIPT_PROPERTIES sp) { boolean useCMAPcheck = !sp.fComplex && !run.analysis.fNoGlyphIndex; if (useCMAPcheck) { short[] glyphs = new short[chars.length]; @@ -3422,16 +3517,23 @@ boolean shape (long /*int*/ hdc, StyleItem run, char[] chars, int[] glyphCount, if (run.psc != 0) { OS.ScriptFreeCache(run.psc); glyphCount[0] = 0; - OS.MoveMemory(run.psc, new long /*int*/ [1], C.PTR_SIZEOF); + OS.MoveMemory(run.psc, new long [1], C.PTR_SIZEOF); } return false; } } - int hr = OS.ScriptShape(hdc, run.psc, chars, chars.length, maxGlyphs, run.analysis, run.glyphs, run.clusters, run.visAttrs, glyphCount); - run.glyphCount = glyphCount[0]; - if (useCMAPcheck) return true; + int scriptShaprHr = OS.ScriptShape(hdc, run.psc, chars, chars.length, maxGlyphs, run.analysis, run.glyphs, + run.clusters, run.visAttrs, glyphCount); + if (scriptShaprHr == OS.S_OK) { + run.glyphCount = glyphCount[0]; + if (useCMAPcheck) return true; - if (hr != OS.USP_E_SCRIPT_NOT_IN_FONT) { + /* + * scriptShapeHr could have been OS.USP_E_SCRIPT_NOT_IN_FONT which indicates + * the whole run doesn't work with the font. The rest of this method verifies that + * none of the individual glyphs are missing an entry in the font. + * The fallback is to try other fonts (See caller) + */ if (run.analysis.fNoGlyphIndex) return true; SCRIPT_FONTPROPERTIES fp = new SCRIPT_FONTPROPERTIES (); fp.cBytes = SCRIPT_FONTPROPERTIES.sizeof; @@ -3447,14 +3549,14 @@ boolean shape (long /*int*/ hdc, StyleItem run, char[] chars, int[] glyphCount, if (run.psc != 0) { OS.ScriptFreeCache(run.psc); glyphCount[0] = 0; - OS.MoveMemory(run.psc, new long /*int*/ [1], C.PTR_SIZEOF); + OS.MoveMemory(run.psc, new long [1], C.PTR_SIZEOF); } run.glyphCount = 0; return false; } -long /*int*/ createMetafileWithChars(long /*int*/ hdc, long /*int*/ hFont, char[] chars, int charCount) { - long /*int*/ hHeap = OS.GetProcessHeap(); +long createMetafileWithChars(long hdc, long hFont, char[] chars, int charCount) { + long hHeap = OS.GetProcessHeap(); /* * The native string must remain unchanged between ScriptStringAnalyse and ScriptStringOut. @@ -3466,12 +3568,12 @@ long /*int*/ createMetafileWithChars(long /*int*/ hdc, long /*int*/ hFont, char[ * temporary native string which will be deallocated upon return from ScriptStringAnalyse. */ int nativeStringSize = charCount * Character.BYTES; - long /*int*/ nativeString = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, nativeStringSize); + long nativeString = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, nativeStringSize); OS.MoveMemory (nativeString, chars, nativeStringSize); - long /*int*/ ssa = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, OS.SCRIPT_STRING_ANALYSIS_sizeof()); - long /*int*/ metaFileDc = OS.CreateEnhMetaFile(hdc, null, null, null); - long /*int*/ oldMetaFont = OS.SelectObject(metaFileDc, hFont); + long ssa = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, OS.SCRIPT_STRING_ANALYSIS_sizeof()); + long metaFileDc = OS.CreateEnhMetaFile(hdc, null, null, null); + long oldMetaFont = OS.SelectObject(metaFileDc, hFont); int flags = OS.SSA_METAFILE | OS.SSA_FALLBACK | OS.SSA_GLYPHS | OS.SSA_LINK; if (OS.ScriptStringAnalyse(metaFileDc, nativeString, charCount, 0, -1, flags, 0, null, null, 0, 0, 0, ssa) == OS.S_OK) { OS.ScriptStringOut(ssa, 0, 0, 0, null, 0, 0, false); @@ -3486,7 +3588,7 @@ long /*int*/ createMetafileWithChars(long /*int*/ hdc, long /*int*/ hFont, char[ /* * Generate glyphs for one Run. */ -void shape (final long /*int*/ hdc, final StyleItem run) { +void shape (final long hdc, final StyleItem run) { if (run.lineBreak) return; if (run.glyphs != 0) return; final int[] buffer = new int[1]; @@ -3494,7 +3596,7 @@ void shape (final long /*int*/ hdc, final StyleItem run) { segmentsText.getChars(run.start, run.start + run.length, chars, 0); final int maxGlyphs = (chars.length * 3 / 2) + 16; - long /*int*/ hHeap = OS.GetProcessHeap(); + long hHeap = OS.GetProcessHeap(); run.glyphs = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2); if (run.glyphs == 0) SWT.error(SWT.ERROR_NO_HANDLES); run.clusters = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2); @@ -3514,8 +3616,8 @@ void shape (final long /*int*/ hdc, final StyleItem run) { } } if (!shapeSucceed) { - long /*int*/ hFont = OS.GetCurrentObject(hdc, OS.OBJ_FONT); - long /*int*/ newFont = 0; + long hFont = OS.GetCurrentObject(hdc, OS.OBJ_FONT); + long newFont = 0; /* * Bug in Uniscribe. In some version of Uniscribe, ScriptStringAnalyse crashes * when the character array is too long. The fix is to limit the size of character @@ -3534,10 +3636,10 @@ void shape (final long /*int*/ hdc, final StyleItem run) { } } if (count > 0) { - long /*int*/ metaFile = createMetafileWithChars(hdc, hFont, sampleChars, count); + long metaFile = createMetafileWithChars(hdc, hFont, sampleChars, count); final EMREXTCREATEFONTINDIRECTW emr = new EMREXTCREATEFONTINDIRECTW(); class MetaFileEnumProc { - long /*int*/ metaFileEnumProc (long /*int*/ hDC, long /*int*/ table, long /*int*/ record, long /*int*/ nObj, long /*int*/ lpData) { + long metaFileEnumProc (long hDC, long table, long record, long nObj, long lpData) { OS.MoveMemory(emr.emr, record, EMR.sizeof); switch (emr.emr.iType) { case OS.EMR_EXTCREATEFONTINDIRECTW: @@ -3554,9 +3656,7 @@ void shape (final long /*int*/ hdc, final StyleItem run) { boolean compilerWarningWorkaround = false; if (compilerWarningWorkaround) object.metaFileEnumProc(0, 0, 0, 0, 0); Callback callback = new Callback(object, "metaFileEnumProc", 5); - long /*int*/ address = callback.getAddress(); - if (address == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); - OS.EnumEnhMetaFile(0, metaFile, address, 0, null); + OS.EnumEnhMetaFile(0, metaFile, callback.getAddress(), 0, null); OS.DeleteEnhMetaFile(metaFile); callback.dispose(); newFont = OS.CreateFontIndirect(emr.elfw.elfLogFont); @@ -3573,7 +3673,7 @@ void shape (final long /*int*/ hdc, final StyleItem run) { if (index > 0) { StyleItem pRun = allRuns[index - 1]; if (pRun.analysis.eScript == run.analysis.eScript) { - long /*int*/ pFont = getItemFont(pRun); + long pFont = getItemFont(pRun); LOGFONT logFont = new LOGFONT (); OS.GetObject(pFont, LOGFONT.sizeof, logFont); newFont = OS.CreateFontIndirect(logFont); @@ -3585,7 +3685,7 @@ void shape (final long /*int*/ hdc, final StyleItem run) { if (nRun.analysis.eScript == run.analysis.eScript) { OS.SelectObject(hdc, getItemFont(nRun)); shape(hdc, nRun); - long /*int*/ nFont = getItemFont(nRun); + long nFont = getItemFont(nRun); LOGFONT logFont = new LOGFONT (); OS.GetObject(nFont, LOGFONT.sizeof, logFont); newFont = OS.CreateFontIndirect(logFont); @@ -3614,19 +3714,16 @@ void shape (final long /*int*/ hdc, final StyleItem run) { } } if (!shapeSucceed) { - if (mLangFontLink2 != 0) { - long /*int*/[] hNewFont = new long /*int*/[1]; + if (mLangFontLink2 != null) { + long [] hNewFont = new long [1]; int[] dwCodePages = new int[1], cchCodePages = new int[1]; - /* GetStrCodePages() */ - OS.VtblCall(4, mLangFontLink2, chars, chars.length, 0, dwCodePages, cchCodePages); - /* MapFont() */ - if (OS.VtblCall(10, mLangFontLink2, hdc, dwCodePages[0], chars[0], hNewFont) == OS.S_OK) { + mLangFontLink2.GetStrCodePages(chars, chars.length, 0, dwCodePages, cchCodePages); + if (mLangFontLink2.MapFont(hdc, dwCodePages[0], chars[0], hNewFont) == OS.S_OK) { LOGFONT logFont = new LOGFONT (); OS.GetObject(hNewFont[0], LOGFONT.sizeof, logFont); - /* ReleaseFont() */ - OS.VtblCall(8, mLangFontLink2, hNewFont[0]); - long /*int*/ mLangFont = OS.CreateFontIndirect(logFont); - long /*int*/ oldFont = OS.SelectObject(hdc, mLangFont); + mLangFontLink2.ReleaseFont(hNewFont[0]); + long mLangFont = OS.CreateFontIndirect(logFont); + long oldFont = OS.SelectObject(hdc, mLangFont); if (shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs, sp)) { run.fallbackFont = mLangFont; } else { |