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中可以用
来表示.
在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;
}
}