cad.net 解析多行文字+文字在位编辑器+字体渲染

多行文字格式解析

在C#上面多行文字去除格式是很简单的,如下:

string content = mText.Text;
mText.Text = content;

尺寸标注本质上就是一个特殊的块,
尺寸标注的文字也是多行文字,
尺寸标注内容是有替换文本和测量值之分,
内容改为"<>"是恢复测量值,想必画图的都知道.

为了创建和修改多行文字,我们需要了解本文.
我对原帖进行校验并补充了一些内容,原帖上面有渲染失败的东西.

原帖
https://adndevblog.typepad.com/autocad/2017/09/dissecting-mtext-format-codes.html
收录在此处(我才能看见的地址)
https://www.cnblogs.com/JJBox/p/18534127#_lab2_0_4

呈现帖子

如何从 MTEXT 内容格式字符串中识别特殊符号.
为了更好地理解这一点,让我们将看一个简单的示例.

案例:下面是询问人的屏幕截图

截图的尺寸标注的文本:

\A1;\fAIGDT|b0|i0;\H2.5000;\ln\fArial|b0|i0;\H2.5000;68{\H1.3;\S+0,8^+0,1;}

很长是不是,我把它切割一下:

\A1;
\fAIGDT|b0|i0;\H2.5000;
\ln
\fArial|b0|i0;\H2.5000;
68{\H1.3;\S+0,8^+0,1;}

了解格式代码

\f 字体文件名

在本例中为 AIGDT 代表:
Autodesk Inventor Geomertic Dimension and Tolerance 字体文件

之后的|b0|i0; 以管道符号|开头的代码是显示字体的特征:
b 表示 'bold' 粗体: 0关闭/1打开
i 表示 'Italic' 斜体: 0关闭/1打开
c 表示 'code page' 字符编码集?后跟代码页号: |c238
p 表示 'pitch' 水平间距,后跟数字: |p10

\L 下划线开始; \l 下划线结束;
\O 上划线开始; \o 上划线结束;
\K 删除线开始; \k 删除线结束;

\P 新段落(换行)
\pxi 项目符号/编号段落/列,控制代码
\X 尺寸线上的段落换行(仅在尺寸中)

\Q 文本倾斜: \Q30;
\H 文本高度: \H3x\H2.500
\W 文本宽度: \W0.8x

\S 堆叠分数:
\S1#4 = ¼
\SA^B = \(A^B\)
1.000\S+0.010^-0.000; = \(1.000^{+0.010}_{-0.000}\)

在Markdown中使用LaTeX表达式: $1.000^{+0.010}_{-0.000}$
桌子要是用LaTeX就好了,就不用学两套东西了,
并且一些开根号的数学符号也可以有,
嵌套改变也很方便:
\(1.000^{2.000^{+2.010}_{-2.000}}_{-0.000}\)

\A 对齐方式:
\A0; = 底部; \A1; = 中心; \A2; = 顶部

\C 颜色变化:
\C1; = 红色; \C2; = 黄色; \C3; = 绿色
\C4; = 青色; \C5; = 蓝色; \C6; = 洋红色
\C7; = 白色

