CadParamPluging/Cad/TemplateLayoutAnnotationExtractor.cs

336 lines
10 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
// 通常图纸在 PaperSpaceModel 不作为候选。
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));
}
}
}