Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/pdf-annotation-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2024"
workspace = true

[features]
typescript = ["dep:ts-rs", "pdf-graphics/typescript"]
typescript = ["dep:ts-rs", "pdf-graphics/typescript", "pdf-optional-content/typescript"]

[dependencies]
bitflags = "2.9.1"
Expand All @@ -18,3 +18,4 @@ serde_json = "1"
thiserror = "2.0.12"
pdf-graphics = { path = "../pdf-graphics", features = ["serde"] }
pdf-object-reader = { path = "../pdf-object-reader" }
pdf-optional-content = { path = "../pdf-optional-content" }
14 changes: 14 additions & 0 deletions crates/pdf-annotation-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ pub enum SourceDecodeError {
ResourceLimit,
}

impl From<pdf_optional_content::OptionalContentError> for SourceDecodeError {
fn from(error: pdf_optional_content::OptionalContentError) -> Self {
use pdf_optional_content::OptionalContentError;
match error {
OptionalContentError::Object(error) => Self::Object(error),
OptionalContentError::InvalidEntry { entry, reason } => {
Self::InvalidEntry { entry, reason }
}
OptionalContentError::MissingEntry { entry } => Self::MissingEntry { entry },
OptionalContentError::ResourceLimit => Self::ResourceLimit,
}
}
}

