mirror of
https://github.com/wmjordan/PDFPatcher.git
synced 2026-10-23 00:38:37 +08:00
164 lines
4.6 KiB
C#
164 lines
4.6 KiB
C#
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<TextInfo> _Texts;
|
|
/// <summary>获取此行内包含的文本。</summary>
|
|
internal IEnumerable<TextInfo> Texts { get { return _Texts; } }
|
|
/// <summary>获取 <see cref="Texts"/> 内的第一个 <see cref="TextInfo"/>。</summary>
|
|
internal TextInfo FirstText { get { return _Texts[0]; } }
|
|
|
|
public WritingDirection Direction { get; private set; }
|
|
public Bound Region { get; private set; }
|
|
internal bool SuppressTextInfoArrangement { get; set; }
|
|
/// <summary>
|
|
/// 默认的书写方向。
|
|
/// </summary>
|
|
internal static WritingDirection DefaultDirection { get; set; }
|
|
|
|
string _Text;
|
|
/// <summary>
|
|
/// 获取将 <see cref="Texts"/> 内所有文本串联起来的字符串。
|
|
/// </summary>
|
|
public string Text {
|
|
get {
|
|
if (_Text == null) {
|
|
_Text = GetConcatinatedText ();
|
|
}
|
|
return _Text;
|
|
}
|
|
}
|
|
|
|
private TextLine () {
|
|
_Texts = new List<TextInfo> ();
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取区域 <paramref name="other"/> 到当前文本行之间的距离。
|
|
/// </summary>
|
|
/// <param name="other">另一个区域。</param>
|
|
/// <returns><paramref name="other"/> 相对于此区域的距离关系。</returns>
|
|
internal DistanceInfo GetDistance (Bound other) {
|
|
return this.Region.GetDistance (other, this.Direction);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取将 <see cref="Texts"/> 内所有文本串联起来的字符串。
|
|
/// </summary>
|
|
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 ();
|
|
}
|
|
|
|
/// <summary>获取 <see cref="Texts"/> 内文字或数字的平均尺寸。</summary>
|
|
/// <returns>返回平均字符尺寸。</returns>
|
|
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; // 平均字符宽度
|
|
}
|
|
}
|
|
}
|