mirror of
https://github.com/UglyToad/PdfPig.git
synced 2026-03-10 00:23:29 +08:00
Compare commits
115 Commits
v0.1.9
...
explore-is
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8af7daeeb | ||
|
|
d7f0fd96d8 | ||
|
|
5abdfcb96c | ||
|
|
00ca268092 | ||
|
|
813d3baa18 | ||
|
|
2b11961c8c | ||
|
|
efb8c2a803 | ||
|
|
e636212ec8 | ||
|
|
3b318e1944 | ||
|
|
377eb507e8 | ||
|
|
ff4e763192 | ||
|
|
6a06452103 | ||
|
|
a5e92cd11c | ||
|
|
4bf746c747 | ||
|
|
bffd51425d | ||
|
|
e3388ec6b6 | ||
|
|
31658ca020 | ||
|
|
1021729727 | ||
|
|
9503f9c137 | ||
|
|
016b754c5b | ||
|
|
de3b6ac6f4 | ||
|
|
b11f936f22 | ||
|
|
7fe60ff8c3 | ||
|
|
781991b6bf | ||
|
|
daaac9350d | ||
|
|
f099dd5827 | ||
|
|
0586713da3 | ||
|
|
62612588c8 | ||
|
|
bf664c3f0b | ||
|
|
6a50160e65 | ||
|
|
73ce5bbb73 | ||
|
|
d1d79b0b4c | ||
|
|
89abf6de54 | ||
|
|
24431b1f9f | ||
|
|
8f9194c9a4 | ||
|
|
fe3d15d5db | ||
|
|
b5b58434e9 | ||
|
|
d9b3891eb3 | ||
|
|
4bdb85d1ff | ||
|
|
f84f2aceec | ||
|
|
2b54a546d3 | ||
|
|
5b566b53da | ||
|
|
ca9f70ffb0 | ||
|
|
67d3dde04a | ||
|
|
e4d7805a1f | ||
|
|
6911f31b49 | ||
|
|
bf7c3c01d0 | ||
|
|
c3c477a2ba | ||
|
|
4dab2ef239 | ||
|
|
0bed135bad | ||
|
|
47584716ec | ||
|
|
afdd1f8924 | ||
|
|
580858348b | ||
|
|
24902f1839 | ||
|
|
87f5735b26 | ||
|
|
eeac910e44 | ||
|
|
5439c07ef5 | ||
|
|
b8bd40e486 | ||
|
|
4fbcc112d3 | ||
|
|
ede77c20f5 | ||
|
|
5fb36d452f | ||
|
|
74d61bd985 | ||
|
|
ba8e1e1b45 | ||
|
|
0754e7f003 | ||
|
|
306642a234 | ||
|
|
204f488ebf | ||
|
|
a4a0fe220a | ||
|
|
f1f27a63e1 | ||
|
|
d36e9a900f | ||
|
|
1b3c7bd355 | ||
|
|
67d8f56366 | ||
|
|
f26e7d90a3 | ||
|
|
d973e03206 | ||
|
|
c4a235fb62 | ||
|
|
7ceeb5bff8 | ||
|
|
b9bcfa5997 | ||
|
|
5a06e1e1cc | ||
|
|
1298356f10 | ||
|
|
1660c734e2 | ||
|
|
fdb8835b37 | ||
|
|
5209850af7 | ||
|
|
b7e2245eca | ||
|
|
24b9a99b8a | ||
|
|
c4576e4ffa | ||
|
|
92d3439465 | ||
|
|
1ca52f4d70 | ||
|
|
f86cc582ab | ||
|
|
53cf4f2ced | ||
|
|
4430a01e43 | ||
|
|
585e940acf | ||
|
|
d1779cc739 | ||
|
|
2b14ae03c1 | ||
|
|
366fc4fe04 | ||
|
|
b6474dda4c | ||
|
|
50dca593da | ||
|
|
7ec4e692a9 | ||
|
|
952a07ef41 | ||
|
|
a2ae1f16d6 | ||
|
|
97ae62ce6f | ||
|
|
9ad51067b0 | ||
|
|
20804245d0 | ||
|
|
bcc8ccecbe | ||
|
|
7db347e9f7 | ||
|
|
8ca539942d | ||
|
|
132ada7fac | ||
|
|
d3bf63fdbf | ||
|
|
40af401364 | ||
|
|
e10609e4e1 | ||
|
|
ea95a7ae7a | ||
|
|
8cee4f480f | ||
|
|
4b5cb4736f | ||
|
|
c6793da4f4 | ||
|
|
e903b6c365 | ||
|
|
f40545b0a5 | ||
|
|
a258090e1c |
28
.github/workflows/build_and_test_macos.yml
vendored
Normal file
28
.github/workflows/build_and_test_macos.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Build and test [MacOS]
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- name: Set up dotnet core
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: |
|
||||
2.1.x
|
||||
6.0.x
|
||||
8.0.x
|
||||
|
||||
# Build the release build
|
||||
- name: Build the solution
|
||||
run: dotnet build -c Release src/UglyToad.PdfPig.sln
|
||||
|
||||
- name: Run the tests
|
||||
run: dotnet test -c Release src/UglyToad.PdfPig.sln
|
||||
58
.github/workflows/run_common_crawl_tests.yml
vendored
Normal file
58
.github/workflows/run_common_crawl_tests.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: Run Common Crawl Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up dotnet core
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: "8.0.x"
|
||||
|
||||
- name: Restore corpus cache 0000, 0001
|
||||
id: restore-corpus
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: corpus/
|
||||
key: ${{ runner.os }}-pdf-corpus-0000-0001
|
||||
|
||||
- name: Download corpus if cache missed 0000, 0001
|
||||
if: steps.restore-corpus.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir -p corpus/zipfiles
|
||||
cd corpus/zipfiles
|
||||
echo "Downloading 0000.zip"
|
||||
wget -nv https://digitalcorpora.s3.amazonaws.com/corpora/files/CC-MAIN-2021-31-PDF-UNTRUNCATED/zipfiles/0000-0999/0000.zip -O 0000.zip
|
||||
wget -nv https://digitalcorpora.s3.amazonaws.com/corpora/files/CC-MAIN-2021-31-PDF-UNTRUNCATED/zipfiles/0000-0999/0001.zip -O 0001.zip
|
||||
cd ..
|
||||
unzip 'zipfiles/*.zip' -d extracted
|
||||
# run: rm -f zipfiles/*.zip
|
||||
|
||||
- name: Remove unwanted test files
|
||||
run: |
|
||||
skip_files=(
|
||||
"corpus/extracted/0000399.pdf"
|
||||
"corpus/extracted/0000819.pdf"
|
||||
"corpus/extracted/0000920.pdf"
|
||||
"corpus/extracted/0000300.pdf"
|
||||
"corpus/extracted/0001589.pdf"
|
||||
"corpus/extracted/0001957.pdf"
|
||||
)
|
||||
|
||||
for file in "${skip_files[@]}"; do
|
||||
if [ -f "$file" ]; then
|
||||
echo "Removing $file"
|
||||
rm "$file"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Run tests against corpus
|
||||
run: dotnet run --project tools/UglyToad.PdfPig.ConsoleRunner/UglyToad.PdfPig.ConsoleRunner.csproj "corpus/extracted"
|
||||
6
.github/workflows/run_integration_tests.yml
vendored
6
.github/workflows/run_integration_tests.yml
vendored
@@ -19,21 +19,21 @@ jobs:
|
||||
|
||||
- name: Restore cached part 1
|
||||
id: restore-cache-p1
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: archive/part1-archive.7z
|
||||
key: ${{ runner.os }}-part-1
|
||||
|
||||
- name: Restore cached part 2
|
||||
id: restore-cache-p2
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: archive/part2-archive.7z
|
||||
key: ${{ runner.os }}-part-2
|
||||
|
||||
- name: Restore cached part 3
|
||||
id: restore-cache-p3
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: archive/part3-archive.7z
|
||||
key: ${{ runner.os }}-part-3
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -105,6 +105,7 @@ $tf/
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
.idea
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
47
README.md
47
README.md
@@ -5,13 +5,14 @@
|
||||
[](https://gitter.im/pdfpig/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
[](https://www.nuget.org/packages/PdfPig/)
|
||||
|
||||
[](https://github.com/UglyToad/PdfPig/actions/workflows/build_and_test.yml)
|
||||
[![Build and test [MacOS]](https://github.com/UglyToad/PdfPig/actions/workflows/build_and_test_macos.yml/badge.svg)](https://github.com/UglyToad/PdfPig/actions/workflows/build_and_test_macos.yml)
|
||||
|
||||
This project allows users to read and extract text and other content from PDF files. In addition the library can be used to create simple PDF documents
|
||||
containing text and geometrical shapes.
|
||||
|
||||
This project aims to port [PDFBox](https://github.com/apache/pdfbox) to C#.
|
||||
|
||||
**Migrating to 0.1.6 from 0.1.x?** Use this guide: [migration to 0.1.6](https://github.com/UglyToad/PdfPig/wiki/Migration-to-0.1.6).
|
||||
|
||||
## Wiki
|
||||
Check out our [wiki](https://github.com/UglyToad/PdfPig/wiki) for more examples and detailed guides on the API.
|
||||
|
||||
@@ -55,7 +56,7 @@ An example of the output of this is shown below:
|
||||
|
||||
Where for the PDF text ("Write something in") shown at the top the 3 words (in pink) are detected and each word contains the individual letters with glyph bounding boxes.
|
||||
|
||||
### Ceate PDF Document
|
||||
### Create PDF Document
|
||||
To create documents use the class `PdfDocumentBuilder`. The Standard 14 fonts provide a quick way to get started:
|
||||
|
||||
```cs
|
||||
@@ -77,10 +78,10 @@ The output is a 1 page PDF document with the text "Hello World!" in Helvetica ne
|
||||
|
||||

|
||||
|
||||
Each font must be registered with the PdfDocumentBuilder prior to use enable pages to share the font resources. Only Standard 14 fonts and TrueType fonts (.ttf) are supported.
|
||||
Each font must be registered with the `PdfDocumentBuilder` prior to use enable pages to share the font resources. Only Standard 14 fonts and TrueType fonts (.ttf) are supported.
|
||||
|
||||
### Advanced Document Extraction
|
||||
In this example a more advanced document extraction is performed. PdfDocumentBuilder is used to create a copy of the pdf with debug information (bounding boxes and reading order) added.
|
||||
In this example a more advanced document extraction is performed. `PdfDocumentBuilder` is used to create a copy of the pdf with debug information (bounding boxes and reading order) added.
|
||||
|
||||
|
||||
```cs
|
||||
@@ -183,7 +184,7 @@ The document contains the version of the PDF specification it complies with, acc
|
||||
|
||||
decimal version = document.Version;
|
||||
|
||||
### Document Creation (0.0.5)
|
||||
### Document Creation
|
||||
|
||||
The `PdfDocumentBuilder` creates a new document with no pages or content.
|
||||
|
||||
@@ -256,7 +257,7 @@ string title = document.Information.Title;
|
||||
// etc...
|
||||
```
|
||||
|
||||
### Document Structure (0.0.3)
|
||||
### Document Structure
|
||||
|
||||
The document now has a Structure member:
|
||||
|
||||
@@ -286,21 +287,21 @@ bool isA4 = size == PageSize.A4;
|
||||
|
||||
string text = page.Text;
|
||||
|
||||
There is a new (0.0.3) method which provides access to the words. This uses basic heuristics and is not reliable or well-tested:
|
||||
There is a method which provides access to the words. The default method uses basic heuristics. For advanced cases, You can also implement your own `IWordExtractor` or use the `NearestNeighbourWordExtractor`:
|
||||
|
||||
IEnumerable<Word> words = page.GetWords();
|
||||
|
||||
You can also (0.0.6) access the raw operations used in the page's content stream for drawing graphics and content on the page:
|
||||
You can also access the raw operations used in the page's content stream for drawing graphics and content on the page:
|
||||
|
||||
IReadOnlyList<IGraphicsStateOperation> operations = page.Operations;
|
||||
|
||||
Consult the PDF specification for the meaning of individual operators.
|
||||
|
||||
There is also an early access (0.0.3) API for retrieving the raw bytes of PDF image objects per page:
|
||||
There is also an API for retrieving the PDF image objects per page:
|
||||
|
||||
IEnumerable<XObjectImage> images = page.ExperimentalAccess.GetRawImages();
|
||||
IEnumerable<XObjectImage> images = page.GetImages();
|
||||
|
||||
This API will be changed in future releases.
|
||||
Please read the [wiki on Images](https://github.com/UglyToad/PdfPig/wiki/Images).
|
||||
|
||||
### Letter
|
||||
|
||||
@@ -322,15 +323,15 @@ These letters contain:
|
||||
|
||||
Letter position is measured in PDF coordinates where the origin is the lower left corner of the page. Therefore a higher Y value means closer to the top of the page.
|
||||
|
||||
### Annotations (0.0.5)
|
||||
### Annotations
|
||||
|
||||
Early support for retrieving annotations on each page is provided using the method:
|
||||
Retrieving annotations on each page is provided using the method:
|
||||
|
||||
page.ExperimentalAccess.GetAnnotations()
|
||||
page.GetAnnotations()
|
||||
|
||||
This call is not cached and the document must not have been disposed prior to use. The annotations API may change in future.
|
||||
This call is not cached and the document must not have been disposed prior to use.
|
||||
|
||||
### Bookmarks (0.0.10)
|
||||
### Bookmarks
|
||||
|
||||
The bookmarks (outlines) of a document may be retrieved at the document level:
|
||||
|
||||
@@ -338,7 +339,7 @@ The bookmarks (outlines) of a document may be retrieved at the document level:
|
||||
|
||||
This will return `false` if the document does not define any bookmarks.
|
||||
|
||||
### Forms (0.0.10)
|
||||
### Forms
|
||||
|
||||
Form fields for interactive forms (AcroForms) can be retrieved using:
|
||||
|
||||
@@ -350,15 +351,15 @@ The fields can be accessed using the `AcroForm`'s `Fields` property. Since the f
|
||||
|
||||
Please note the forms are readonly and values cannot be changed or added using PdfPig.
|
||||
|
||||
### Hyperlinks (0.1.0)
|
||||
### Hyperlinks
|
||||
|
||||
A page has a method to extract hyperlinks (annotations of link type):
|
||||
|
||||
IReadOnlyList<UglyToad.PdfPig.Content.Hyperlink> hyperlinks = page.GetHyperlinks();
|
||||
|
||||
### TrueType (0.1.0)
|
||||
### TrueType
|
||||
|
||||
The classes used to work with TrueType fonts in the PDF file are now available for public consumption. Given an input file:
|
||||
The classes used to work with TrueType fonts in the PDF file are available for public consumption. Given an input file:
|
||||
|
||||
|
||||
```cs
|
||||
@@ -372,7 +373,7 @@ TrueTypeFont font = TrueTypeFontParser.Parse(input);
|
||||
|
||||
The parsed font can then be inspected.
|
||||
|
||||
### Embedded Files (0.1.0)
|
||||
### Embedded Files
|
||||
|
||||
PDF files may contain other files entirely embedded inside them for document annotations. The list of embedded files and their byte content may be accessed:
|
||||
|
||||
@@ -386,7 +387,7 @@ if (document.Advanced.TryGetEmbeddedFiles(out IReadOnlyList<EmbeddedFile> files)
|
||||
}
|
||||
```
|
||||
|
||||
### Merging (0.1.2)
|
||||
### Merging
|
||||
|
||||
You can merge 2 or more existing PDF files using the `PdfMerger` class:
|
||||
|
||||
|
||||
6
src/Directory.Build.targets
Normal file
6
src/Directory.Build.targets
Normal file
@@ -0,0 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup Condition="'$(IsTestProject)' != 'true'">
|
||||
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0'))">true</IsTrimmable>
|
||||
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -3,20 +3,33 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
// https://github.com/apache/pdfbox/blob/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSObjectKey.java#L25
|
||||
|
||||
/// <summary>
|
||||
/// Used to uniquely identify and refer to objects in the PDF file.
|
||||
/// </summary>
|
||||
public readonly struct IndirectReference
|
||||
public readonly struct IndirectReference : IEquatable<IndirectReference>
|
||||
{
|
||||
private const int NUMBER_OFFSET = sizeof(ushort) * 8;
|
||||
private static readonly long GENERATION_MASK = (long)Math.Pow(2, NUMBER_OFFSET) - 1;
|
||||
private static readonly long MAX_OBJECT_NUMBER = (long)(Math.Pow(2, sizeof(long) * 8 - NUMBER_OFFSET) - 1) / 2;
|
||||
|
||||
// combined number and generation
|
||||
// The lowest 16 bits hold the generation 0-65535
|
||||
// The rest is used for the number (even though 34 bit are sufficient for 10 digits)
|
||||
private readonly long numberAndGeneration;
|
||||
|
||||
/// <summary>
|
||||
/// A positive integer object number.
|
||||
/// </summary>
|
||||
public long ObjectNumber { get; }
|
||||
// Below is different from PdfBox as we keep the sign of the offset number (use >> instead of >>> (unsigned right shift))
|
||||
public long ObjectNumber => numberAndGeneration >> NUMBER_OFFSET;
|
||||
|
||||
/// <summary>
|
||||
/// A non-negative integer generation number which starts as 0 and increases if the file is updated incrementally.
|
||||
/// <para>The maximum generation number is 65,535.</para>
|
||||
/// </summary>
|
||||
public int Generation { get; }
|
||||
public int Generation => (int)(numberAndGeneration & GENERATION_MASK);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="IndirectReference"/>
|
||||
@@ -26,26 +39,47 @@
|
||||
[DebuggerStepThrough]
|
||||
public IndirectReference(long objectNumber, int generation)
|
||||
{
|
||||
ObjectNumber = objectNumber;
|
||||
Generation = generation;
|
||||
if (generation < 0)
|
||||
{
|
||||
// Note: We do not check generation for max value and let it overflow
|
||||
throw new ArgumentOutOfRangeException(nameof(generation), "Generation number must not be a negative value.");
|
||||
}
|
||||
|
||||
if (objectNumber < -MAX_OBJECT_NUMBER || objectNumber > MAX_OBJECT_NUMBER)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(objectNumber), $"Object number must be between -{MAX_OBJECT_NUMBER:##,###} and {MAX_OBJECT_NUMBER:##,###}.");
|
||||
}
|
||||
|
||||
numberAndGeneration = ComputeInternalHash(objectNumber, generation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the internal hash value for the given object number and generation number.
|
||||
/// </summary>
|
||||
/// <param name="num">The object number.</param>
|
||||
/// <param name="gen">The generation number.</param>
|
||||
/// <returns>The internal hash for the given values.</returns>
|
||||
private static long ComputeInternalHash(long num, int gen)
|
||||
{
|
||||
return num << NUMBER_OFFSET | (gen & GENERATION_MASK);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(IndirectReference other)
|
||||
{
|
||||
return other.numberAndGeneration == numberAndGeneration;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is IndirectReference reference)
|
||||
{
|
||||
return reference.ObjectNumber == ObjectNumber
|
||||
&& reference.Generation == Generation;
|
||||
}
|
||||
|
||||
return false;
|
||||
return obj is IndirectReference other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(ObjectNumber, Generation);
|
||||
return numberAndGeneration.GetHashCode();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -53,5 +87,17 @@
|
||||
{
|
||||
return $"{ObjectNumber} {Generation}";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static bool operator ==(IndirectReference left, IndirectReference right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static bool operator !=(IndirectReference left, IndirectReference right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/UglyToad.PdfPig.Core/MemoryHelper.cs
Normal file
38
src/UglyToad.PdfPig.Core/MemoryHelper.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
namespace UglyToad.PdfPig.Core
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// <summary>
|
||||
/// Memory extensions.
|
||||
/// </summary>
|
||||
public static class MemoryHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a read-only <see cref="MemoryStream"/> from a ReadOnlyMemory<byte>, attempting without memory allocation.
|
||||
/// </summary>
|
||||
public static MemoryStream AsReadOnlyMemoryStream(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
if (MemoryMarshal.TryGetArray(memory, out ArraySegment<byte> array))
|
||||
{
|
||||
return new MemoryStream(array.Array!, array.Offset, array.Count, false);
|
||||
}
|
||||
|
||||
return new MemoryStream(memory.ToArray(), false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Memory<byte> from a <see cref="MemoryStream"/>, attempting without memory allocation.
|
||||
/// </summary>
|
||||
public static Memory<byte> AsMemory(this MemoryStream stream)
|
||||
{
|
||||
if (stream.TryGetBuffer(out ArraySegment<byte> segment))
|
||||
{
|
||||
return segment.AsMemory();
|
||||
}
|
||||
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
/// The Y-axis extends vertically upwards and the X-axis horizontally to the right.
|
||||
/// Unless otherwise specified on a per-page basis, units in PDF space are equivalent to a typographic point (1/72 inch).
|
||||
/// </remarks>
|
||||
public readonly struct PdfLine
|
||||
public readonly struct PdfLine : IEquatable<PdfLine>
|
||||
{
|
||||
/// <summary>
|
||||
/// Length of the line.
|
||||
@@ -70,14 +70,17 @@
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this <see cref="PdfLine"/> is equal to a specified <see cref="PdfLine"/> .
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is PdfLine line)
|
||||
{
|
||||
return line.Point1.Equals(Point1) && line.Point2.Equals(Point2);
|
||||
}
|
||||
return false;
|
||||
return obj is PdfLine other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this <see cref="PdfLine"/> is equal to a specified <see cref="PdfLine"/> .
|
||||
/// </summary>
|
||||
public bool Equals(PdfLine other)
|
||||
{
|
||||
return Point1.Equals(other.Point1) && Point2.Equals(other.Point2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -87,5 +90,17 @@
|
||||
{
|
||||
return HashCode.Combine(Point1, Point2);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static bool operator ==(PdfLine left, PdfLine right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static bool operator !=(PdfLine left, PdfLine right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// The Y-axis extends vertically upwards and the X-axis horizontally to the right.
|
||||
/// Unless otherwise specified on a per-page basis, units in PDF space are equivalent to a typographic point (1/72 inch).
|
||||
/// </remarks>
|
||||
public readonly struct PdfPoint
|
||||
public readonly struct PdfPoint : IEquatable<PdfPoint>
|
||||
{
|
||||
/// <summary>
|
||||
/// The origin of the coordinates system.
|
||||
@@ -83,14 +83,17 @@
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this <see cref="PdfPoint"/> is equal to a specified <see cref="PdfPoint"/> .
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is PdfPoint point)
|
||||
{
|
||||
return point.X == X && point.Y == Y;
|
||||
}
|
||||
return false;
|
||||
return obj is PdfPoint other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this <see cref="PdfPoint"/> is equal to a specified <see cref="PdfPoint"/> .
|
||||
/// </summary>
|
||||
public bool Equals(PdfPoint other)
|
||||
{
|
||||
return X.Equals(other.X) && Y.Equals(other.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -106,5 +109,17 @@
|
||||
{
|
||||
return $"(x:{X.ToString(CultureInfo.InvariantCulture)}, y:{Y.ToString(CultureInfo.InvariantCulture)})";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static bool operator ==(PdfPoint left, PdfPoint right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static bool operator !=(PdfPoint left, PdfPoint right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
/// The Y-axis extends vertically upwards and the X-axis horizontally to the right.
|
||||
/// Unless otherwise specified on a per-page basis, units in PDF space are equivalent to a typographic point (1/72 inch).
|
||||
/// </remarks>
|
||||
public readonly struct PdfRectangle
|
||||
public readonly struct PdfRectangle : IEquatable<PdfRectangle>
|
||||
{
|
||||
/// <summary>
|
||||
/// Top left point of the rectangle.
|
||||
@@ -166,5 +166,38 @@
|
||||
{
|
||||
return $"[{TopLeft}, {Width.ToString(CultureInfo.InvariantCulture)}, {Height.ToString(CultureInfo.InvariantCulture)}]";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(PdfRectangle other)
|
||||
{
|
||||
return TopLeft.Equals(other.TopLeft) &&
|
||||
TopRight.Equals(other.TopRight) &&
|
||||
BottomRight.Equals(other.BottomRight) &&
|
||||
BottomLeft.Equals(other.BottomLeft);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is PdfRectangle other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(TopLeft, TopRight, BottomRight, BottomLeft);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static bool operator ==(PdfRectangle left, PdfRectangle right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static bool operator !=(PdfRectangle left, PdfRectangle right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
/// <summary>
|
||||
/// A supbpath is made up of a sequence of connected segments.
|
||||
/// </summary>
|
||||
public class PdfSubpath
|
||||
public sealed class PdfSubpath
|
||||
{
|
||||
private readonly List<IPathCommand> commands = new List<IPathCommand>();
|
||||
|
||||
@@ -416,8 +416,10 @@
|
||||
/// <summary>
|
||||
/// Close the current <see cref="PdfSubpath"/>.
|
||||
/// </summary>
|
||||
public class Close : IPathCommand
|
||||
public sealed class Close : IPathCommand
|
||||
{
|
||||
private static readonly int _hash = typeof(Close).GetHashCode();
|
||||
|
||||
/// <inheritdoc />
|
||||
public PdfRectangle? GetBoundingRectangle()
|
||||
{
|
||||
@@ -439,15 +441,14 @@
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// ReSharper disable once BaseObjectGetHashCodeCallInGetHashCode
|
||||
return base.GetHashCode();
|
||||
return _hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move drawing of the current <see cref="PdfSubpath"/> to the specified location.
|
||||
/// </summary>
|
||||
public class Move : IPathCommand
|
||||
public sealed class Move : IPathCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// The location to move to.
|
||||
@@ -497,7 +498,7 @@
|
||||
/// <summary>
|
||||
/// Draw a straight line between two points.
|
||||
/// </summary>
|
||||
public class Line : IPathCommand
|
||||
public sealed class Line : IPathCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// The start of the line.
|
||||
|
||||
@@ -189,7 +189,34 @@
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Whether the given string is at this position in the input.
|
||||
/// Resets to the current offset once read.
|
||||
/// </summary>
|
||||
public static bool IsString(IInputBytes bytes, ReadOnlySpan<byte> s)
|
||||
{
|
||||
bool found = true;
|
||||
|
||||
var startOffset = bytes.CurrentOffset;
|
||||
|
||||
foreach (var c in s)
|
||||
{
|
||||
if (bytes.CurrentByte != c)
|
||||
{
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
|
||||
bytes.MoveNext();
|
||||
}
|
||||
|
||||
bytes.Seek(startOffset);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a long from the input.
|
||||
/// </summary>
|
||||
@@ -252,14 +279,6 @@
|
||||
throw new PdfDocumentFormatException($"Error: Expected an integer type at offset {bytes.CurrentOffset}, instead got \'{OtherEncodings.BytesAsLatin1String(intBytes)}\'");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the given character is a space.
|
||||
/// </summary>
|
||||
public static bool IsSpace(int c)
|
||||
{
|
||||
return c == ' ';
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the given character value is a valid hex value.
|
||||
|
||||
@@ -32,17 +32,17 @@
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
|
||||
if (!stream.CanRead)
|
||||
{
|
||||
throw new ArgumentException("The provided stream did not support reading.");
|
||||
throw new ArgumentException("The provided stream did not support reading.", nameof(stream));
|
||||
}
|
||||
|
||||
if (!stream.CanSeek)
|
||||
{
|
||||
throw new ArgumentException("The provided stream did not support seeking.");
|
||||
throw new ArgumentException("The provided stream did not support seeking.", nameof(stream));
|
||||
}
|
||||
|
||||
this.stream = stream;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/// <summary>
|
||||
/// Specifies the conversion from the transformed coordinate space to the original untransformed coordinate space.
|
||||
/// </summary>
|
||||
public readonly struct TransformationMatrix
|
||||
public readonly struct TransformationMatrix : IEquatable<TransformationMatrix>
|
||||
{
|
||||
/// <summary>
|
||||
/// The default <see cref="TransformationMatrix"/>.
|
||||
@@ -465,12 +465,21 @@
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is TransformationMatrix m))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return obj is TransformationMatrix other && Equals(other);
|
||||
}
|
||||
|
||||
return Equals(this, m);
|
||||
/// <inheritdoc />
|
||||
public bool Equals(TransformationMatrix other)
|
||||
{
|
||||
return row1.Equals(other.row1) &&
|
||||
row2.Equals(other.row2) &&
|
||||
row3.Equals(other.row3) &&
|
||||
A.Equals(other.A) &&
|
||||
B.Equals(other.B) &&
|
||||
C.Equals(other.C) &&
|
||||
D.Equals(other.D) &&
|
||||
E.Equals(other.E) &&
|
||||
F.Equals(other.F);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -478,25 +487,13 @@
|
||||
/// </summary>
|
||||
public static bool Equals(TransformationMatrix a, TransformationMatrix b)
|
||||
{
|
||||
for (var i = 0; i < Rows; i++)
|
||||
{
|
||||
for (var j = 0; j < Columns; j++)
|
||||
{
|
||||
if (a[i, j] != b[i, j])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hashCode = new HashCode();
|
||||
|
||||
hashCode.Add(row1);
|
||||
hashCode.Add(row2);
|
||||
hashCode.Add(row3);
|
||||
@@ -506,7 +503,6 @@
|
||||
hashCode.Add(D);
|
||||
hashCode.Add(E);
|
||||
hashCode.Add(F);
|
||||
|
||||
return hashCode.ToHashCode();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net462;net471;net6.0;net8.0</TargetFrameworks>
|
||||
<LangVersion>12</LangVersion>
|
||||
<Version>0.1.10-alpha001</Version>
|
||||
<IsTestProject>False</IsTestProject>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\pdfpig.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard2.0|AnyCPU'">
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0' or '$(TargetFramework)'=='net462' OR '$(TargetFramework)'=='net471'">
|
||||
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0' or '$(TargetFramework)'=='net462' or '$(TargetFramework)'=='net471'">
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\pdfpig.snk" Link="pdfpig.snk" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net462;net471;net6.0;net8.0</TargetFrameworks>
|
||||
<LangVersion>12</LangVersion>
|
||||
<Version>0.1.11-alpha001</Version>
|
||||
<IsTestProject>False</IsTestProject>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\pdfpig.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0' or '$(TargetFramework)'=='net462' OR '$(TargetFramework)'=='net471'">
|
||||
<PackageReference Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0' or '$(TargetFramework)'=='net462' or '$(TargetFramework)'=='net471'">
|
||||
<PackageReference Include="System.Memory" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\pdfpig.snk" Link="pdfpig.snk" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -4,6 +4,7 @@
|
||||
using Content;
|
||||
using DocumentLayoutAnalysis;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
@@ -94,6 +95,9 @@
|
||||
/// </summary>
|
||||
/// <param name="document">The document to extract page layouts from.</param>
|
||||
/// <param name="includePaths">Draw PdfPaths present in the page.</param>
|
||||
#if NET6_0_OR_GREATER
|
||||
[RequiresUnreferencedCode("Members from AltoDocument may be trimmed if not referenced directly")]
|
||||
#endif
|
||||
public string Get(PdfDocument document, bool includePaths = false)
|
||||
{
|
||||
var altoDocument = CreateAltoDocument("unknown");
|
||||
@@ -105,6 +109,9 @@
|
||||
/// Get the Alto (XML) string of the page layout. Excludes <see cref="T:UglyToad.PdfPig.Geometry.PdfSubpath" />s.
|
||||
/// </summary>
|
||||
/// <param name="page">The page to export the XML layout for.</param>
|
||||
#if NET6_0_OR_GREATER
|
||||
[RequiresUnreferencedCode("Members from AltoDocument may be trimmed if not referenced directly")]
|
||||
#endif
|
||||
public string Get(Page page) => Get(page, false);
|
||||
|
||||
/// <summary>
|
||||
@@ -112,6 +119,9 @@
|
||||
/// </summary>
|
||||
/// <param name="page">The page to export the XML layout for.</param>
|
||||
/// <param name="includePaths">Whether the output should include the PdfPaths present in the page.</param>
|
||||
#if NET6_0_OR_GREATER
|
||||
[RequiresUnreferencedCode("Members from AltoDocument may be trimmed if not referenced directly")]
|
||||
#endif
|
||||
public string Get(Page page, bool includePaths)
|
||||
{
|
||||
var document = CreateAltoDocument("unknown");
|
||||
@@ -184,7 +194,7 @@
|
||||
|
||||
if (includePaths)
|
||||
{
|
||||
altoPage.PrintSpace.GraphicalElements = page.ExperimentalAccess.Paths
|
||||
altoPage.PrintSpace.GraphicalElements = page.Paths
|
||||
.Select(p => ToAltoGraphicalElement(p, page.Height))
|
||||
.ToArray();
|
||||
}
|
||||
@@ -355,6 +365,9 @@
|
||||
};
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[RequiresUnreferencedCode("Members from AltoDocument may be trimmed if not referenced directly")]
|
||||
#endif
|
||||
private string Serialize(AltoDocument altoDocument)
|
||||
{
|
||||
var serializer = new XmlSerializer(typeof(AltoDocument));
|
||||
@@ -377,6 +390,9 @@
|
||||
/// <summary>
|
||||
/// Deserialize an <see cref="AltoDocument"/> from a given Alto format XML document.
|
||||
/// </summary>
|
||||
#if NET6_0_OR_GREATER
|
||||
[RequiresUnreferencedCode("Members from AltoDocument may be trimmed if not referenced directly")]
|
||||
#endif
|
||||
public static AltoDocument Deserialize(string xmlPath)
|
||||
{
|
||||
var serializer = new XmlSerializer(typeof(AltoDocument));
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using Core;
|
||||
using DocumentLayoutAnalysis;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using UglyToad.PdfPig.DocumentLayoutAnalysis.PageSegmenter;
|
||||
using UglyToad.PdfPig.Graphics;
|
||||
@@ -118,6 +119,9 @@
|
||||
/// Get the hOCR (HTML) string of the page layout. Excludes PdfPaths.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
#if NET6_0_OR_GREATER
|
||||
[RequiresUnreferencedCode("'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.")]
|
||||
#endif
|
||||
public string Get(Page page)
|
||||
{
|
||||
return Get(page, false);
|
||||
@@ -193,7 +197,7 @@
|
||||
|
||||
if (includePaths)
|
||||
{
|
||||
foreach (var path in page.ExperimentalAccess.Paths)
|
||||
foreach (var path in page.Paths)
|
||||
{
|
||||
hocr += "\n" + GetCode(path, page.Height, true, level + 1);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
namespace UglyToad.PdfPig.DocumentLayoutAnalysis.Export
|
||||
{
|
||||
using Content;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Exports the page's text into the desired format.
|
||||
@@ -12,6 +13,9 @@
|
||||
/// </summary>
|
||||
/// <param name="page">The page to convert to the format.</param>
|
||||
/// <returns>The <see langword="string"/> containing the page contents represented in a compatible format.</returns>
|
||||
#if NET6_0_OR_GREATER
|
||||
[RequiresUnreferencedCode("'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.")]
|
||||
#endif
|
||||
string Get(Page page);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
using ReadingOrderDetector;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
@@ -105,6 +106,9 @@
|
||||
/// Get the PAGE-XML (XML) string of the pages layout. Excludes PdfPaths.
|
||||
/// </summary>
|
||||
/// <param name="page"></param>
|
||||
#if NET6_0_OR_GREATER
|
||||
[RequiresUnreferencedCode("Members from PageXmlDocument may be trimmed if not referenced directly")]
|
||||
#endif
|
||||
public string Get(Page page)
|
||||
{
|
||||
return Get(page, false);
|
||||
@@ -115,6 +119,9 @@
|
||||
/// </summary>
|
||||
/// <param name="page"></param>
|
||||
/// <param name="includePaths">Draw PdfPaths present in the page.</param>
|
||||
#if NET6_0_OR_GREATER
|
||||
[RequiresUnreferencedCode("Members from PageXmlDocument may be trimmed if not referenced directly")]
|
||||
#endif
|
||||
public string Get(Page page, bool includePaths)
|
||||
{
|
||||
PageXmlData data = new PageXmlData();
|
||||
@@ -240,7 +247,7 @@
|
||||
|
||||
if (includePaths)
|
||||
{
|
||||
foreach (var path in page.ExperimentalAccess.Paths)
|
||||
foreach (var path in page.Paths)
|
||||
{
|
||||
var graphicalElement = ToPageXmlLineDrawingRegion(path, data, page.Width, page.Height);
|
||||
|
||||
@@ -373,6 +380,9 @@
|
||||
};
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[RequiresUnreferencedCode("Members from PageXmlDocument may be trimmed if not referenced directly")]
|
||||
#endif
|
||||
private string Serialize(PageXmlDocument pageXmlDocument)
|
||||
{
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(PageXmlDocument));
|
||||
@@ -395,6 +405,9 @@
|
||||
/// <summary>
|
||||
/// Deserialize an <see cref="PageXmlDocument"/> from a given PAGE format XML document.
|
||||
/// </summary>
|
||||
#if NET6_0_OR_GREATER
|
||||
[RequiresUnreferencedCode("Members from PageXmlDocument may be trimmed if not referenced directly")]
|
||||
#endif
|
||||
public static PageXmlDocument Deserialize(string xmlPath)
|
||||
{
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(PageXmlDocument));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
@@ -65,11 +66,14 @@
|
||||
/// <summary>
|
||||
/// Get the page contents as an SVG.
|
||||
/// </summary>
|
||||
#if NET6_0_OR_GREATER
|
||||
[RequiresUnreferencedCode("'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.")]
|
||||
#endif
|
||||
public string Get(Page page)
|
||||
{
|
||||
var builder = new StringBuilder($"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width='{Math.Round(page.Width, Rounding)}' height='{Math.Round(page.Height, Rounding)}'>\n<g transform=\"scale(1, 1) translate(0, 0)\">\n");
|
||||
|
||||
foreach (var path in page.ExperimentalAccess.Paths)
|
||||
foreach (var path in page.Paths)
|
||||
{
|
||||
if (!path.IsClipping)
|
||||
{
|
||||
|
||||
@@ -226,7 +226,13 @@
|
||||
throw new ArgumentException("DocstrumBoundingBoxes: the bin length must be positive when commputing peak average distance.", nameof(binLength));
|
||||
}
|
||||
|
||||
var max = (int)Math.Ceiling(distances.Max());
|
||||
double maxDbl = Math.Ceiling(distances.Max());
|
||||
if (maxDbl > int.MaxValue)
|
||||
{
|
||||
throw new OverflowException($"Error while casting maximum distance of {maxDbl} to integer.");
|
||||
}
|
||||
|
||||
int max = (int)maxDbl;
|
||||
if (max == 0)
|
||||
{
|
||||
max = binLength;
|
||||
|
||||
@@ -110,6 +110,11 @@
|
||||
{
|
||||
var gap = letter.StartBaseLine.X - previous.EndBaseLine.X;
|
||||
|
||||
if (options.NegativeGapAsWhitespace)
|
||||
{
|
||||
gap = Math.Abs(gap);
|
||||
}
|
||||
|
||||
if (WhitespaceSizeStatistics.IsProbablyWhitespace(gap, previous))
|
||||
{
|
||||
sb.Append(" ");
|
||||
@@ -178,6 +183,13 @@
|
||||
/// character. Default <see langword="false"/>.
|
||||
/// </summary>
|
||||
public bool ReplaceWhitespaceWithSpace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When parsing PDF files with tables containing multiple lines in a cell or "merged" cells,
|
||||
/// the separate words can appear out of horizontal order. This option can better predict the
|
||||
/// spaces between the words. Default <see langword="false"/>.
|
||||
/// </summary>
|
||||
public bool NegativeGapAsWhitespace { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net462;net471;net6.0;net8.0</TargetFrameworks>
|
||||
<LangVersion>12</LangVersion>
|
||||
<Version>0.1.10-alpha001</Version>
|
||||
<IsTestProject>False</IsTestProject>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\pdfpig.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard2.0|AnyCPU'">
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\pdfpig.snk" Link="pdfpig.snk" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UglyToad.PdfPig.Core\UglyToad.PdfPig.Core.csproj" />
|
||||
<ProjectReference Include="..\UglyToad.PdfPig.Fonts\UglyToad.PdfPig.Fonts.csproj" />
|
||||
<ProjectReference Include="..\UglyToad.PdfPig\UglyToad.PdfPig.csproj" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net462;net471;net6.0;net8.0</TargetFrameworks>
|
||||
<LangVersion>12</LangVersion>
|
||||
<Version>0.1.11-alpha001</Version>
|
||||
<IsTestProject>False</IsTestProject>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\pdfpig.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\pdfpig.snk" Link="pdfpig.snk" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UglyToad.PdfPig.Core\UglyToad.PdfPig.Core.csproj" />
|
||||
<ProjectReference Include="..\UglyToad.PdfPig.Fonts\UglyToad.PdfPig.Fonts.csproj" />
|
||||
<ProjectReference Include="..\UglyToad.PdfPig\UglyToad.PdfPig.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -13,7 +13,7 @@
|
||||
public class GlyphList
|
||||
{
|
||||
/// <summary>
|
||||
/// <c>.notdef</c>.
|
||||
/// <c>.notdef</c> name.
|
||||
/// </summary>
|
||||
public const string NotDefined = ".notdef";
|
||||
|
||||
@@ -22,22 +22,15 @@
|
||||
|
||||
private readonly Dictionary<string, string> oddNameToUnicodeCache = new Dictionary<string, string>();
|
||||
|
||||
private static readonly Lazy<GlyphList> LazyAdobeGlyphList = new Lazy<GlyphList>(() => GlyphListFactory.Get("glyphlist"));
|
||||
private static readonly Lazy<GlyphList> LazyAdobeGlyphList = new Lazy<GlyphList>(() => GlyphListFactory.Get("glyphlist", "additional"));
|
||||
|
||||
/// <summary>
|
||||
/// The Adobe Glyph List.
|
||||
/// The Adobe Glyph List (includes an extension to the Adobe Glyph List.).
|
||||
/// </summary>
|
||||
public static GlyphList AdobeGlyphList => LazyAdobeGlyphList.Value;
|
||||
|
||||
private static readonly Lazy<GlyphList> LazyAdditionalGlyphList = new Lazy<GlyphList>(() => GlyphListFactory.Get("additional"));
|
||||
|
||||
/// <summary>
|
||||
/// An extension to the Adobe Glyph List.
|
||||
/// </summary>
|
||||
public static GlyphList AdditionalGlyphList => LazyAdditionalGlyphList.Value;
|
||||
|
||||
private static readonly Lazy<GlyphList> LazyZapfDingbatsGlyphList = new Lazy<GlyphList>(() => GlyphListFactory.Get("zapfdingbats"));
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Zapf Dingbats.
|
||||
/// </summary>
|
||||
@@ -84,6 +77,7 @@
|
||||
|
||||
/// <summary>
|
||||
/// Get the unicode value for the glyph name.
|
||||
/// See <see href="https://github.com/adobe-type-tools/agl-specification"/>.
|
||||
/// </summary>
|
||||
public string NameToUnicode(string name)
|
||||
{
|
||||
@@ -102,26 +96,48 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
string unicode;
|
||||
// Remove suffixes
|
||||
string? unicode;
|
||||
// 1. Drop all the characters from the glyph name starting with the first occurrence of a period (U+002E FULL STOP), if any.
|
||||
if (name.IndexOf('.') > 0)
|
||||
{
|
||||
unicode = NameToUnicode(name.Substring(0, name.IndexOf('.')));
|
||||
}
|
||||
else if (name.StartsWith("uni") && name.Length == 7)
|
||||
// 2. Split the remaining string into a sequence of components, using underscore (U+005F LOW LINE) as the delimiter.
|
||||
else if (name.IndexOf('_') > 0)
|
||||
{
|
||||
/*
|
||||
* MOZILLA-3136-0.pdf
|
||||
* 68-1990-01_A.pdf
|
||||
* TIKA-2054-0.pdf
|
||||
*/
|
||||
var sb = new StringBuilder();
|
||||
foreach (var s in name.Split('_'))
|
||||
{
|
||||
sb.Append(NameToUnicode(s));
|
||||
}
|
||||
|
||||
unicode = sb.ToString();
|
||||
}
|
||||
// Otherwise, if the component is of the form ‘uni’ (U+0075, U+006E, and U+0069) followed by a sequence of uppercase hexadecimal
|
||||
// digits (0–9 and A–F, meaning U+0030 through U+0039 and U+0041 through U+0046), if the length of that sequence is a multiple
|
||||
// of four, and if each group of four digits represents a value in the ranges 0000 through D7FF or E000 through FFFF, then
|
||||
// interpret each as a Unicode scalar value and map the component to the string made of those scalar values. Note that the range
|
||||
// and digit-length restrictions mean that the ‘uni’ glyph name prefix can be used only with UVs in the Basic Multilingual Plane (BMP).
|
||||
else if (name.StartsWith("uni") && (name.Length - 3) % 4 == 0)
|
||||
{
|
||||
// test for Unicode name in the format uniXXXX where X is hex
|
||||
int nameLength = name.Length;
|
||||
|
||||
var uniStr = new StringBuilder();
|
||||
|
||||
var foundUnicode = true;
|
||||
for (int chPos = 3; chPos + 4 <= nameLength; chPos += 4)
|
||||
{
|
||||
if (!int.TryParse(name.AsSpanOrSubstring(chPos, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var codePoint))
|
||||
if (!int.TryParse(name.AsSpanOrSubstring(chPos, 4),
|
||||
NumberStyles.HexNumber,
|
||||
CultureInfo.InvariantCulture,
|
||||
out var codePoint))
|
||||
{
|
||||
foundUnicode = false;
|
||||
break;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (codePoint > 0xD7FF && codePoint < 0xE000)
|
||||
@@ -132,26 +148,30 @@
|
||||
uniStr.Append((char)codePoint);
|
||||
}
|
||||
|
||||
if (!foundUnicode)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
unicode = uniStr.ToString();
|
||||
}
|
||||
else if (name.StartsWith("u", StringComparison.Ordinal) && name.Length == 5)
|
||||
// Otherwise, if the component is of the form ‘u’ (U+0075) followed by a sequence of four to six uppercase hexadecimal digits (0–9
|
||||
// and A–F, meaning U+0030 through U+0039 and U+0041 through U+0046), and those digits represents a value in the ranges 0000 through
|
||||
// D7FF or E000 through 10FFFF, then interpret it as a Unicode scalar value and map the component to the string made of this scalar value.
|
||||
else if (name.StartsWith("u", StringComparison.Ordinal) && name.Length >= 5 && name.Length <= 7)
|
||||
{
|
||||
// test for an alternate Unicode name representation uXXXX
|
||||
var codePoint = int.Parse(name.AsSpanOrSubstring(1), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
|
||||
if (codePoint > 0xD7FF && codePoint < 0xE000)
|
||||
{
|
||||
throw new InvalidFontFormatException(
|
||||
$"Unicode character name with disallowed code area: {name}");
|
||||
throw new InvalidFontFormatException($"Unicode character name with disallowed code area: {name}");
|
||||
}
|
||||
|
||||
unicode = char.ConvertFromUtf32(codePoint);
|
||||
}
|
||||
// Ad-hoc special cases
|
||||
else if (name.StartsWith("c", StringComparison.OrdinalIgnoreCase) && name.Length >= 3 && name.Length <= 4)
|
||||
{
|
||||
// name representation cXXX
|
||||
var codePoint = int.Parse(name.AsSpanOrSubstring(1), NumberStyles.Integer, CultureInfo.InvariantCulture);
|
||||
unicode = char.ConvertFromUtf32(codePoint);
|
||||
}
|
||||
// Otherwise, map the component to an empty string.
|
||||
else
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -4,49 +4,56 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Util;
|
||||
|
||||
internal class GlyphListFactory
|
||||
internal static class GlyphListFactory
|
||||
{
|
||||
public static GlyphList Get(string listName)
|
||||
#if NET
|
||||
private const char Semicolon = ';';
|
||||
#else
|
||||
private static readonly char[] Semicolon = [';'];
|
||||
#endif
|
||||
|
||||
public static GlyphList Get(params string[] listNames)
|
||||
{
|
||||
using (var resource =
|
||||
typeof(GlyphListFactory).Assembly.GetManifestResourceStream(
|
||||
$"UglyToad.PdfPig.Fonts.Resources.GlyphList.{listName}"))
|
||||
var result = new Dictionary<string, string>(listNames.Any(n => string.Equals("glyphlist", n, StringComparison.OrdinalIgnoreCase)) ? 4300 : 0);
|
||||
|
||||
foreach (var listName in listNames)
|
||||
{
|
||||
if (resource == null)
|
||||
using (var resource =
|
||||
typeof(GlyphListFactory).Assembly.GetManifestResourceStream(
|
||||
$"UglyToad.PdfPig.Fonts.Resources.GlyphList.{listName}"))
|
||||
{
|
||||
throw new ArgumentException($"No embedded glyph list resource was found with the name {listName}.");
|
||||
}
|
||||
if (resource == null)
|
||||
{
|
||||
throw new ArgumentException($"No embedded glyph list resource was found with the name {listName}.");
|
||||
}
|
||||
|
||||
int? capacity = null;
|
||||
// Prevent too much wasted memory capacity for Adobe GlyphList
|
||||
if (string.Equals("glyphlist", listName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
capacity = 4300;
|
||||
ReadInternal(resource, result);
|
||||
}
|
||||
|
||||
return ReadInternal(resource, capacity);
|
||||
}
|
||||
|
||||
#if NET
|
||||
result.TrimExcess();
|
||||
#endif
|
||||
return new GlyphList(result);
|
||||
}
|
||||
|
||||
public static GlyphList Read(Stream stream)
|
||||
{
|
||||
return ReadInternal(stream);
|
||||
var result = new Dictionary<string, string>();
|
||||
ReadInternal(stream, result);
|
||||
return new GlyphList(result);
|
||||
}
|
||||
|
||||
private static readonly char[] Semicolon = [';'];
|
||||
|
||||
private static GlyphList ReadInternal(Stream stream, int? defaultDictionaryCapacity = 0)
|
||||
private static void ReadInternal(Stream stream, Dictionary<string, string> result)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
|
||||
var result = defaultDictionaryCapacity.HasValue ? new Dictionary<string, string>(defaultDictionaryCapacity.Value) : [];
|
||||
|
||||
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
@@ -62,7 +69,7 @@
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var parts = line.Split(Semicolon, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (parts.Length != 2)
|
||||
@@ -86,11 +93,10 @@
|
||||
value += char.ConvertFromUtf32(code);
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.Assert(!result.ContainsKey(key));
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return new GlyphList(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,10 +66,12 @@ bracketleftbig;005B
|
||||
bracketleftBig;005B
|
||||
bracketleftbigg;005B
|
||||
bracketleftBigg;005B
|
||||
bracketleftmath;005B
|
||||
bracketrightBig;005D
|
||||
bracketrightbig;005D
|
||||
bracketrightbigg;005D
|
||||
bracketrightBigg;005D
|
||||
bracketrightmath;005D
|
||||
ceilingleftbig;2308
|
||||
ceilingleftBig;2308
|
||||
ceilingleftBigg;2308
|
||||
@@ -88,6 +90,8 @@ contintegraldisplay;222E
|
||||
contintegraltext;222E
|
||||
coproductdisplay;2210
|
||||
coproducttext;2210
|
||||
epsilon1;03B5
|
||||
equalmath;003D
|
||||
floorleftBig;230A
|
||||
floorleftbig;230A
|
||||
floorleftbigg;230A
|
||||
@@ -113,10 +117,13 @@ parenleftBig;0028
|
||||
parenleftbig;0028
|
||||
parenleftBigg;0028
|
||||
parenleftbigg;0028
|
||||
parenleftmath;0028
|
||||
parenrightBig;0029
|
||||
parenrightbig;0029
|
||||
parenrightBigg;0029
|
||||
parenrightbigg;0029
|
||||
parenrightmath;0029
|
||||
plusmath;002B
|
||||
prime;2032
|
||||
productdisplay;220F
|
||||
producttext;220F
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace UglyToad.PdfPig.Fonts.SystemFonts
|
||||
{
|
||||
#if NET
|
||||
using System.Collections.Generic;
|
||||
|
||||
internal sealed class BrowserSystemFontLister : ISystemFontLister
|
||||
{
|
||||
// Very early version, intended to help developing support for browser
|
||||
|
||||
public IEnumerable<SystemFontRecord> GetAllFonts()
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -92,7 +92,15 @@ namespace UglyToad.PdfPig.Fonts.SystemFonts
|
||||
#if NET
|
||||
else if (OperatingSystem.IsAndroid())
|
||||
{
|
||||
lister = new AndroidSystemFontLister();
|
||||
lister = new AndroidSystemFontLister();
|
||||
}
|
||||
else if (OperatingSystem.IsBrowser())
|
||||
{
|
||||
lister = new BrowserSystemFontLister();
|
||||
}
|
||||
else if (OperatingSystem.IsMacCatalyst())
|
||||
{
|
||||
lister = new MacSystemFontLister();
|
||||
}
|
||||
#endif
|
||||
else
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
{
|
||||
public IEnumerable<SystemFontRecord> GetAllFonts()
|
||||
{
|
||||
// TODO: Could use System.Drawing InstalledFontCollection to do this?
|
||||
|
||||
var winDir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
|
||||
|
||||
var fonts = Path.Combine(winDir, "Fonts");
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace UglyToad.PdfPig.Fonts.TrueType.Glyphs
|
||||
r2c1 = r2C1;
|
||||
}
|
||||
|
||||
public static CompositeTransformMatrix3By2 Identity { get; } = new CompositeTransformMatrix3By2(1, 0, 0, 1, 0, 0);
|
||||
public static readonly CompositeTransformMatrix3By2 Identity = new CompositeTransformMatrix3By2(1, 0, 0, 1, 0, 0);
|
||||
public static CompositeTransformMatrix3By2 CreateTranslation(double x, double y) => new CompositeTransformMatrix3By2(1, 0, 0, 1, x, y);
|
||||
|
||||
public CompositeTransformMatrix3By2 WithTranslation(double x, double y)
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
|
||||
private ushort[] MergeContourEndPoints(IGlyphDescription glyph)
|
||||
{
|
||||
var destinationLastEndPoint = EndPointsOfContours[EndPointsOfContours.Length - 1] + 1;
|
||||
var destinationLastEndPoint = (EndPointsOfContours.Length > 0 ? EndPointsOfContours[EndPointsOfContours.Length - 1] : 0) + 1;
|
||||
|
||||
var endPoints = new ushort[EndPointsOfContours.Length + glyph.EndPointsOfContours.Length];
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
/// <summary>
|
||||
/// A TrueType font.
|
||||
/// </summary>
|
||||
public class TrueTypeFont
|
||||
public sealed class TrueTypeFont
|
||||
{
|
||||
/// <summary>
|
||||
/// The font version number.
|
||||
@@ -107,8 +107,12 @@
|
||||
{
|
||||
boundingBox = default(PdfRectangle);
|
||||
|
||||
if (!TryGetGlyphIndex(characterCode, characterCodeToGlyphId, out var index)
|
||||
|| TableRegister.GlyphTable == null)
|
||||
if (TableRegister.GlyphTable == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetGlyphIndex(characterCode, characterCodeToGlyphId, out var index))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -139,8 +143,12 @@
|
||||
{
|
||||
path = null;
|
||||
|
||||
if (!TryGetGlyphIndex(characterCode, characterCodeToGlyphId, out var index)
|
||||
|| TableRegister.GlyphTable == null)
|
||||
if (TableRegister.GlyphTable == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetGlyphIndex(characterCode, characterCodeToGlyphId, out var index))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,44 +1,40 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net462;net471;net6.0;net8.0</TargetFrameworks>
|
||||
<LangVersion>12</LangVersion>
|
||||
<Version>0.1.10-alpha001</Version>
|
||||
<IsTestProject>False</IsTestProject>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\pdfpig.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard2.0|AnyCPU'">
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\AdobeFontMetrics\*" />
|
||||
<None Remove="Resources\GlyphList\*" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\AdobeFontMetrics\MustRead.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\AdobeFontMetrics\*" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\GlyphList\*" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\pdfpig.snk" Link="pdfpig.snk" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UglyToad.PdfPig.Core\UglyToad.PdfPig.Core.csproj" />
|
||||
<ProjectReference Include="..\UglyToad.PdfPig.Tokenization\UglyToad.PdfPig.Tokenization.csproj" />
|
||||
<ProjectReference Include="..\UglyToad.PdfPig.Tokens\UglyToad.PdfPig.Tokens.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0' or '$(TargetFramework)'=='net462' or '$(TargetFramework)'=='net471'">
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net462;net471;net6.0;net8.0</TargetFrameworks>
|
||||
<LangVersion>12</LangVersion>
|
||||
<Version>0.1.11-alpha001</Version>
|
||||
<IsTestProject>False</IsTestProject>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>..\pdfpig.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\AdobeFontMetrics\*" />
|
||||
<None Remove="Resources\GlyphList\*" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\AdobeFontMetrics\MustRead.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\AdobeFontMetrics\*" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\GlyphList\*" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\pdfpig.snk" Link="pdfpig.snk" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UglyToad.PdfPig.Core\UglyToad.PdfPig.Core.csproj" />
|
||||
<ProjectReference Include="..\UglyToad.PdfPig.Tokenization\UglyToad.PdfPig.Tokenization.csproj" />
|
||||
<ProjectReference Include="..\UglyToad.PdfPig.Tokens\UglyToad.PdfPig.Tokens.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0' or '$(TargetFramework)'=='net462' or '$(TargetFramework)'=='net471'">
|
||||
<PackageReference Include="System.Memory" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -30,6 +30,55 @@
|
||||
Assert.True(reference1.Equals(reference2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IndirectReferenceHashTest()
|
||||
{
|
||||
var reference0 = new IndirectReference(1574, 690);
|
||||
Assert.Equal(1574, reference0.ObjectNumber);
|
||||
Assert.Equal(690, reference0.Generation);
|
||||
|
||||
var reference1 = new IndirectReference(-1574, 690);
|
||||
Assert.Equal(-1574, reference1.ObjectNumber);
|
||||
Assert.Equal(690, reference1.Generation);
|
||||
|
||||
var reference2 = new IndirectReference(58949797283757, 16);
|
||||
Assert.Equal(58949797283757, reference2.ObjectNumber);
|
||||
Assert.Equal(16, reference2.Generation);
|
||||
|
||||
var reference3 = new IndirectReference(-58949797283757, ushort.MaxValue);
|
||||
Assert.Equal(-58949797283757, reference3.ObjectNumber);
|
||||
Assert.Equal(ushort.MaxValue, reference3.Generation);
|
||||
|
||||
var reference4 = new IndirectReference(140737488355327, ushort.MaxValue);
|
||||
Assert.Equal(140737488355327, reference4.ObjectNumber);
|
||||
Assert.Equal(ushort.MaxValue, reference4.Generation);
|
||||
|
||||
var reference5 = new IndirectReference(-140737488355327, ushort.MaxValue);
|
||||
Assert.Equal(-140737488355327, reference5.ObjectNumber);
|
||||
Assert.Equal(ushort.MaxValue, reference5.Generation);
|
||||
|
||||
var ex0 = Assert.Throws<ArgumentOutOfRangeException>(() => new IndirectReference(140737488355328, 0));
|
||||
Assert.StartsWith("Object number must be between -140,737,488,355,327 and 140,737,488,355,327.", ex0.Message);
|
||||
var ex1 = Assert.Throws<ArgumentOutOfRangeException>(() => new IndirectReference(-140737488355328, 0));
|
||||
Assert.StartsWith("Object number must be between -140,737,488,355,327 and 140,737,488,355,327.", ex1.Message);
|
||||
|
||||
var ex2 = Assert.Throws<ArgumentOutOfRangeException>(() => new IndirectReference(1574, -1));
|
||||
Assert.StartsWith("Generation number must not be a negative value.", ex2.Message);
|
||||
|
||||
// We make sure object number is still correct even if generation is not
|
||||
var reference6 = new IndirectReference(1574, int.MaxValue);
|
||||
Assert.Equal(1574, reference6.ObjectNumber);
|
||||
|
||||
var reference7 = new IndirectReference(-1574, ushort.MaxValue + 10);
|
||||
Assert.Equal(-1574, reference7.ObjectNumber);
|
||||
|
||||
var reference9 = new IndirectReference(-140737488355327, ushort.MaxValue + 10);
|
||||
Assert.Equal(-140737488355327, reference9.ObjectNumber);
|
||||
|
||||
var reference10 = new IndirectReference(140737488355327, ushort.MaxValue * 10);
|
||||
Assert.Equal(140737488355327, reference10.ObjectNumber);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TwoIndirectReferenceNotEqual()
|
||||
{
|
||||
|
||||
@@ -20,9 +20,13 @@ O<DJ+*.@<*K0@<6L(Df-\0Ec5e;DffZ(EZee.Bl.9pF""AGXBPCsi + DGm >@3BB / F * &OCAfu2
|
||||
l(DId<j@<? 3r@:F % a + D58'ATD4$Bl@l3De:,-DJs`8ARoFb/0JMK@qB4^F!,R<AKZ&-DfTqBG%G
|
||||
> uD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c~>");
|
||||
|
||||
var result = filter.Decode(bytes, dictionary, 0);
|
||||
var result = filter.Decode(bytes, dictionary, TestFilterProvider.Instance, 0);
|
||||
|
||||
var text = Encoding.ASCII.GetString(result.ToArray());
|
||||
#if !NET
|
||||
string text = Encoding.ASCII.GetString(result.ToArray());
|
||||
#else
|
||||
string text = Encoding.ASCII.GetString(result.Span);
|
||||
#endif
|
||||
|
||||
Assert.Equal("Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, " +
|
||||
"that by a perseverance of delight in the continued and indefatigable generation of knowledge, " +
|
||||
@@ -30,16 +34,49 @@ O<DJ+*.@<*K0@<6L(Df-\0Ec5e;DffZ(EZee.Bl.9pF""AGXBPCsi + DGm >@3BB / F * &OCAfu2
|
||||
text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReplacesZWithEmptyBytes()
|
||||
[Theory]
|
||||
[InlineData("BE", "h")]
|
||||
[InlineData("BOq", "he")]
|
||||
[InlineData("BOtu", "hel")]
|
||||
[InlineData("BOu!r", "hell")]
|
||||
[InlineData("BOu!rDZ", "hello")]
|
||||
[InlineData("BOu!rD]f", "hello ")]
|
||||
[InlineData("BOu!rD]j6", "hello w")]
|
||||
[InlineData("BOu!rD]j7B", "hello wo")]
|
||||
[InlineData("BOu!rD]j7BEW", "hello wor")]
|
||||
[InlineData("BOu!rD]j7BEbk", "hello worl")]
|
||||
[InlineData("BOu!rD]j7BEbo7", "hello world")]
|
||||
[InlineData("BOu!rD]j7BEbo80", "hello world!")]
|
||||
public void DecodesHelloWorld(string encoded, string decoded)
|
||||
{
|
||||
var bytes = Encoding.ASCII.GetBytes("9jqo^zBlbD-");
|
||||
var result = filter.Decode(
|
||||
Encoding.ASCII.GetBytes(encoded),
|
||||
dictionary,
|
||||
TestFilterProvider.Instance,
|
||||
0);
|
||||
|
||||
var result = filter.Decode(bytes, dictionary, 1);
|
||||
Assert.Equal(decoded, Encoding.ASCII.GetString(result.ToArray()));
|
||||
}
|
||||
|
||||
var text = Encoding.ASCII.GetString(result.ToArray());
|
||||
[Theory]
|
||||
[InlineData("9jqo^zBlbD-", "Man \0\0\0\0is d")]
|
||||
[InlineData("", "")]
|
||||
[InlineData("z", "\0\0\0\0")]
|
||||
[InlineData("zz", "\0\0\0\0\0\0\0\0")]
|
||||
[InlineData("zzz", "\0\0\0\0\0\0\0\0\0\0\0\0")]
|
||||
public void ReplacesZWithEmptyBytes(string encoded, string decoded)
|
||||
{
|
||||
var bytes = Encoding.ASCII.GetBytes(encoded);
|
||||
|
||||
Assert.Equal("Man \0\0\0\0is d", text);
|
||||
var result = filter.Decode(bytes, dictionary, TestFilterProvider.Instance, 1);
|
||||
|
||||
#if !NET
|
||||
string text = Encoding.ASCII.GetString(result.ToArray());
|
||||
#else
|
||||
string text = Encoding.ASCII.GetString(result.Span);
|
||||
#endif
|
||||
|
||||
Assert.Equal(decoded, text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -47,19 +84,22 @@ O<DJ+*.@<*K0@<6L(Df-\0Ec5e;DffZ(EZee.Bl.9pF""AGXBPCsi + DGm >@3BB / F * &OCAfu2
|
||||
{
|
||||
var bytes = Encoding.ASCII.GetBytes("qjzqo^");
|
||||
|
||||
Action action = () => filter.Decode(bytes, dictionary, 0);
|
||||
Action action = () => filter.Decode(bytes, dictionary, TestFilterProvider.Instance, 0);
|
||||
|
||||
Assert.Throws<InvalidOperationException>(action);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SingleCharacterLastThrows()
|
||||
[Theory]
|
||||
[InlineData("@rH:%B", "cool")]
|
||||
[InlineData("A~>", "")]
|
||||
[InlineData("@rH:%A~>", "cool")]
|
||||
public void SingleCharacterLastIgnores(string encoded, string decoded)
|
||||
{
|
||||
var bytes = Encoding.ASCII.GetBytes("9jqo^B");
|
||||
var bytes = Encoding.ASCII.GetBytes(encoded);
|
||||
|
||||
Action action = () => filter.Decode(bytes, dictionary, 1);
|
||||
var result = filter.Decode(bytes, dictionary, TestFilterProvider.Instance, 1);
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(action);
|
||||
Assert.Equal(decoded, Encoding.ASCII.GetString(result.ToArray()));
|
||||
}
|
||||
|
||||
private const string PdfContent = @"1 0 obj
|
||||
@@ -102,10 +142,14 @@ T()<(%'A;f?Ma+CT;%+E_a:A0>K&EZek1D/aN,F)u&6DBNA*A0>f4BOu4*+EM76E,9eK+B3(_<%9""p.
|
||||
F*22=@:F%a+=SF4C'moi+=Li?EZeh0FD)e-@<>p#@;]TuBl.9kATKCFGA(],AKYo5BOu4*+CT;%+C#7pF_Pr+@VfTuDf0B:+=SF4C'moi+=
|
||||
Li?EZek1DKKT1F`2DD/TboKAKY](@:s.m/h%oBC'mC/$>""*cF*)G6@;Q?_DIdZpC&~>";
|
||||
|
||||
var result = filter.Decode(Encoding.ASCII.GetBytes(input), dictionary, 0);
|
||||
var result = filter.Decode(Encoding.ASCII.GetBytes(input), dictionary, TestFilterProvider.Instance, 0);
|
||||
|
||||
#if !NET
|
||||
string text = Encoding.ASCII.GetString(result.ToArray());
|
||||
#else
|
||||
string text = Encoding.ASCII.GetString(result.Span);
|
||||
#endif
|
||||
|
||||
var text = Encoding.ASCII.GetString(result.ToArray());
|
||||
|
||||
Assert.Equal(PdfContent.Replace("\r\n", "\n"), text);
|
||||
}
|
||||
|
||||
@@ -122,13 +166,59 @@ T()<(%'A;f?Ma+CT;%+E_a:A0>K&EZek1D/aN,F)u&6DBNA*A0>f4BOu4*+EM76E,9eK+B3(_<%9""p.
|
||||
F*22=@:F%a+=SF4C'moi+=Li?EZeh0FD)e-@<>p#@;]TuBl.9kATKCFGA(],AKYo5BOu4*+CT;%+C#7pF_Pr+@VfTuDf0B:+=SF4C'moi+=
|
||||
Li?EZek1DKKT1F`2DD/TboKAKY](@:s.m/h%oBC'mC/$>""*cF*)G6@;Q?_DIdZpC&";
|
||||
|
||||
var result = filter.Decode(Encoding.ASCII.GetBytes(input), dictionary, 0);
|
||||
var result = filter.Decode(Encoding.ASCII.GetBytes(input), dictionary, TestFilterProvider.Instance, 0);
|
||||
|
||||
var text = Encoding.ASCII.GetString(result.ToArray());
|
||||
#if !NET
|
||||
string text = Encoding.ASCII.GetString(result.ToArray());
|
||||
#else
|
||||
string text = Encoding.ASCII.GetString(result.Span);
|
||||
#endif
|
||||
|
||||
Assert.Equal(PdfContent.Replace("\r\n", "\n"), text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DecodeParallel()
|
||||
{
|
||||
Parallel.For(0, 100_000, i =>
|
||||
{
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
var bytes = Encoding.ASCII.GetBytes("9jqo^zBlbD-");
|
||||
|
||||
var result = filter.Decode(bytes, dictionary, TestFilterProvider.Instance, 1);
|
||||
|
||||
#if !NET
|
||||
string text = Encoding.ASCII.GetString(result.ToArray());
|
||||
#else
|
||||
string text = Encoding.ASCII.GetString(result.Span);
|
||||
#endif
|
||||
|
||||
Assert.Equal("Man \0\0\0\0is d", text);
|
||||
}
|
||||
else
|
||||
{
|
||||
const string input =
|
||||
@"0d&.mDdmGg4?O`>9P&*SFD)dS2E2gC4pl@QEb/Zr$8N_r$:7]!01IZ=0eskNAdU47<+?7h+B3Ol2_m!C+?)#1+B1
|
||||
`9>:<KhASu!rA7]9oF*)G6@;U'.@ps6t@V$[&ART*lARTXoCj@HP2DlU*/0HBI+B1r?0H_r%1a#ac$<nof.3LB""+=MAS+D58'ATD3qCj@.
|
||||
F@;@;70ea^uAKYi.Eb-A7E+*6f+EV:*DBN1?0ek+_+B1r?<%9""=ASu!rA7]9oF*)G6@;U'<.3MT)$8<SS1,pCU6jd-H;e7
|
||||
C#1,U1&Ft""Og2'=;YEa`c,ASu!rA8,po+Dk\3BQ%F&+CT;%+CQ]A1,'h!Ft""Oh2'=;UBl%3eCh4`'DBMbD7O]H>0H_br.:""&q8d[6p/M
|
||||
T()<(%'A;f?Ma+CT;%+E_a:A0>K&EZek1D/aN,F)u&6DBNA*A0>f4BOu4*+EM76E,9eK+B3(_<%9""p.!0AMEb031ATMF#F<G%,DIIR2+Cno
|
||||
&@3B9%+CT.1.3LK*+=KNS6V0ilAoD^,@<=+N>p**=$</Jt-rY&$AKYo'+EV:.+Cf>,E,oN2F(oQ1+D#G#De*R""B-;&&FD,T'F!+n3AKY4b
|
||||
F*22=@:F%a+=SF4C'moi+=Li?EZeh0FD)e-@<>p#@;]TuBl.9kATKCFGA(],AKYo5BOu4*+CT;%+C#7pF_Pr+@VfTuDf0B:+=SF4C'moi+=
|
||||
Li?EZek1DKKT1F`2DD/TboKAKY](@:s.m/h%oBC'mC/$>""*cF*)G6@;Q?_DIdZpC&~>";
|
||||
|
||||
var result = filter.Decode(Encoding.ASCII.GetBytes(input), dictionary, TestFilterProvider.Instance, 0);
|
||||
|
||||
#if !NET
|
||||
string text = Encoding.ASCII.GetString(result.ToArray());
|
||||
#else
|
||||
string text = Encoding.ASCII.GetString(result.Span);
|
||||
#endif
|
||||
|
||||
Assert.Equal(PdfContent.Replace("\r\n", "\n"), text);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
var input = Encoding.ASCII.GetBytes(
|
||||
"7368652073656C6C73207365617368656C6C73206F6E20746865207365612073686F7265");
|
||||
|
||||
var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, 1);
|
||||
var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, TestFilterProvider.Instance, 1);
|
||||
|
||||
var decodedText = Encoding.ASCII.GetString(decoded.ToArray());
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
var input = Encoding.ASCII.GetBytes(
|
||||
"<7368652073656C6C73207365617368656C6C73206F6E20746865207365612073686F7265>");
|
||||
|
||||
var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, 1);
|
||||
var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, TestFilterProvider.Instance, 1);
|
||||
|
||||
var decodedText = Encoding.ASCII.GetString(decoded.ToArray());
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
@"6F6E6365207 5706F6E206120 74696D6520696E
|
||||
20612067616C6178792046617220466172204177 6179");
|
||||
|
||||
var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, 1);
|
||||
var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, TestFilterProvider.Instance, 1);
|
||||
|
||||
var decodedText = Encoding.ASCII.GetString(decoded.ToArray());
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
|
||||
var input = Encoding.ASCII.GetBytes("6f6e63652075706f6e20612074696d6520696e20612067616c61787920466172204661722041776179");
|
||||
|
||||
var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, 1);
|
||||
var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, TestFilterProvider.Instance, 1);
|
||||
|
||||
var decodedText = Encoding.ASCII.GetString(decoded.ToArray());
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
{
|
||||
var input = Encoding.ASCII.GetBytes(inputString);
|
||||
|
||||
Action action = () => new AsciiHexDecodeFilter().Decode(input, dictionary, 1);
|
||||
Action action = () => new AsciiHexDecodeFilter().Decode(input, dictionary, TestFilterProvider.Instance, 1);
|
||||
|
||||
Assert.Throws<InvalidOperationException>(action);
|
||||
}
|
||||
@@ -85,7 +85,7 @@
|
||||
{
|
||||
var input = Encoding.ASCII.GetBytes("AE5>");
|
||||
|
||||
var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, 1);
|
||||
var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, TestFilterProvider.Instance, 1);
|
||||
|
||||
#pragma warning disable SYSLIB0001
|
||||
var decodedText = Encoding.UTF7.GetString(decoded.ToArray());
|
||||
@@ -101,7 +101,7 @@
|
||||
|
||||
var input = Encoding.ASCII.GetBytes("6f6e63652075706f6e20612074696d6520696e20612067616c61787920466172204661722041776179> There is stuff following the EOD.");
|
||||
|
||||
var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, 1);
|
||||
var decoded = new AsciiHexDecodeFilter().Decode(input, dictionary, TestFilterProvider.Instance, 1);
|
||||
|
||||
var decodedText = Encoding.ASCII.GetString(decoded.ToArray());
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace UglyToad.PdfPig.Tests.Filters
|
||||
{
|
||||
using PdfPig.Filters;
|
||||
using PdfPig.Filters.Lzw;
|
||||
|
||||
public class BitStreamTests
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
};
|
||||
|
||||
var expectedBytes = ImageHelpers.LoadFileBytes("ccittfax-decoded.bin");
|
||||
var decodedBytes = filter.Decode(encodedBytes, new DictionaryToken(dictionary), 0);
|
||||
var decodedBytes = filter.Decode(encodedBytes, new DictionaryToken(dictionary), TestFilterProvider.Instance, 0);
|
||||
Assert.Equal(expectedBytes, decodedBytes.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
{
|
||||
inputStream.Seek(0, SeekOrigin.Begin);
|
||||
var result = filter.Encode(inputStream, parameters, 0);
|
||||
var decoded = filter.Decode(result, parameters, 0);
|
||||
var decoded = filter.Decode(result, parameters, TestFilterProvider.Instance, 0);
|
||||
Assert.Equal(input, decoded.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
1, 10, 19
|
||||
};
|
||||
|
||||
var decoded = filter.Decode(data, new DictionaryToken(new Dictionary<NameToken, IToken>()), 1);
|
||||
var decoded = filter.Decode(data, new DictionaryToken(new Dictionary<NameToken, IToken>()), TestFilterProvider.Instance, 1);
|
||||
|
||||
var expectedResult = new byte[]
|
||||
{
|
||||
@@ -53,7 +53,7 @@
|
||||
90, 6, 7
|
||||
};
|
||||
|
||||
var decoded = filter.Decode(data, new DictionaryToken(new Dictionary<NameToken, IToken>()), 0);
|
||||
var decoded = filter.Decode(data, new DictionaryToken(new Dictionary<NameToken, IToken>()), TestFilterProvider.Instance, 0);
|
||||
|
||||
var expectedResult = new byte[]
|
||||
{
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
{
|
||||
var list = new GlyphList(new Dictionary<string, string>
|
||||
{
|
||||
{"Boris", "B"}
|
||||
{ "Boris", "B" }
|
||||
});
|
||||
|
||||
var result = list.NameToUnicode("Boris.Special");
|
||||
@@ -70,7 +70,7 @@
|
||||
{
|
||||
var list = new GlyphList(new Dictionary<string, string>
|
||||
{
|
||||
{"B", "X"}
|
||||
{ "B", "X" }
|
||||
});
|
||||
|
||||
var result = list.NameToUnicode("uni0042");
|
||||
@@ -83,12 +83,27 @@
|
||||
{
|
||||
var list = new GlyphList(new Dictionary<string, string>
|
||||
{
|
||||
{"E", "Æ"}
|
||||
{ "E", "Æ" }
|
||||
});
|
||||
|
||||
var result = list.NameToUnicode("u0045");
|
||||
|
||||
Assert.Equal("E", result);
|
||||
}
|
||||
|
||||
|
||||
[Fact(Skip = "TODO - String don't match")]
|
||||
public void NameToUnicodeConvertAglSpecification()
|
||||
{
|
||||
// https://github.com/adobe-type-tools/agl-specification?tab=readme-ov-file#3-examples
|
||||
var list = new GlyphList(new Dictionary<string, string>
|
||||
{
|
||||
{ "Lcommaaccent", "\u013B" }
|
||||
});
|
||||
|
||||
var result = list.NameToUnicode("Lcommaaccent_uni20AC0308_u1040C.alternate");
|
||||
|
||||
Assert.Equal("\u013B\u20AC\u0308\u1040C", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,72 @@ endbfchar";
|
||||
Assert.Equal("10.003", cmap.Version);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanParseFile0001413CMap()
|
||||
{
|
||||
const string cmap =
|
||||
"""
|
||||
/CIDInit /ProcSet findresource
|
||||
begin
|
||||
12 dict
|
||||
begin
|
||||
begincmap
|
||||
/CIDSystemInfo <</Ordering (GB1) /Registry (Adobe) /Supplement 2 >> def
|
||||
/CMapName /Adobe-GB1-2 def
|
||||
/CMapType 2 def
|
||||
1 begincodespacerange
|
||||
<0000> <ffff> endcodespacerange
|
||||
100 beginbfrange
|
||||
<0001> <0002> <0022> <0003> <0019> <0025> <001b> <0023> <003f> <0024> <0025> <0049> <0026> <0033> <004c> <0034> <0036> <005d> <0037> <003d> <0061> <003e> <004b> <0069> <004c> <004d> <0079> <0053> <0054> <3001> <0055> <005a> <300a> <005c> <005d> <4e00> <0060> <0062> <4e09> <0063> <0064> <4e0d> <0066> <0067> <4e13> <006a> <006d> <4e1a> <006f> <0070> <4e24> <0076> <0079> <4e38> <007a> <007b> <4e3d> <007e> <007f> <4e48> <0080> <0085> <4e4b> <0089> <008b> <4e5f> <008d> <008e> <4e70> <0090> <0091> <4e88> <0092> <0093> <4e8b> <0094> <0095> <4e8e> <0097> <0098> <4e94> <0099> <009a> <4e9a> <009c> <00a0> <4ea4> <00a3> <00a4> <4ec0> <00a5> <00a6> <4ec5> <00a7> <00a8> <4eca> <00a9> <00aa> <4ecd> <00ac> <00af> <4ed3> <00b0> <00b1> <4ed8> <00b3> <00b5> <4ee3> <00b9> <00ba> <4ef6> <00bf> <00c1> <4f0f> <00c2> <00c5> <4f17> <00c6> <00c7> <4f1f> <00cb> <00cc> <4f2f> <00d2> <00d4> <4f4d> <00d8> <00d9> <4f5b> <00e2> <00e3> <4fa6> <00e7> <00e8> <4fc3> <00ef> <00f0> <5018> <0108> <010a> <516b> <010b> <010c> <5170> <010d> <010e> <5173> <010f> <0112> <5176> <0113> <0114> <517b> <0117> <0118> <518c> <011a> <011b> <519b> <011e> <011f> <51b2> <0124> <0125> <51e0> <0128> <012a> <51f9> <012b> <012c> <5200> <012d> <012e> <5206> <0132> <0134> <5219> <0138> <0139> <5228> <0144> <0147> <529e> <0149> <014b> <52a8> <0156> <0157> <5316> <0158> <0159> <533a> <015e> <015f> <534e> <0161> <0163> <5355> <0167> <0169> <5360> <016b> <016c> <5370> <016d> <016e> <5373> <0175> <0176> <538b> <017c> <0181> <53c8> <0183> <0186> <53d6> <0187> <018a> <53e3> <018b> <0193> <53e8> <0194> <0195> <53f2> <0196> <0197> <53f7> <019a> <019b> <5403> <019c> <019d> <5408> <019e> <01a0> <540c> <01a4> <01a5> <542b> <01ae> <01b0> <547b> <01be> <01bf> <552e> <01cb> <01cc> <56da> <01d5> <01d6> <56fd> <01e3> <01e4> <574e> <01e7> <01e8> <575a> <01e9> <01ea> <5760> <01f8> <01f9> <57f9> <0211> <0212> <5915> <0216> <0218> <5929> <0223> <0224> <5956> <0233> <0234> <5a03> <023e> <023f> <5b57> <0242> <0243> <5b63> <0248> <0249> <5b88> <024e> <0250> <5b9c> <0251> <0253> <5ba1> <0256> <0257> <5bb3> <025a> <025c> <5bbd> endbfrange
|
||||
100 beginbfrange
|
||||
<0260> <0264> <5bf8> <0268> <0269> <5c09> <0272> <0274> <5c3c> <0279> <027a> <5c4a> <0286> <0289> <5de5> <028c> <028f> <5df1> <0291> <0293> <5e01> <0298> <0299> <5e15> <029a> <029b> <5e1c> <029d> <029e> <5e2d> <02a2> <02a4> <5e72> <02ab> <02ad> <5e93> <02b0> <02b1> <5ea6> <02b3> <02b4> <5eb6> <02b6> <02b7> <5ef6> <02b9> <02ba> <5eff> <02bb> <02bc> <5f02> <02c1> <02c2> <5f17> <02c7> <02c8> <5f39> <02c9> <02ca> <5f52> <02cf> <02d0> <5f7b> <02d1> <02d2> <5f80> <02d3> <02d4> <5f84> <02e6> <02e7> <6000> <0300> <0301> <6167> <0308> <0309> <6210> <030e> <030f> <623f> <0311> <0312> <624d> <0315> <0316> <6266> <031c> <031e> <627e> <0321> <0324> <6295> <0327> <0328> <62a4> <0330> <0331> <62cc> <0332> <0333> <62d8> <0337> <0338> <62e8> <033b> <033c> <6301> <0340> <0341> <631a> <0355> <0356> <63a7> <0358> <0359> <63cf> <036b> <036c> <653e> <037d> <037e> <65af> <0387> <038b> <65e5> <038d> <038e> <65f6> <0393> <0394> <661f> <0397> <0398> <666e> <03a0> <03a2> <66fe> <03a3> <03a4> <6708> <03aa> <03ad> <672a> <03b2> <03b3> <6742> <03b4> <03b5> <6750> <03c2> <03c3> <67d0> <03ca> <03cc> <6837> <03db> <03de> <6b20> <03e2> <03e7> <6b62> <03e8> <03e9> <6b8a> <03f1> <03f2> <6bd4> <03fb> <03fc> <6c2e> <040d> <040e> <6cbb> <040f> <0410> <6cbe> <0421> <0422> <6d3d> <0425> <0426> <6d4a> <042d> <042e> <6d88> <043b> <043c> <6e20> <0457> <0458> <707e> <045a> <045b> <70b8> <045d> <045e> <70c2> <046d> <046e> <7247> <0476> <0477> <7279> <047b> <047c> <72ec> <0485> <0486> <73af> <0494> <0495> <7530> <0498> <0499> <7537> <04a9> <04aa> <767d> <04b0> <04b1> <76d0> <04ca> <04cb> <786b> <04d5> <04d6> <795d> <04e1> <04e2> <79ef> <04e9> <04ea> <7a76> <04f2> <04f4> <7ade> <0515> <0516> <7ea6> <0519> <051a> <7eb2> <051e> <051f> <7ec3> <0520> <0522> <7ec6> <0526> <0528> <7ed8> <0529> <052a> <7edc> <0531> <0532> <7f13> <0542> <0543> <800c> <0546> <0547> <804b> <0564> <0565> <81f3> <056f> <0570> <826f> <0579> <057a> <82e5> <0586> <0587> <83b1> <05b6> <05b7> <89c1> <05ba> <05bb> <89c8> <05c1> <05c2> <8ba1> <05c4> <05c5> <8ba8> <05c6> <05c9> <8bad> <05cd> <05ce> <8bbe> <05da> <05db> <8be5> <05dc> <05dd> <8bec> endbfrange
|
||||
33 beginbfrange
|
||||
<05df> <05e0> <8bf1> <05e2> <05e3> <8bf7> <05f4> <05f6> <8d21> <05f8> <05f9> <8d27> <05fa> <05fb> <8d2a> <05fc> <05fd> <8d2e> <05ff> <0601> <8d37> <060e> <060f> <8d8a> <061e> <061f> <8f90> <0623> <0624> <8fa8> <0629> <062a> <8fc4> <062d> <062e> <8fd0> <0630> <0631> <8fd8> <0632> <0636> <8fdb> <0637> <0638> <8fea> <063d> <0640> <9000> <0641> <0642> <9009> <0646> <0647> <901a> <0648> <0649> <901f> <0651> <0652> <906d> <0654> <0655> <907f> <0658> <0659> <90ae> <065d> <065e> <90e7> <0660> <0662> <914b> <0667> <066a> <91cc> <0671> <0672> <94c0> <067f> <0680> <95ed> <068d> <068f> <9644> <0694> <0695> <9676> <069c> <069d> <96c5> <069f> <06a0> <96f6> <06b0> <06b2> <9879> <06b6> <06b7> <9886> <06bc> <06bd> <98de> endbfrange
|
||||
100 beginbfchar
|
||||
<0000> <0020> <001a> <003d> <004e> <007d> <004f> <00a8> <0050> <2014> <0051> <2026> <0052> <25a1> <005b> <ff0c> <005e> <4e03> <005f> <4e07> <0065> <4e11> <0068> <4e16> <0069> <4e18> <006e> <4e22> <0071> <4e27> <0072> <4e2a> <0073> <4e2d> <0074> <4e30> <0075> <4e34> <007c> <4e43> <007d> <4e45> <0086> <4e53> <0087> <4e59> <0088> <4e5d> <008c> <4e66> <008f> <4e86> <0096> <4e92> <009b> <4ea2> <00a1> <4eab> <00a2> <4eba> <00ab> <4ed1> <00b2> <4edf> <00b6> <4eea> <00b7> <4eec> <00b8> <4ef2> <00bb> <4efb> <00bc> <4efd> <00bd> <4f01> <00be> <4f0a> <00c8> <4f24> <00c9> <4f26> <00ca> <4f2a> <00cd> <4f34> <00ce> <4f36> <00cf> <4f38> <00d0> <4f3c> <00d1> <4f46> <00d5> <4f53> <00d6> <4f55> <00d7> <4f59> <00da> <4f69> <00db> <4f6f> <00dc> <4f73> <00dd> <4f7f> <00de> <4f8b> <00df> <4f9b> <00e0> <4f9d> <00e1> <4fa3> <00e4> <4fae> <00e5> <4fb5> <00e6> <4fbf> <00e9> <4fdd> <00ea> <4fe1> <00eb> <4fee> <00ec> <4ff1> <00ed> <500d> <00ee> <5012> <00f1> <501f> <00f2> <5021> <00f3> <503c> <00f4> <503e> <00f5> <5047> <00f6> <505a> <00f7> <505c> <00f8> <5065> <00f9> <507f> <00fa> <5085> <00fb> <50a8> <00fc> <50b2> <00fd> <50f3> <00fe> <50fb> <00ff> <513f> <0100> <5141> <0101> <5143> <0102> <5145> <0103> <5148> <0104> <514b> <0105> <514d> <0106> <5165> <0107> <5168> <0115> <5180> <0116> <5185> <0119> <5199> <011c> <51ac> <011d> <51b0> <0120> <51b5> <0121> <51c0> <0122> <51c6> <0123> <51cf> <0126> <51ed> endbfchar
|
||||
100 beginbfchar
|
||||
<0127> <51ef> <012f> <520a> <0130> <5212> <0131> <5217> <0135> <521d> <0136> <5220> <0137> <5224> <013a> <522b> <013b> <5230> <013c> <5236> <013d> <523b> <013e> <5242> <013f> <524a> <0140> <524d> <0141> <5269> <0142> <526f> <0143> <529b> <0148> <52a3> <014c> <52b1> <014d> <52b3> <014e> <52bf> <014f> <52c3> <0150> <52d2> <0151> <52d8> <0152> <52e4> <0153> <52fa> <0154> <5300> <0155> <5305> <015a> <5341> <015b> <5343> <015c> <5347> <015d> <534a> <0160> <5353> <0164> <535a> <0165> <535c> <0166> <535e> <016a> <536b> <016f> <5378> <0170> <537f> <0171> <5382> <0172> <5384> <0173> <5386> <0174> <5389> <0177> <5398> <0178> <539a> <0179> <539f> <017a> <53bb> <017b> <53c2> <0182> <53d1> <0198> <53fc> <0199> <5401> <01a1> <5411> <01a2> <5417> <01a3> <5426> <01a6> <542f> <01a7> <5431> <01a8> <5446> <01a9> <544a> <01aa> <5458> <01ab> <5468> <01ac> <5473> <01ad> <5475> <01b1> <548c> <01b2> <5490> <01b3> <5496> <01b4> <54a8> <01b5> <54b1> <01b6> <54c1> <01b7> <54c8> <01b8> <54cd> <01b9> <54df> <01ba> <54e5> <01bb> <54ea> <01bc> <5510> <01bd> <5524> <01c0> <5531> <01c1> <5546> <01c2> <554a> <01c3> <5564> <01c4> <5566> <01c5> <5580> <01c6> <5584> <01c7> <558a> <01c8> <559c> <01c9> <5668> <01ca> <566a> <01cd> <56de> <01ce> <56e0> <01cf> <56e2> <01d0> <56e4> <01d1> <56ed> <01d2> <56f0> <01d3> <56f4> <01d4> <56fa> <01d7> <5703> <01d8> <5706> <01d9> <5708> <01da> <571f> <01db> <5723> <01dc> <5728> endbfchar
|
||||
100 beginbfchar
|
||||
<01dd> <572d> <01de> <5730> <01df> <573a> <01e0> <5740> <01e1> <5747> <01e2> <574a> <01e5> <5751> <01e6> <5757> <01eb> <5764> <01ec> <5766> <01ed> <5782> <01ee> <5784> <01ef> <578b> <01f0> <5792> <01f1> <579b> <01f2> <57a6> <01f3> <57ab> <01f4> <57c3> <01f5> <57ce> <01f6> <57d4> <01f7> <57df> <01fa> <5802> <01fb> <5806> <01fc> <5815> <01fd> <5821> <01fe> <5824> <01ff> <582a> <0200> <5851> <0201> <5854> <0202> <585e> <0203> <586b> <0204> <5883> <0205> <5893> <0206> <5899> <0207> <589e> <0208> <58a8> <0209> <58c1> <020a> <58e4> <020b> <58eb> <020c> <58f0> <020d> <5904> <020e> <5907> <020f> <590d> <0210> <590f> <0213> <591a> <0214> <591f> <0215> <5927> <0219> <592e> <021a> <5931> <021b> <5934> <021c> <5938> <021d> <593a> <021e> <5947> <021f> <5949> <0220> <594b> <0221> <594e> <0222> <5951> <0225> <5962> <0226> <5965> <0227> <5973> <0228> <5979> <0229> <597d> <022a> <5982> <022b> <5999> <022c> <59a5> <022d> <59a8> <022e> <59bb> <022f> <59c6> <0230> <59cb> <0231> <59d4> <0232> <5a01> <0235> <5a1c> <0236> <5a31> <0237> <5a74> <0238> <5a92> <0239> <5ac1> <023a> <5acc> <023b> <5ae9> <023c> <5b50> <023d> <5b54> <0240> <5b5c> <0241> <5b5f> <0244> <5b66> <0245> <5b6a> <0246> <5b83> <0247> <5b85> <024a> <5b8c> <024b> <5b8f> <024c> <5b97> <024d> <5b9a> <0254> <5baa> <0255> <5bb0> <0258> <5bb6> <0259> <5bb9> <025d> <5bc6> <025e> <5bcc> <025f> <5bdf> <0265> <5bff> <0266> <5c04> <0267> <5c06> endbfchar
|
||||
100 beginbfchar
|
||||
<026a> <5c0f> <026b> <5c11> <026c> <5c14> <026d> <5c1a> <026e> <5c1d> <026f> <5c24> <0270> <5c31> <0271> <5c38> <0275> <5c40> <0276> <5c42> <0277> <5c45> <0278> <5c48> <027b> <5c55> <027c> <5c5e> <027d> <5c65> <027e> <5c71> <027f> <5c7f> <0280> <5c97> <0281> <5c9b> <0282> <5cad> <0283> <5cb8> <0284> <5ddd> <0285> <5de1> <028a> <5deb> <028b> <5dee> <0290> <5dfe> <0294> <5e05> <0295> <5e08> <0296> <5e0c> <0297> <5e10> <029c> <5e26> <029f> <5e38> <02a0> <5e45> <02a1> <5e55> <02a5> <5e76> <02a6> <5e78> <02a7> <5e7f> <02a8> <5e86> <02a9> <5e8a> <02aa> <5e8f> <02ae> <5e9c> <02af> <5e9f> <02b2> <5ead> <02b5> <5ec9> <02b8> <5efa> <02bd> <5f0a> <02be> <5f0f> <02bf> <5f13> <02c0> <5f15> <02c3> <5f20> <02c4> <5f25> <02c5> <5f27> <02c6> <5f31> <02cb> <5f55> <02cc> <5f62> <02cd> <5f64> <02ce> <5f71> <02d5> <5f88> <02d6> <5f8b> <02d7> <5f92> <02d8> <5f97> <02d9> <5fa1> <02da> <5faa> <02db> <5fae> <02dc> <5fb5> <02dd> <5fb7> <02de> <5fc3> <02df> <5fc5> <02e0> <5fcd> <02e1> <5fd7> <02e2> <5fd9> <02e3> <5feb> <02e4> <5ff1> <02e5> <5ff5> <02e8> <600e> <02e9> <6015> <02ea> <601d> <02eb> <6025> <02ec> <6027> <02ed> <603b> <02ee> <6050> <02ef> <6062> <02f0> <6069> <02f1> <606f> <02f2> <6076> <02f3> <6089> <02f4> <60a0> <02f5> <60ac> <02f6> <60c5> <02f7> <60ca> <02f8> <60e7> <02f9> <60ef> <02fa> <60f3> <02fb> <6108> <02fc> <610f> <02fd> <611f> <02fe> <613f> <02ff> <614e> <0302> <6177> <0303> <61be> endbfchar
|
||||
100 beginbfchar
|
||||
<0304> <61c2> <0305> <61c8> <0306> <61d2> <0307> <6208> <030a> <6216> <030b> <6218> <030c> <621a> <030d> <6234> <0310> <624b> <0313> <6258> <0314> <6263> <0317> <6269> <0318> <6270> <0319> <6273> <031a> <6276> <031b> <6279> <031f> <628a> <0320> <6291> <0325> <629b> <0326> <62a2> <0329> <62b1> <032a> <62b5> <032b> <62b9> <032c> <62bd> <032d> <62c2> <032e> <62c5> <032f> <62c9> <0334> <62db> <0335> <62df> <0336> <62e5> <0339> <62ec> <033a> <62ff> <033d> <6307> <033e> <6309> <033f> <6311> <0342> <631d> <0343> <6325> <0344> <6328> <0345> <632a> <0346> <633d> <0347> <634e> <0348> <6350> <0349> <6355> <034a> <635f> <034b> <6362> <034c> <636e> <034d> <6377> <034e> <6388> <034f> <638c> <0350> <6392> <0351> <6398> <0352> <63a0> <0353> <63a2> <0354> <63a5> <0357> <63aa> <035a> <63d2> <035b> <63e1> <035c> <63ed> <035d> <63f4> <035e> <641c> <035f> <641e> <0360> <642d> <0361> <6458> <0362> <6469> <0363> <64a4> <0364> <64ad> <0365> <64bc> <0366> <64cd> <0367> <652f> <0368> <6536> <0369> <6539> <036a> <653b> <036d> <6545> <036e> <6548> <036f> <6551> <0370> <6559> <0371> <6563> <0372> <6566> <0373> <656c> <0374> <6570> <0375> <6574> <0376> <6587> <0377> <6597> <0378> <6599> <0379> <659f> <037b> <65a5> <037c> <65ad> <037f> <65b9> <0380> <65bd> <0381> <65c5> <0382> <65cb> <0383> <65cf> <0384> <65d7> <0385> <65e0> <0386> <65e2> <038c> <65f1> <038f> <65fa> <0390> <6606> <0391> <660e> <0392> <6613> endbfchar
|
||||
100 beginbfchar
|
||||
<0395> <662f> <0396> <663e> <0399> <667a> <039a> <6682> <039b> <66b4> <039c> <66dd> <039d> <66f2> <039e> <66f4> <039f> <66fc> <03a5> <670d> <03a6> <6717> <03a7> <671b> <03a8> <671f> <03a9> <6728> <03ae> <672f> <03af> <6734> <03b0> <673a> <03b1> <6740> <03b6> <675f> <03b7> <6761> <03b8> <6765> <03b9> <6770> <03ba> <677e> <03bb> <6781> <03bc> <6784> <03bd> <6790> <03be> <6797> <03bf> <679c> <03c0> <67af> <03c1> <67b6> <03c4> <67d3> <03c5> <67e5> <03c6> <6807> <03c7> <6811> <03c8> <6816> <03c9> <6821> <03cd> <683c> <03ce> <6848> <03cf> <6851> <03d0> <6863> <03d1> <6886> <03d2> <68a2> <03d3> <68c0> <03d4> <68da> <03d5> <68ee> <03d6> <690d> <03d7> <695a> <03d8> <6982> <03d9> <6a0a> <03da> <6a21> <03df> <6b27> <03e0> <6b32> <03e1> <6b3e> <03ea> <6b96> <03eb> <6bb5> <03ec> <6bc1> <03ed> <6bcb> <03ee> <6bcd> <03ef> <6bcf> <03f0> <6bd2> <03f3> <6bd7> <03f4> <6bdb> <03f5> <6beb> <03f6> <6c0f> <03f7> <6c11> <03f8> <6c14> <03f9> <6c1b> <03fa> <6c27> <03fd> <6c34> <03fe> <6c38> <03ff> <6c42> <0400> <6c47> <0401> <6c50> <0402> <6c57> <0403> <6c5f> <0404> <6c61> <0405> <6c7e> <0406> <6c89> <0407> <6c99> <0408> <6c9f> <0409> <6ca1> <040a> <6caa> <040b> <6cb3> <040c> <6cb9> <0411> <6cc4> <0412> <6cca> <0413> <6cd5> <0414> <6cdb> <0415> <6ce2> <0416> <6ce5> <0417> <6ce8> <0418> <6cf0> <0419> <6cfb> <041a> <6cfd> <041b> <6d01> <041c> <6d0b> <041d> <6d1b> <041e> <6d2a> <041f> <6d32> <0420> <6d3b> endbfchar
|
||||
100 beginbfchar
|
||||
<0423> <6d41> <0424> <6d46> <0427> <6d4e> <0428> <6d53> <0429> <6d66> <042a> <6d6a> <042b> <6d6e> <042c> <6d77> <042f> <6d95> <0430> <6da8> <0431> <6db2> <0432> <6dc0> <0433> <6dc6> <0434> <6de1> <0435> <6df1> <0436> <6df7> <0437> <6dfb> <0438> <6e05> <0439> <6e10> <043a> <6e14> <043d> <6e23> <043e> <6e29> <043f> <6e2f> <0440> <6e38> <0441> <6e56> <0442> <6e58> <0443> <6e7f> <0444> <6e89> <0445> <6e90> <0446> <6ecb> <0447> <6ee1> <0448> <6ee8> <0449> <6f0f> <044a> <6f14> <044b> <6f20> <044c> <6f2b> <044d> <6f5c> <044e> <6f6e> <044f> <6f84> <0450> <6fb3> <0451> <6fc0> <0452> <704c> <0453> <706b> <0454> <706d> <0455> <7070> <0456> <7075> <0459> <70ad> <045c> <70bc> <045f> <70c8> <0460> <70e7> <0461> <70ed> <0462> <711a> <0463> <7136> <0464> <7164> <0465> <7167> <0466> <719f> <0467> <71c3> <0468> <71e5> <0469> <7206> <046a> <722a> <046b> <722c> <046c> <7231> <046f> <7259> <0470> <725b> <0471> <7261> <0472> <7267> <0473> <7269> <0474> <7272> <0475> <7275> <0478> <72af> <0479> <72b6> <047a> <72de> <047d> <730e> <047e> <731b> <047f> <732e> <0480> <736d> <0481> <7387> <0482> <738b> <0483> <7396> <0484> <73ab> <0487> <73cd> <0488> <73d0> <0489> <73ed> <048a> <7403> <048b> <7406> <048c> <745e> <048d> <74a7> <048e> <74dc> <048f> <74e3> <0490> <74e6> <0491> <751a> <0492> <751f> <0493> <7528> <0496> <7533> <0497> <7535> <049a> <754c> <049b> <7559> <049c> <755c> <049d> <7565> <049e> <7574> endbfchar
|
||||
100 beginbfchar
|
||||
<049f> <7578> <04a0> <7591> <04a1> <75ab> <04a2> <75be> <04a3> <75c5> <04a4> <75c7> <04a5> <75d8> <04a6> <75db> <04a7> <764c> <04a8> <767b> <04ab> <7684> <04ac> <7687> <04ad> <76bf> <04ae> <76c6> <04af> <76ca> <04b2> <76d6> <04b3> <76d8> <04b4> <76df> <04b5> <76ee> <04b6> <76f2> <04b7> <76f4> <04b8> <76f8> <04b9> <76fe> <04ba> <770b> <04bb> <771f> <04bc> <773c> <04bd> <7740> <04be> <7763> <04bf> <77db> <04c0> <77e5> <04c1> <77ed> <04c2> <77ff> <04c3> <7801> <04c4> <780d> <04c5> <7814> <04c6> <7834> <04c7> <7838> <04c8> <7840> <04c9> <7845> <04cc> <786e> <04cd> <7877> <04ce> <788d> <04cf> <7891> <04d0> <78b0> <04d1> <78b3> <04d2> <78cb> <04d3> <793a> <04d4> <793e> <04d7> <7965> <04d8> <7968> <04d9> <7978> <04da> <7981> <04db> <798f> <04dc> <79bb> <04dd> <79c1> <04de> <79cd> <04df> <79d1> <04e0> <79d8> <04e3> <79fb> <04e4> <7a00> <04e5> <7a0b> <04e6> <7a0e> <04e7> <7a33> <04e8> <7a46> <04eb> <7a7a> <04ec> <7a81> <04ed> <7a84> <04ee> <7a92> <04ef> <7a96> <04f0> <7acb> <04f1> <7ad9> <04f5> <7ae5> <04f6> <7aed> <04f7> <7aef> <04f8> <7af9> <04f9> <7b14> <04fa> <7b26> <04fb> <7b2c> <04fc> <7b49> <04fd> <7b4f> <04fe> <7b51> <04ff> <7b54> <0500> <7b56> <0501> <7b79> <0502> <7b7e> <0503> <7b80> <0504> <7b97> <0505> <7ba1> <0506> <7c73> <0507> <7c7b> <0508> <7c92> <0509> <7c95> <050a> <7cae> <050b> <7cbe> <050c> <7cca> <050d> <7cfb> <050e> <7d20> <050f> <7d22> <0510> <7d27> <0511> <7d2f> endbfchar
|
||||
100 beginbfchar
|
||||
<0512> <7e41> <0513> <7e82> <0514> <7ea0> <0517> <7eaa> <0518> <7eaf> <051b> <7eb5> <051c> <7ebd> <051d> <7ebf> <0523> <7ecd> <0524> <7ecf> <0525> <7ed3> <052b> <7edf> <052c> <7ee7> <052d> <7ee9> <052e> <7eed> <052f> <7ef4> <0530> <7efc> <0533> <7f16> <0534> <7f29> <0535> <7f3a> <0536> <7f51> <0537> <7f57> <0538> <7f6e> <0539> <7f72> <053a> <7f8e> <053b> <7fa4> <053c> <7fc5> <053d> <7ff0> <053e> <7ffb> <053f> <8001> <0540> <8003> <0541> <8005> <0544> <8017> <0545> <8033> <0548> <8054> <0549> <8058> <054a> <805a> <054b> <806a> <054c> <8083> <054d> <8089> <054e> <8098> <054f> <809b> <0550> <80a5> <0551> <80a9> <0552> <80af> <0553> <80b2> <0554> <80c1> <0555> <80cc> <0556> <80ce> <0557> <80da> <0558> <80dc> <0559> <80fd> <055a> <8106> <055b> <811a> <055c> <8131> <055d> <814a> <055e> <8155> <055f> <8179> <0560> <818a> <0561> <818f> <0562> <81ea> <0563> <81ed> <0566> <8206> <0567> <820c> <0568> <8212> <0569> <821f> <056a> <822a> <056b> <822c> <056c> <8236> <056d> <8239> <056e> <8247> <0571> <8272> <0572> <827a> <0573> <8282> <0574> <82a6> <0575> <82ac> <0576> <82af> <0577> <82c7> <0578> <82cf> <057b> <82ef> <057c> <82f1> <057d> <8303> <057e> <8328> <057f> <8349> <0580> <835a> <0581> <8363> <0582> <836f> <0583> <8377> <0584> <8392> <0585> <83ab> <0588> <83b7> <0589> <83c7> <058a> <83cc> <058b> <83f2> <058c> <8404> <058d> <841d> <058e> <8425> <058f> <8428> <0590> <843d> <0591> <8461> endbfchar
|
||||
100 beginbfchar
|
||||
<0592> <848b> <0593> <8499> <0594> <84c4> <0595> <84dd> <0596> <8584> <0597> <85c9> <0598> <85cf> <0599> <8651> <059a> <866b> <059b> <867d> <059c> <8680> <059d> <8682> <059e> <86cb> <059f> <8718> <05a0> <8757> <05a1> <878d> <05a2> <87f9> <05a3> <8840> <05a4> <884c> <05a5> <8854> <05a6> <8857> <05a7> <8861> <05a8> <8863> <05a9> <8865> <05aa> <8868> <05ab> <8877> <05ac> <8896> <05ad> <88ab> <05ae> <88c1> <05af> <88f4> <05b0> <8902> <05b1> <8925> <05b2> <8944> <05b3> <897f> <05b4> <8981> <05b5> <8986> <05b8> <89c4> <05b9> <89c6> <05bc> <89e3> <05bd> <89e6> <05be> <8a00> <05bf> <8b66> <05c0> <8b6c> <05c3> <8ba4> <05ca> <8bb2> <05cb> <8bb8> <05cc> <8bba> <05cf> <8bc1> <05d0> <8bc4> <05d1> <8bc6> <05d2> <8bca> <05d3> <8bcd> <05d4> <8bd1> <05d5> <8bd5> <05d6> <8bd7> <05d7> <8bda> <05d8> <8bdd> <05d9> <8be2> <05de> <8bef> <05e1> <8bf4> <05e4> <8bfa> <05e5> <8bfe> <05e6> <8c03> <05e7> <8c08> <05e8> <8c0b> <05e9> <8c10> <05ea> <8c13> <05eb> <8c22> <05ec> <8c28> <05ed> <8c34> <05ee> <8c37> <05ef> <8c41> <05f0> <8c61> <05f1> <8c6a> <05f2> <8d1d> <05f3> <8d1f> <05f7> <8d25> <05fe> <8d35> <0602> <8d44> <0603> <8d4b> <0604> <8d4f> <0605> <8d54> <0606> <8d56> <0607> <8d5b> <0608> <8d5e> <0609> <8d66> <060a> <8d70> <060b> <8d75> <060c> <8d77> <060d> <8d85> <0610> <8da3> <0611> <8db3> <0612> <8dc3> <0613> <8dd1> <0614> <8ddd> <0615> <8def> <0616> <8e0a> <0617> <8eab> <0618> <8f67> <0619> <8f6c> endbfchar
|
||||
100 beginbfchar
|
||||
<061a> <8f7b> <061b> <8f7d> <061c> <8f83> <061d> <8f85> <0620> <8f93> <0621> <8f96> <0622> <8f9b> <0625> <8fb1> <0626> <8fb9> <0627> <8fbe> <0628> <8fc1> <062b> <8fc7> <062c> <8fce> <062f> <8fd4> <0639> <8ff0> <063a> <8ff7> <063b> <8ff9> <063c> <8ffd> <0643> <9010> <0644> <9012> <0645> <9014> <064a> <903b> <064b> <9047> <064c> <904d> <064d> <9053> <064e> <9057> <064f> <9063> <0650> <9065> <0653> <9075> <0656> <90a3> <0657> <90a6> <065a> <90b1> <065b> <90bb> <065c> <90d1> <065f> <90fd> <0663> <9192> <0664> <919a> <0665> <91c7> <0666> <91ca> <066b> <91d1> <066c> <9274> <066d> <9488> <066e> <94ae> <066f> <94b1> <0670> <94b3> <0673> <94f2> <0674> <94f6> <0675> <94fe> <0676> <9500> <0677> <9504> <0678> <9519> <0679> <9521> <067a> <9528> <067b> <952e> <067c> <953a> <067d> <957f> <067e> <95e8> <0681> <95f4> <0682> <95f7> <0683> <95fb> <0684> <95fd> <0685> <9602> <0686> <9610> <0687> <9614> <0688> <9632> <0689> <9634> <068a> <9636> <068b> <963b> <068c> <963f> <0690> <9650> <0691> <9662> <0692> <9664> <0693> <9669> <0696> <9686> <0697> <968f> <0698> <9694> <0699> <969c> <069a> <96be> <069b> <96c0> <069e> <96e8> <06a1> <9700> <06a2> <9707> <06a3> <970d> <06a4> <9713> <06a5> <9732> <06a6> <9752> <06a7> <9759> <06a8> <975e> <06a9> <9760> <06aa> <9762> <06ab> <978b> <06ac> <97e6> <06ad> <97e9> <06ae> <97f3> <06af> <9877> <06b3> <987e> <06b4> <9881> <06b5> <9884> <06b8> <9898> <06b9> <989d> endbfchar
|
||||
24 beginbfchar
|
||||
<06ba> <98a0> <06bb> <98ce> <06be> <9996> <06bf> <9a6c> <06c0> <9a71> <06c1> <9a7b> <06c2> <9a8c> <06c3> <9aa4> <06c4> <9aa8> <06c5> <9ad8> <06c6> <9c7c> <06c7> <9c81> <06c8> <9c9c> <06c9> <9ca4> <06ca> <9cb8> <06cb> <9cc3> <06cc> <9cd6> <06cd> <9e64> <06ce> <9ea6> <06cf> <9ece> <06d0> <9f13> <06d1> <9f3b> <06d2> <9f84> <06d3> <9f9a> endbfchar
|
||||
endcmap
|
||||
CMapName
|
||||
currentdict
|
||||
/CMap defineresource
|
||||
pop
|
||||
end
|
||||
end
|
||||
""";
|
||||
|
||||
var input = StringBytesTestConverter.Convert(cmap, false);
|
||||
|
||||
var result = cMapParser.Parse(input.Bytes);
|
||||
|
||||
Assert.Equal("Adobe", result.Info.Registry);
|
||||
Assert.Equal("GB1", result.Info.Ordering);
|
||||
Assert.Equal(2, result.Info.Supplement);
|
||||
|
||||
Assert.Equal("Adobe-GB1-2", result.Name);
|
||||
Assert.Equal(2, result.Type);
|
||||
}
|
||||
|
||||
private static byte[] ReadResourceBytes(string name)
|
||||
{
|
||||
using (var resource = typeof(CMapParser).Assembly.GetManifestResourceStream(name))
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace UglyToad.PdfPig.Tests.Geometry
|
||||
new ParsingOptions { ClipPaths = true }))
|
||||
{
|
||||
var page = document.GetPage(45);
|
||||
Assert.Equal(28, page.ExperimentalAccess.Paths.Count);
|
||||
Assert.Equal(28, page.Paths.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
var page = document.GetPage(1);
|
||||
var words = page.GetWords().ToList();
|
||||
|
||||
foreach (var path in page.ExperimentalAccess.Paths)
|
||||
foreach (var path in page.Paths)
|
||||
{
|
||||
Assert.NotEqual(FillingRule.NonZeroWinding, path.FillingRule); // allow none and even-odd
|
||||
|
||||
|
||||
@@ -68,12 +68,20 @@
|
||||
[Fact]
|
||||
public void ReflectionGraphicsStateOperationFactoryKnowsAllOperations()
|
||||
{
|
||||
var operationsField = typeof(ReflectionGraphicsStateOperationFactory).GetField("operations", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
var operationDictionary = operationsField.GetValue(null) as IReadOnlyDictionary<string, Type>;
|
||||
Assert.NotNull(operationDictionary);
|
||||
var operationsField = typeof(ReflectionGraphicsStateOperationFactory)
|
||||
.GetField("Operations", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
|
||||
Assert.NotNull(operationsField);
|
||||
|
||||
var operationDictionaryRaw = operationsField.GetValue(null) as IReadOnlyDictionary<string, Type>;
|
||||
|
||||
Assert.NotNull(operationDictionaryRaw);
|
||||
|
||||
var operationDictionary = new Dictionary<string, Type>(operationDictionaryRaw!.ToDictionary(x => x.Key, x => x.Value));
|
||||
|
||||
var allOperations = GetOperationTypes();
|
||||
Assert.Equal(allOperations.Count(), operationDictionary.Count);
|
||||
|
||||
Assert.Equal(allOperations.Count, operationDictionary.Count);
|
||||
|
||||
var mapped = allOperations.Select(o =>
|
||||
{
|
||||
@@ -84,14 +92,14 @@
|
||||
Assert.Equivalent(operationDictionary, mapped, strict: true);
|
||||
}
|
||||
|
||||
private static IEnumerable<Type> GetOperationTypes()
|
||||
private static IReadOnlyList<Type> GetOperationTypes()
|
||||
{
|
||||
var assembly = Assembly.GetAssembly(typeof(IGraphicsStateOperation));
|
||||
|
||||
var operationTypes = assembly.GetTypes().Where(x => typeof(IGraphicsStateOperation).IsAssignableFrom(x)
|
||||
&& !x.IsInterface);
|
||||
|
||||
return operationTypes;
|
||||
return operationTypes.ToList();
|
||||
}
|
||||
|
||||
private static object[] GetConstructorParameters(ParameterInfo[] parameters)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
Assert.Equal("Q", Pop.Value.Operator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Fact(Skip = "The stack size check has been moved out of the Pop Operation, and is now in BaseStreamProcessor.PopState().")]
|
||||
public void CannotPopWithSingleFrame()
|
||||
{
|
||||
Action action = () => Pop.Value.Run(context);
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
{
|
||||
}
|
||||
|
||||
public void EndInlineImage(ReadOnlyMemory<byte> bytes)
|
||||
public void EndInlineImage(Memory<byte> bytes)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
55
src/UglyToad.PdfPig.Tests/Images/Jpeg2000HelperTests.cs
Normal file
55
src/UglyToad.PdfPig.Tests/Images/Jpeg2000HelperTests.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
namespace UglyToad.PdfPig.Tests.Images
|
||||
{
|
||||
using PdfPig.Images;
|
||||
using System;
|
||||
using UglyToad.PdfPig.Tests.Integration;
|
||||
|
||||
public class Jpeg2000HelperTests
|
||||
{
|
||||
private static readonly Lazy<string> DocumentFolder = new Lazy<string>(() => Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "Images", "Files", "Jpx")));
|
||||
|
||||
public static IEnumerable<object[]> GetAllJp2Files
|
||||
{
|
||||
get
|
||||
{
|
||||
return Directory.GetFiles(DocumentFolder.Value, "*.jp2").Select(x => new object[] { Path.GetFileName(x) });
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetAllJp2Files))]
|
||||
public void GetJp2BitsPerComponent_ReturnsCorrectBitsPerComponent_WhenValidInput(string path)
|
||||
{
|
||||
byte[] image = File.ReadAllBytes(Path.Combine(DocumentFolder.Value, path));
|
||||
Assert.Equal(8, Jpeg2000Helper.GetBitsPerComponent(image));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetJp2BitsPerComponent_ThrowsException_WhenInputIsTooShort()
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => Jpeg2000Helper.GetBitsPerComponent(new byte[11]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetJp2BitsPerComponent_ThrowsException_WhenSignatureBoxIsInvalid()
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => Jpeg2000Helper.GetBitsPerComponent(new byte[12]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetJp2BitsPerComponentJ2K()
|
||||
{
|
||||
string path = IntegrationHelpers.GetSpecificTestDocumentPath("GHOSTSCRIPT-688999-2.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path))
|
||||
{
|
||||
var page1 = document.GetPage(1);
|
||||
var jpxImage = page1.GetImages().Single();
|
||||
|
||||
var bpc = Jpeg2000Helper.GetBitsPerComponent(jpxImage.RawBytes);
|
||||
|
||||
Assert.Equal(8, bpc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,6 +103,30 @@
|
||||
Assert.True(ImageHelpers.ImagesAreEqual(LoadImage("3x3.png"), bytes));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanExtractPngFromPdfWithIndexedImageData8bpc()
|
||||
{
|
||||
var documetFolder = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "Images", "Files"));
|
||||
|
||||
var pdfPath = Path.Combine(documetFolder, "Pdf", "indexed-png-with-mask.pdf");
|
||||
|
||||
using (PdfDocument document = PdfDocument.Open(pdfPath))
|
||||
{
|
||||
var page = document.GetPage(1);
|
||||
|
||||
var img = page.GetImages().First();
|
||||
|
||||
var result = img.TryGetPng(out var bytes);
|
||||
|
||||
if (result)
|
||||
{
|
||||
var outputPath = Path.Combine(documetFolder, "Pdf", "indexed-png-with-mask.png");
|
||||
|
||||
File.WriteAllBytes(outputPath, bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanGeneratePngFromIndexedImageData1bpc()
|
||||
{
|
||||
@@ -148,11 +172,12 @@
|
||||
var decodedBytes = ImageHelpers.LoadFileBytes("ccittfax-decoded.bin");
|
||||
var image = new TestPdfImage
|
||||
{
|
||||
ColorSpaceDetails = IndexedColorSpaceDetails.Stencil(DeviceGrayColorSpaceDetails.Instance, new[] { 1.0, 0 }),
|
||||
ColorSpaceDetails = IndexedColorSpaceDetails.Stencil(DeviceGrayColorSpaceDetails.Instance),
|
||||
DecodedBytes = decodedBytes,
|
||||
WidthInSamples = 1800,
|
||||
HeightInSamples = 3113,
|
||||
BitsPerComponent = 1
|
||||
BitsPerComponent = 1,
|
||||
Decode = [1.0, 0]
|
||||
};
|
||||
|
||||
Assert.True(PngFromPdfImageFactory.TryGenerate(image, out var bytes));
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace UglyToad.PdfPig.Tests.Integration
|
||||
{
|
||||
using System.Linq;
|
||||
|
||||
public class AdditionalGlyphListTests
|
||||
{
|
||||
[Fact]
|
||||
public void Type1FontSimple1()
|
||||
{
|
||||
using (var document = PdfDocument.Open(IntegrationHelpers.GetDocumentPath("2108.11480")))
|
||||
{
|
||||
var page = document.GetPage(2);
|
||||
Assert.Contains("\u22c3", page.Letters.Select(l => l.Value));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Type1FontSimple2()
|
||||
{
|
||||
using (var document = PdfDocument.Open(IntegrationHelpers.GetDocumentPath("ICML03-081")))
|
||||
{
|
||||
var page = document.GetPage(2);
|
||||
Assert.Contains("\u2211", page.Letters.Select(l => l.Value));
|
||||
Assert.Contains("\u220f", page.Letters.Select(l => l.Value));
|
||||
Assert.Contains("[", page.Letters.Select(l => l.Value));
|
||||
Assert.Contains("]", page.Letters.Select(l => l.Value));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Type1FontSimple3()
|
||||
{
|
||||
using (var document = PdfDocument.Open(IntegrationHelpers.GetDocumentPath("Math119FakingData")))
|
||||
{
|
||||
var page = document.GetPage(4);
|
||||
Assert.Contains("(", page.Letters.Select(l => l.Value));
|
||||
Assert.Contains(")", page.Letters.Select(l => l.Value));
|
||||
Assert.Contains("\u2211", page.Letters.Select(l => l.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,9 @@ public class AnnotationReplyToTests
|
||||
|
||||
var page = document.GetPage(1);
|
||||
|
||||
var annotations = page.ExperimentalAccess.GetAnnotations().ToList();
|
||||
var annotations = page.GetAnnotations().ToArray();
|
||||
|
||||
Assert.Equal(4, annotations.Count);
|
||||
Assert.Equal(4, annotations.Length);
|
||||
|
||||
Assert.Equal(AnnotationType.Text, annotations[0].Type);
|
||||
Assert.Equal(AnnotationType.Popup, annotations[1].Type);
|
||||
@@ -33,7 +33,7 @@ public class AnnotationReplyToTests
|
||||
|
||||
var page = document.GetPage(1);
|
||||
|
||||
var annotations = page.ExperimentalAccess.GetAnnotations().ToList();
|
||||
var annotations = page.GetAnnotations().ToArray();
|
||||
|
||||
Assert.Equal(annotations[0], annotations[2].InReplyTo);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
using (var doc = PdfDocument.Open(pdf))
|
||||
{
|
||||
var annots = doc.GetPage(1).ExperimentalAccess.GetAnnotations().ToArray();
|
||||
var annots = doc.GetPage(1).GetAnnotations().ToArray();
|
||||
Assert.Equal(5, annots.Length);
|
||||
Assert.All(annots, a => Assert.NotNull(a.Action));
|
||||
Assert.All(annots, a => Assert.IsType<GoToAction>(a.Action));
|
||||
@@ -25,7 +25,7 @@
|
||||
var pdf = IntegrationHelpers.GetSpecificTestDocumentPath("appearances");
|
||||
using (var doc = PdfDocument.Open(pdf))
|
||||
{
|
||||
var annotations = doc.GetPage(1).ExperimentalAccess.GetAnnotations().ToArray();
|
||||
var annotations = doc.GetPage(1).GetAnnotations().ToArray();
|
||||
var annotation = Assert.Single(annotations);
|
||||
|
||||
Assert.True(annotation.HasDownAppearance);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
{
|
||||
var page = document.GetPage(1);
|
||||
|
||||
var annotations = page.ExperimentalAccess.GetAnnotations().ToList();
|
||||
var annotations = page.GetAnnotations().ToArray();
|
||||
|
||||
Assert.NotEmpty(annotations);
|
||||
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
{
|
||||
var page1 = document.GetPage(1);
|
||||
|
||||
var background = page1.ExperimentalAccess.Paths[0];
|
||||
var background = page1.Paths[0];
|
||||
Assert.True(background.IsFilled);
|
||||
|
||||
var (r, g, b) = background.FillColor.ToRGBValues();
|
||||
@@ -327,7 +327,7 @@
|
||||
using (var document = PdfDocument.Open(path))
|
||||
{
|
||||
Page page1 = document.GetPage(1);
|
||||
var paths1 = page1.ExperimentalAccess.Paths.Where(p => p.IsFilled).ToArray();
|
||||
var paths1 = page1.Paths.Where(p => p.IsFilled).ToArray();
|
||||
var reflexRed = paths1[0].FillColor.ToRGBValues(); // 'Reflex Red' Separation color space
|
||||
Assert.Equal(0.930496, reflexRed.r, 6);
|
||||
Assert.Equal(0.111542, reflexRed.g, 6);
|
||||
@@ -341,7 +341,7 @@
|
||||
Assert.Equal("w", firstLetter.Value);
|
||||
Assert.Equal((0, 0, 1), firstLetter.Color.ToRGBValues()); // Blue
|
||||
|
||||
var paths2 = page2.ExperimentalAccess.Paths;
|
||||
var paths2 = page2.Paths;
|
||||
var filledPath = paths2.Where(p => p.IsFilled).ToArray();
|
||||
var filledRects = filledPath.Where(p => p.Count == 1 && p[0].IsDrawnAsRectangle).ToArray();
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
src/UglyToad.PdfPig.Tests/Integration/Documents/PDFBOX-624-2.pdf
Normal file
BIN
src/UglyToad.PdfPig.Tests/Integration/Documents/PDFBOX-624-2.pdf
Normal file
Binary file not shown.
BIN
src/UglyToad.PdfPig.Tests/Integration/Documents/TIKA-2054-0.pdf
Normal file
BIN
src/UglyToad.PdfPig.Tests/Integration/Documents/TIKA-2054-0.pdf
Normal file
Binary file not shown.
BIN
src/UglyToad.PdfPig.Tests/Integration/Documents/TIKA-469-0.pdf
Normal file
BIN
src/UglyToad.PdfPig.Tests/Integration/Documents/TIKA-469-0.pdf
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/UglyToad.PdfPig.Tests/Integration/Documents/capas.pdf
Normal file
BIN
src/UglyToad.PdfPig.Tests/Integration/Documents/capas.pdf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -63,6 +63,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanReadDocumentWithNoKeyLengthAndRevision4()
|
||||
{
|
||||
using (var document = PdfDocument.Open(IntegrationHelpers.GetSpecificTestDocumentPath("r4_aesv2_no_length")))
|
||||
{
|
||||
Assert.Empty(document.Information.Producer);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetPath() => IntegrationHelpers.GetSpecificTestDocumentPath(FileName);
|
||||
}
|
||||
}
|
||||
126
src/UglyToad.PdfPig.Tests/Integration/FilterTests.cs
Normal file
126
src/UglyToad.PdfPig.Tests/Integration/FilterTests.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
namespace UglyToad.PdfPig.Tests.Integration
|
||||
{
|
||||
using PdfPig.Filters;
|
||||
using PdfPig.Tokens;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public class FilterTests
|
||||
{
|
||||
private static readonly Lazy<string> DocumentFolder = new Lazy<string>(() => Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "Integration", "Documents")));
|
||||
private static readonly HashSet<string> _documentsToIgnore =
|
||||
[
|
||||
"issue_671.pdf",
|
||||
"GHOSTSCRIPT-698363-0.pdf",
|
||||
"ErcotFacts.pdf"
|
||||
];
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetAllDocuments))]
|
||||
public void NoImageDecoding(string documentName)
|
||||
{
|
||||
// Add the full path back on, we removed it so we could see it in the test explorer.
|
||||
documentName = Path.Combine(DocumentFolder.Value, documentName);
|
||||
|
||||
var parsingOptions = new ParsingOptions
|
||||
{
|
||||
UseLenientParsing = true,
|
||||
FilterProvider = MyFilterProvider.Instance
|
||||
};
|
||||
|
||||
using (var document = PdfDocument.Open(documentName, parsingOptions))
|
||||
{
|
||||
for (var i = 0; i < document.NumberOfPages; i++)
|
||||
{
|
||||
var page = document.GetPage(i + 1);
|
||||
|
||||
foreach (var pdfImage in page.GetImages())
|
||||
{
|
||||
if (pdfImage.ImageDictionary.TryGet(NameToken.Filter, out NameToken filter))
|
||||
{
|
||||
if (filter.Data.Equals(NameToken.FlateDecode.Data) ||
|
||||
filter.Data.Equals(NameToken.FlateDecodeAbbreviation.Data) ||
|
||||
filter.Data.Equals(NameToken.LzwDecode.Data) ||
|
||||
filter.Data.Equals(NameToken.LzwDecodeAbbreviation.Data))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Assert.False(pdfImage.TryGetPng(out _));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NoFilter : IFilter
|
||||
{
|
||||
public bool IsSupported => false;
|
||||
|
||||
public Memory<byte> Decode(Memory<byte> input, DictionaryToken streamDictionary, IFilterProvider filterProvider, int filterIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class MyFilterProvider : BaseFilterProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The single instance of this provider.
|
||||
/// </summary>
|
||||
public static readonly IFilterProvider Instance = new MyFilterProvider();
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected MyFilterProvider() : base(GetDictionary())
|
||||
{
|
||||
}
|
||||
|
||||
private static Dictionary<string, IFilter> GetDictionary()
|
||||
{
|
||||
var ascii85 = new Ascii85Filter();
|
||||
var asciiHex = new AsciiHexDecodeFilter();
|
||||
var flate = new FlateFilter();
|
||||
var runLength = new RunLengthFilter();
|
||||
var lzw = new LzwFilter();
|
||||
|
||||
var noFilter = new NoFilter();
|
||||
|
||||
return new Dictionary<string, IFilter>
|
||||
{
|
||||
{ NameToken.Ascii85Decode.Data, ascii85 },
|
||||
{ NameToken.Ascii85DecodeAbbreviation.Data, ascii85 },
|
||||
{ NameToken.AsciiHexDecode.Data, asciiHex },
|
||||
{ NameToken.AsciiHexDecodeAbbreviation.Data, asciiHex },
|
||||
{ NameToken.CcittfaxDecode.Data, noFilter },
|
||||
{ NameToken.CcittfaxDecodeAbbreviation.Data, noFilter },
|
||||
{ NameToken.DctDecode.Data, noFilter },
|
||||
{ NameToken.DctDecodeAbbreviation.Data, noFilter },
|
||||
{ NameToken.FlateDecode.Data, flate },
|
||||
{ NameToken.FlateDecodeAbbreviation.Data, flate },
|
||||
{ NameToken.Jbig2Decode.Data, noFilter },
|
||||
{ NameToken.JpxDecode.Data, noFilter },
|
||||
{ NameToken.RunLengthDecode.Data, runLength },
|
||||
{ NameToken.RunLengthDecodeAbbreviation.Data, runLength },
|
||||
{NameToken.LzwDecode, lzw },
|
||||
{NameToken.LzwDecodeAbbreviation, lzw }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetAllDocuments
|
||||
{
|
||||
get
|
||||
{
|
||||
var files = Directory.GetFiles(DocumentFolder.Value, "*.pdf");
|
||||
|
||||
// Return the shortname so we can see it in the test explorer.
|
||||
return files.Where(x => !_documentsToIgnore.Any(i => x.EndsWith(i))).Select(x => new object[] { Path.GetFileName(x) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,415 @@
|
||||
namespace UglyToad.PdfPig.Tests.Integration
|
||||
{
|
||||
using Content;
|
||||
using DocumentLayoutAnalysis.PageSegmenter;
|
||||
using DocumentLayoutAnalysis.WordExtractor;
|
||||
using PdfPig.Core;
|
||||
|
||||
public class GithubIssuesTests
|
||||
{
|
||||
[Fact]
|
||||
public void Issue1096()
|
||||
{
|
||||
// Ensure no StackOverflowException
|
||||
// (already fixed by https://github.com/UglyToad/PdfPig/pull/1097)
|
||||
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("issue_1096.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }))
|
||||
{
|
||||
for (int p = 1; p <= document.NumberOfPages; p++)
|
||||
{
|
||||
var page = document.GetPage(p);
|
||||
foreach (var image in page.GetImages())
|
||||
{
|
||||
Assert.NotNull(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue1067()
|
||||
{
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("GHOSTSCRIPT-691770-0.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }))
|
||||
{
|
||||
var ex = Assert.Throws<PdfDocumentFormatException>(() => document.GetPage(1));
|
||||
Assert.StartsWith("Decoded stream size exceeds the estimated maximum size.", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue1054()
|
||||
{
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("MOZILLA-11518-0.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }))
|
||||
{
|
||||
for (int p = 1; p <= document.NumberOfPages; p++)
|
||||
{
|
||||
var page = document.GetPage(p);
|
||||
foreach (var image in page.GetImages())
|
||||
{
|
||||
Assert.NotNull(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue1050()
|
||||
{
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("SpookyPass.pdf");
|
||||
var ex = Assert.Throws<PdfDocumentFormatException>(() => PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }));
|
||||
Assert.Equal("Avoiding infinite recursion in ObjectLocationProvider.TryGetOffset() as 'offset' and 'reference.ObjectNumber' have the same value and opposite signs.", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue1047()
|
||||
{
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("Hang.pdf");
|
||||
|
||||
var ex = Assert.Throws<PdfDocumentFormatException>(() => PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }));
|
||||
Assert.Equal("The cross reference was not found.", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue1048()
|
||||
{
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("InvalidCast.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }))
|
||||
{
|
||||
var page = document.GetPage(1);
|
||||
Assert.NotNull(page.Letters);
|
||||
|
||||
var words = NearestNeighbourWordExtractor.Instance.GetWords(page.Letters);
|
||||
var blocks = DocstrumBoundingBoxes.Instance.GetBlocks(words);
|
||||
|
||||
Assert.Single(blocks);
|
||||
Assert.Equal("hey, i'm a bug.", blocks[0].Text);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue554()
|
||||
{
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("2022.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }))
|
||||
{
|
||||
for (int p = 1; p <= document.NumberOfPages; p++)
|
||||
{
|
||||
var page = document.GetPage(p);
|
||||
Assert.NotNull(page.Letters);
|
||||
|
||||
if (p < document.NumberOfPages)
|
||||
{
|
||||
Assert.NotEmpty(page.Letters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue822()
|
||||
{
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("FileData_7.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }))
|
||||
{
|
||||
for (int p = 1; p <= document.NumberOfPages; p++)
|
||||
{
|
||||
var page = document.GetPage(p);
|
||||
Assert.NotNull(page.Letters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue1040()
|
||||
{
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("pdfpig-issue-1040.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true}))
|
||||
{
|
||||
var page1 = document.GetPage(1);
|
||||
Assert.NotEmpty(page1.Letters);
|
||||
|
||||
var page2 = document.GetPage(2);
|
||||
Assert.NotEmpty(page2.Letters);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue1013()
|
||||
{
|
||||
// NB: We actually do not fix issue 953 here, but another bug found with the same document.
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("document_with_failed_fonts.pdf");
|
||||
|
||||
// Lenient parsing ON + Skip missing fonts
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true, SkipMissingFonts = true }))
|
||||
{
|
||||
var page2 = document.GetPage(2);
|
||||
Assert.NotEmpty(page2.Letters);
|
||||
|
||||
var words2 = NearestNeighbourWordExtractor.Instance.GetWords(page2.Letters).ToArray();
|
||||
Assert.Equal("Doplňující", words2[0].Text);
|
||||
|
||||
var page3 = document.GetPage(3);
|
||||
Assert.NotEmpty(page3.Letters);
|
||||
|
||||
var words3 = NearestNeighbourWordExtractor.Instance.GetWords(page3.Letters).ToArray();
|
||||
Assert.Equal("Vinohradská", words3[8].Text);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue1016()
|
||||
{
|
||||
// Doc has letters with Shading pattern color
|
||||
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("colorcomparecrash.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true, SkipMissingFonts = true }))
|
||||
{
|
||||
var page = document.GetPage(1);
|
||||
|
||||
var letters = page.Letters;
|
||||
|
||||
var firstLetter = letters[0];
|
||||
Assert.NotNull(firstLetter.Color);
|
||||
|
||||
var secondLetter = letters[1];
|
||||
Assert.NotNull(secondLetter.Color);
|
||||
|
||||
Assert.True(firstLetter.Color.Equals(secondLetter.Color));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue953()
|
||||
{
|
||||
// NB: We actually do not fix issue 953 here, but another bug found with the same document.
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("FailedToParseContentForPage32.pdf");
|
||||
|
||||
// Lenient parsing ON + Skip missing fonts
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true, SkipMissingFonts = true}))
|
||||
{
|
||||
var page = document.GetPage(33);
|
||||
Assert.Equal(33, page.Number);
|
||||
Assert.Equal(792, page.Height);
|
||||
Assert.Equal(612, page.Width);
|
||||
}
|
||||
|
||||
// Lenient parsing ON + Do not Skip missing fonts
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true, SkipMissingFonts = false }))
|
||||
{
|
||||
var pageException = Assert.Throws<InvalidOperationException>(() => document.GetPage(33));
|
||||
Assert.Equal("Could not find the font with name /TT4 in the resource store. It has not been loaded yet.", pageException.Message);
|
||||
}
|
||||
|
||||
var docException = Assert.Throws<PdfDocumentFormatException>(() =>
|
||||
PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = false, SkipMissingFonts = false }));
|
||||
Assert.Equal("Could not find dictionary associated with reference in pages kids array: 102 0.", docException.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue953_IntOverflow()
|
||||
{
|
||||
// There is an integer overflow in Docstrum. We might want to fix that later on.
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("FailedToParseContentForPage32.pdf");
|
||||
|
||||
// Lenient parsing ON + Skip missing fonts
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true, SkipMissingFonts = true }))
|
||||
{
|
||||
var page = document.GetPage(13);
|
||||
Assert.Throws<OverflowException>(() => DocstrumBoundingBoxes.Instance.GetBlocks(page.GetWords()));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue987()
|
||||
{
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("zeroheightdemo.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path))
|
||||
{
|
||||
var page = document.GetPage(1);
|
||||
var words = page.GetWords().ToArray();
|
||||
foreach (var word in words)
|
||||
{
|
||||
Assert.True(word.BoundingBox.Width > 0);
|
||||
Assert.True(word.BoundingBox.Height > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue982()
|
||||
{
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("PDFBOX-659-0.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path))
|
||||
{
|
||||
for (int p = 1; p <= document.NumberOfPages; ++p)
|
||||
{
|
||||
var page = document.GetPage(p);
|
||||
foreach (var pdfImage in page.GetImages())
|
||||
{
|
||||
Assert.True(pdfImage.TryGetPng(out _));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue973()
|
||||
{
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("JD5008.pdf");
|
||||
|
||||
// Lenient parsing ON
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }))
|
||||
{
|
||||
var page = document.GetPage(2);
|
||||
Assert.NotNull(page);
|
||||
Assert.Equal(2, page.Number);
|
||||
Assert.NotEmpty(page.Letters);
|
||||
}
|
||||
|
||||
// Lenient parsing OFF
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = false }))
|
||||
{
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => document.GetPage(2));
|
||||
Assert.Equal("Cannot execute a pop of the graphics state stack, it would leave the stack empty.",
|
||||
exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue959()
|
||||
{
|
||||
var path = IntegrationHelpers.GetSpecificTestDocumentPath("algo.pdf");
|
||||
|
||||
// Lenient parsing ON
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }))
|
||||
{
|
||||
for (int i = 1; i <= document.NumberOfPages; ++i)
|
||||
{
|
||||
var page = document.GetPage(i);
|
||||
Assert.NotNull(page);
|
||||
Assert.Equal(i, page.Number);
|
||||
}
|
||||
}
|
||||
|
||||
// Lenient parsing OFF
|
||||
var exception = Assert.Throws<PdfDocumentFormatException>(() =>
|
||||
PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = false }));
|
||||
|
||||
Assert.Equal("The cross references formed an infinite loop.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue945()
|
||||
{
|
||||
// Odd ligatures names
|
||||
var path = IntegrationHelpers.GetDocumentPath("MOZILLA-3136-0.pdf");
|
||||
using (var document = PdfDocument.Open(path))
|
||||
{
|
||||
var page = document.GetPage(2);
|
||||
Assert.Contains("ff", page.Letters.Select(l => l.Value));
|
||||
}
|
||||
|
||||
path = IntegrationHelpers.GetDocumentPath("68-1990-01_A.pdf");
|
||||
using (var document = PdfDocument.Open(path))
|
||||
{
|
||||
var page = document.GetPage(7);
|
||||
Assert.Contains("fi", page.Letters.Select(l => l.Value));
|
||||
}
|
||||
|
||||
path = IntegrationHelpers.GetDocumentPath("TIKA-2054-0.pdf");
|
||||
using (var document = PdfDocument.Open(path))
|
||||
{
|
||||
var page = document.GetPage(3);
|
||||
Assert.Contains("fi", page.Letters.Select(l => l.Value));
|
||||
|
||||
page = document.GetPage(4);
|
||||
Assert.Contains("ff", page.Letters.Select(l => l.Value));
|
||||
|
||||
page = document.GetPage(6);
|
||||
Assert.Contains("fl", page.Letters.Select(l => l.Value));
|
||||
|
||||
page = document.GetPage(16);
|
||||
Assert.Contains("ffi", page.Letters.Select(l => l.Value));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue943()
|
||||
{
|
||||
var path = IntegrationHelpers.GetDocumentPath("MOZILLA-10225-0.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path))
|
||||
{
|
||||
var page = document.GetPage(1);
|
||||
Assert.NotNull(page);
|
||||
|
||||
var letters = page.Letters;
|
||||
Assert.NotNull(letters);
|
||||
|
||||
var words = NearestNeighbourWordExtractor.Instance.GetWords(page.Letters);
|
||||
var blocks = DocstrumBoundingBoxes.Instance.GetBlocks(words);
|
||||
|
||||
Assert.Equal("Rocket and Spacecraft Propulsion", blocks[0].TextLines[0].Text);
|
||||
Assert.Equal("Principles, Practice and New Developments (Second Edition)", blocks[0].TextLines[1].Text);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue736()
|
||||
{
|
||||
var doc = IntegrationHelpers.GetDocumentPath("Approved_Document_B__fire_safety__volume_2_-_Buildings_other_than_dwellings__2019_edition_incorporating_2020_and_2022_amendments.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(doc, new ParsingOptions() { UseLenientParsing = true, SkipMissingFonts = true }))
|
||||
{
|
||||
Assert.True(document.TryGetBookmarks(out var bookmarks));
|
||||
Assert.Single(bookmarks.Roots);
|
||||
Assert.Equal(36, bookmarks.Roots[0].Children.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue693()
|
||||
{
|
||||
var doc = IntegrationHelpers.GetDocumentPath("reference-2-numeric-error.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(doc, new ParsingOptions() { UseLenientParsing = true, SkipMissingFonts = true }))
|
||||
{
|
||||
var page1 = document.GetPage(1);
|
||||
Assert.Equal(1269, page1.Letters.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue692()
|
||||
{
|
||||
var doc = IntegrationHelpers.GetDocumentPath("cmap-parsing-exception.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(doc, new ParsingOptions() { UseLenientParsing = true, SkipMissingFonts = true }))
|
||||
{
|
||||
var page1 = document.GetPage(1);
|
||||
Assert.Equal(796, page1.Letters.Count);
|
||||
}
|
||||
|
||||
using (var document = PdfDocument.Open(doc, new ParsingOptions() { UseLenientParsing = false, SkipMissingFonts = false }))
|
||||
{
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => document.GetPage(1));
|
||||
Assert.StartsWith("Read byte called on input bytes which was at end of byte set.", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue874()
|
||||
{
|
||||
@@ -22,5 +430,32 @@
|
||||
Assert.StartsWith("Value cannot be null.", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Issue913()
|
||||
{
|
||||
var doc = IntegrationHelpers.GetSpecificTestDocumentPath("Rotation 45.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(doc))
|
||||
{
|
||||
var page1 = document.GetPage(1);
|
||||
|
||||
for (int l = 131; l <= 137; ++l)
|
||||
{
|
||||
var letter = page1.Letters[l];
|
||||
Assert.Equal(TextOrientation.Other, letter.TextOrientation);
|
||||
Assert.Equal(45.0, letter.GlyphRectangle.Rotation, 5);
|
||||
}
|
||||
|
||||
var page2 = document.GetPage(2);
|
||||
Assert.Equal(157, page2.Letters.Count);
|
||||
|
||||
var page3 = document.GetPage(3);
|
||||
Assert.Equal(283, page3.Letters.Count);
|
||||
|
||||
var page4 = document.GetPage(4);
|
||||
Assert.Equal(304, page4.Letters.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace UglyToad.PdfPig.Tests.Integration
|
||||
{
|
||||
using DocumentLayoutAnalysis.WordExtractor;
|
||||
using System.Linq;
|
||||
|
||||
public class IndirectReferenceTests
|
||||
{
|
||||
[Fact]
|
||||
public void Generation65536()
|
||||
{
|
||||
using (var document = PdfDocument.Open(IntegrationHelpers.GetSpecificTestDocumentPath("93101_1.pdf")))
|
||||
{
|
||||
var page = document.GetPage(1);
|
||||
var words = NearestNeighbourWordExtractor.Instance.GetWords(page.Letters).ToArray();
|
||||
Assert.NotEmpty(words);
|
||||
|
||||
Assert.Equal("Railway", words[0].Text);
|
||||
|
||||
for (int p = 2; p <= document.NumberOfPages; ++p)
|
||||
{
|
||||
page = document.GetPage(p);
|
||||
Assert.NotEmpty(page.Letters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,8 @@
|
||||
[
|
||||
"issue_671.pdf",
|
||||
"GHOSTSCRIPT-698363-0.pdf",
|
||||
"ErcotFacts.pdf"
|
||||
"ErcotFacts.pdf",
|
||||
"cmap-parsing-exception.pdf"
|
||||
];
|
||||
|
||||
[Theory]
|
||||
@@ -23,7 +24,7 @@
|
||||
{
|
||||
var page = document.GetPage(i + 1);
|
||||
|
||||
Assert.NotNull(page.ExperimentalAccess.GetAnnotations().ToList());
|
||||
Assert.NotNull(page.GetAnnotations().ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +45,7 @@
|
||||
{
|
||||
var page = document.GetPage(i + 1);
|
||||
|
||||
Assert.NotNull(page.ExperimentalAccess.GetAnnotations().ToList());
|
||||
Assert.NotNull(page.GetAnnotations().ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
namespace UglyToad.PdfPig.Tests.Integration
|
||||
{
|
||||
using PdfPig.Fonts.SystemFonts;
|
||||
using System.Linq;
|
||||
|
||||
public class LetterFilterTests
|
||||
{
|
||||
[Fact]
|
||||
[SkippableFact]
|
||||
public void CanFilterClippedLetters()
|
||||
{
|
||||
var one = IntegrationHelpers.GetDocumentPath("ClipPathLetterFilter-Test1.pdf");
|
||||
|
||||
// The 'TimesNewRomanPSMT' font is used by this particular document. Thus, results cannot be trusted on
|
||||
// platforms where this font isn't generally available (e.g. OSX, Linux, etc.), so we skip it!
|
||||
var font = SystemFontFinder.Instance.GetTrueTypeFont("TimesNewRomanPSMT");
|
||||
var font1 = SystemFontFinder.Instance.GetTrueTypeFont("TimesNewRomanPS-BoldMT");
|
||||
var font2 = SystemFontFinder.Instance.GetTrueTypeFont("TimesNewRomanPS-ItalicMT");
|
||||
Skip.If(font is null || font1 is null || font2 is null, "Skipped because the font TimesNewRomanPSMT or a font from TimesNewRoman family could not be found in the execution environment.");
|
||||
|
||||
using (var doc1 = PdfDocument.Open(one, new ParsingOptions { ClipPaths = true }))
|
||||
using (var doc2 = PdfDocument.Open(one, new ParsingOptions { ClipPaths = false }))
|
||||
{
|
||||
|
||||
73
src/UglyToad.PdfPig.Tests/Integration/MaskTests.cs
Normal file
73
src/UglyToad.PdfPig.Tests/Integration/MaskTests.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
namespace UglyToad.PdfPig.Tests.Integration
|
||||
{
|
||||
using SkiaSharp;
|
||||
using System.Linq;
|
||||
|
||||
public class MaskTests
|
||||
{
|
||||
[Fact]
|
||||
public void PigProductionHandbook()
|
||||
{
|
||||
var path = IntegrationHelpers.GetDocumentPath("Pig Production Handbook.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true, SkipMissingFonts = true }))
|
||||
{
|
||||
var page = document.GetPage(1);
|
||||
|
||||
var images = page.GetImages().ToArray();
|
||||
|
||||
var image1 = images[1];
|
||||
Assert.NotNull(image1.MaskImage);
|
||||
Assert.True(image1.TryGetPng(out var png1));
|
||||
using (var skImage1 = SKImage.FromEncodedData(png1))
|
||||
using (var skBitmap1 = SKBitmap.FromImage(skImage1))
|
||||
{
|
||||
var pixel = skBitmap1.GetPixel(0, 0);
|
||||
Assert.Equal(0, pixel.Alpha);
|
||||
}
|
||||
|
||||
var image2 = images[2];
|
||||
Assert.NotNull(image2.MaskImage);
|
||||
Assert.True(image2.TryGetPng(out var png2));
|
||||
using (var skImage2 = SKImage.FromEncodedData(png2))
|
||||
using (var skBitmap2 = SKBitmap.FromImage(skImage2))
|
||||
{
|
||||
var pixel = skBitmap2.GetPixel(0, 0);
|
||||
Assert.Equal(0, pixel.Alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MOZILLA_LINK_3264_0()
|
||||
{
|
||||
var path = IntegrationHelpers.GetDocumentPath("MOZILLA-LINK-3264-0.pdf");
|
||||
|
||||
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true, SkipMissingFonts = true }))
|
||||
{
|
||||
var page = document.GetPage(1);
|
||||
|
||||
var images = page.GetImages().ToArray();
|
||||
|
||||
var image1 = images[1];
|
||||
Assert.NotNull(image1.MaskImage);
|
||||
Assert.True(image1.TryGetPng(out var png1));
|
||||
using (var skImage1 = SKImage.FromEncodedData(png1))
|
||||
using (var skBitmap1 = SKBitmap.FromImage(skImage1))
|
||||
{
|
||||
var pixel = skBitmap1.GetPixel(0, 0);
|
||||
Assert.Equal(0, pixel.Alpha);
|
||||
}
|
||||
|
||||
page = document.GetPage(2);
|
||||
|
||||
images = page.GetImages().ToArray();
|
||||
|
||||
var image2 = images[1];
|
||||
Assert.NotNull(image2.MaskImage);
|
||||
Assert.True(image2.TryGetPng(out var png2));
|
||||
// TODO - Check alpha value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user