| File: ExtractionDocumentSources.cs | Web Access |
| Project: src\src\Libraries\Microsoft.Extensions.Documents.Abstractions\Microsoft.Extensions.Documents.Abstractions.csproj (Microsoft.Extensions.Documents.Abstractions) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.Shared.Diagnostics; namespace Microsoft.Extensions.Documents; /// <summary>Identifies the unit of a source page's coordinates.</summary> public enum ExtractionDocumentCoordinateUnit { /// <summary>Pixels in the source raster.</summary> Pixel, /// <summary>Points, each equal to 1/72 inch.</summary> Point, /// <summary>Inches.</summary> Inch, /// <summary>Coordinates relative to the page, in the range zero to one.</summary> Normalized } /// <summary>Identifies the origin and vertical axis of source coordinates.</summary> public enum ExtractionDocumentCoordinateOrigin { /// <summary>Top-left origin, with the vertical axis increasing downwards.</summary> TopLeft, /// <summary>Bottom-left origin, with the vertical axis increasing upwards.</summary> BottomLeft } /// <summary>Represents a vertex in its source page's coordinate frame.</summary> public readonly struct ExtractionDocumentPoint : IEquatable<ExtractionDocumentPoint> { /// <summary>Initializes a new instance of the <see cref="ExtractionDocumentPoint"/> struct.</summary> public ExtractionDocumentPoint(double x, double y) { DocumentSourceValidation.Finite(x, nameof(x)); DocumentSourceValidation.Finite(y, nameof(y)); X = x; Y = y; } /// <summary>Gets the horizontal coordinate.</summary> public double X { get; } /// <summary>Gets the vertical coordinate.</summary> public double Y { get; } /// <inheritdoc/> public bool Equals(ExtractionDocumentPoint other) => X.Equals(other.X) && Y.Equals(other.Y); /// <inheritdoc/> public override bool Equals(object? obj) => obj is ExtractionDocumentPoint other && Equals(other); /// <inheritdoc/> public override int GetHashCode() => unchecked((X.GetHashCode() * 397) ^ Y.GetHashCode()); /// <summary>Determines whether two points have equal coordinates.</summary> public static bool operator ==(ExtractionDocumentPoint left, ExtractionDocumentPoint right) { return left.Equals(right); } /// <summary>Determines whether two points have different coordinates.</summary> public static bool operator !=(ExtractionDocumentPoint left, ExtractionDocumentPoint right) { return !left.Equals(right); } } /// <summary>Represents source page dimensions in the page's coordinate unit, or an empty (0, 0) value.</summary> /// <remarks>Empty dimensions cannot be assigned to a source page. Use <see langword="null"/> for unknown page dimensions.</remarks> public readonly struct ExtractionDocumentPageDimensions : IEquatable<ExtractionDocumentPageDimensions> { /// <summary>Initializes a new instance of the <see cref="ExtractionDocumentPageDimensions"/> struct.</summary> public ExtractionDocumentPageDimensions(double width, double height) { DocumentSourceValidation.Finite(width, nameof(width)); DocumentSourceValidation.Finite(height, nameof(height)); if (!width.Equals(0.0) || !height.Equals(0.0)) { _ = Throw.IfLessThanOrEqual(width, 0); _ = Throw.IfLessThanOrEqual(height, 0); } Width = width; Height = height; } /// <summary>Gets the width.</summary> public double Width { get; } /// <summary>Gets the height.</summary> public double Height { get; } /// <inheritdoc/> public bool Equals(ExtractionDocumentPageDimensions other) => Width.Equals(other.Width) && Height.Equals(other.Height); /// <inheritdoc/> public override bool Equals(object? obj) => obj is ExtractionDocumentPageDimensions other && Equals(other); /// <inheritdoc/> public override int GetHashCode() => unchecked((Width.GetHashCode() * 397) ^ Height.GetHashCode()); /// <summary>Determines whether two dimensions have equal width and height.</summary> public static bool operator ==(ExtractionDocumentPageDimensions left, ExtractionDocumentPageDimensions right) { return left.Equals(right); } /// <summary>Determines whether two dimensions have different width or height.</summary> public static bool operator !=(ExtractionDocumentPageDimensions left, ExtractionDocumentPageDimensions right) { return !left.Equals(right); } } /// <summary>Describes a producer-supplied source page, independently of document assembly order.</summary> public sealed class ExtractionDocumentSourcePage { /// <summary>Initializes a new instance of the <see cref="ExtractionDocumentSourcePage"/> class.</summary> public ExtractionDocumentSourcePage( string identifier, int? pageNumber, ExtractionDocumentPageDimensions? dimensions = null, ExtractionDocumentCoordinateUnit? coordinateUnit = null, ExtractionDocumentCoordinateOrigin? coordinateOrigin = null) { Identifier = Throw.IfNullOrEmpty(identifier); PageNumber = pageNumber is { } number ? Throw.IfLessThanOrEqual(number, 0, nameof(pageNumber)) : null; if (dimensions is { } size && (size.Width <= 0 || size.Height <= 0)) { Throw.ArgumentOutOfRangeException(nameof(dimensions)); } if (coordinateUnit is { } unit && !Enum.IsDefined(typeof(ExtractionDocumentCoordinateUnit), unit)) { Throw.ArgumentOutOfRangeException(nameof(coordinateUnit)); } if (coordinateOrigin is { } origin && !Enum.IsDefined(typeof(ExtractionDocumentCoordinateOrigin), origin)) { Throw.ArgumentOutOfRangeException(nameof(coordinateOrigin)); } Dimensions = dimensions; CoordinateUnit = coordinateUnit; CoordinateOrigin = coordinateOrigin; } /// <summary>Gets the producer's frame identifier.</summary> public string Identifier { get; } /// <summary>Gets the one-based originating page number, or <see langword="null"/> for an unnumbered source frame.</summary> /// <remarks>The source identifier remains meaningful without a number. This value is never an assembly index.</remarks> public int? PageNumber { get; } /// <summary>Gets reported dimensions, or <see langword="null"/> when unknown.</summary> public ExtractionDocumentPageDimensions? Dimensions { get; } /// <summary>Gets the reported unit, or <see langword="null"/> when unknown.</summary> public ExtractionDocumentCoordinateUnit? CoordinateUnit { get; } /// <summary>Gets the reported origin, or <see langword="null"/> when unknown.</summary> public ExtractionDocumentCoordinateOrigin? CoordinateOrigin { get; } } /// <summary>Associates content with an originating page and an optional source polygon.</summary> /// <remarks> /// A reference without a polygon identifies a page, not a precise location. Native observations are /// borrowed and excluded from semantic projection. A source association does not verify content. /// </remarks> public sealed class ExtractionDocumentSourceReference { private readonly object? _rawRepresentation; /// <summary>Initializes a new instance of the <see cref="ExtractionDocumentSourceReference"/> class.</summary> /// <param name="page">The originating page and coordinate frame.</param> /// <param name="polygon">Ordered polygon vertices, or no vertices for a page-only reference.</param> /// <param name="rawRepresentation">An optional borrowed native observation.</param> public ExtractionDocumentSourceReference( ExtractionDocumentSourcePage page, IEnumerable<ExtractionDocumentPoint>? polygon = null, object? rawRepresentation = null) { Page = Throw.IfNull(page); Polygon = new ReadOnlyCollection<ExtractionDocumentPoint>((polygon ?? Array.Empty<ExtractionDocumentPoint>()).ToArray()); if (Polygon.Count is 1 or 2) { Throw.ArgumentException(nameof(polygon), "A source polygon must contain at least three vertices."); } _rawRepresentation = rawRepresentation; } /// <summary>Gets the originating page and coordinate frame.</summary> public ExtractionDocumentSourcePage Page { get; } /// <summary>Gets every supplied polygon vertex in order, or an empty list for a page-only reference.</summary> public IReadOnlyList<ExtractionDocumentPoint> Polygon { get; } /// <summary>Gets the borrowed native observation for explicit inspection, when supplied.</summary> /// <returns>The borrowed native observation, or <see langword="null"/>.</returns> [SuppressMessage("Design", "CA1024:Use properties where appropriate", Justification = "Raw inspection is explicit and excluded from automatic property serialization.")] public object? GetRawRepresentation() => _rawRepresentation; } internal static class DocumentSourceValidation { internal static void Finite(double value, string name) { if (double.IsNaN(value) || double.IsInfinity(value)) { Throw.ArgumentOutOfRangeException(name, "A coordinate must be finite."); } } }