Skip to content

Commit

Permalink
General documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
dwarring committed Oct 1, 2024
1 parent 332e309 commit 53fc544
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 109 deletions.
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,13 @@ tagged internal link to a PDF.

As a rule, all content doesn't have to form part of the structure tree, but should be tagged to meet accessibility guidelines.

This sometimes requires tagging of incidental graphics. `PDF::Content` has a `tag()` method for this. The content is be tagged, but does not appear in the content stream.
This sometimes requires tagging of incidental graphics. `PDF::Content` has a `tag()` method for this. The content is tagged, but does not appear in the structure tree.

Some of the commonly used content tags are:

#### Artifact

Artifact content forms part of the visual display, but does not belong in the Structure Tree and is tagged using the `PDF::Content` `tag` method.
Artifact content forms part of the visual display, but does not belong in the structure Tree.

For example:
```raku
Expand Down Expand Up @@ -295,16 +295,14 @@ text is being inserted as a paragraph in the structure tree.

#### Span

This tag may be used in the structure tree, or at the content level to defined attributes of a graphics sequence. Its usage is similar to the XHTML `span` tag.
This tag may be used either in the structure tree, or at the content level to defined attributes of a graphics sequence. Its usage is similar to the XHTML `span` tag.

```raku
$gfx.tag: Span: :Lang<es-MX>, {
.say('Hasta la vista', :position[50, 80]);
}
```

It can be used almost anywhere in the structure tree, or at the content level, as above.

### Role Maps

The PDF standard allows user defined tags, which are mapped to
Expand All @@ -319,8 +317,8 @@ use PDF::Tags::Elem;
use PDF::Class;
use PDF::Page;

enum RoleMap ( :Body<Section>, :Footnote<Note>, :Book<Document> );
constant %role-map = RoleMap.enums.Hash;
enum Roles ( :Book<Document>, :FootNote<Note> );
constant %role-map = Roles.enums.Hash;

my PDF::Class $pdf .= new;
my PDF::Tags $tags .= create: :$pdf, :%role-map;
Expand All @@ -334,7 +332,7 @@ $page.graphics: -> $gfx {
.say: 'Some body text¹', :position[50, 150];
};

$doc.Footnote: $gfx, {
$doc.FootNote: $gfx, {
.say: '¹With a foot-note', :position[50, 20];
};

Expand Down
199 changes: 99 additions & 100 deletions lib/PDF/Tags.rakumod
Original file line number Diff line number Diff line change
@@ -1,124 +1,123 @@
#| Tagged PDF root node
class PDF::Tags:ver<0.1.17> {

use PDF::Tags::Node::Parent :&att-owner;
also is PDF::Tags::Node::Parent;

use PDF::Tags::Node::Root;
also does PDF::Tags::Node::Root;

use PDF::Class:ver<0.4.10+>;
use PDF::NumberTree :NumberTree;
use PDF::StructElem;
use PDF::StructTreeRoot;

has Hash $.class-map is built;
has Hash $.role-map is built;
has NumberTree $.parent-tree is built;
has $.styler;
has Lock $!lock handles<protect> .= new;
method root { self }
method marks { True }

submethod TWEAK(PDF::StructTreeRoot :$cos!, :%role-map) {
$!class-map = $_ with $cos.ClassMap;
$!role-map = $_ with $cos.RoleMap;
self.set-role(.key, .value) for %role-map.pairs;
$!parent-tree = .number-tree
given $cos.ParentTree //= { :Nums[] };
}
unit class PDF::Tags:ver<0.1.17>;

use PDF::Tags::Node::Parent :&att-owner;
also is PDF::Tags::Node::Parent;

use PDF::Tags::Node::Root;
also does PDF::Tags::Node::Root;

use PDF::Class:ver<0.4.10+>;
use PDF::NumberTree :NumberTree;
use PDF::StructElem;
use PDF::StructTreeRoot;

has Hash $.class-map is built;
has Hash $.role-map is built;
has NumberTree $.parent-tree is built;
has $.styler;
has Lock $!lock handles<protect> .= new;
method root { self }
method marks { True }

submethod TWEAK(PDF::StructTreeRoot :$cos!, :%role-map) {
$!class-map = $_ with $cos.ClassMap;
$!role-map = $_ with $cos.RoleMap;
self.set-role(.key, .value) for %role-map.pairs;
$!parent-tree = .number-tree
given $cos.ParentTree //= { :Nums[] };
}

method read(|c) {
my constant Reader = 'PDF::Tags::Reader';
CATCH {
when X::CompUnit::UnsatisfiedDependency {
fail "{Reader.raku} needs to be installed to read tagged PDF files";
}
method read(|c) {
my constant Reader = 'PDF::Tags::Reader';
CATCH {
when X::CompUnit::UnsatisfiedDependency {
fail "{Reader.raku} needs to be installed to read tagged PDF files";
}
require ::(Reader);
::(Reader).read(|c);
}
require ::(Reader);
::(Reader).read(|c);
}

method set-role(Str:D $role, Str:D $base) {
$!role-map //= {};
with $!role-map{$role} {
warn "role mapping $role => $base conflicts with $role => $_"
unless $base eq $_;
}
else {
$_ = $base;
}
method set-role(Str:D $role, Str:D $base) {
$!role-map //= {};
with $!role-map{$role} {
warn "role mapping $role => $base conflicts with $role => $_"
unless $base eq $_;
}
else {
$_ = $base;
}
}

sub build-class-map($cos, %class-map) {
for %class-map {
my $class = .key;
my %attributes = .value;
my Hash %atts-by-owner;
for %attributes {
#Raku 2022.06+ my :($owner, $key) := att-owner(.key);
my ($owner, $key) = att-owner(.key);
%atts-by-owner{$owner}{$key} = .value;
}
my PDF::Attributes() @atts = %atts-by-owner.keys.sort.map: -> $owner {
my %atts = %atts-by-owner{$owner};
%atts<O> = $owner;
%atts;
}

if @atts {
$cos.ClassMap //= %();
if @atts == 1 {
$cos.ClassMap{$class} = @atts[0];
}
else {
$cos.ClassMap{$class} = @atts;
}
}
sub build-class-map($cos, %class-map) {
for %class-map {
my $class = .key;
my %attributes = .value;
my Hash %atts-by-owner;
for %attributes {
#Raku 2022.06+ my :($owner, $key) := att-owner(.key);
my ($owner, $key) = att-owner(.key);
%atts-by-owner{$owner}{$key} = .value;
}
my PDF::Attributes() @atts = %atts-by-owner.keys.sort.map: -> $owner {
my %atts = %atts-by-owner{$owner};
%atts<O> = $owner;
%atts;
}
}

method create(
PDF::Class:D :$pdf!,
PDF::StructTreeRoot() :$cos = { :Type( :name<StructTreeRoot> )},
:%role-map,
:%class-map,
|c
--> PDF::Tags:D
) {
$cos.RoleMap = %role-map if %role-map;
build-class-map($cos, %class-map)
if %class-map;
$cos.check;

given $pdf {
with .catalog.StructTreeRoot {
fail "document already contains marked content";
if @atts {
$cos.ClassMap //= %();
if @atts == 1 {
$cos.ClassMap{$class} = @atts[0];
}
else {
$_ = $cos;
$cos.ClassMap{$class} = @atts;
}
.<Marked> = True
given .Root<MarkInfo> //= {};
.creator.push: "{self.^name}-{self.^ver}";
}
self.new: :$cos, :root(self.WHAT), :marks, |c
}
}

# Set the page to a given index.
# To create identical PDF files. Mostly for thread-testing purposes.
method set-page-index(PDF::Page:D $Pg, UInt:D $idx) {
self.protect: {
$Pg.StructParents = $idx;
$!parent-tree[$idx] //= [];
method create(
PDF::Class:D :$pdf!,
PDF::StructTreeRoot() :$cos = { :Type( :name<StructTreeRoot> )},
:%role-map,
:%class-map,
|c
--> PDF::Tags:D
) {
$cos.RoleMap = %role-map if %role-map;
build-class-map($cos, %class-map)
if %class-map;
$cos.check;

given $pdf {
with .catalog.StructTreeRoot {
fail "document already contains marked content";
}
else {
$_ = $cos;
}
.<Marked> = True
given .Root<MarkInfo> //= {};
.creator.push: "{self.^name}-{self.^ver}";
}
self.new: :$cos, :root(self.WHAT), :marks, |c
}

method canvas-tags(|) {
fail "PDF::Tags::Reader is required to read PDF tags";
# Set the page to a given index.
# To create identical PDF files. Mostly for thread-testing purposes.
method set-page-index(PDF::Page:D $Pg, UInt:D $idx) {
self.protect: {
$Pg.StructParents = $idx;
$!parent-tree[$idx] //= [];
}
}

method canvas-tags(|) {
fail "PDF::Tags::Reader is required to read PDF tags";
}

=begin pod
=head2 Synopsis
Expand Down
1 change: 0 additions & 1 deletion lib/PDF/Tags/Node/Parent.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ multi method fragment(Str:D :$name!, *%o --> PDF::Tags::Node:D) {
multi method fragment(Str:D $name = 'DocumentFragment', *%o) {
self.fragment(:$name, |%o);
}

multi method FALLBACK(Str:D $name where $_ TagSet, |c) {
self.add-kid(:$name, |c)
}
Expand Down

0 comments on commit 53fc544

Please sign in to comment.