mirror of
https://github.com/UglyToad/PdfPig.git
synced 2026-03-10 00:23:29 +08:00
Some checks failed
Build, test and publish draft / build (push) Has been cancelled
Build and test [MacOS] / build (push) Has been cancelled
Run Common Crawl Tests / build (0000-0001) (push) Has been cancelled
Run Common Crawl Tests / build (0002-0003) (push) Has been cancelled
Run Common Crawl Tests / build (0004-0005) (push) Has been cancelled
Run Common Crawl Tests / build (0006-0007) (push) Has been cancelled
Run Common Crawl Tests / build (0008-0009) (push) Has been cancelled
Run Common Crawl Tests / build (0010-0011) (push) Has been cancelled
Run Common Crawl Tests / build (0012-0013) (push) Has been cancelled
Run Integration Tests / build (push) Has been cancelled
Tag Release / tag_if_version_changed (push) Has been cancelled
Nightly Release / Check if this commit has already been published (push) Has been cancelled
Nightly Release / tests (push) Has been cancelled
Nightly Release / build_and_publish_nightly (push) Has been cancelled
* Code review changes - Keep the Bounds property on the image classes so this isn't a major breaking API change - Don't expose letters collection * Minor fix * Switch to using BoundingBox in the library --------- Co-authored-by: davmarksman <david@brokit.co.uk>
943 lines
50 KiB
C#
943 lines
50 KiB
C#
namespace UglyToad.PdfPig.Tests.Writer.Fonts
|
||
{
|
||
using PdfPig.Fonts;
|
||
using PdfPig.Content;
|
||
using UglyToad.PdfPig.Core;
|
||
using UglyToad.PdfPig.Fonts.Standard14Fonts;
|
||
using UglyToad.PdfPig.Writer;
|
||
|
||
using System.Reflection;
|
||
using UglyToad.PdfPig.Fonts.AdobeFontMetrics;
|
||
using System.Diagnostics;
|
||
|
||
public class Standard14WritingFontTests
|
||
{
|
||
[Fact]
|
||
public void ZapfDingbatsFontAddText()
|
||
{
|
||
PdfDocumentBuilder pdfBuilder = new PdfDocumentBuilder();
|
||
PdfDocumentBuilder.AddedFont F1 = pdfBuilder.AddStandard14Font(Standard14Font.ZapfDingbats);
|
||
var encodingTable = GetEncodingTable(typeof(UglyToad.PdfPig.Fonts.Encodings.ZapfDingbatsEncoding));
|
||
var unicodesCharacters = GetUnicodeCharacters(encodingTable, GlyphList.ZapfDingbats);
|
||
{
|
||
PdfDocumentBuilder.AddedFont F2 = pdfBuilder.AddStandard14Font(Standard14Font.TimesRoman);
|
||
PdfPageBuilder page = pdfBuilder.AddPage(PageSize.A4);
|
||
|
||
double topPageY = page.PageSize.Top - 50;
|
||
double inch = (page.PageSize.Width / 8.5);
|
||
double cm = inch / 2.54;
|
||
double leftX = 1 * cm;
|
||
|
||
var point = new PdfPoint(leftX, topPageY);
|
||
DateTimeStampPage(pdfBuilder, page, point, cm);
|
||
var letters = page.AddText("Adobe Standard Font ZapfDingbats", 21, point, F2);
|
||
var newY = topPageY - letters.Select(v => v.BoundingBox.Height).Max() * 1.2;
|
||
point = new PdfPoint(leftX, newY);
|
||
letters = page.AddText("Font Specific encoding in Black (octal) and Unicode in Blue (hex)", 10, point, F2);
|
||
newY = newY - letters.Select(v => v.BoundingBox.Height).Max() * 3;
|
||
point = new PdfPoint(leftX, newY);
|
||
var eachRowY = new List<double>();
|
||
eachRowY.Add(newY); // First row
|
||
|
||
(var maxCharacterHeight, var maxCharacterWidth) = GetCharacterDetails(page, F1, 12d, unicodesCharacters);
|
||
var context = GetContext(F1, page, nameof(F1), F2, maxCharacterHeight, maxCharacterWidth);
|
||
|
||
// Font specific character codes (in black)
|
||
page.SetTextAndFillColor(0, 0, 0); //Black
|
||
foreach ((var code, var name) in encodingTable)
|
||
{
|
||
var ch = (char)code; // Note code is already base 10 no need to use OctalHelpers.FromOctalInt or System.Convert.ToInt32($"{code}", 8);
|
||
point = AddLetterWithContext(point, $"{ch}", context, true);
|
||
|
||
if (eachRowY.Last() != point.Y) { eachRowY.Add(point.Y); }
|
||
}
|
||
|
||
// Second set of rows for (unicode) characters : Test mapping from (C#) unicode chars to PDF encoding
|
||
newY = newY - maxCharacterHeight * 1.2;
|
||
point = new PdfPoint(leftX, newY);
|
||
|
||
// Unicode character codes (in blue)
|
||
page.SetTextAndFillColor(0, 0, 200); //Blue
|
||
foreach (var unicodeCh in unicodesCharacters)
|
||
{
|
||
point = AddLetterWithContext(point, $"{unicodeCh}", context, isHexLabel: true);
|
||
}
|
||
}
|
||
|
||
// Save one page PDF to file system for manual review.
|
||
var pdfBytes = pdfBuilder.Build();
|
||
WritePdfFile(nameof(ZapfDingbatsFontAddText), pdfBytes);
|
||
|
||
|
||
// Check extracted letters
|
||
using (var document = PdfDocument.Open(pdfBytes))
|
||
{
|
||
var page1 = document.GetPage(1);
|
||
var letters = page1.Letters;
|
||
|
||
{
|
||
var lettersFontSpecificCodes = letters.Where(l => l.FontName == "ZapfDingbats"
|
||
&& l.Color.ToRGBValues().b == 0)
|
||
.ToList();
|
||
|
||
|
||
Assert.Equal(188, lettersFontSpecificCodes.Count);
|
||
for (int i = 0; i < lettersFontSpecificCodes.Count; i++)
|
||
{
|
||
var letter = lettersFontSpecificCodes[i];
|
||
|
||
(var code, var name) = encodingTable[i];
|
||
var unicodeString = GlyphList.ZapfDingbats.NameToUnicode(name);
|
||
|
||
var letterCharacter = letter.Value[0];
|
||
var unicodeCharacter = unicodeString[0];
|
||
Assert.Equal(letterCharacter, unicodeCharacter);
|
||
//Debug.WriteLine($"{letterCharacter} , {unicodeCharacter}");
|
||
}
|
||
}
|
||
|
||
{
|
||
var lettersUnicode = letters.Where(l => l.FontName == "ZapfDingbats"
|
||
&& l.Color.ToRGBValues().b > 0.78)
|
||
.ToList();
|
||
Assert.Equal(188, lettersUnicode.Count);
|
||
for (int i = 0; i < lettersUnicode.Count; i++)
|
||
{
|
||
var letter = lettersUnicode[i];
|
||
|
||
var letterCharacter = letter.Value[0];
|
||
var unicodeCharacter = unicodesCharacters[i];
|
||
Assert.Equal(letterCharacter, unicodeCharacter);
|
||
//Debug.WriteLine($"{letterCharacter} , {unicodeCharacter}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void ZapfDingbatsFontErrorResponseAddingInvalidText()
|
||
{
|
||
PdfDocumentBuilder pdfBuilder = new PdfDocumentBuilder();
|
||
PdfDocumentBuilder.AddedFont F1 = pdfBuilder.AddStandard14Font(Standard14Font.ZapfDingbats);
|
||
var EncodingTable = GetEncodingTable(typeof(UglyToad.PdfPig.Fonts.Encodings.ZapfDingbatsEncoding));
|
||
|
||
{
|
||
PdfPageBuilder page = pdfBuilder.AddPage(PageSize.A4);
|
||
var cm = (page.PageSize.Width / 8.5 / 2.54);
|
||
var point = new PdfPoint(cm, page.PageSize.Top - cm);
|
||
|
||
{
|
||
// Get the codes that have no character associated in the font specific coding.
|
||
var codesUnder255 = Enumerable.Range(0, 255).Select(v => (char)v).ToArray();
|
||
var codesFromEncodingTable = EncodingTable.Select(v => (char)v.code).ToArray();
|
||
var invalidCharactersUnder255 = codesUnder255.Except(codesFromEncodingTable);
|
||
//Debug.WriteLine($"Number of invalid under 255 characters: {invalidCharactersUnder255.Count()}");
|
||
Assert.Equal(67, invalidCharactersUnder255.Count());
|
||
foreach (var ch in invalidCharactersUnder255)
|
||
{
|
||
try
|
||
{
|
||
var letter = page.AddText($"{ch}", 12, point, F1);
|
||
Assert.True(true, $"Unexpected. Character: '{ch}' (0x{(int)ch:X}) should throw. Not supported.");
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
// Expected
|
||
// "The font does not contain a character: '?' (0xnn)." where ? is a character and nn is hex number.
|
||
Assert.Contains("The font does not contain a character", ex.Message);
|
||
}
|
||
try
|
||
{
|
||
var letter = page.MeasureText($"{ch}", 12, point, F1);
|
||
Assert.True(true, $"Unexpected. Character: '{ch}' (0x{(int)ch:X}) should throw. Not supported.");
|
||
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
// Expected
|
||
// "The font does not contain a character: '?' (0xnn)." where ? is a character and nn is hex number.
|
||
Assert.Contains("The font does not contain a character", ex.Message);
|
||
}
|
||
}
|
||
}
|
||
|
||
{
|
||
// UnicodeRanges.Dingbats - 0x2700 - 0x27BF
|
||
var codesFromUnicodeDingbatBlock = Enumerable.Range(0x2700, 0xBF).Select(v => (char)v).ToArray();
|
||
var unicodesCharacters = GetUnicodeCharacters(EncodingTable, GlyphList.ZapfDingbats);
|
||
var invalidCharactersInUnicodeDingbaBlock = codesFromUnicodeDingbatBlock.Except(unicodesCharacters);
|
||
//Debug.WriteLine($"Number of invalid unicode characters: {invalidCharactersInUnicodeDingbaBlock.Count()}");
|
||
Assert.Equal(31, invalidCharactersInUnicodeDingbaBlock.Count());
|
||
foreach (var ch in invalidCharactersInUnicodeDingbaBlock)
|
||
{
|
||
try
|
||
{
|
||
var letter = page.AddText($"{ch}", 12, point, F1);
|
||
Assert.True(true, $"Unexpected. Character: '{ch}' (0x{(int)ch:X}) should throw. Not supported.");
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
// Expected
|
||
// "The font does not contain a character: '?' (0xnn)." where ? is a character and nn is hex number.
|
||
Assert.Contains("The font does not contain a character", ex.Message);
|
||
}
|
||
try
|
||
{
|
||
var letter = page.MeasureText($"{ch}", 12, point, F1);
|
||
Assert.True(true, $"Unexpected. Character: '{ch}' (0x{(int)ch:X}) should throw. Not supported.");
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
// Expected
|
||
// "The font does not contain a character: '?' (0xnn)." where ? is a character and nn is hex number.
|
||
Assert.Contains("The font does not contain a character", ex.Message);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void SymbolFontAddText()
|
||
{
|
||
PdfDocumentBuilder pdfBuilder = new PdfDocumentBuilder();
|
||
PdfDocumentBuilder.AddedFont F1 = pdfBuilder.AddStandard14Font(Standard14Font.Symbol);
|
||
var EncodingTable = GetEncodingTable(typeof(UglyToad.PdfPig.Fonts.Encodings.SymbolEncoding));
|
||
var unicodesCharacters = GetUnicodeCharacters(EncodingTable, GlyphList.AdobeGlyphList);
|
||
{
|
||
PdfDocumentBuilder.AddedFont F2 = pdfBuilder.AddStandard14Font(Standard14Font.TimesRoman);
|
||
PdfPageBuilder page = pdfBuilder.AddPage(PageSize.A4);
|
||
|
||
double topPageY = page.PageSize.Top - 50;
|
||
double inch = (page.PageSize.Width / 8.5);
|
||
double cm = inch / 2.54;
|
||
double leftX = 1 * cm;
|
||
|
||
var point = new PdfPoint(leftX, topPageY);
|
||
DateTimeStampPage(pdfBuilder, page, point, cm);
|
||
var letters = page.AddText("Adobe Standard Font Symbol ", 21, point, F2);
|
||
var newY = topPageY - letters.Select(v => v.BoundingBox.Height).Max() * 1.2;
|
||
point = new PdfPoint(leftX, newY);
|
||
letters = page.AddText("Font Specific encoding in Black (octal), Unicode in Blue (hex), Red only available using Unicode", 10, point, F2);
|
||
newY = newY - letters.Select(v => v.BoundingBox.Height).Max() * 3;
|
||
|
||
|
||
|
||
(var maxCharacterHeight, var maxCharacterWidth) = GetCharacterDetails(page, F1, 12d, unicodesCharacters);
|
||
var context = GetContext(F1, page, nameof(F1), F2, maxCharacterHeight, maxCharacterWidth);
|
||
|
||
// First set of rows for direct PDF font specific character codes
|
||
newY = newY - maxCharacterHeight;
|
||
point = new PdfPoint(leftX, newY);
|
||
var eachRowY = new List<double>(new[] { newY });
|
||
page.SetTextAndFillColor(0, 0, 0); //Black
|
||
bool isTextColorBlack = true;
|
||
foreach ((var codeFontSpecific, var name) in EncodingTable)
|
||
{
|
||
var code = codeFontSpecific; // Code is already converted [neither OctalHelpers.FromOctalInt or System.Convert.ToInt32($"{code}", 8); is required]
|
||
// For a clash library uses unicode interpretation.
|
||
// Substitue if code is any of the 4 codes that clash (in Unicode and font specific encodes for Symbol)
|
||
if (code == 0xac) code = '\u2190'; // 0xac in unicode is logicalnot ('¬') use Unicode alternative for arrowleft ('←') 0x2190
|
||
if (code == 0xf7) code = '\uf8f7'; // 0xf7 in unicode is divide ('÷') (different form '/') use Unicode alternative for parenrightex Unicode 0xF8F7
|
||
if (code == 0xb5) code = '\u221D'; // 0xb5 in unicode is lowercase mu ('µ') use Unicode alternative for proportiona('∝') 0x221D
|
||
if (code == 0xd7) code = '\u22c5'; // 0xd7 in unicode is muliply ('×') (different from '*') use Unicode alternative for dotmath ('⋅') 0x22C5
|
||
if (code != codeFontSpecific && isTextColorBlack) { page.SetTextAndFillColor(200, 0, 0); isTextColorBlack = false; }
|
||
if (code == codeFontSpecific && isTextColorBlack == false) { page.SetTextAndFillColor(0, 0, 0); isTextColorBlack = true; }
|
||
|
||
char ch = (char)code;
|
||
point = AddLetterWithContext(point, $"{ch}", context, isTextColorBlack);
|
||
if (eachRowY.Last() != point.Y) { eachRowY.Add(point.Y); }
|
||
}
|
||
|
||
// Second set of rows for (unicode) characters : Test mapping from (C#) unicode chars to font specific encoding
|
||
newY = newY - maxCharacterHeight * 1.2;
|
||
point = new PdfPoint(leftX, newY);
|
||
|
||
page.SetTextAndFillColor(0, 0, 200); //Blue
|
||
foreach (var unicodeCh in unicodesCharacters)
|
||
{
|
||
point = AddLetterWithContext(point, $"{unicodeCh}", context, isHexLabel: true);
|
||
}
|
||
}
|
||
|
||
// Save two page PDF to file system for manual review.
|
||
var pdfBytes = pdfBuilder.Build();
|
||
WritePdfFile(nameof(SymbolFontAddText), pdfBytes);
|
||
|
||
|
||
// Check extracted letters
|
||
using (var document = PdfDocument.Open(pdfBytes))
|
||
{
|
||
var page1 = document.GetPage(1);
|
||
var letters = page1.Letters;
|
||
|
||
{
|
||
var lettersFontSpecificCodes = letters.Where(l => l.FontName == "Symbol"
|
||
&& l.Color.ToRGBValues().b == 0
|
||
&& (l.Color.ToRGBValues().b == 0
|
||
|| l.Color.ToRGBValues().r == 200)
|
||
)
|
||
.ToList();
|
||
|
||
|
||
Assert.Equal(189, lettersFontSpecificCodes.Count);
|
||
Assert.Equal(EncodingTable.Length, lettersFontSpecificCodes.Count);
|
||
for (int i = 0; i < lettersFontSpecificCodes.Count; i++)
|
||
{
|
||
var letter = lettersFontSpecificCodes[i];
|
||
|
||
(var code, var name) = EncodingTable[i];
|
||
var unicodeString = GlyphList.AdobeGlyphList.NameToUnicode(name);
|
||
|
||
var letterCharacter = letter.Value[0];
|
||
var unicodeCharacter = unicodeString[0];
|
||
//Debug.WriteLine($"{letterCharacter} , {unicodeCharacter}");
|
||
Assert.Equal(letterCharacter, unicodeCharacter);
|
||
}
|
||
}
|
||
|
||
{
|
||
var lettersUnicode = letters.Where(l => l.FontName == "Symbol"
|
||
&& l.Color.ToRGBValues().b > 0.78)
|
||
.ToList();
|
||
Assert.Equal(189, lettersUnicode.Count);
|
||
for (int i = 0; i < lettersUnicode.Count; i++)
|
||
{
|
||
var letter = lettersUnicode[i];
|
||
|
||
var letterCharacter = letter.Value[0];
|
||
var unicodeCharacter = unicodesCharacters[i];
|
||
//Debug.WriteLine($"{letterCharacter} , {unicodeCharacter}");
|
||
Assert.Equal(letterCharacter, unicodeCharacter);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void SymbolFontErrorResponseAddingInvalidText()
|
||
{
|
||
PdfDocumentBuilder pdfBuilder = new PdfDocumentBuilder();
|
||
PdfDocumentBuilder.AddedFont F1 = pdfBuilder.AddStandard14Font(Standard14Font.Symbol);
|
||
var EncodingTable = GetEncodingTable(typeof(UglyToad.PdfPig.Fonts.Encodings.SymbolEncoding));
|
||
|
||
{
|
||
PdfPageBuilder page = pdfBuilder.AddPage(PageSize.A4);
|
||
var cm = (page.PageSize.Width / 8.5 / 2.54);
|
||
var point = new PdfPoint(cm, page.PageSize.Top - cm);
|
||
|
||
{
|
||
// Get the codes that have no character associated in the font specific coding.
|
||
var codesUnder255 = Enumerable.Range(0, 255).Select(v => (char)v).ToArray();
|
||
var codesFromEncodingTable = EncodingTable.Select(v => (char)v.code).ToArray();
|
||
var invalidCharactersUnder255 = codesUnder255.Except(codesFromEncodingTable);
|
||
Debug.WriteLine($"Number of invalid under 255 characters: {invalidCharactersUnder255.Count()}");
|
||
foreach (var ch in invalidCharactersUnder255)
|
||
{
|
||
try
|
||
{
|
||
var letter = page.AddText($"{ch}", 12, point, F1);
|
||
Assert.True(true, $"Unexpected. Character: '{ch}' (0x{(int)ch:X}) should throw. Not supported.");
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
// Expected
|
||
// "The font does not contain a character: '?' (0xnn)." where ? is a character and nn is hex number.
|
||
Assert.Contains("The font does not contain a character", ex.Message);
|
||
}
|
||
try
|
||
{
|
||
var letter = page.MeasureText($"{ch}", 12, point, F1);
|
||
Assert.True(true, $"Unexpected. Character: '{ch}' (0x{(int)ch:X}) should throw. Not supported.");
|
||
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
// Expected
|
||
// "The font does not contain a character: '?' (0xnn)." where ? is a character and nn is hex number.
|
||
Assert.Contains("The font does not contain a character", ex.Message);
|
||
}
|
||
}
|
||
}
|
||
|
||
{
|
||
var unicodesCharacters = GetUnicodeCharacters(EncodingTable, GlyphList.AdobeGlyphList);
|
||
|
||
var randomCharacters = new char[10];
|
||
{
|
||
var listUnicodeCharacters = unicodesCharacters.Select(v => (int)v).ToList();
|
||
var rnd = new Random();
|
||
int nextIndex = 0;
|
||
while (nextIndex < randomCharacters.Length)
|
||
{
|
||
var value = rnd.Next(0x10ffff);
|
||
|
||
if (listUnicodeCharacters.Contains(value)) { continue; }
|
||
char ch = (char)value;
|
||
int i = (int)ch;
|
||
if (i >= 0xd800 && i <= 0xdfff) { continue; }
|
||
randomCharacters[nextIndex++] = ch;
|
||
Debug.WriteLine($"{value:X}");
|
||
}
|
||
}
|
||
foreach (var ch in randomCharacters)
|
||
{
|
||
int i = (int)ch;
|
||
if (i > 0x10ffff)
|
||
{
|
||
Debug.WriteLine("Unexpected unicode point. Too large to be unicode. Expected: <0x10ffff. Got: 0x{i:X}");
|
||
continue;
|
||
}
|
||
if (i >= 0xd800 && i <= 0xdfff)
|
||
{
|
||
Debug.WriteLine("Unexpected unicode point that is not a surrogate Expected: <0xd800 && >0xdfff. Got: 0x{i:X}");
|
||
continue;
|
||
}
|
||
try
|
||
{
|
||
var letter = page.AddText($"{ch}", 12, point, F1);
|
||
Assert.True(true, $"Unexpected. Character: '{ch}' (0x{(int)ch:X}) should throw. Not supported.");
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
// Expected
|
||
// "The font does not contain a character: '?' (0xnn)." where ? is a character and nn is hex number.
|
||
Assert.Contains("The font does not contain a character", ex.Message);
|
||
}
|
||
try
|
||
{
|
||
var letter = page.MeasureText($"{ch}", 12, point, F1);
|
||
Assert.True(true, $"Unexpected. Character: '{ch}' (0x{(int)ch:X}) should throw. Not supported.");
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
// Expected
|
||
// "The font does not contain a character: '?' (0xnn)." where ? is a character and nn is hex number.
|
||
Assert.Contains("The font does not contain a character", ex.Message);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
[Fact]
|
||
public void StandardFontsAddText()
|
||
{
|
||
PdfDocumentBuilder pdfBuilder = new PdfDocumentBuilder();
|
||
PdfDocumentBuilder.AddedFont F1 = pdfBuilder.AddStandard14Font(Standard14Font.TimesRoman);
|
||
PdfDocumentBuilder.AddedFont F2 = pdfBuilder.AddStandard14Font(Standard14Font.TimesBold);
|
||
PdfDocumentBuilder.AddedFont F3 = pdfBuilder.AddStandard14Font(Standard14Font.TimesItalic);
|
||
PdfDocumentBuilder.AddedFont F4 = pdfBuilder.AddStandard14Font(Standard14Font.TimesBoldItalic);
|
||
PdfDocumentBuilder.AddedFont F5 = pdfBuilder.AddStandard14Font(Standard14Font.Helvetica);
|
||
PdfDocumentBuilder.AddedFont F6 = pdfBuilder.AddStandard14Font(Standard14Font.HelveticaBold);
|
||
PdfDocumentBuilder.AddedFont F7 = pdfBuilder.AddStandard14Font(Standard14Font.HelveticaOblique);
|
||
PdfDocumentBuilder.AddedFont F8 = pdfBuilder.AddStandard14Font(Standard14Font.HelveticaBoldOblique);
|
||
PdfDocumentBuilder.AddedFont F9 = pdfBuilder.AddStandard14Font(Standard14Font.Courier);
|
||
PdfDocumentBuilder.AddedFont F10 = pdfBuilder.AddStandard14Font(Standard14Font.CourierBold);
|
||
PdfDocumentBuilder.AddedFont F11 = pdfBuilder.AddStandard14Font(Standard14Font.CourierOblique);
|
||
PdfDocumentBuilder.AddedFont F12 = pdfBuilder.AddStandard14Font(Standard14Font.CourierBoldOblique);
|
||
|
||
var standardFontsWithStandardEncoding = new PdfDocumentBuilder.AddedFont[]
|
||
{
|
||
F1,
|
||
F2,
|
||
F3,
|
||
F4,
|
||
F5,
|
||
F6,
|
||
F7,
|
||
F8,
|
||
F9,
|
||
F10,
|
||
F11,
|
||
F12
|
||
};
|
||
|
||
//AddLetterWithFont(page, point, "v", F1, nameof(F1));
|
||
//AddLetterWithFont(page, point, "v", F2, nameof(F2));
|
||
//AddLetterWithFont(page, point, "v", F3, nameof(F3));
|
||
//AddLetterWithFont(page, point, "v", F4, nameof(F4));
|
||
//AddLetterWithFont(page, point, "v", F5, nameof(F5));
|
||
//AddLetterWithFont(page, point, "v", F6, nameof(F6));
|
||
//AddLetterWithFont(page, point, "v", F7, nameof(F7));
|
||
//AddLetterWithFont(page, point, "v", F8, nameof(F8));
|
||
//AddLetterWithFont(page, point, "v", F9, nameof(F9));
|
||
//AddLetterWithFont(page, point, "v", F10, nameof(F10));
|
||
//AddLetterWithFont(page, point, "v", F11, nameof(F11));
|
||
//AddLetterWithFont(page, point, "v", F12, nameof(F12));
|
||
|
||
|
||
// Get all characters codes in font using existing metrics in (private) Standard14Cache class (using reflection).
|
||
var Standard14Cache = GetStandard14Cache();
|
||
|
||
// All 12 fonts should conform to 'StanardEncoding'
|
||
var EncodingTable = ((int code, string name)[])GetEncodingTable(typeof(UglyToad.PdfPig.Fonts.Encodings.StandardEncoding));
|
||
var unicodesCharacters = GetUnicodeCharacters(EncodingTable, GlyphList.AdobeGlyphList);
|
||
|
||
int fontNumber = 0;
|
||
foreach (var font in standardFontsWithStandardEncoding)
|
||
{
|
||
fontNumber++;
|
||
var storedFont = pdfBuilder.Fonts[font.Id];
|
||
var fontProgram = storedFont.FontProgram;
|
||
var fontName = fontProgram.Name;
|
||
|
||
{
|
||
PdfPageBuilder page = pdfBuilder.AddPage(PageSize.A4);
|
||
|
||
double topPageY = page.PageSize.Top - 50;
|
||
double inch = (page.PageSize.Width / 8.5);
|
||
double cm = inch / 2.54;
|
||
double leftX = 1 * cm;
|
||
|
||
var point = new PdfPoint(leftX, topPageY);
|
||
DateTimeStampPage(pdfBuilder, page, point, cm);
|
||
var letters = page.AddText("Adobe Standard Font " + fontName, 21, point, F2);
|
||
var newY = topPageY - letters.Select(v => v.BoundingBox.Height).Max() * 1.2;
|
||
point = new PdfPoint(leftX, newY);
|
||
letters = page.AddText("Font Specific encoding in Black, Unicode in Blue, Red only available using Unicode", 10, point, F2);
|
||
newY = newY - letters.Select(v => v.BoundingBox.Height).Max() * 3;
|
||
point = new PdfPoint(leftX, newY);
|
||
|
||
|
||
var eachRowY = new List<double>(new[] { newY });
|
||
|
||
var metrics = Standard14Cache[fontName];
|
||
|
||
var codesFromMetrics = new HashSet<int>();
|
||
page.SetTextAndFillColor(0, 0, 0); //Black
|
||
|
||
(var maxCharacterHeight, var maxCharacterWidth) = GetCharacterDetails(page, F1, 12d, unicodesCharacters);
|
||
var context = GetContext(font, page, $"F{fontNumber}", F2, maxCharacterHeight, maxCharacterWidth);
|
||
|
||
// Detect if all codes in Standard encoding table are in metrics for font.
|
||
bool isMissing = false;
|
||
bool isTextColorBlack = true;
|
||
foreach ((var codeNotBase8Converted, var name) in EncodingTable)
|
||
{
|
||
var codeFontSpecific = Convert.ToInt32($"{codeNotBase8Converted}", 8);
|
||
var code = codeFontSpecific;
|
||
if (codeFontSpecific == 0xc6) { code = 0x02D8; }
|
||
else if (codeFontSpecific == 0xb4) { code = 0x00b7; }
|
||
else if (codeFontSpecific == 0xb7) { code = 0x2022; }
|
||
else if (codeFontSpecific == 0xb8) { code = 0x201A; }
|
||
else if (codeFontSpecific == 0xa4) { code = 0x2044; }
|
||
else if (codeFontSpecific == 0xa8) { code = 0x00a4; }
|
||
else if (codeFontSpecific == 0x60) { code = 0x2018; }
|
||
else if (codeFontSpecific == 0xaf) { code = 0xFB02; }
|
||
else if (codeFontSpecific == 0xaa) { code = 0x201C; }
|
||
else if (codeFontSpecific == 0xba) { code = 0x201D; }
|
||
else if (codeFontSpecific == 0xf8) { code = 0x0142; }
|
||
else if (codeFontSpecific == 0x27) { code = 0x2019; }
|
||
if (code != codeFontSpecific && isTextColorBlack) { page.SetTextAndFillColor(200, 0, 0); isTextColorBlack = false; }
|
||
if (code == codeFontSpecific && isTextColorBlack == false) { page.SetTextAndFillColor(0, 0, 0); isTextColorBlack = true; }
|
||
|
||
char ch = (char)code;
|
||
point = AddLetterWithContext(point, $"{ch}", context, isTextColorBlack);
|
||
|
||
if (eachRowY.Last() != point.Y) { eachRowY.Add(point.Y); }
|
||
}
|
||
|
||
foreach (var metric in metrics.CharacterMetrics)
|
||
{
|
||
var code = metric.Value.CharacterCode;
|
||
if (code == -1) continue;
|
||
codesFromMetrics.Add(code);
|
||
}
|
||
|
||
foreach ((var codeNotBase8Converted, var name) in EncodingTable)
|
||
{
|
||
var codeBase10 = System.Convert.ToInt32($"{codeNotBase8Converted}", 8);
|
||
if (codesFromMetrics.Contains(codeBase10) == false)
|
||
{
|
||
var ch = (char)codeBase10;
|
||
isMissing = true;
|
||
Debug.WriteLine($"In Adobe Standard Font '{fontName}' code {codeBase10} is in Standard encoding table but not in font metrics.");
|
||
}
|
||
}
|
||
|
||
Assert.False(isMissing, $"Adobe Standard Font '{fontName}' contains code(s) in Standard encoding table but not in font metrics. See Debug output for details.");
|
||
|
||
// Second set of rows for (unicode) characters : Test mapping from (C#) unicode chars to PDF encoding
|
||
newY = newY - maxCharacterHeight * 1.2;
|
||
point = new PdfPoint(leftX, newY);
|
||
page.SetTextAndFillColor(0, 0, 200); //Blue
|
||
foreach (var unicodeCh in unicodesCharacters)
|
||
{
|
||
point = AddLetterWithContext(point, $"{unicodeCh}", context, isHexLabel: true);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Save one page per standard font to file system for manual review.
|
||
var pdfBytes = pdfBuilder.Build();
|
||
WritePdfFile($"{nameof(StandardFontsAddText)}", pdfBytes);
|
||
|
||
// Check extracted letters
|
||
using (var document = PdfDocument.Open(pdfBytes))
|
||
{
|
||
foreach (var page in document.GetPages())
|
||
{
|
||
var letters = page.Letters;
|
||
var expectedFontName = letters.FirstOrDefault(l => l.FontSize == 12d).FontName;
|
||
|
||
|
||
{
|
||
var lettersFontSpecificCodes = letters.Where(l => l.FontName == expectedFontName
|
||
&& l.FontSize == 12d
|
||
&& (l.Color.ToRGBValues().b == 0
|
||
|| l.Color.ToRGBValues().r == 200)
|
||
)
|
||
.ToList();
|
||
|
||
|
||
Assert.Equal(149, lettersFontSpecificCodes.Count);
|
||
Assert.Equal(lettersFontSpecificCodes.Count, EncodingTable.Length);
|
||
for (int i = 0; i < lettersFontSpecificCodes.Count; i++)
|
||
{
|
||
var letter = lettersFontSpecificCodes[i];
|
||
|
||
(var code, var name) = EncodingTable[i];
|
||
var unicodeString = GlyphList.AdobeGlyphList.NameToUnicode(name);
|
||
|
||
var letterCharacter = letter.Value[0];
|
||
var unicodeCharacter = unicodeString[0];
|
||
if (letterCharacter != unicodeCharacter) Debug.WriteLine($"{letterCharacter} , {unicodeCharacter}");
|
||
Assert.Equal(unicodeCharacter, letterCharacter);
|
||
}
|
||
}
|
||
|
||
{
|
||
var lettersUnicode = letters.Where(l => l.FontName == expectedFontName
|
||
&& l.FontSize == 12d
|
||
&& l.Color.ToRGBValues().b > 0.78)
|
||
.ToList();
|
||
Assert.Equal(149, lettersUnicode.Count);
|
||
for (int i = 0; i < lettersUnicode.Count; i++)
|
||
{
|
||
var letter = lettersUnicode[i];
|
||
|
||
var letterCharacter = letter.Value[0];
|
||
var unicodeCharacter = unicodesCharacters[i];
|
||
//Debug.WriteLine($"{letterCharacter} , {unicodeCharacter}");
|
||
Assert.Equal(unicodeCharacter, letterCharacter);
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
[Fact]
|
||
public void StandardFontErrorResponseAddingInvalidText()
|
||
{
|
||
PdfDocumentBuilder pdfBuilder = new PdfDocumentBuilder();
|
||
PdfPageBuilder page = pdfBuilder.AddPage(PageSize.A4);
|
||
var cm = (page.PageSize.Width / 8.5 / 2.54);
|
||
var point = new PdfPoint(cm, page.PageSize.Top - cm);
|
||
|
||
PdfDocumentBuilder.AddedFont[] standardFontsWithStandardEncoding;
|
||
{
|
||
PdfDocumentBuilder.AddedFont F1 = pdfBuilder.AddStandard14Font(Standard14Font.TimesRoman);
|
||
PdfDocumentBuilder.AddedFont F2 = pdfBuilder.AddStandard14Font(Standard14Font.TimesBold);
|
||
PdfDocumentBuilder.AddedFont F3 = pdfBuilder.AddStandard14Font(Standard14Font.TimesItalic);
|
||
PdfDocumentBuilder.AddedFont F4 = pdfBuilder.AddStandard14Font(Standard14Font.TimesBoldItalic);
|
||
PdfDocumentBuilder.AddedFont F5 = pdfBuilder.AddStandard14Font(Standard14Font.Helvetica);
|
||
PdfDocumentBuilder.AddedFont F6 = pdfBuilder.AddStandard14Font(Standard14Font.HelveticaBold);
|
||
PdfDocumentBuilder.AddedFont F7 = pdfBuilder.AddStandard14Font(Standard14Font.HelveticaOblique);
|
||
PdfDocumentBuilder.AddedFont F8 = pdfBuilder.AddStandard14Font(Standard14Font.HelveticaBoldOblique);
|
||
PdfDocumentBuilder.AddedFont F9 = pdfBuilder.AddStandard14Font(Standard14Font.Courier);
|
||
PdfDocumentBuilder.AddedFont F10 = pdfBuilder.AddStandard14Font(Standard14Font.CourierBold);
|
||
PdfDocumentBuilder.AddedFont F11 = pdfBuilder.AddStandard14Font(Standard14Font.CourierOblique);
|
||
PdfDocumentBuilder.AddedFont F12 = pdfBuilder.AddStandard14Font(Standard14Font.CourierBoldOblique);
|
||
|
||
standardFontsWithStandardEncoding = new PdfDocumentBuilder.AddedFont[]
|
||
{
|
||
F1,
|
||
F2,
|
||
F3,
|
||
F4,
|
||
F5,
|
||
F6,
|
||
F7,
|
||
F8,
|
||
F9,
|
||
F10,
|
||
F11,
|
||
F12
|
||
};
|
||
}
|
||
var EncodingTable = GetEncodingTable(typeof(UglyToad.PdfPig.Fonts.Encodings.StandardEncoding));
|
||
|
||
// Get the codes that have no character associated in the font specific coding.
|
||
char[] invalidCharactersUnder255;
|
||
{
|
||
var codesUnder255 = Enumerable.Range(0, 255).Select(v => (char)v).ToArray();
|
||
var codesFromEncodingTable = EncodingTable.Select(v => (char)v.code).ToArray();
|
||
invalidCharactersUnder255 = codesUnder255.Except(codesFromEncodingTable).ToArray();
|
||
Debug.WriteLine($"Number of invalid under 255 characters: {invalidCharactersUnder255.Count()}");
|
||
}
|
||
|
||
// Get random unicodes not valid for any font with Standard encoding.
|
||
var randomUnicodeCharacters = new char[10];
|
||
{
|
||
var unicodesCharacters = GetUnicodeCharacters(EncodingTable, GlyphList.AdobeGlyphList);
|
||
{
|
||
var listUnicodeCharacters = unicodesCharacters.Select(v => (int)v).ToList();
|
||
var rnd = new Random();
|
||
int nextIndex = 0;
|
||
while (nextIndex < randomUnicodeCharacters.Length)
|
||
{
|
||
var value = rnd.Next(0x10ffff);
|
||
|
||
if (listUnicodeCharacters.Contains(value)) { continue; }
|
||
char ch = (char)value;
|
||
int i = (int)ch;
|
||
if (i >= 0xd800 && i <= 0xdfff) { continue; }
|
||
randomUnicodeCharacters[nextIndex++] = ch;
|
||
Debug.WriteLine($"{value:X}");
|
||
}
|
||
}
|
||
}
|
||
|
||
int fontNumber = 0;
|
||
foreach (var font in standardFontsWithStandardEncoding)
|
||
{
|
||
fontNumber++;
|
||
var storedFont = pdfBuilder.Fonts[font.Id];
|
||
var fontProgram = storedFont.FontProgram;
|
||
var fontName = fontProgram.Name;
|
||
|
||
foreach (var ch in invalidCharactersUnder255)
|
||
{
|
||
try
|
||
{
|
||
var letter = page.AddText($"{ch}", 12, point, font);
|
||
Assert.True(true, $"Unexpected. Character: '{ch}' (0x{(int)ch:X}) should throw. Not supported. Font: '{fontName}'");
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
// Expected
|
||
// "The font does not contain a character: '?' (0xnn)." where ? is a character and nn is hex number.
|
||
Assert.Contains("The font does not contain a character", ex.Message);
|
||
}
|
||
try
|
||
{
|
||
var letter = page.MeasureText($"{ch}", 12, point, font);
|
||
Assert.True(true, $"Unexpected. Character: '{ch}' (0x{(int)ch:X}) should throw. Not supported. Font: '{fontName}'");
|
||
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
// Expected
|
||
// "The font does not contain a character: '?' (0xnn)." where ? is a character and nn is hex number.
|
||
Assert.Contains("The font does not contain a character", ex.Message);
|
||
}
|
||
}
|
||
|
||
|
||
foreach (var ch in randomUnicodeCharacters)
|
||
{
|
||
int i = (int)ch;
|
||
if (i > 0x10ffff)
|
||
{
|
||
Debug.WriteLine("Unexpected unicode point. Too large to be unicode. Expected: <0x10ffff. Got: 0x{i:X}");
|
||
continue;
|
||
}
|
||
if (i >= 0xd800 && i <= 0xdfff)
|
||
{
|
||
Debug.WriteLine("Unexpected unicode point that is not a surrogate Expected: <0xd800 && >0xdfff. Got: 0x{i:X}");
|
||
continue;
|
||
}
|
||
try
|
||
{
|
||
var letter = page.AddText($"{ch}", 12, point, font);
|
||
Assert.True(true, $"Unexpected. Character: '{ch}' (0x{(int)ch:X}) should throw. Not supported.");
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
// Expected
|
||
// "The font does not contain a character: '?' (0xnn)." where ? is a character and nn is hex number.
|
||
Assert.Contains("The font does not contain a character", ex.Message);
|
||
}
|
||
try
|
||
{
|
||
var letter = page.MeasureText($"{ch}", 12, point, font);
|
||
Assert.True(true, "Unexpected. Character: '{ch}' (0x{(int)ch:X}) should throw. Not supported.");
|
||
}
|
||
catch (InvalidOperationException ex)
|
||
{
|
||
// Expected
|
||
// "The font does not contain a character: '?' (0xnn)." where ? is a character and nn is hex number.
|
||
Assert.Contains("The font does not contain a character", ex.Message);
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
internal PdfPoint AddLetterWithContext(PdfPoint point, string stringToAdd, (PdfDocumentBuilder.AddedFont font, PdfPageBuilder page, string fontName, PdfDocumentBuilder.AddedFont fontLabel, double maxCharacterHeight, double maxCharacterWidth) context, bool isOctalLabel = false, bool isHexLabel = false)
|
||
{
|
||
var font = context.font;
|
||
var page = context.page;
|
||
var fontName = context.fontName;
|
||
var fontLabel = context.fontLabel;
|
||
var maxCharacterHeight = context.maxCharacterHeight;
|
||
var maxCharacterWidth = context.maxCharacterWidth;
|
||
|
||
return AddLetter(page, point, stringToAdd, font, fontName, fontLabel, maxCharacterHeight, maxCharacterWidth, isOctalLabel, isHexLabel);
|
||
}
|
||
internal PdfPoint AddLetter(PdfPageBuilder page, PdfPoint point, string stringToAdd, PdfDocumentBuilder.AddedFont font, string fontName, PdfDocumentBuilder.AddedFont fontLabel, double maxCharacterHeight, double maxCharacterWidth, bool isOctalLabel = false, bool isHexLabel = false)
|
||
{
|
||
if (stringToAdd is null) { throw new ArgumentException("Text to add must be a single letter.", nameof(stringToAdd)); }
|
||
if (stringToAdd.Length > 1) { throw new ArgumentException("Text to add must be a single letter.", nameof(stringToAdd)); }
|
||
if (fontName.ToUpper() != fontName) { throw new ArgumentException(@"FontName must be in uppercase eg. ""F1"".", nameof(fontName)); }
|
||
|
||
var letter = page.AddText(stringToAdd, 12, point, font);
|
||
if (isOctalLabel)
|
||
{
|
||
var labelPointSize = 5;
|
||
var octalString = System.Convert.ToString((int)stringToAdd[0], 8).PadLeft(3, '0');
|
||
var label = octalString;
|
||
var codeMidPoint = point.X + letter[0].BoundingBox.Width / 2;
|
||
var ml = page.MeasureText(label, labelPointSize, point, fontLabel);
|
||
var labelY = point.Y + ml.Max(v => v.BoundingBox.Height) * 0.1 + maxCharacterHeight;
|
||
var xLabel = codeMidPoint - (ml.Sum(v => v.BoundingBox.Width) / 2);
|
||
var labelPoint = new PdfPoint(xLabel, labelY);
|
||
page.AddText(label, labelPointSize, labelPoint, fontLabel);
|
||
}
|
||
|
||
if (isHexLabel)
|
||
{
|
||
var labelPointSize = 3;
|
||
var hexString = $"{(int)stringToAdd[0]:X}".PadLeft(4, '0');
|
||
var label = "0x" + hexString;
|
||
var codeMidPoint = point.X + letter[0].BoundingBox.Width / 2;
|
||
var ml = page.MeasureText(label, labelPointSize, point, fontLabel);
|
||
var labelY = point.Y - ml.Max(v => v.BoundingBox.Height) * 2.5;
|
||
var xLabel = codeMidPoint - (ml.Sum(v => v.BoundingBox.Width) / 2);
|
||
var labelPoint = new PdfPoint(xLabel, labelY);
|
||
page.AddText(label, labelPointSize, labelPoint, fontLabel);
|
||
}
|
||
|
||
|
||
Assert.NotNull(letter); // We should get back something.
|
||
Assert.Single(letter); // There should be only one letter returned after the add operation.
|
||
Assert.Equal(stringToAdd, letter[0].Value); // Check we got back the name letter (eg. "v")
|
||
//Debug.WriteLine($"{letter[0]}");
|
||
|
||
double inch = (page.PageSize.Width / 8.5);
|
||
double cm = inch / 2.54;
|
||
|
||
var letterWidth = letter[0].BoundingBox.Width * 2;
|
||
var letterHeight = letter[0].BoundingBox.Height * 2;
|
||
|
||
var newX = point.X + maxCharacterWidth * 1.1;
|
||
var newY = point.Y;
|
||
|
||
if (newX > page.PageSize.Width - cm)
|
||
{
|
||
return newLine(cm, point.Y, maxCharacterHeight);
|
||
}
|
||
return new PdfPoint(newX, newY);
|
||
}
|
||
|
||
PdfPoint newLine(double cm, double y, double maxCharacterHeight)
|
||
{
|
||
var newX = 1 * cm;
|
||
var newY = y - maxCharacterHeight * 5;
|
||
return new PdfPoint(newX, newY);
|
||
}
|
||
|
||
private static void WritePdfFile(string name, byte[] bytes, string extension = "pdf")
|
||
{
|
||
const string subFolder = nameof(Standard14WritingFontTests);
|
||
var folderPath = subFolder;
|
||
|
||
if (!Directory.Exists(folderPath))
|
||
{
|
||
Directory.CreateDirectory(folderPath);
|
||
}
|
||
|
||
var filePath = Path.Combine(folderPath, $"{name}.{extension}");
|
||
File.WriteAllBytes(filePath, bytes);
|
||
Debug.WriteLine($@"{Path.Combine(Directory.GetCurrentDirectory(), filePath)}");
|
||
}
|
||
|
||
private static (int code, string name)[] GetEncodingTable(Type t)
|
||
{
|
||
// Get existing (but private) EncodingTable from encoding class using reflection so we can obtain all codes
|
||
var EncodingTableFieldInfo = t.GetFields(BindingFlags.NonPublic | BindingFlags.Static)
|
||
.FirstOrDefault(v => v.Name == "EncodingTable");
|
||
(int, string)[] EncodingTable = ((int, string)[])EncodingTableFieldInfo.GetValue(Activator.CreateInstance(t, true));
|
||
return EncodingTable;
|
||
}
|
||
|
||
|
||
private (PdfDocumentBuilder.AddedFont font, PdfPageBuilder page, string fontName, PdfDocumentBuilder.AddedFont fontLabel, double maxCharacterHeight, double maxCharacterWidth) GetContext(PdfDocumentBuilder.AddedFont font, PdfPageBuilder page, string fontName, PdfDocumentBuilder.AddedFont fontLabel, double maxCharacterHeight, double maxCharacterWidth)
|
||
{
|
||
return (font, page, fontName, fontLabel, maxCharacterHeight, maxCharacterWidth);
|
||
|
||
}
|
||
|
||
private static char[] GetUnicodeCharacters((int code, string name)[] EncodingTable, GlyphList glyphList)
|
||
{
|
||
var gylphNamesFromEncodingTable = EncodingTable.Select(v => v.name).ToArray();
|
||
char[] unicodesCharacters = gylphNamesFromEncodingTable.Select(v => (char)glyphList.NameToUnicode(v)[0]).ToArray();
|
||
return unicodesCharacters;
|
||
}
|
||
(double maxCharacterHeight, double maxCharacterWidth) GetCharacterDetails(PdfPageBuilder page, PdfDocumentBuilder.AddedFont font, double fontSize, char[] unicodesCharacters)
|
||
{
|
||
double maxCharacterHeight;
|
||
double maxCharacterWidth;
|
||
{
|
||
var point = new PdfPoint(10, 10);
|
||
var characterRectangles = unicodesCharacters.Select(v => page.MeasureText($"{v}", 12, point, font)[0].BoundingBox);
|
||
maxCharacterHeight = characterRectangles.Max(v => v.Height);
|
||
maxCharacterWidth = characterRectangles.Max(v => v.Height);
|
||
}
|
||
return (maxCharacterHeight, maxCharacterWidth);
|
||
}
|
||
|
||
|
||
private static Dictionary<string, AdobeFontMetrics> GetStandard14Cache()
|
||
{
|
||
var Standard14Type = typeof(UglyToad.PdfPig.Fonts.Standard14Fonts.Standard14);
|
||
var Standard14CacheFieldInfos = Standard14Type.GetFields(BindingFlags.NonPublic | BindingFlags.Static);
|
||
var Standard14Cache = (Dictionary<string, AdobeFontMetrics>)Standard14CacheFieldInfos.FirstOrDefault(v => v.Name == "Standard14Cache").GetValue(null);
|
||
return Standard14Cache;
|
||
}
|
||
|
||
private static void DateTimeStampPage(PdfDocumentBuilder pdfBuilder, PdfPageBuilder page, PdfPoint point, double cm)
|
||
{
|
||
var courierFont = pdfBuilder.AddStandard14Font(Standard14Font.Courier);
|
||
|
||
var stampTextUTC = " UTC: " + DateTime.UtcNow.ToString("yyyy-MMM-dd HH:mm");
|
||
var stampTextLocal = "Local: " + DateTimeOffset.Now.ToString("yyyy-MMM-dd HH:mm zzz");
|
||
|
||
const double fontSize = 7;
|
||
|
||
var indentFromLeft = page.PageSize.Width - cm;
|
||
{
|
||
var mtUTC = page.MeasureText(stampTextUTC, fontSize, point, courierFont);
|
||
var mtlocal = page.MeasureText(stampTextLocal, fontSize, point, courierFont);
|
||
var widthUTC = mtUTC.Sum(v => v.BoundingBox.Width);
|
||
var widthLocal = mtlocal.Sum(v => v.BoundingBox.Width);
|
||
|
||
indentFromLeft -= Math.Max(widthUTC, widthLocal);
|
||
}
|
||
|
||
{
|
||
point = new PdfPoint(indentFromLeft, point.Y);
|
||
var letters = page.AddText(stampTextUTC, 7, point, courierFont);
|
||
var maxHeight = letters.Max(v => v.BoundingBox.Height);
|
||
point = new PdfPoint(indentFromLeft, point.Y - maxHeight * 1.2);
|
||
}
|
||
|
||
{
|
||
var letters = page.AddText(stampTextLocal, 7, point, courierFont);
|
||
}
|
||
}
|
||
}
|
||
}
|