impl From<SourceDecodeError> for pdf_object_reader::ObjectReadError {
fn from(source: SourceDecodeError) -> Self {
Self::Decode {
Expand Down
8 changes: 0 additions & 8 deletions crates/pdf-annotation-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ pub mod free_text;
pub mod kind;
/// Source snapshots and live annotation capabilities.
pub mod metadata;
/// Optional content group state retained from `SetOCGState` actions.
pub mod ocg_state;
/// Optional content group visibility applied to annotation presentation.
pub mod optional_content;
/// Owned source PDF annotation types, independent of parser resources.
pub mod pdf_data;
/// Canonical native PDF annotation record and subtype vocabulary.
Expand All @@ -50,7 +46,6 @@ mod validation;

mod source_decode;
mod source_decode_actions;
mod source_decode_optional_content;
mod source_decode_subtypes;
mod source_decode_widget;
/// Shared fields normalized from the widget records that display them.
Expand Down Expand Up @@ -78,9 +73,6 @@ mod widget_control;

pub use entry::{AnnotationEntry, ControlKind, ControlTag, WidgetControl};
pub use layer_error::{AnnotationLayerError, AnnotationLayerResult};
pub use optional_content::{
AnnotationOptionalContent, OptionalContentProperties, OptionalContentState,
};
pub use overlay::{AnnotationOverlay, PreparedAnnotations};
pub use requests::{
AnnotationCommandRequest, AnnotationReceipt, AnnotationTarget, OptionalContentReceipt,
Expand Down
8 changes: 6 additions & 2 deletions crates/pdf-annotation-core/src/overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ use crate::{
kind::AnnotationKind,
layer_error::{AnnotationLayerError, AnnotationLayerResult},
models::{Annotation, AnnotationId, Point, Revision},
ocg_state::OcgStateEntry,
optional_content::{OptionalContentProperties, OptionalContentState},
pdf_data::{AnnotationAction, SourceAnnotation},
projection,
requests::{
Expand All @@ -18,6 +16,7 @@ use crate::{
};
use pdf_graphics::{rect::Rect, viewport::PageViewport};
use pdf_object_reader::diagnostic::PdfReadDiagnostic;
use pdf_optional_content::{OcgStateEntry, OptionalContentProperties, OptionalContentState};
use std::{
collections::{BTreeMap, BTreeSet},
sync::Arc,
Expand Down Expand Up @@ -102,6 +101,11 @@ impl AnnotationOverlay {
})
}

/// Returns the runtime optional content visibility shared with page content.
pub fn optional_content(&self) -> &OptionalContentState {
&self.optional_content
}

/// Returns import diagnostics ordered by page and object; does not mutate state.
pub fn diagnostics(&self) -> &[PdfReadDiagnostic] {
&self.document.diagnostics
Expand Down
8 changes: 3 additions & 5 deletions crates/pdf-annotation-core/src/pdf_data.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
//! Owned PDF metadata. These values retain source information without resolving resources.
//! Raw byte fields are never interpreted as UTF-8 merely to serialize them.
use crate::{
metadata::AnnotationFlags, models::Quad, ocg_state::OcgStateEntry,
optional_content::AnnotationOptionalContent,
};
use crate::{metadata::AnnotationFlags, models::Quad};
use pdf_graphics::{DashPattern, color::Color, polyline::Polyline, rect::Rect};
use pdf_optional_content::{OcgStateEntry, OptionalContentMembership};

bitflags::bitflags! {
/// Widget form field flags parsed from the inherited `/Ff` entry.
Expand Down Expand Up @@ -894,7 +892,7 @@ pub struct SourceAnnotation {
/// Defaulted on load so sidecars written before this entry still deserialize.
#[serde(default)]
#[cfg_attr(feature = "typescript", ts(optional))]
pub optional_content: Option<AnnotationOptionalContent>,
pub optional_content: Option<OptionalContentMembership>,
/// The parsed subtype-specific payload.
pub kind: NativeAnnotation,
}
2 changes: 1 addition & 1 deletion crates/pdf-annotation-core/src/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ use crate::{
kind::AnnotationKind as Kind,
layer_error::{AnnotationLayerError, AnnotationLayerResult},
models::{Annotation, Revision},
optional_content::OptionalContentState,
pdf_data::AnnotationAction,
shapes, widget_control,
};
use pdf_graphics::{point::Point, rect::Rect, size::Size, viewport::PageViewport};
use pdf_optional_content::OptionalContentState;

/// The shared field a widget is bound to; `None` for other kinds.
fn widget_field<'a>(
Expand Down
6 changes: 3 additions & 3 deletions crates/pdf-annotation-core/src/source_decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ use pdf_object_reader::{
object_resolver::{DanglingAsNullResolver, ObjectResolver},
object_variant::ObjectVariant,
};
use pdf_optional_content::OptionalContentMembership;

pub(crate) type DecodeResult<T> = Result<T, SourceDecodeError>;

impl FromPdfObject for SourceAnnotation {
fn from_pdf_object(context: ObjectContext<'_, impl ObjectAccess + ?Sized>) -> ReadResult<Self> {
let context = context.dictionary()?;
let mut context = context.dictionary()?;
let dictionary = context.dictionary();
let optional_content = context.optional::<OptionalContentMembership>(b"OC")?;
// Annotations often carry stale references, which read as null (ISO 32000 §7.3.10).
let objects = &DanglingAsNullResolver(context.source());
// Some PDFs omit `/Type` on annotation dictionaries even though the
Expand Down Expand Up @@ -85,8 +87,6 @@ impl FromPdfObject for SourceAnnotation {
.map(|value| value.try_bytes(objects).map(Vec::from))
.transpose()?;
let struct_parent = dictionary.optional_number::<u64>(b"StructParent", objects)?;
let optional_content =
crate::source_decode_optional_content::optional_content(dictionary, objects)?;

let border = AnnotationBorder::from_dictionary(dictionary, objects)?;
let color = color(dictionary, b"C", objects)?;
Expand Down
2 changes: 1 addition & 1 deletion crates/pdf-annotation-core/src/source_decode_actions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Action, destination, and file-specification decoding; nothing is executed.
use crate::error::SourceDecodeError;
use crate::ocg_state::{OcgStateEntry, OcgTarget};
use crate::pdf_data::{
AnnotationAction, AnnotationDestination, DestinationTarget, ExplicitDestination,
FileSpecification, FileSpecificationDictionary,
Expand All @@ -10,6 +9,7 @@ use pdf_object_reader::{
dictionary::Dictionary, object_error::ObjectError, object_lookup::ObjectLookupExt,
object_resolver::ObjectResolver, object_variant::ObjectVariant,
};
use pdf_optional_content::{OcgStateEntry, OcgTarget};

impl AnnotationAction {
pub(crate) fn from_dictionary(
Expand Down
Loading
Loading