cad.net 文字偏移及符号表
动图演示
绑定参照后出现
代码克隆后出现
有问题的代码(不要抄我)
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
namespace BlockImport {
public class BlockImportClass
{
[CommandMethod("tt")]
public void ImportBlocks()
{
DocumentCollection dm = Application.DocumentManager;
Editor ed = dm.MdiActiveDocument.Editor;
Database destDb = dm.MdiActiveDocument.Database;
Database sourceDb = new Database(false, true);
PromptResult sourceFileName;
try
{
//从命令行要求用户输入以得到要导入的块所在的源 DWG 文件的名字
#if false
sourceFileName = ed.GetString("\n输入来源DWG的完整路径: "); //把源 DWG 读入辅助数据库
sourceDb.ReadDwgFile(sourceFileName.StringResult, System.IO.FileShare.Read, true, "");
#else
sourceDb.ReadDwgFile(@"D:\桌面\T.dwg", System.IO.FileShare.Read, true, "");
#endif
//用集合变量来存储块 ID 的列表
var blockIds = new ObjectIdCollection();
using (Transaction tr = sourceDb.TransactionManager.StartTransaction())
{
//打开块表
using BlockTable bt = tr.GetObject(sourceDb.BlockTableId, OpenMode.ForRead, false) as BlockTable;
//在块表中检查每个块
foreach (ObjectId btrId in bt)
{
using var btr = tr.GetObject(btrId, OpenMode.ForRead, false) as BlockTableRecord;
//只添加有名块和非 layout 块(layout 块是非 MS 和非 PS 的块)
if (!btr.IsAnonymous && !btr.IsLayout)
blockIds.Add(btrId);
btr.Dispose(); //释放块表记录引用变量所占用的资源
}
bt.Dispose();//释放块表引用变量所占用的资源 //没有作改变,不需要提交事务
tr.Dispose();
}
//用 WblockCloneObjects 把所有的块从源库拷贝块到目的库的块表中
//这只能实现导入块到指定的数据库中,但不是深度克隆,
//若对块参照实行深度克隆的话,其所引用的块也会被克隆到指定的数据库中
IdMapping mapping = new IdMapping();
sourceDb.WblockCloneObjects(blockIds, destDb.BlockTableId, mapping, DuplicateRecordCloning.Replace, false);
ed.WriteMessage
("\n复制 "
+ blockIds.Count.ToString()
+ " 个块的定义"
//+ sourceFileName.StringResult
+ " 到当前绘图的块表记录."
);
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
ed.WriteMessage("\n复制过程中的错误: " + ex.Message);
}
sourceDb.Dispose();
}
}
}
这是一个kean在博客的dbx例子,也就是后台打开dwg.
使用它时,
在某些情况下单行文字/属性会出现不在文字图形的基点位置,
此问题我称为"字体偏移".
bug图元
单行文字/属性.
当克隆来源使用了如下,将导致偏移发生.
字体:"新宋体.TTF",
对齐样式:"中间,正中,中"等...非"左"的对齐方式,
左: AttachmentPoint.BaseLeft
测试环境
但是由于本人测试过少,便已经找到了解决方案了.
不清楚此问题的出现是否存在于所有的.TTF文件下,
测试环境: 中文版Acad2008~Acad2019
1: 使用外部参照,
参照的dwg中有本文列明的bug图元,
然后绑定,就会触发
2: 使用克隆命令,跟上面kean的代码一样
测试环境: 中文版Acad2020+
1: 同旧版本
2: 同旧版本
3: 由于新版本桌子重写了一个新的insert命令,
也就是i这个命令,它会弹出一个带预览的小面板,
这个命令也会导致此问题发生.
解决方案
补锅
适用于:自带的绑定参照命令后.
一旦出现了字体偏移,那么此情况会保存到dwg内.
而当你不加载问题功能前提下,
打开图纸,双击文字,它会从偏移态恢复为正常态.
遇到此情况不妨用代码:
遍历全图,移动(0,0,0)到(0,0,0)一个长度为0距离.
克隆方案1
根据网友反映,根据前台后台分别处理:
// 有doc就是前台
var dbBak = doc.Database;
HostApplicationServices.WorkingDatabase = Database;
action.Invoke();//这里执行插入db.insert(...)
HostApplicationServices.WorkingDatabase = dbBak;
这样就能保证不出现文字偏移了,
这个操作在IFox上面DBTans.Task(()=>{...})提供,
所以还是用开源库吧
https://gitee.com/inspirefunction/ifoxcad/blob/jing/src/IFoxCAD.Cad.Shared/Runtime/DBTrans.cs#L593
克隆方案2
1: 来源数据库的文字样式表修改"新宋体"为任何一种大字体.
2: 再更改文字实体对齐方式为"左" AttachmentPoint.BaseLeft
3: 再进行克隆(WblockCloneObjects).
4: 最后在目标数据库还原样式.
(已经亲测,必须这样做,只改一项都不行)
5.插入了之后需要刷新块,否则显示还是不正确的,刷新的方法是块内图元移动0,0到0,0,不需要嵌套遍历仅需遍历一层.(此处无代码,自己干)
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
namespace JoinBox
{
/*调用*/
public static partial class BlockTool
{
/// <summary>
/// 跨图克隆,不允许事务包裹(克隆来源的某个块到目标数据库)
/// </summary>
/// <param name="ddb">目标数据库,通常是当前数据库</param>
/// <param name="path">来源dwg的路径</param>
/// <param name="blockName">拿到插入的块名</param>
/// <returns></returns>
public static IdMapping CloneBlock(this Database ddb, string path, string blockName = null)
{
var cb = new CloneBlock(ddb, path, blockName);
return cb.Mapping;
}
}
/*封装*/
public class CloneBlock
{
//key是来源图块的id,value是克隆到新图块的id
public IdMapping Mapping { get; private set; }
//对齐方式
AttachmentPoint _zuo = Attachment.Get("左");//AttachmentPoint.BaseLeft
//引起偏移的字体
string[] _fontsCausesOffset = new string[] { "新宋体", "宋体", "黑体" };
/// <summary>
/// 跨图克隆,不允许事务包裹(克隆来源的某个块到目标数据库)
/// </summary>
/// <param name="ddb">目标数据库,通常是当前数据库</param>
/// <param name="path">来源dwg的路径</param>
/// <param name="blockName">拿到插入的块名</param>
/// <returns></returns>
public CloneBlock(Database ddb, string path, string blockName = null)
{
if (ddb == null)
{
throw new ArgumentNullException(nameof(ddb));
}
if (!File.Exists(path))
{
throw new ArgumentNullException(nameof(path));
}
Mapping = new IdMapping();
//记录将要修改了对齐的图元
List<JustifyInfo> justifyOfLeft = new();
//来源的块list
ObjectIdCollection btRecIds = new();
//key用户定义的名称,value字体本名
Dictionary<string, FontDescriptor> fonstNameAndFile = new();
//打开来源数据库,有引起错误偏移的字体,就修改为大字体
OpendDwg.Read(path, sdb =>
{
// 设置所有的实体从"非左"到"左",并记录图元对齐点信息,
// 要在修改文字样式前,否则点信息被改了
sdb.Action(tr =>
{
sdb.TraverseEntitys(tr, ent =>
{
var entType = ent.GetType();
var entJustify = entType.GetProperty("Justify");//反射获取属性
if (entJustify != null)
{
var justifyObj = entJustify.GetValue(ent, null);
var justify = (AttachmentPoint)justifyObj;
if (justify != _zuo)
{
var entPosition = entType.GetProperty("Position");//反射获取属性
var pt3 = (Point3d)entPosition.GetValue(ent, null);
var entAlignmentPoint = entType.GetProperty("AlignmentPoint");//反射获取属性
var ptAl = (Point3d)entAlignmentPoint.GetValue(ent, null);
var jinfo = new JustifyInfo()
{
EntityName = entType.Name,
ObjectId = ent.ObjectId,
Justify = justify,
Position = pt3,
AlignmentPoint = ptAl
};
justifyOfLeft.Add(jinfo);
entJustify.SetValue(ent, _zuo, null); // txt.Justify = _zuo;
}
}
return false;
});
});
sdb.Action(tr =>
{
// 修改文字样式
sdb.TraverseTextStyleTable(tr, tsRec =>
{
if (_fontsCausesOffset.Contains(tsRec.Font.TypeFace))
{
fonstNameAndFile.Add(tsRec.Name, tsRec.Font);
tr.AddTextStyle(sdb, tsRec.Name, "txt.shx", "gbcbig.shx");
}
return false;
});
});
// 打开块表,找到指定块
sdb.Action(tr =>
{
sdb.TraverseBlockTable(tr, btRec =>
{
//有名块 && 非layout块
if (!btRec.IsAnonymous && !btRec.IsLayout)
{
if (btRec.Name == blockName)//指定块的时候就中断
{
btRecIds.Clear();
btRecIds.Add(btRec.ObjectId);
return true;
}
else //没有来源块就记录所有的
{
btRecIds.Add(btRec.ObjectId);
}
}
btRec.Dispose(); //释放块表记录引用变量所占用的资源
return false;
});
});
//克隆到目标块表内
sdb.WblockCloneObjects(btRecIds, ddb.BlockTableId, Mapping, DuplicateRecordCloning.Replace, false);
});
//还原文字样式
ddb.Action(tr =>
{
foreach (var item in fonstNameAndFile)
{
tr.AddTextStyle(ddb, item.Key, item.Value);
}
});
//"左"到"非左",还原对齐方式
ddb.Action(tr =>
{
var kv = Mapping.ToDictionary();
foreach (var changInfo in justifyOfLeft.ToArray())
{
if (kv.Keys.Contains(changInfo.ObjectId))
{
var ent = kv[changInfo.ObjectId].ToEntity(tr);
ent.UpgradeOpen();
//和反射等效的
//if (ent is DBText txt)//因为属性继承DBText,所以这句参与属性的.
//{
// txt.Justify = changInfo.Justify;
// txt.Position = changInfo.Position;
// txt.AlignmentPoint = changInfo.AlignmentPoint;//设置对齐点
// justifyOfLeft.Remove(changInfo);
//}
//利用反射设置
var entType = ent.GetType();
var entJustify = entType.GetProperty("Justify");//反射获取属性
if (entJustify != null)
{
entJustify.SetValue(ent, changInfo.Justify, null);
}
var entPosition = entType.GetProperty("Position");//反射获取属性
if (entPosition != null)
{
entPosition.SetValue(ent, changInfo.Position, null);
}
var entAlignmentPoint = entType.GetProperty("AlignmentPoint");//反射获取属性
if (entAlignmentPoint != null)
{
entAlignmentPoint.SetValue(ent, changInfo.AlignmentPoint, null);
}
justifyOfLeft.Remove(changInfo);//并不会处理完集合内所有的图元,因为有些是来源图非插入块的,了解之后可以删除本句.
ent.DowngradeOpen();
}
}
});
//之后再插入和刷新克隆过来的块,需要遍历块内并且移动0
}
/// <summary>
/// 简易的文字类
/// </summary>
public class JustifyInfo
{
/// <summary>
/// 名称
/// </summary>
public string EntityName;
/// <summary>
/// 文字的实体id
/// </summary>
public ObjectId ObjectId;
/// <summary>
/// 文字的对齐方式
/// </summary>
public AttachmentPoint Justify;
/// <summary>
/// 文字的基点
/// </summary>
public Point3d Position;
/// <summary>
/// 文字的对齐点
/// </summary>
public Point3d AlignmentPoint;
}
}
}
子方法:
public static partial class BlockTool {
/// <summary>
/// 转换为词典
/// </summary>
/// <param name="mapping"></param>
/// <returns></returns>
public static Dictionary<ObjectId, ObjectId> ToDictionary(this IdMapping mapping)
{
var keyValuePairs = new Dictionary<ObjectId, ObjectId>();
foreach (IdPair item in mapping)
{
keyValuePairs.Add(item.Key, item.Value);
}
return keyValuePairs;
}
}
/// <summary>
/// 修改文字样式
/// </summary>
/// <param name="tr"></param>
/// <param name="db"></param>
/// <param name="name"></param>
/// <param name="fontDescriptor">已有字体数据</param>
/// <returns></returns>
public static ObjectId AddTextStyle(this Transaction tr, Database db, string name, FontDescriptor fontDescriptor)
{
ObjectId ida = ObjectId.Null;
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException("字体参数空");
}
//已有就修改内容
db.TraverseTextStyleTable(tr, teRec =>
{
if (teRec.Name == name)
{
teRec.UpgradeOpen();
teRec.Font = fontDescriptor;
teRec.DowngradeOpen();
ida = teRec.ObjectId;
return true;
}
return false;
});
return ida;
}
/// <summary>
/// 新建文字样式
/// </summary>
/// <param name="db"></param>
/// <param name="name">样式名</param>
/// <param name="smallfont">字体名</param>
/// <param name="bigfont">大字体</param>
/// <param name="height">高度</param>
/// <param name="xscale">宽度因子</param>
/// <returns></returns>
public static ObjectId AddTextStyle(this Transaction tr, Database db, string name, string smallfont,
string bigfont = null, double xscale = 1, double height = 0)
{
ObjectId ida = ObjectId.Null;
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(smallfont))//如果字体不设置,就只返回
{
throw new ArgumentNullException("字体参数空");
}
#if HC2020
if (!string.IsNullOrEmpty(smallfont))
{
smallfont = smallfont.Split('.')[0]; //奇葩的浩辰不支持后缀名
}
#endif
// 获取已有这个名字的文字样式表记录
using var st = tr.GetObject(db.TextStyleTableId, OpenMode.ForRead) as TextStyleTable;
// 已有就修改内容
db.TraverseTextStyleTable(tr, teRec =>
{
if (teRec.Name == name)
{
teRec.UpgradeOpen();
teRec.FileName = smallfont;
if (string.IsNullOrEmpty(bigfont))
{
teRec.BigFontFileName = "";
}
else
{
teRec.BigFontFileName = bigfont;
}
teRec.TextSize = height;
teRec.XScale = xscale;
teRec.DowngradeOpen();
ida = teRec.ObjectId;
return true;
}
return false;
});
//没有就新建一个
if (ida == ObjectId.Null)
{
var tstr = new TextStyleTableRecord
{
Name = name,
FileName = smallfont,
TextSize = height,
XScale = xscale
};
if (string.IsNullOrEmpty(bigfont))
{
tstr.BigFontFileName = "";
}
else
{
tstr.BigFontFileName = bigfont;
}
st.UpgradeOpen();
ida = st.Add(tstr);
tr.AddNewlyCreatedDBObject(tstr, true);
st.DowngradeOpen();
}
return ida;
}
后记
我的配置是32位修改的Acad2008安装到64位的win10上,
但是我的Acad2019也会出现同样的情况,
而e大用英文的2019却没有复现成功,
所以我没能正确告诉桌子如何修改的bug(毕竟他们是歪果仁),
后来群里有人说过,如果使用了开图复制,
那么文字矩阵不会出错.
Acad2008字体偏移在外部参照绑定的时候也会出现,
但是insert却不会,
而Acad2020里面的insert小面板也会,
可见桌子的代码传承有问题,
(这个时候没有说我为什么喜欢用旧版本了吧)
bug:Acad2008符号表NetAPI
Acad2008.NetAPI 如果你建一个块,再删除它,
再添加一个同名块,用名称获取块定义会得到删除了的那个,
然后爆了.
这在2008所有符号表上均存在,
所以要循环获取并容错处理,或检测id是否已经被删除,
而高版本已经修复了这个问题.
参考文章
cad.net 所有的克隆方式
https://www.cnblogs.com/JJBox/p/10713005.html
e大此处记录了标注也会出现偏移的情况
https://www.cnblogs.com/edata/p/16199051.html
(完)