UGlyphRun BuildGlyphRun(int start, int length, UStyleRange currentStyle, UText text, BreakType bt) { FontClass useFont = Translate(currentStyle == null ? text.font : currentStyle.fontOverride ?? text.font); var gr = new UGlyphRun() { startPosition = start, runLength = length, breakingType = bt, drawStyle = currentStyle, charSizes = new Size[length] }; float tw = 0; gr.content = gr.runLength > 0 ? text.text.Substring(gr.startPosition, gr.runLength) : ""; gr.runSize = MeasureString(text, gr.content, useFont); if (bt == BreakType.word) { // handle space spacing ;) String ltx = gr.startPosition > 0 ? text.text.Substring(gr.startPosition - 1, 1) : ""; String rtx = gr.startPosition + length < text.text.Length ? text.text.Substring(length, 1) : ""; var s1 = MeasureString(text, ltx + gr.content + rtx, useFont); var s2 = MeasureString(text, ltx + rtx, useFont); gr.runSize.width = s1.width - s2.width; } for (int i = 0; i < gr.content.Length; i++) { gr.charSizes[i] = MeasureString(text, gr.content.Substring(i, 1), useFont); tw += gr.charSizes[i].width; } float corr = gr.runSize.width / tw; for (int i = 0; i < gr.content.Length; i++) { gr.charSizes[i].width *= corr; } return(gr); }
// FIXME Cache!! (most importantly) public UTextGlyphingInfo GetTextInfo(UText text) { // gotta do a line or end before we can decide the char tops (baseline aligned, presumably...) float currX = 0, currY = 0, maxGlyphHeight = 0; int lglst = 0; // last glyphrun line start int lastWordBreak = -1; // last wordbreaking glyph on the current line int currLine = 0; UTextGlyphingInfo ret = new UTextGlyphingInfo(); // begin on assumption we're top left align..then correct after UGlyphRun lastGr = null; foreach (var gr in GetGlyphRuns(text)) { // tracking max height for the line baselineing maxGlyphHeight = Math.Max(gr.runSize.height, maxGlyphHeight); // whichever way, this line is this many chars longer // Potential runs include words, spaces and linebreaks, and font breaks if (gr.breakingType == BreakType.line) {//LineBreaking // add the glyphrun, resolve info for the line, and begin on new line! ret.glyphRuns.Add(new UGlyphRunLayoutInfo() { lineNumber = currLine, location = new Point(currX, 0), // dunno about y position yet run = gr }); currY += maxGlyphHeight; maxGlyphHeight = 0; lastWordBreak = -1; float xAlighnShifty = GetShift(text.width, (currX + gr.runSize.width), text.halign); currX = 0; // resolving the glyphruns of this line do { var igr = ret.glyphRuns[lglst]; igr.location = new Point(igr.location.X + xAlighnShifty, currY - igr.run.runSize.height); } while (++lglst < ret.glyphRuns.Count); // begin a new line currLine++; } else if (lastWordBreak > -1 && currX + gr.runSize.width > text.width && text.wrapped) {// WordWrapping // Must define the concept of glyph groups here. Those between line and/or word breaks. // The whole of such a group must be broken. We need to define the breaking glyph. currY += maxGlyphHeight; maxGlyphHeight = 0; currX = 0; // #1 Is there a word break previous to this glyphrun on this line? // then put all glyphs following that on the next line. for (int i = lastWordBreak + 1; i < ret.glyphRuns.Count; i++) { ret.glyphRuns[i].lineNumber++; ret.glyphRuns[i].location = new Point(currX, 0); } lastWordBreak = -1; currLine++; // Is this glyphrun a wordbreak? who cares, next iteration will take care via #1. // Not wordbreak? no prev worbreak? who cares, carry on. ret.glyphRuns.Add(new UGlyphRunLayoutInfo() { lineNumber = currLine, location = new Point(currX, 0), // dunno about y position yet run = gr }); // resolving the glyphruns of the last line do { var igr = ret.glyphRuns[lglst]; igr.location = new Point(igr.location.X, currY - igr.run.runSize.height); } while (++lglst <= lastWordBreak); lastGr = gr; } else {// Buisness as Normal // add glyphrun, increment currX ret.glyphRuns.Add(new UGlyphRunLayoutInfo() { lineNumber = currLine, location = new Point(currX, 0), run = gr }); if (gr.breakingType == BreakType.word) { lastWordBreak = ret.glyphRuns.Count - 1; } currX += gr.runSize.width; } } currY += maxGlyphHeight; maxGlyphHeight = 0; lastWordBreak = -1; float lastXShift = GetShift(text.width, (currX + (lastGr == null ? 0 :lastGr.runSize.width)), text.halign); currX = 0; // resolving the glyphruns of the final line for (; lglst < ret.glyphRuns.Count; lglst++) { var igr = ret.glyphRuns[lglst]; igr.location.Y = currY - igr.run.runSize.height; igr.location.X += lastXShift; } float yAlignShifty = GetShift(text.height, currY, text.valign); // assign the linelengths to textinfo and do y alignment int cl = 0; int cc = 0; int cnl = 0; float cx = 0, cy = 0, maxy = 0, maxx = 0; for (int i = 0; i < ret.glyphRuns.Count; i++) { var gri = ret.glyphRuns[i]; gri.location.Y += yAlignShifty; if (gri.lineNumber > cl) { // add lineinfo (and reset counters) cl = gri.lineNumber; ret.lineLengths.Add(cc); ret.newLineLengths.Add(cl); ret.lineSizes.Add(new Size(cx, maxy)); cc = cnl = 0; cy += maxy; maxx = Math.Max(maxx, cx); maxy = cx = 0; } // Continue adding lineinfo data cc += gri.run.runLength; cx += gri.run.runSize.width; if (gri.run.breakingType == BreakType.line) { cnl += gri.run.runLength; } maxy = Math.Max(gri.run.runSize.height, maxy); } ret.minSize = new Size(maxx, cy); return(ret); }