2018-11-25 04:51:27 +08:00
|
|
|
|
namespace UglyToad.PdfPig.Content
|
|
|
|
|
{
|
2020-01-16 23:43:01 +08:00
|
|
|
|
using Core;
|
2018-11-25 04:51:27 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2020-01-16 22:43:06 +08:00
|
|
|
|
using System.Linq;
|
2019-08-25 22:06:37 +08:00
|
|
|
|
using System.Text;
|
2018-11-25 04:51:27 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// A word.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class Word
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The text of the word.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string Text { get; }
|
|
|
|
|
|
2019-05-13 02:34:00 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The text direction of the word.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public TextDirection TextDirection { get; }
|
|
|
|
|
|
2018-11-25 04:51:27 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The rectangle completely containing the word.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PdfRectangle BoundingBox { get; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The name of the font for the word.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string FontName { get; }
|
|
|
|
|
|
2019-05-13 02:34:00 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The letters contained in the word.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public IReadOnlyList<Letter> Letters { get; }
|
|
|
|
|
|
2018-11-25 04:51:27 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Create a new <see cref="Word"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="letters">The letters contained in the word.</param>
|
|
|
|
|
public Word(IReadOnlyList<Letter> letters)
|
|
|
|
|
{
|
|
|
|
|
if (letters == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(letters));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (letters.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Empty letters provided.", nameof(letters));
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-13 02:34:00 +08:00
|
|
|
|
Letters = letters;
|
|
|
|
|
|
2020-01-16 22:43:06 +08:00
|
|
|
|
var tempTextDirection = letters[0].TextDirection;
|
|
|
|
|
if (letters.Any(l => l.TextDirection != tempTextDirection))
|
|
|
|
|
{
|
2020-01-17 19:33:59 +08:00
|
|
|
|
tempTextDirection = TextDirection.Other;
|
2020-01-16 22:43:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-16 23:43:01 +08:00
|
|
|
|
Tuple<string, PdfRectangle> data;
|
2020-01-16 22:43:06 +08:00
|
|
|
|
|
|
|
|
|
switch (tempTextDirection)
|
|
|
|
|
{
|
|
|
|
|
case TextDirection.Horizontal:
|
|
|
|
|
data = GetBoundingBoxH(letters);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TextDirection.Rotate180:
|
|
|
|
|
data = GetBoundingBox180(letters);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TextDirection.Rotate90:
|
|
|
|
|
data = GetBoundingBox90(letters);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TextDirection.Rotate270:
|
|
|
|
|
data = GetBoundingBox270(letters);
|
|
|
|
|
break;
|
|
|
|
|
|
2020-01-17 19:33:59 +08:00
|
|
|
|
case TextDirection.Other:
|
2020-01-16 22:43:06 +08:00
|
|
|
|
default:
|
2020-01-17 19:33:59 +08:00
|
|
|
|
data = GetBoundingBoxOther(letters);
|
2020-01-16 22:43:06 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Text = data.Item1;
|
|
|
|
|
BoundingBox = data.Item2;
|
|
|
|
|
|
|
|
|
|
FontName = letters[0].FontName;
|
|
|
|
|
TextDirection = tempTextDirection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Bounding box
|
2020-01-16 23:43:01 +08:00
|
|
|
|
private Tuple<string, PdfRectangle> GetBoundingBoxH(IReadOnlyList<Letter> letters)
|
2020-01-16 22:43:06 +08:00
|
|
|
|
{
|
|
|
|
|
var builder = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
var minX = double.MaxValue;
|
|
|
|
|
var maxX = double.MinValue;
|
|
|
|
|
var minY = double.MaxValue;
|
|
|
|
|
var maxY = double.MinValue;
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < letters.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var letter = letters[i];
|
|
|
|
|
builder.Append(letter.Value);
|
|
|
|
|
|
|
|
|
|
if (letter.StartBaseLine.X < minX)
|
|
|
|
|
{
|
|
|
|
|
minX = letter.StartBaseLine.X;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (letter.StartBaseLine.Y < minY)
|
|
|
|
|
{
|
|
|
|
|
minY = letter.StartBaseLine.Y;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-19 18:56:59 +08:00
|
|
|
|
var right = letter.StartBaseLine.X + Math.Max(letter.Width, letter.GlyphRectangle.Width);
|
2020-01-16 22:43:06 +08:00
|
|
|
|
if (right > maxX)
|
|
|
|
|
{
|
|
|
|
|
maxX = right;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (letter.GlyphRectangle.Top > maxY)
|
|
|
|
|
{
|
|
|
|
|
maxY = letter.GlyphRectangle.Top;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-16 23:43:01 +08:00
|
|
|
|
return new Tuple<string, PdfRectangle>(builder.ToString(), new PdfRectangle(minX, minY, maxX, maxY));
|
2020-01-16 22:43:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-16 23:43:01 +08:00
|
|
|
|
private Tuple<string, PdfRectangle> GetBoundingBox180(IReadOnlyList<Letter> letters)
|
2020-01-16 22:43:06 +08:00
|
|
|
|
{
|
2019-08-25 22:06:37 +08:00
|
|
|
|
var builder = new StringBuilder();
|
2018-11-25 04:51:27 +08:00
|
|
|
|
|
2020-01-16 22:43:06 +08:00
|
|
|
|
var maxX = double.MinValue;
|
2019-12-22 02:09:49 +08:00
|
|
|
|
var minX = double.MaxValue;
|
2020-01-16 22:43:06 +08:00
|
|
|
|
var maxY = double.MinValue;
|
2019-12-22 02:09:49 +08:00
|
|
|
|
var minY = double.MaxValue;
|
2020-01-16 22:43:06 +08:00
|
|
|
|
|
|
|
|
|
for (var i = 0; i < letters.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var letter = letters[i];
|
|
|
|
|
builder.Append(letter.Value);
|
|
|
|
|
|
|
|
|
|
if (letter.StartBaseLine.X > maxX)
|
|
|
|
|
{
|
|
|
|
|
maxX = letter.StartBaseLine.X;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (letter.StartBaseLine.Y > maxY)
|
|
|
|
|
{
|
|
|
|
|
maxY = letter.StartBaseLine.Y;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-19 19:00:20 +08:00
|
|
|
|
var right = letter.StartBaseLine.X + Math.Min(letter.Width, letter.GlyphRectangle.Width);
|
2020-01-16 22:43:06 +08:00
|
|
|
|
if (right < minX)
|
|
|
|
|
{
|
|
|
|
|
minX = right;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (letter.GlyphRectangle.Top < minY)
|
|
|
|
|
{
|
|
|
|
|
minY = letter.GlyphRectangle.Top;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-16 23:43:01 +08:00
|
|
|
|
return new Tuple<string, PdfRectangle>(builder.ToString(), new PdfRectangle(maxX, maxY, minX, minY));
|
2020-01-16 22:43:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-16 23:43:01 +08:00
|
|
|
|
private Tuple<string, PdfRectangle> GetBoundingBox90(IReadOnlyList<Letter> letters)
|
2020-01-16 22:43:06 +08:00
|
|
|
|
{
|
|
|
|
|
var builder = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
var minX = double.MaxValue;
|
2019-12-22 02:09:49 +08:00
|
|
|
|
var maxX = double.MinValue;
|
2020-01-16 22:43:06 +08:00
|
|
|
|
var minY = double.MaxValue;
|
2019-12-22 02:09:49 +08:00
|
|
|
|
var maxY = double.MinValue;
|
2019-08-25 22:06:37 +08:00
|
|
|
|
|
|
|
|
|
for (var i = 0; i < letters.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var letter = letters[i];
|
|
|
|
|
builder.Append(letter.Value);
|
|
|
|
|
|
2020-01-16 22:43:06 +08:00
|
|
|
|
if (letter.StartBaseLine.X < minX)
|
2019-08-25 22:06:37 +08:00
|
|
|
|
{
|
2020-01-16 22:43:06 +08:00
|
|
|
|
minX = letter.StartBaseLine.X;
|
2019-08-25 22:06:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-16 22:43:06 +08:00
|
|
|
|
if (letter.EndBaseLine.Y < minY)
|
2019-08-25 22:06:37 +08:00
|
|
|
|
{
|
2020-01-16 22:43:06 +08:00
|
|
|
|
minY = letter.EndBaseLine.Y;
|
2019-08-25 22:06:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-17 19:03:09 +08:00
|
|
|
|
var right = letter.StartBaseLine.X + letter.GlyphRectangle.Height;
|
2019-08-25 22:06:37 +08:00
|
|
|
|
if (right > maxX)
|
|
|
|
|
{
|
|
|
|
|
maxX = right;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (letter.GlyphRectangle.Top > maxY)
|
|
|
|
|
{
|
|
|
|
|
maxY = letter.GlyphRectangle.Top;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-16 23:43:01 +08:00
|
|
|
|
return new Tuple<string, PdfRectangle>(builder.ToString(), new PdfRectangle(new PdfPoint(maxX, maxY),
|
2020-01-16 22:43:06 +08:00
|
|
|
|
new PdfPoint(maxX, minY),
|
|
|
|
|
new PdfPoint(minX, maxY),
|
|
|
|
|
new PdfPoint(minX, minY)));
|
|
|
|
|
}
|
2019-05-13 02:34:00 +08:00
|
|
|
|
|
2020-01-16 23:43:01 +08:00
|
|
|
|
private Tuple<string, PdfRectangle> GetBoundingBox270(IReadOnlyList<Letter> letters)
|
2020-01-16 22:43:06 +08:00
|
|
|
|
{
|
|
|
|
|
var builder = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
var maxX = double.MinValue;
|
|
|
|
|
var minX = double.MaxValue;
|
|
|
|
|
var minY = double.MaxValue;
|
|
|
|
|
var maxY = double.MinValue;
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < letters.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var letter = letters[i];
|
|
|
|
|
builder.Append(letter.Value);
|
|
|
|
|
|
|
|
|
|
if (letter.StartBaseLine.X > maxX)
|
|
|
|
|
{
|
|
|
|
|
maxX = letter.StartBaseLine.X;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (letter.StartBaseLine.Y < minY)
|
|
|
|
|
{
|
|
|
|
|
minY = letter.StartBaseLine.Y;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-17 19:03:09 +08:00
|
|
|
|
var right = letter.StartBaseLine.X - letter.GlyphRectangle.Height;
|
2020-01-16 22:43:06 +08:00
|
|
|
|
if (right < minX)
|
|
|
|
|
{
|
|
|
|
|
minX = right;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (letter.GlyphRectangle.Bottom > maxY)
|
|
|
|
|
{
|
|
|
|
|
maxY = letter.GlyphRectangle.Bottom;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-16 23:43:01 +08:00
|
|
|
|
return new Tuple<string, PdfRectangle>(builder.ToString(), new PdfRectangle(new PdfPoint(minX, minY),
|
2020-01-16 22:43:06 +08:00
|
|
|
|
new PdfPoint(minX, maxY),
|
|
|
|
|
new PdfPoint(maxX, minY),
|
|
|
|
|
new PdfPoint(maxX, maxY)));
|
2018-11-25 04:51:27 +08:00
|
|
|
|
}
|
2020-01-16 23:43:01 +08:00
|
|
|
|
|
2020-01-17 19:33:59 +08:00
|
|
|
|
private Tuple<string, PdfRectangle> GetBoundingBoxOther(IReadOnlyList<Letter> letters)
|
2020-01-16 23:43:01 +08:00
|
|
|
|
{
|
|
|
|
|
var builder = new StringBuilder();
|
|
|
|
|
for (var i = 0; i < letters.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
builder.Append(letters[i].Value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var minX = letters.Min(l => Min(l.GlyphRectangle.BottomLeft.X,
|
|
|
|
|
l.GlyphRectangle.BottomRight.X,
|
|
|
|
|
l.GlyphRectangle.TopLeft.X,
|
|
|
|
|
l.GlyphRectangle.TopRight.X));
|
|
|
|
|
var maxX = letters.Max(l => Max(l.GlyphRectangle.BottomLeft.X,
|
|
|
|
|
l.GlyphRectangle.BottomRight.X,
|
|
|
|
|
l.GlyphRectangle.TopLeft.X,
|
|
|
|
|
l.GlyphRectangle.TopRight.X));
|
|
|
|
|
|
|
|
|
|
var minY = letters.Min(l => Min(l.GlyphRectangle.BottomLeft.Y,
|
|
|
|
|
l.GlyphRectangle.BottomRight.Y,
|
|
|
|
|
l.GlyphRectangle.TopLeft.Y,
|
|
|
|
|
l.GlyphRectangle.TopRight.Y));
|
|
|
|
|
var maxY = letters.Max(l => Max(l.GlyphRectangle.BottomLeft.Y,
|
|
|
|
|
l.GlyphRectangle.BottomRight.Y,
|
|
|
|
|
l.GlyphRectangle.TopLeft.Y,
|
|
|
|
|
l.GlyphRectangle.TopRight.Y));
|
|
|
|
|
|
|
|
|
|
return new Tuple<string, PdfRectangle>(builder.ToString(), new PdfRectangle(minX, minY, maxX, maxY));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double Min(double d1, double d2, double d3, double d4)
|
|
|
|
|
{
|
|
|
|
|
return Math.Min(d1, Math.Min(d2, Math.Min(d3, d4)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double Max(double d1, double d2, double d3, double d4)
|
|
|
|
|
{
|
|
|
|
|
return Math.Max(d1, Math.Max(d2, Math.Max(d3, d4)));
|
|
|
|
|
}
|
2020-01-16 22:43:06 +08:00
|
|
|
|
#endregion
|
2018-11-25 04:51:27 +08:00
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
return Text;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|