using System; using System.Collections.Generic; using System.Text; namespace PDFPatcher.Model { [System.Diagnostics.DebuggerDisplay ("{Direction}({Region.Middle},{Region.Center}):Text = {Text}")] sealed class TextLine : IDirectionalBoundObject { List _Texts; /// 获取此行内包含的文本。 internal IEnumerable Texts { get { return _Texts; } } /// 获取 内的第一个 。 internal TextInfo FirstText { get { return _Texts[0]; } } public WritingDirection Direction { get; private set; } public Bound Region { get; private set; } internal bool SuppressTextInfoArrangement { get; set; } /// /// 默认的书写方向。 /// internal static WritingDirection DefaultDirection { get; set; } string _Text; /// /// 获取将 内所有文本串联起来的字符串。 /// public string Text { get { if (_Text == null) { _Text = GetConcatinatedText (); } return _Text; } } private TextLine () { _Texts = new List (); this.Direction = DefaultDirection; } internal TextLine (TextInfo text) : this() { _Texts.Add (text); this.Region = new Bound (text.Region); if (text.Text.Length > 2 && text.Region.Height > 0 && text.Region.Width > text.Region.Height * 2) { this.Direction = WritingDirection.Hortizontal; } } internal void AddText (TextInfo text) { if (this.Direction == WritingDirection.Unknown) { var d = this.GetDistance (text.Region); this.Direction = InferWritingDirection (d); if (this.Direction == WritingDirection.Unknown) { d = this.GetDistance (new Bound (text.Region.Center, text.Region.Middle)); this.Direction = InferWritingDirection (d); } } _Text = null; _Texts.Add (text); this.Region.Merge (text.Region); } private static WritingDirection InferWritingDirection (DistanceInfo d) { return d.IsVerticallyAligned ? WritingDirection.Vertical : d.IsHorizontallyAligned ? WritingDirection.Hortizontal : WritingDirection.Unknown; } internal void Merge (TextLine source) { _Text = null; this.Region.Merge (source.Region); _Texts.AddRange (source.Texts); } /// /// 获取区域 到当前文本行之间的距离。 /// /// 另一个区域。 /// 相对于此区域的距离关系。 internal DistanceInfo GetDistance (Bound other) { return this.Region.GetDistance (other, this.Direction); } /// /// 获取将 内所有文本串联起来的字符串。 /// private string GetConcatinatedText () { var l = _Texts.Count; if (l == 0) { return String.Empty; } else if (l == 1) { return _Texts[0].Text; } var tl = _Texts; if (SuppressTextInfoArrangement == false) { if (this.Direction == WritingDirection.Vertical) { tl.Sort (TextInfo.CompareRegionY); } else { tl.Sort (TextInfo.CompareRegionX); } } var cs = GetAverageCharSize (); var sb = new StringBuilder (); sb.Append (tl[0].Text); char c; for (int i = 1; i < l; i++) { if (cs > 0) { var dx = this.Direction == WritingDirection.Vertical ? tl[i].Region.Top - tl[i - 1].Region.Bottom : tl[i].Region.Left - tl[i - 1].Region.Right; if (dx > cs) { var t = tl[i - 1].Text; // 调整标点留下的空白 if (t.Length > 0) { c = t[t.Length - 1]; if (Char.IsPunctuation (c) && c > 128) { dx -= cs; } } t = tl[i].Text; if (t.Length > 0) { c = tl[i].Text[0]; if (Char.IsPunctuation (c) && c > 128) { dx -= cs; } } } while ((dx -= cs) > 0) { sb.Append (' '); } } sb.Append (tl[i].Text); } return sb.ToString (); } /// 获取 内文字或数字的平均尺寸。 /// 返回平均字符尺寸。 internal float GetAverageCharSize () { var tl = _Texts; float ts = 0, cc = 0; if (this.Direction == WritingDirection.Vertical) { tl.ForEach ((t) => { ts += t.LetterWidth; cc += t.Text.Length; }); } else { foreach (var t in tl) { ts += t.LetterWidth; foreach (var c in t.Text) { if (Char.IsLetterOrDigit (c) == false) { continue; } cc += (c > 0x36F ? 2 : 1); } } } return ts / cc; // 平均字符宽度 } } }