Skip to content
Open
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
2 changes: 2 additions & 0 deletions lib/pdf/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,7 @@ class InvalidPageLayout < StandardError
require_relative 'core/page_geometry'
require_relative 'core/outline_root'
require_relative 'core/outline_item'
require_relative 'core/marked_content'
require_relative 'core/structure_tree'
require_relative 'core/renderer'
require_relative 'core/text'
14 changes: 5 additions & 9 deletions lib/pdf/core/document_state.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,11 @@ class DocumentState
def initialize(options)
normalize_metadata(options)

@store =
if options[:print_scaling]
PDF::Core::ObjectStore.new(
info: options[:info],
print_scaling: options[:print_scaling],
)
else
PDF::Core::ObjectStore.new(info: options[:info])
end
store_opts = { info: options[:info] }
store_opts[:print_scaling] = options[:print_scaling] if options[:print_scaling]
store_opts[:marked] = options[:marked] if options[:marked]

@store = PDF::Core::ObjectStore.new(store_opts)

@version = 1.3
@pages = []
Expand Down
60 changes: 60 additions & 0 deletions lib/pdf/core/marked_content.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true

module PDF
module Core
# Provides methods for emitting marked content operators (BMC/BDC/EMC)
# in PDF content streams. These operators associate content with structure
# elements for accessibility (tagged PDF).
#
# @api private
module MarkedContent
# Begin a marked content sequence with no properties.
#
# @param tag [Symbol] structure type tag (e.g., :P, :Span, :Artifact)
# @return [void]
def begin_marked_content(tag)
add_content("/#{tag} BMC")
end

# Begin a marked content sequence with properties (BDC operator).
#
# @param tag [Symbol] structure type tag
# @param properties [Hash] properties dict (typically includes :MCID)
# @return [void]
def begin_marked_content_with_properties(tag, properties = {})
props = PDF::Core.pdf_object(properties, true)
add_content("/#{tag} #{props} BDC")
end

# End a marked content sequence.
#
# @return [void]
def end_marked_content
add_content('EMC')
end

# Wrap a block in a marked content sequence (BMC/EMC).
#
# @param tag [Symbol] structure type tag
# @yield content to wrap
# @return [void]
def marked_content_sequence(tag)
begin_marked_content(tag)
yield if block_given?
end_marked_content
end

# Wrap a block in a marked content sequence with properties (BDC/EMC).
#
# @param tag [Symbol] structure type tag
# @param properties [Hash] properties dict
# @yield content to wrap
# @return [void]
def marked_content_sequence_with_properties(tag, properties = {})
begin_marked_content_with_properties(tag, properties)
yield if block_given?
end_marked_content
end
end
end
end
12 changes: 12 additions & 0 deletions lib/pdf/core/object_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class ObjectStore
# @option opts :info [Hash] Document info dict
# @option opts :print_scaling [:none, nil] (nil) Print scaling viewer
# option
# @option opts :marked [Boolean] (false) Whether this is a tagged
# (accessible) PDF
def initialize(opts = {})
@objects = {}
@identifiers = []
Expand All @@ -25,11 +27,21 @@ def initialize(opts = {})
if opts[:print_scaling] == :none
root.data[:ViewerPreferences] = { PrintScaling: :None }
end
if opts[:marked]
root.data[:MarkInfo] = { Marked: true }
end
if pages.nil?
root.data[:Pages] = ref(Type: :Pages, Count: 0, Kids: [])
end
end

# Whether this document is marked (tagged for accessibility).
#
# @return [Boolean]
def marked?
root.data.key?(:MarkInfo) && root.data[:MarkInfo][:Marked] == true
end

# Wrap an object into a reference.
#
# @param data [Hash, Array, Numeric, String, Symbol, Date, Time, nil]
Expand Down
20 changes: 20 additions & 0 deletions lib/pdf/core/renderer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ module PDF
module Core
# Document renderer serializes document into its binary representation.
class Renderer
include PDF::Core::MarkedContent

# @param state [PDF::Core::DocumentState]
def initialize(state)
@state = state
Expand All @@ -14,6 +16,24 @@ def initialize(state)
min_version(state.store.min_version) if state.store.min_version

@page_number = 0

if state.store.marked?
@structure_tree = PDF::Core::StructureTree.new(self)
before_render { |_doc_state| @structure_tree.finalize! }
min_version(1.7)
end
end

# The structure tree for this document, if tagged.
#
# @return [PDF::Core::StructureTree, nil]
attr_reader :structure_tree

# Whether this document is marked (tagged for accessibility).
#
# @return [Boolean]
def marked?
state.store.marked?
end

# Document state
Expand Down
Loading
Loading