336 lines
10 KiB
C#
336 lines
10 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Text.RegularExpressions;
|
||
using Autodesk.AutoCAD.DatabaseServices;
|
||
|
||
namespace CadParamPluging.Cad
|
||
{
|
||
public sealed class LayoutAnnotation
|
||
{
|
||
public string LayoutName { get; set; }
|
||
public List<string> DeliveryStatuses { get; set; }
|
||
public List<string> ProcessMethods { get; set; }
|
||
public List<string> StructuralFeatures { get; set; }
|
||
public List<string> SpecialConditions { get; set; }
|
||
|
||
public LayoutAnnotation()
|
||
{
|
||
DeliveryStatuses = new List<string>();
|
||
ProcessMethods = new List<string>();
|
||
StructuralFeatures = new List<string>();
|
||
SpecialConditions = new List<string>();
|
||
}
|
||
}
|
||
|
||
public static class TemplateLayoutAnnotationExtractor
|
||
{
|
||
private static readonly Regex FieldRegex = new Regex(
|
||
@"^\s*(交付状态|工艺方法|结构特征|特殊条件)\s*[::]\s*(.+?)\s*$",
|
||
RegexOptions.Compiled);
|
||
|
||
private static readonly Regex MTextFormatRegex = new Regex(@"\\[A-Za-z]+[^;]*;", RegexOptions.Compiled);
|
||
|
||
public static List<LayoutAnnotation> ExtractPerLayout(string templatePath)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(templatePath))
|
||
{
|
||
throw new ArgumentNullException(nameof(templatePath));
|
||
}
|
||
|
||
var result = new List<LayoutAnnotation>();
|
||
|
||
using (var db = new Database(false, true))
|
||
{
|
||
ReadTemplateDatabase(db, templatePath);
|
||
db.CloseInput(true);
|
||
|
||
using (var tr = db.TransactionManager.StartTransaction())
|
||
{
|
||
var layoutDict = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
|
||
foreach (DBDictionaryEntry entry in layoutDict)
|
||
{
|
||
var layout = (Layout)tr.GetObject(entry.Value, OpenMode.ForRead);
|
||
var layoutName = layout.LayoutName;
|
||
|
||
// 通常图纸在 PaperSpace,Model 不作为候选。
|
||
if (string.Equals(layoutName, "Model", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
var ann = new LayoutAnnotation { LayoutName = layoutName };
|
||
|
||
var btr = (BlockTableRecord)tr.GetObject(layout.BlockTableRecordId, OpenMode.ForRead);
|
||
var visitedBlocks = new HashSet<ObjectId>();
|
||
foreach (ObjectId id in btr)
|
||
{
|
||
var obj = tr.GetObject(id, OpenMode.ForRead);
|
||
if (obj is Entity ent)
|
||
{
|
||
ExtractInto(ent, tr, visitedBlocks, ann);
|
||
}
|
||
}
|
||
|
||
Normalize(ann);
|
||
if (HasAnyValue(ann))
|
||
{
|
||
result.Add(ann);
|
||
}
|
||
}
|
||
|
||
tr.Commit();
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
private static void ReadTemplateDatabase(Database db, string templatePath)
|
||
{
|
||
try
|
||
{
|
||
db.ReadDwgFile(templatePath, FileShare.ReadWrite, true, string.Empty);
|
||
}
|
||
catch (MissingMethodException)
|
||
{
|
||
db.ReadDwgFile(templatePath, FileOpenMode.OpenForReadAndAllShare, true, string.Empty);
|
||
}
|
||
}
|
||
|
||
private static void ExtractInto(Entity ent, Transaction tr, HashSet<ObjectId> visitedBlocks, LayoutAnnotation ann)
|
||
{
|
||
if (ent is DBText text)
|
||
{
|
||
ParseTextBlock(text.TextString, ann);
|
||
return;
|
||
}
|
||
|
||
if (ent is MText mt)
|
||
{
|
||
var plain = SimplifyMText(mt.Contents);
|
||
ParseTextBlock(plain, ann);
|
||
return;
|
||
}
|
||
|
||
if (ent is BlockReference br)
|
||
{
|
||
foreach (ObjectId attId in br.AttributeCollection)
|
||
{
|
||
var att = tr.GetObject(attId, OpenMode.ForRead) as AttributeReference;
|
||
if (att == null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
ParseTextBlock(att.TextString, ann);
|
||
}
|
||
|
||
// 说明文字可能在块定义内部(非属性),需要递归扫描块内容。
|
||
var blockId = br.BlockTableRecord;
|
||
if (blockId.IsNull || visitedBlocks.Contains(blockId))
|
||
{
|
||
return;
|
||
}
|
||
|
||
visitedBlocks.Add(blockId);
|
||
var blockBtr = tr.GetObject(blockId, OpenMode.ForRead) as BlockTableRecord;
|
||
if (blockBtr == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
foreach (ObjectId childId in blockBtr)
|
||
{
|
||
var child = tr.GetObject(childId, OpenMode.ForRead) as Entity;
|
||
if (child != null)
|
||
{
|
||
ExtractInto(child, tr, visitedBlocks, ann);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private static void ParseTextBlock(string text, LayoutAnnotation ann)
|
||
{
|
||
if (ann == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var lines = SplitLines(text).ToList();
|
||
if (lines.Count == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var hasAnyField = false;
|
||
foreach (var line in lines)
|
||
{
|
||
if (TryParseFieldLine(line, ann))
|
||
{
|
||
hasAnyField = true;
|
||
}
|
||
}
|
||
|
||
if (!hasAnyField)
|
||
{
|
||
return;
|
||
}
|
||
|
||
foreach (var line in lines)
|
||
{
|
||
if (IsSpecialConditionValueLine(line))
|
||
{
|
||
ann.SpecialConditions.Add(NormalizeValue(line));
|
||
}
|
||
}
|
||
}
|
||
|
||
private static IEnumerable<string> SplitLines(string text)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(text))
|
||
{
|
||
return Enumerable.Empty<string>();
|
||
}
|
||
|
||
return (text ?? string.Empty)
|
||
.Replace("\r\n", "\n")
|
||
.Replace("\r", "\n")
|
||
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||
.Select(s => (s ?? string.Empty).Trim())
|
||
.Where(s => s.Length > 0);
|
||
}
|
||
|
||
private static string SimplifyMText(string contents)
|
||
{
|
||
if (string.IsNullOrEmpty(contents))
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
var s = contents
|
||
.Replace("\\P", "\n")
|
||
.Replace("\\p", "\n")
|
||
.Replace("\\~", " ");
|
||
|
||
s = s.Replace("{", string.Empty).Replace("}", string.Empty);
|
||
s = MTextFormatRegex.Replace(s, string.Empty);
|
||
return s;
|
||
}
|
||
|
||
private static bool TryParseFieldLine(string line, LayoutAnnotation ann)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(line))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
var m = FieldRegex.Match(line);
|
||
if (!m.Success)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
var key = m.Groups[1].Value;
|
||
var val = (m.Groups[2].Value ?? string.Empty).Trim();
|
||
if (val.Length == 0)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
val = NormalizeValue(val);
|
||
|
||
switch (key)
|
||
{
|
||
case "交付状态":
|
||
ann.DeliveryStatuses.Add(val);
|
||
break;
|
||
case "工艺方法":
|
||
ann.ProcessMethods.Add(val);
|
||
break;
|
||
case "结构特征":
|
||
ann.StructuralFeatures.Add(val);
|
||
break;
|
||
case "特殊条件":
|
||
ann.SpecialConditions.Add(val);
|
||
break;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
private static bool IsSpecialConditionValueLine(string line)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(line))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// 特殊条件“最后一行”为不带冒号的单独值,且必须在同一文本块内已出现过字段行。
|
||
if (FieldRegex.IsMatch(line))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return line.IndexOf(':') < 0 && line.IndexOf(':') < 0;
|
||
}
|
||
|
||
private static void Normalize(LayoutAnnotation ann)
|
||
{
|
||
ann.DeliveryStatuses = NormalizeList(ann.DeliveryStatuses);
|
||
ann.ProcessMethods = NormalizeList(ann.ProcessMethods);
|
||
ann.StructuralFeatures = NormalizeList(ann.StructuralFeatures);
|
||
ann.SpecialConditions = NormalizeList(ann.SpecialConditions);
|
||
}
|
||
|
||
private static string NormalizeValue(string s)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(s))
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
var trimmed = s.Trim();
|
||
var chars = trimmed.Where(c => !char.IsWhiteSpace(c)).ToArray();
|
||
return new string(chars);
|
||
}
|
||
|
||
private static List<string> NormalizeList(IEnumerable<string> values)
|
||
{
|
||
var result = new List<string>();
|
||
if (values == null)
|
||
{
|
||
return result;
|
||
}
|
||
|
||
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||
foreach (var raw in values)
|
||
{
|
||
var v = (raw ?? string.Empty).Trim();
|
||
if (v.Length == 0)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (seen.Add(v))
|
||
{
|
||
result.Add(v);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
private static bool HasAnyValue(LayoutAnnotation ann)
|
||
{
|
||
return ann != null
|
||
&& ((ann.DeliveryStatuses != null && ann.DeliveryStatuses.Count > 0)
|
||
|| (ann.ProcessMethods != null && ann.ProcessMethods.Count > 0)
|
||
|| (ann.StructuralFeatures != null && ann.StructuralFeatures.Count > 0)
|
||
|| (ann.SpecialConditions != null && ann.SpecialConditions.Count > 0));
|
||
}
|
||
}
|
||
}
|