查找文本的确切边界 [英] Finding the exact bounds of text

查看:26
本文介绍了查找文本的确切边界的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要知道一段文本的确切边界——相当于

我需要该矩形的边正对着8"的黑色像素.(在这种特殊情况下,widthmaxIntrinsicWidth 给出与 minIntrinsicWidth 相同的值;此外,preferredLineHeight 给出相同的值值作为 height.)

  • ParagraphgetBoxesForRange -- 与 TextPainter 的结果基本相同.

  • FittedBox -- 也会留下空格,无论 BoxFit 枚举值如何.我使用这个答案作为起点.

  • TextPainter 代码如下.(我意识到这是低效的;在获得所需的边界框之前我不在乎.我使用场景/图片/等希望在较低级别找到合适的功能.)

    import 'dart:ui';导入 'dart:typed_data';导入'包:颤振/painting.dart';const black = Color(0xff000000);const 白色 = 颜色(0xffffffff);最终的身份转换 = 新的 Float64List(16)..[0]= 1.0..[5]= 1.0..[10]= 1.0..[15]= 1.0;TextPainter createTextPainter() {返回新的 TextPainter(文本:新的文本跨度(文字:'8',样式:新文本样式(颜色:黑色,字体大小:200.0,fontFamily: "Roboto",),),文本方向:TextDirection.ltr,)..布局();}void drawBackground(Canvas canvas, Rect screen) {canvas.drawRect(屏幕,新油漆()..颜色 = 白色..style = PaintingStyle.fill);}图片创建图片(){最终记录器 = new PictureRecorder();最终屏幕 = Offset.zero &window.physicalSize;最终画布 = 新画布(记录器,屏幕);最终偏移量 = screen.center;最终画家 = createTextPainter();最终边界 = 偏移量 &尺寸(painter.minIntrinsicWidth,painter.height);drawBackground(画布,屏幕);Painter.paint(画布,偏移量);canvas.drawRect(界限,新油漆()..颜色 = 黑色..style = PaintingStyle.stroke..strokeWidth = 3.0);返回 recorder.endRecording();}场景 createScene() {最终生成器 = new SceneBuilder()..pushTransform(identityTransform)..addPicture(Offset.zero, createPicture())..流行音乐();返回 builder.build();}无效开始帧(持续时间时间戳){window.render(createScene());}无效主(){window.onBeginFrame = beginFrame;window.scheduleFrame();}

    解决方案

    要忽略该填充,您必须真正将低级调用作为 Text 和实际上的空间一起画.我尝试在互联网上搜索是否还有其他出路,但找不到任何可以真正解决您的问题的方法.

    我尝试搜索 Flutter SDK (Widget_Library) 的源代码,但我只能看到最终生成的小部件是一个 RichText,其源代码消失在空气中.(我刚刚在 GitHub 上搜索它时找不到该小部件的定义......在 Android Studio 中,您可能会通过将鼠标悬停在它上面并按 Ctrl+LeftClick 来获得对它的一些引用......但我我很确定它会带你到另一个未知的世界,因为字体是根据它们的字体系列绘制的(这将是 .ttf 或 .otf 文件......无论是哪种方式,它们都会与您所指的填充一起绘制).

    您现在所能做的就是创建我们自己的字符集小部件,它接受原始字符串并读取每个字符并使用一些自定义绘制函数绘制您已经定义的数字/字符,

    或者如果您准备放弃填充以节省一些时间(这对您现在可能很重要也可能不重要),您可以使用一些预定义的函数来获得空间,或者使用以下解决方案来获得您想要的任何小部件的尺寸.

    <块引用>

    我需要知道一段文本的确切边界...

    了解任何小部件宽度的最简单方法是使用 GlobalKey

    声明并初始化一个变量来存储一个GlobalKey,

    GlobalKey textKey = GlobalKey();

    GlobalKey 传递给您的 Text 小部件的关键参数,

    文本(你好",键:文本键,)

    现在我将使用 GestureDetector 包装上述小部件以打印上述小部件的宽度.

    GestureDetector(onTap: (){打印(textKey.currentContext.size.width);//这是你的宽度打印(textKey.currentContext.size.height);//这是你的身高},孩子:文本(你好",键:文本键,),)

    正如我之前提到的,你可以在任何小部件上使用它,你也可以在 TextSpan 小部件上使用它:

    GestureDetector(onTap: (){打印(textKey.currentContext.size.width);//这是你的宽度},文本跨度(键:文本键,文字:'8',样式:新文本样式(颜色:黑色,字体大小:200.0,fontFamily: "Roboto",),),)

    现在,在 GlobalKey() 的帮助下,您可以获得关于那段 Text 所需的所有信息,并相应地绘制边界/矩形.

    注意:GlobalKey() 为您的小部件提供唯一标识,因此不要在多个小部件上重复使用它.

    ==================

    如果能够以某种方式获取应用/文本中每个像素的颜色,那么就有可能实现您想要实现的目标.

    注意:您必须分别找到每一边的偏移/填充.

    x1,y1 是容器起点的坐标,x2,y2 是终点.

    minL,minR,minT,minB 是您要查找的偏移量.

    基本逻辑:根据所考虑的边垂直/水平扫描每一行,直到获得颜色等于文本颜色的像素,然后对所有假想的行执行此操作,直到获得最小偏移量(文本的距离)从边界)为所有边.

    想象一下您从容器的边界画一条线,直到遇到文本的边界之一(/满足文本颜色),然后对所有其他组(水平[左/右] 或垂直[顶/底])线,你只需要记住最短的距离.

    //左侧for(i=y1+1;ij) {minL = j;休息;}//右边for(i=y1+1;ix1;j--)if(colorof(j,i)==textcolor && minR>j) {minR = j;休息;}//顶部for(i=x1+1;ij) {minT = j;休息;}//底部for(i=x1+1;iy1;j--)if(colorof(i,j)==textcolor && minB>j) {minB = j;休息;}x1 = x1 + minL;y1 = y1 + minT;x2 = x2 - minR;y2 = y2 - minB;

    在实现上述逻辑时,确保没有其他重叠/底层小部件(脚手架/堆栈组合)的颜色以某种方式与文本颜色匹配.

    I need to know the exact bounds a piece of text -- the equivalent of getTextBounds for Android. I realize this goes somewhat counter to Flutter's design, but I am using text in a non-traditional way (as if the text were, say, embedded into an artistic picture at a precise location and size).

    I've tried three methods:

    • TextPainter's minIntrinsicWidth and height. Source below. This produces a bounding box with space on all sides:

      I need the sides of that rectangle to be right up against the black pixels of the '8'. (In this particular case, width and maxIntrinsicWidth give the same value as minIntrinsicWidth; also, preferredLineHeight gives the same value as height.)

    • Paragraph's getBoxesForRange -- basically the same result as with TextPainter.

    • FittedBox -- also leaves spaces, no matter the BoxFit enum value. I used this answer as a starting point.

    TextPainter code follows. (I realize this is inefficient; I don't care until I get the needed bounding box. I used Scene/Picture/etc in hopes of finding appropriate functionality in the lower levels.)

    import 'dart:ui';
    import 'dart:typed_data';
    import 'package:flutter/painting.dart';
    
    const black = Color(0xff000000);
    const white = Color(0xffffffff);
    
    final identityTransform = new Float64List(16)
      ..[0] = 1.0
      ..[5] = 1.0
      ..[10] = 1.0
      ..[15] = 1.0;
    
    TextPainter createTextPainter() {
      return new TextPainter(
        text: new TextSpan(
          text: '8',
          style: new TextStyle(
            color: black,
            fontSize: 200.0,
            fontFamily: "Roboto",
          ),
        ),
        textDirection: TextDirection.ltr,
      )..layout();
    }
    
    void drawBackground(Canvas canvas, Rect screen) {
      canvas.drawRect(
          screen,
          new Paint()
            ..color = white
            ..style = PaintingStyle.fill);
    }
    
    Picture createPicture() {
      final recorder = new PictureRecorder();
      final screen = Offset.zero & window.physicalSize;
      final canvas = new Canvas(recorder, screen);
      final offset = screen.center;
      final painter = createTextPainter();
      final bounds = offset & Size(painter.minIntrinsicWidth, painter.height);
      drawBackground(canvas, screen);
      painter.paint(canvas, offset);
      canvas.drawRect(
          bounds,
          new Paint()
            ..color = black
            ..style = PaintingStyle.stroke
            ..strokeWidth = 3.0);
      return recorder.endRecording();
    }
    
    Scene createScene() {
      final builder = new SceneBuilder()
        ..pushTransform(identityTransform)
        ..addPicture(Offset.zero, createPicture())
        ..pop();
      return builder.build();
    }
    
    void beginFrame(Duration timeStamp) {
      window.render(createScene());
    }
    
    void main() {
      window.onBeginFrame = beginFrame;
      window.scheduleFrame();
    }
    

    解决方案

    To ignore that padding you have to make really low level calls as the Text and the space practically get painted together. I tried searching if there is any way other way out in on the Internet but couldn't find anything which could actually solve your issue.

    I tried hunting through the source code of Flutter SDK (Widget_Library), but all I could see is that the final resultant widget is a RichText whose source code disappears into thin air. (I couldn't find that widget's definition as I just searched it on GitHub...In Android Studio you might get some reference to it by hovering over it and pressing Ctrl+LeftClick... But I am pretty sure that it will take you to another unknown world as fonts are painted based on their font family(which would be .ttf or .otf file...Either ways they'll be painted along with the padding your referring to).

    All you can do for now is create our own character set widget which accepts a raw string and reads through each character and paints a number/character already defined by you using some custom paint function,

    or if your ready to let go that padding to save some time(which might or might not be crucial for you right now), you either use some predefined function to get that along with the space or use the below solution to get the dimensions of any widget you want.

    I need to know the exact bounds a piece of text...

    The easiest way to know the width of the any widget is by using a GlobalKey

    Declare and initialize a variable to store a GlobalKey,

    GlobalKey textKey = GlobalKey();
    

    Pass the GlobalKey to the key parameter of your Text widget,

    Text(
      "hello",
       key: textKey,
     )
    

    Now I'll wrap the above widget with a GestureDetector to print the width of the above widget.

    GestureDetector(
      onTap: (){
        print(textKey.currentContext.size.width); //This is your width
        print(textKey.currentContext.size.height); //This is your height
          },
       child: Text(
         "hello",
         key: textKey,
           ),
         )
    

    As I mentioned earlier you can use it on any widget, so can you use it on TextSpan widget:

    GestureDetector(
      onTap: (){
        print(textKey.currentContext.size.width); //This is your width
          },
    TextSpan(
          key: textKey,
          text: '8',
          style: new TextStyle(
            color: black,
            fontSize: 200.0,
            fontFamily: "Roboto",
          ),
        ),
      )
    

    Now with the help of GlobalKey() you can get all the information required about that piece of Text and paint your boundary/rectangle accordingly.

    Note: GlobalKey() gives your widget a unique identity, so don't reuse it on multiple widgets.

    ==================

    Edit:

    If can somehow get the color of each and every pixel in your app/text, then there is a possibility of getting what your trying to achieve.

    Note: You'll have to find the offset/padding of each side separately.

    x1,y1 are the co-ordinates for the starting point of your container and x2,y2 are the ending points.

    minL,minR,minT,minB are the offset(s) you were looking for.

    Basic Logic: Scan each line vertically/horizontally based on the side taken under consideration, until you get a pixel whose color equals the text color and do this for all the imaginary lines until you get the minimum offset(distance of the text from the boundary) for all sides.

    Imagine your drawing a line from the boundary of your Container until you meet one of the bounds of the text(/meet the text color) and you do that for all other set of (horizontal[Left/Right] or vertical[Top/Bottom])lines and you just keep the shortest distance in your mind.

    // Left Side
    for(i=y1+1;i<y2;i++)
     for(j=x1+1;j<x2;j++)
       if(colorof(j,i)==textcolor && minL>j) {minL = j; break;}
    
    // Right Side
    for(i=y1+1;i<y2;i++)
     for(j=x2-1;j>x1;j--)
       if(colorof(j,i)==textcolor && minR>j) {minR = j; break;}
    
    // Top Side
    for(i=x1+1;i<x2;i++)
     for(j=y1;j<y2;j++)
       if(colorof(i,j)==textcolor && minT>j) {minT = j; break;}
    
    // Bottom Side
    for(i=x1+1;i<x1;i++)
     for(j=y2;j>y1;j--)
       if(colorof(i,j)==textcolor && minB>j) {minB = j; break;}
    
    x1 = x1 + minL;
    y1 = y1 + minT;
    x2 = x2 - minR;
    y2 = y2 - minB;
    
    

    While implementing the above logic ensure that there is no other overlapping/underlying widget(Scaffold/Stack Combo) whose color somehow matches the text color.

    这篇关于查找文本的确切边界的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