private void cmdMeasureString_Click(object sender, EventArgs e) { //How to measure user's string... //this demostrate step-by-step //similar to ... selectedTextPrinter.DrawString(printTextBuffer, x_pos, y_pos); string str = txtInputChar.Text; // Typeface typeface = _basicOptions.Typeface; float fontSizeInPoints = _basicOptions.FontSizeInPoints; var layout = new Typography.TextLayout.GlyphLayout(); layout.Typeface = typeface; layout.ScriptLang = _basicOptions.ScriptLang; layout.PositionTechnique = _basicOptions.PositionTech; layout.EnableLigature = false;// true layout.EnableComposition = true; //3. //3.1 : if you want GlyphPlanList too. //var resultGlyphPlanList = new Typography.TextLayout.GlyphPlanList(); //Typography.TextLayout.MeasuredStringBox box = layout.LayoutAndMeasureString(str.ToCharArray(), 0, str.Length, _basicOptions.FontSizeInPoints, resultGlyphPlanList); //or //3.2 : only MeasuredStringBox Typography.TextLayout.MeasuredStringBox box = layout.LayoutAndMeasureString( str.ToCharArray(), 0, str.Length, fontSizeInPoints); this.lblStringSize.Text = "measure (W,H)= (" + box.width.ToString() + "," + (box.AscendingInPx - box.DescendingInPx) + ") px"; }
public static MeasuredStringBox LayoutAndMeasureString( this GlyphLayout glyphLayout, char[] textBuffer, int startAt, int len, float fontSizeInPoints, bool snapToGrid = true) { //1. unscale layout, in design unit glyphLayout.Layout(textBuffer, startAt, len); //2. scale to specific font size Typeface typeface = glyphLayout.Typeface; float pxscale = typeface.CalculateScaleToPixelFromPointSize(fontSizeInPoints); //.... GenerateScaledGlyphPlans( glyphLayout, pxscale, snapToGrid, out float scaled_accumX); return(new MeasuredStringBox( scaled_accumX, typeface.Ascender * pxscale, typeface.Descender * pxscale, typeface.LineGap * pxscale, Typography.OpenFont.Extensions.TypefaceExtensions.CalculateRecommendLineSpacing(typeface) * pxscale)); }
public static void MeasureString( this GlyphLayout glyphLayout, char[] textBuffer, int startAt, int len, out MeasuredStringBox strBox, float scale = 1) { //TODO: consider extension method List <GlyphPlan> outputGlyphPlans = glyphLayout._myGlyphPlans; outputGlyphPlans.Clear(); glyphLayout.Layout(textBuffer, startAt, len, outputGlyphPlans); // int j = outputGlyphPlans.Count; Typeface currentTypeface = glyphLayout.Typeface; if (j == 0) { //not scale strBox = new MeasuredStringBox(0, currentTypeface.Ascender * scale, currentTypeface.Descender * scale, currentTypeface.LineGap * scale); } //get last one GlyphPlan lastOne = outputGlyphPlans[j - 1]; strBox = new MeasuredStringBox((lastOne.x + lastOne.advX) * scale, currentTypeface.Ascender * scale, currentTypeface.Descender * scale, currentTypeface.LineGap * scale); }
/// <summary> /// read latest layout output /// </summary> /// <param name="glyphLayout"></param> /// <param name="readDel"></param> public static void ReadOutput(this GlyphLayout glyphLayout, GlyphReadOutputDelegate readDel) { Typeface typeface = glyphLayout.Typeface; List <GlyphPos> glyphPositions = glyphLayout._glyphPositions; //3.read back int finalGlyphCount = glyphPositions.Count; int cx = 0; short cy = 0; PositionTechnique posTech = glyphLayout.PositionTechnique; ushort prev_index = 0; for (int i = 0; i < finalGlyphCount; ++i) { GlyphPos glyphPos = glyphPositions[i]; //---------------------------------- switch (posTech) { default: throw new NotSupportedException(); case PositionTechnique.None: readDel(i, new GlyphPlan(glyphPos.glyphIndex, cx, cy, glyphPos.advWidth)); break; case PositionTechnique.OpenFont: readDel(i, new GlyphPlan( glyphPos.glyphIndex, cx + glyphPos.xoffset, (short)(cy + glyphPos.yoffset), glyphPos.advWidth)); break; case PositionTechnique.Kerning: if (i > 0) { cx += typeface.GetKernDistance(prev_index, glyphPos.glyphIndex); } readDel(i, new GlyphPlan( prev_index = glyphPos.glyphIndex, cx, cy, glyphPos.advWidth)); break; } cx += glyphPos.advWidth; } }
/// <summary> /// read latest layout output /// </summary> /// <param name="glyphLayout"></param> /// <param name="readDel"></param> public static void ReadOutput(this GlyphLayout glyphLayout, GlyphReadOutputDelegate readDel) { throw new NotSupportedException(); //Typeface typeface = glyphLayout.Typeface; //List<GlyphPos> glyphPositions = glyphLayout._glyphPositions; ////3.read back //int finalGlyphCount = glyphPositions.Count; //int cx = 0; //short cy = 0; //PositionTechnique posTech = glyphLayout.PositionTechnique; //ushort prev_index = 0; //for (int i = 0; i < finalGlyphCount; ++i) //{ // GlyphPos glyphPos = glyphPositions[i]; // //---------------------------------- // switch (posTech) // { // default: throw new NotSupportedException(); // case PositionTechnique.None: // readDel(i, new GlyphPlan(glyphPos.glyphIndex, cx, cy, glyphPos.AdvWidth)); // break; // case PositionTechnique.OpenFont: // readDel(i, new GlyphPlan( // glyphPos.glyphIndex, // cx + glyphPos.xoffset, // (short)(cy + glyphPos.yoffset), // glyphPos.AdvWidth)); // break; // case PositionTechnique.Kerning: // if (i > 0) // { // cx += typeface.GetKernDistance(prev_index, glyphPos.glyphIndex); // } // readDel(i, new GlyphPlan( // prev_index = glyphPos.glyphIndex, // cx, // cy, // glyphPos.AdvWidth)); // break; // } // cx += glyphPos.AdvWidth; //} }
public static void GenerateGlyphPlans(this GlyphLayout glyphLayout, char[] textBuffer, int startAt, int len, List <GlyphPlan> userGlyphPlanList, List <UserCharToGlyphIndexMap> charToGlyphMapList) { //generate glyph plan based on its current setting glyphLayout.Layout(textBuffer, startAt, len, userGlyphPlanList); //note that we print to userGlyphPlanList //---------------- //3. user char to glyph index map if (charToGlyphMapList != null) { glyphLayout.ReadOutput(charToGlyphMapList); } }
static void GenerateScaledGlyphPlans(this GlyphLayout glyphLayout, float pxscale, bool snapToGrid, out float accumW) { //user can implement this with some 'PixelScaleEngine' IGlyphPositions glyphPositions = glyphLayout.ResultUnscaledGlyphPositions; accumW = 0; //acummulate Width if (snapToGrid) { int finalGlyphCount = glyphPositions.Count; for (int i = 0; i < finalGlyphCount; ++i) { short offsetX, offsetY, advW; //all from pen-pos ushort glyphIndex = glyphPositions.GetGlyph(i, out ushort input_offset, out offsetX, out offsetY, out advW); float scaled_advW = (short)Math.Round(advW * pxscale); accumW += scaled_advW; } } else { //not snap to grid //scaled but not snap to grid int finalGlyphCount = glyphPositions.Count; for (int i = 0; i < finalGlyphCount; ++i) { short offsetX, offsetY, advW; //all from pen-pos ushort glyphIndex = glyphPositions.GetGlyph(i, out ushort input_offset, out offsetX, out offsetY, out advW); accumW += advW * pxscale; } } }
/// <summary> /// read latest layout output into outputGlyphPlanList /// </summary> public static void ReadOutput(this GlyphLayout glyphLayout, List <GlyphPlan> outputGlyphPlanList) { GlyphPosStream glyphPositions = glyphLayout._glyphPositions; //from opentype's layout result, int finalGlyphCount = glyphPositions.Count; //------------------------ IPixelScaleLayout pxscaleLayout = glyphLayout.PxScaleLayout; if (pxscaleLayout != null) { //use custom pixel scale layout engine pxscaleLayout.SetFont(glyphLayout.Typeface, glyphLayout.FontSizeInPoints); pxscaleLayout.Layout(glyphPositions, outputGlyphPlanList); } else { //default scale float pxscale = glyphLayout.PixelScale; double cx = 0; short cy = 0; for (int i = 0; i < finalGlyphCount; ++i) { GlyphPos glyph_pos = glyphPositions[i]; float advW = glyph_pos.advanceW * pxscale; float exact_x = (float)(cx + glyph_pos.OffsetX * pxscale); float exact_y = (float)(cy + glyph_pos.OffsetY * pxscale); outputGlyphPlanList.Add(new GlyphPlan( glyph_pos.glyphIndex, exact_x, exact_y, advW)); cx += advW; } } }
public EditableTextBlockLayoutEngine() { _textBlockLexer = new TextBlockLexer(); _glyphLayout = new GlyphLayout(); FontSizeInPts = 10; }
//static void ConcatMeasureBox(ref float accumW, ref float accumH, ref MeasuredStringBox measureBox) //{ // accumW += measureBox.width; // float h = measureBox.CalculateLineHeight(); // if (h > accumH) // { // accumH = h; // } //} public static MeasuredStringBox LayoutAndMeasureString( this GlyphLayout glyphLayout, char[] textBuffer, int startAt, int len, float fontSizeInPoints, float limitW = -1,//-1 unlimit scaled width (px) bool snapToGrid = true) { //1. unscale layout, in design unit glyphLayout.Layout(textBuffer, startAt, len); //2. scale to specific font size Typeface typeface = glyphLayout.Typeface; float pxscale = typeface.CalculateScaleToPixelFromPointSize(fontSizeInPoints); //.... float scaled_accumX = 0; if (limitW < 0) { //no limit scaled_accumX = MeasureGlyphPlans( glyphLayout, pxscale, snapToGrid); return(new MeasuredStringBox( scaled_accumX, typeface.Ascender, typeface.Descender, typeface.LineGap, typeface.ClipedAscender, typeface.ClipedDescender, pxscale)); } else if (limitW > 0) { scaled_accumX = MeasureGlyphPlanWithLimitWidth( glyphLayout, pxscale, limitW, snapToGrid, out int stopAtChar); var mstrbox = new MeasuredStringBox( scaled_accumX, typeface.Ascender, typeface.Descender, typeface.LineGap, typeface.ClipedAscender, typeface.ClipedDescender, pxscale); mstrbox.StopAt = (ushort)stopAtChar; return(mstrbox); } else { return(new MeasuredStringBox( 0, typeface.Ascender, typeface.Descender, typeface.LineGap, typeface.ClipedAscender, typeface.ClipedDescender, pxscale)); } }
static float MeasureGlyphPlanWithLimitWidth(this GlyphLayout glyphLayout, float pxscale, float limitWidth, bool snapToGrid, out int stopAtGlyphIndex) { //user can implement this with some 'PixelScaleEngine' IGlyphPositions glyphPositions = glyphLayout.ResultUnscaledGlyphPositions; float accumW = 0; //acummulate Width stopAtGlyphIndex = 0; if (snapToGrid) { int finalGlyphCount = glyphPositions.Count; for (int i = 0; i < finalGlyphCount; ++i) { //all from pen-pos ushort glyphIndex = glyphPositions.GetGlyph(i, out ushort input_offset, out short offsetX, out short offsetY, out short advW); stopAtGlyphIndex = i; //*** // short w = (short)Math.Round(advW * pxscale); if (accumW + w > limitWidth) { //stop break; } else { accumW += w; } } } else { //not snap to grid //scaled but not snap to grid int finalGlyphCount = glyphPositions.Count; for (int i = 0; i < finalGlyphCount; ++i) { //all from pen-pos ushort glyphIndex = glyphPositions.GetGlyph(i, out ushort input_offset, out short offsetX, out short offsetY, out short advW); stopAtGlyphIndex = i; //*** float w = advW * pxscale; if (accumW + w > limitWidth) { //stop break; } else { accumW += w; } } } return(accumW); ////measure string //if (str.Length < 1) //{ // charFitWidth = 0; //} //_reusableMeasureBoxList.Clear(); //reset //float pxscale = _currentTypeface.CalculateScaleToPixelFromPointSize(_fontSizeInPts); ////NOET:at this moment, simple operation ////may not be simple... ////------------------- ////input string may contain more than 1 script lang ////user can parse it by other parser ////but in this code, we use our Typography' parser ////------------------- ////user must setup the CustomBreakerBuilder before use //int cur_startAt = startAt; //float accumW = 0; //float acc_x = 0;//accum_x //float acc_y = 0;//accum_y //float g_x = 0; //float g_y = 0; //float x = 0; //float y = 0; //foreach (Typography.TextLayout.BreakSpan breakSpan in BreakToLineSegments(str, startAt, len)) //{ // //measure string at specific px scale // _glyphLayout.Layout(str, breakSpan.startAt, breakSpan.len); // // // _reusableGlyphPlanList.Clear(); // _glyphLayout.GenerateUnscaledGlyphPlans(_reusableGlyphPlanList); // //measure ... // //measure each glyph // //limit at specific width // int glyphCount = _reusableGlyphPlanList.Count; // for (int i = 0; i < glyphCount; ++i) // { // UnscaledGlyphPlan glyphPlan = _reusableGlyphPlanList[i]; // float ngx = acc_x + (float)Math.Round(glyphPlan.OffsetX * pxscale); // float ngy = acc_y + (float)Math.Round(glyphPlan.OffsetY * pxscale); // //NOTE: // // -glyphData.TextureXOffset => restore to original pos // // -glyphData.TextureYOffset => restore to original pos // //-------------------------- // g_x = (float)(x + (ngx)); //ideal x // g_y = (float)(y + (ngy)); // float g_w = (float)Math.Round(glyphPlan.AdvanceX * pxscale); // acc_x += g_w; // //g_x = (float)Math.Round(g_x); // g_y = (float)Math.Floor(g_y); // float right = g_x + g_w; // if (right >= accumW) // { // //stop here at this glyph // charFit = i - 1; // //TODO: review this // charFitWidth = (int)System.Math.Round(accumW); // return; // } // else // { // accumW = right; // } // } //} //charFit = 0; //charFitWidth = 0; }
public static void Layout(this GlyphLayout glyphLayout, char[] str, int startAt, int len, GlyphReadOutputDelegate readDel) { glyphLayout.Layout(str, startAt, len); glyphLayout.ReadOutput(readDel); }
public static void Layout(this GlyphLayout glyphLayout, char[] str, int startAt, int len, List <GlyphPlan> outputGlyphList) { glyphLayout.Layout(str, startAt, len); glyphLayout.ReadOutput(outputGlyphList); }
/// <summary> /// read UserCharToGlyphIndexMap from latest layout output /// </summary> /// <param name="glyphLayout"></param> /// <param name="outputGlyphPlanList"></param> public static void ReadOutput(this GlyphLayout glyphLayout, List <UserCharToGlyphIndexMap> outputGlyphPlanList) { outputGlyphPlanList.AddRange(glyphLayout._inputGlyphs._mapUserCharToGlyphIndics); }
/// <summary> /// read latest layout output into outputGlyphPlanList /// </summary> /// <param name="glyphLayout"></param> /// <param name="outputGlyphPlanList"></param> public static void dbugReadOutput(this GlyphLayout glyphLayout, List <UserCharToGlyphIndexMap> outputGlyphPlanList) { //TODO: review here outputGlyphPlanList.AddRange(glyphLayout._inputGlyphs._mapUserCharToGlyphIndices); }