{} 大括号,定义受代码影响的文本区域,它是递归的,最多嵌套八层
\ 转义字符,字符串内表达需要转义: \\=\, \{={
\T 字符间距,有效值为原始间距的0.75~4倍: \T2;
\~ 插入不间断空格(Non-breaking space)

也称为“非换行空间”或“硬空间”,在Unicode中表示为U+00A0
用于文本中需要保持单词或短语在一起的地方,以防止它们在文本换行时被分隔开.
例如,在网址/电话号码/缩写词等情况下,
使用不间断空格可以确保它们在文本中保持完整,不会因为换行而断开.
在HTML中可以用&nbsp;来表示.
在LaTeX中可以用~来表示.

acad2016官方帮助文档,里面还有一些图片展示:
https://help.autodesk.com/view/ACD/2016/CHS/?guid=GUID-7D8BB40F-5C4E-4AE5-BD75-9ED7112E5967
这里甚至有文字编辑器用途动画
https://help.autodesk.com/view/ACDLT/2022/CHS/?guid=GUID-1B3E8624-ED88-4409-AEA2-32836332AB27

控制代码和特殊符号

案例头部节选: \fAIGDT|b0|i0;\H2.5000;\ln
\l 是下划线的格式代码,来自字体系列 AIGDT,
n 是∅的特殊符号

下表显示了控制代码特殊符号之间的字符映射

例如,以下格式代码
\A1;{\fAIGDT|b0|i0;m}\H2.5000;80
将呈现此

对于字符映射工具,
本文档包含如何检索各种版本 Windows 的工具
http://sites.psu.edu/symbolcodes/windows/charmap/

MTEXT 还支持直接的 Unicode 工具,
并非所有字体文件都支持所有符号,
每个字体文件都有其存在的原因,
但会有唯一的 Unicode 来防止符号之间发生冲突.

使用不同的字体文件(如 ISOCPEUR),
我可以呈现直径符号或“带对角线 stoke 的拉丁大写字母”,
或者我在本文中开头的符号∅, unicode 是\U+00D8

{\fISOCPEUR|b0|i0|c0|p34;\H0.95833x;\C256;\U+00D8\A1;\H1.04348x;\C7;80}

文字在位编辑器

// 示例为修改多行文字的高宽比
// 其实在acad中,首次调用比较卡
public static void ChangeMtextWidthFactor(ObjectId mtextId, double widthFactor)
{
    Debug.Assert(mtextId is not null,"文字在位编辑器参数为空");
    if (mtextId == ObjectId.Null)
        return;
    using var tr = db.TransactionManager.StartTransaction();
    using var ent = (MText)tr.GetObject(mtextId, OpenMode.ForWrite);

    // 文字编辑器
    using TextEditor te = InplaceTextEditor.CreateTextEditor(ent);       
    te.SelectAll();
    te.Selection.WidthScale = widthFactor;
    te.Close(TextEditor.ExitStatus.ExitSave);

    tr.Commit();
}

SHX

github有很多开源工程.

TTF

渲染字体
字体渲染拦截掉自己实现低精度采样,定点数用途这不就来了...
FreeType已经写好常用的系统字体了,Shx也有解析库...怎么拦截?

1,删掉全部文字就不渲染了,保存和打印记得设置回去,
但是遍历块时候获取不到文字,为了遍历得到又要去建立索引.

2,由于1的工程量太大,为了不这样这样做,
所以应该做一种空字体进行等替,
这样cad渲染字体时候会得到空转渲染字体

3,甚至反汇编之后直接屏蔽它采样字体函数,就不会触发这个渲染了.

功能:
然后我们可以在外部参照的时候使用配置才渲染字体和填充(二维线框似乎也),
打印时候打开.
其实我是为了引入一键翻译,秒改全图文字,又秒改回去,
一闪一闪的显示进行给对照检查(把优化了的性能又优化了回去,喜).

字体渲染库介绍:
https://www.cnblogs.com/cpuimage/p/13337475.html
定点数用TrueSync库:
https://www.zhihu.com/question/278042407
字体渲染:
https://blog.csdn.net/u014629601/article/details/126807571
C#字体渲染:[Sebastian Lague]渲染文本
https://b23.tv/VyfutsD

明经通道多行文字贴子

http://bbs.mjtd.com/thread-81065-1-1.html

/// <summary>
/// 单行文字分解
/// </summary>
[CommandMethod(nameof(Test1))]
public static void Test1()
{
    var ed = SystemManager.Editor;
    var opts = new PromptEntityOptions("\n请选择文本:");
    opts.SetRejectMessage("你选择的不是文本,请重新选择!");
    opts.AddAllowedClass(typeof(DBText), false);
    var res = ed.GetEntity(opts);
    if (res.Status != PromptStatus.OK) return;

    using (var tr = new DBTransaction())
    {
        using var btr = tr.OpenCurrentSpace(OpenMode.ForWrite);
        using var txt = tr.GetObject<DBText>(res.ObjectId).Copy();
        var ext = txt.GeometricExtents;
        var pt = ext.MinPoint;
        var txts = new List<DBText>();
        int color = 1;
        foreach (char c in txt.TextString)
        {
            var s = c.ToString();
            var txtx = new DBText
            {
                TextString = s,
                TextStyleId = txt.TextStyleId,
                Height = txt.Height,
                Position = pt,
                ColorIndex = color++
            };
            txtx.SetDatabaseDefaults();
            txts.Add(txtx);
            var width = AcUtils.GetTextExtents(txt.TextStyleId, s, txt.Height).X;
            pt += new Vector3d(width, 0, 0);
        }

        tr.AddEntity(btr, txts);

        // 简单的拖动方式
        var ss = SelectionSet.FromObjectIds(txts.Select(t => t.ObjectId).ToArray());
        var ptBase = ext.MinPoint;
        var resDrag = ed.Drag(
            ss,
            "\n输入插入点",
            (Point3d p, ref Matrix3d m) =>
            {
                m = Matrix3d.Displacement(p - ptBase);
                return SamplerStatus.OK;
            });
        if (resDrag.Status == PromptStatus.OK)
        {
            var mat = Matrix3d.Displacement(resDrag.Value - ptBase);
            txts.ForEach(c => c.TransformBy(mat));
        }
        else
        {
            tr.Erase(txts);
        }
    }
}

/// <summary>
/// 单行文字曲线分布,简单的示例
/// </summary>
[CommandMethod(nameof(Test2))]
public static void Test2()
{
    var ed = SystemManager.Editor;
    var opts = new PromptEntityOptions("\n请选择文本:");
    opts.SetRejectMessage("你选择的不是文本,请重新选择!");
    opts.AddAllowedClass(typeof(DBText), true);
    var res1 = ed.GetEntity(opts);
    if (res1.Status != PromptStatus.OK) return;
    opts = new PromptEntityOptions("\n请选择曲线:");
    opts.SetRejectMessage("你选择的不是曲线,请重新选择!");
    opts.AddAllowedClass(typeof(Curve), false);
    var res2 = ed.GetEntity(opts);
    if (res2.Status != PromptStatus.OK) return;
    using (var tr = new DBTransaction())
    {
        using var btr = tr.OpenCurrentSpace(OpenMode.ForWrite);
        using var txt = tr.GetObject<DBText>(res1.ObjectId);
        var ext = txt.GeometricExtents;
        var txts = new List<DBText>();
        int color = 1;
        using var curve = tr.GetObject<Curve>(res2.ObjectId);
        double w = 0;
        foreach (char c in txt.TextString)
        {
            var s = c.ToString();
            var txtx = new DBText
            {
                TextString = s,
                TextStyleId = txt.TextStyleId,
                Height = txt.Height,
                ColorIndex = color++
            };
            txtx.SetDatabaseDefaults();
            txts.Add(txtx);
            var width = AcUtils.GetTextExtents(txt.TextStyleId, s, txt.Height).X;
            w += width / 2;
            txtx.Justify = AttachmentPoint.BottomCenter;
            txtx.AlignmentPoint = curve.GetPointAtDist(w);
            txtx.Rotation = curve.GetFirstDerivative(curve.GetParameterAtDistance(w)).GetAngleTo(Vector3d.XAxis, -Vector3d.ZAxis);
            w += width / 2;
        }
        tr.AddEntity(btr, txts);
    }
}

/// <summary>
/// 单行文字曲线分布+拖动
/// </summary>
[CommandMethod(nameof(Test3))]
public static void Test3()
{
    var ed = SystemManager.Editor;
    var opts = new PromptEntityOptions("\n请选择文本:");
    opts.SetRejectMessage("你选择的不是文本,请重新选择!");
    opts.AddAllowedClass(typeof(DBText), true);
    var res1 = ed.GetEntity(opts);
    if (res1.Status != PromptStatus.OK) return;
    opts = new PromptEntityOptions("\n请选择曲线:");
    opts.SetRejectMessage("你选择的不是曲线,请重新选择!");
    opts.AddAllowedClass(typeof(Curve), false);
    var res2 = ed.GetEntity(opts);
    if (res2.Status != PromptStatus.OK) return;
    using (var tr = new DBTransaction())
    {
        using var txt = tr.GetObject<DBText>(res1.ObjectId);
        var ext = txt.GeometricExtents;
        var txts = new List<DBText>();
        var dists = new List<double>();
        using var curve = tr.GetObject<Curve>(res2.ObjectId);
        double w = 0;
        foreach (char c in txt.TextString)
        {
            var s = c.ToString();
            var txtx = new DBText
            {
                Height = txt.Height,
                TextStyleId = txt.TextStyleId,
                TextString = s,
            };
            txtx.SetDatabaseDefaults();
            var width = AcUtils.GetTextExtents(txt.TextStyleId, s, txt.Height).X;
            txts.Add(txtx);
            dists.Add(w);
            w += width;
        }
        TextJig jig = new TextJig(txts, dists, curve);
        var resdrag = ed.Drag(jig);
        if (resdrag.Status == PromptStatus.OK)
        {
            tr.AddEntity(tr.OpenCurrentSpace(), txts);
        }
    }
}

private class TextJig : DrawJig
{
    List<DBText> _txts;
    List<double> _dists;
    Curve _curve;
    Point3d _pos;

    public TextJig(List<DBText> txts, List<double> dists, Curve curve)
    {
        _txts = txts;
        _dists = dists;
        _curve = curve;
    }

    protected override bool WorldDraw(Autodesk.AutoCAD.GraphicsInterface.WorldDraw draw)
    {
        try
        {
            Point3d pnt = _curve.GetClosestPointTo(_pos, false);
            double w = _curve.GetDistAtPoint(pnt);
            for (int i = 0; i < _txts.Count; i++)
            {
                double dist = w + _dists[i];
                double par = _curve.GetParameterAtDistance(dist);
                _txts[i].Position = _curve.GetPointAtParameter(par);
                _txts[i].Rotation = _curve.GetFirstDerivative(par).GetAngleTo(Vector3d.XAxis, -Vector3d.ZAxis);
                draw.Geometry.Draw(_txts[i]);
            }
            return true;
        }
        catch
        {
            return false;
        }
    }

    protected override SamplerStatus Sampler(JigPrompts prompts)
    {
        JigPromptPointOptions jigOpts = new JigPromptPointOptions("\n请输入起点:");
        jigOpts.UserInputControls =
            UserInputControls.Accept3dCoordinates |
            UserInputControls.NoZeroResponseAccepted |
            UserInputControls.NoNegativeResponseAccepted;
        PromptPointResult res = prompts.AcquirePoint(jigOpts);
        Point3d pnt = res.Value;
        if (pnt == _pos)
            return SamplerStatus.NoChange;      
        if (res.Status == PromptStatus.Cancel) 
            return SamplerStatus.Cancel; 
        _pos = pnt;
        return SamplerStatus.OK;      
    }
}

相关链接

cad.net 文字宽度获取

posted @ 2024-11-07 22:31  惊惊  阅读(97)  评论(0编辑  收藏  举报