From 23e2217a1c815363234ccfacdf01a43153a4b79b Mon Sep 17 00:00:00 2001 From: David Warring Date: Sat, 10 Aug 2024 07:25:01 +1200 Subject: [PATCH] Convert to unit classes --- lib/PDF/Tags.rakumod | 6 +- lib/PDF/Tags/Attr.rakumod | 46 +-- lib/PDF/Tags/Elem.rakumod | 635 +++++++++++++++---------------- lib/PDF/Tags/Mark.rakumod | 197 +++++----- lib/PDF/Tags/Node.rakumod | 131 ++++--- lib/PDF/Tags/Node/Parent.rakumod | 321 ++++++++-------- lib/PDF/Tags/ObjRef.rakumod | 42 +- lib/PDF/Tags/Text.rakumod | 40 +- lib/PDF/Tags/XPath.rakumod | 72 ++-- t/threaded.t | 6 +- 10 files changed, 747 insertions(+), 749 deletions(-) diff --git a/lib/PDF/Tags.rakumod b/lib/PDF/Tags.rakumod index 72fa641..9513777 100644 --- a/lib/PDF/Tags.rakumod +++ b/lib/PDF/Tags.rakumod @@ -60,10 +60,10 @@ class PDF::Tags:ver<0.1.16> { my ($owner, $key) = att-owner(.key); %atts-by-owner{$owner}{$key} = .value; } - my @atts = %atts-by-owner.keys.sort.map: -> $owner { + my PDF::Attributes() @atts = %atts-by-owner.keys.sort.map: -> $owner { my %atts = %atts-by-owner{$owner}; %atts = $owner; - PDF::Attributes.COERCE: %atts + %atts; } if @atts { @@ -79,7 +79,7 @@ class PDF::Tags:ver<0.1.16> { } method create( - PDF::Class:D :$pdf, + PDF::Class:D :$pdf!, PDF::StructTreeRoot() :$cos = { :Type( :name )}, :%role-map, :%class-map, diff --git a/lib/PDF/Tags/Attr.rakumod b/lib/PDF/Tags/Attr.rakumod index 09eab58..09d7913 100644 --- a/lib/PDF/Tags/Attr.rakumod +++ b/lib/PDF/Tags/Attr.rakumod @@ -1,27 +1,29 @@ -use PDF::Tags::Node; #| Attribute node -class PDF::Tags::Attr - is PDF::Tags::Node { - use PDF::Tags::Node::Parent; - use Method::Also; - - has PDF::Tags::Node::Parent $.parent is rw; - submethod TWEAK(Pair :$cos!) { - self.set-cos($cos); - } - method xpath { - my Str $xpath = .xpath with $!parent; - $xpath ~= '@' ~ self.name; - $xpath; - } - multi sub to-str(@a) { @a».Str.join: ' '} - multi sub to-str($_) { .Str} - method name { $.cos.key } - method value { $.cos.value } - method text is also { to-str($.value) } - method gist { [~] '@', $.cos.key, '=', $.text } - method cos(--> Pair) is also { callsame() } +unit class PDF::Tags::Attr; + +use PDF::Tags::Node; +also is PDF::Tags::Node; + +use PDF::Tags::Node::Parent; +use Method::Also; + +has PDF::Tags::Node::Parent $.parent is rw; +submethod TWEAK(Pair :$cos!) { + self.set-cos($cos); +} +method xpath { + my Str $xpath = .xpath with $!parent; + $xpath ~= '@' ~ self.name; + $xpath; } +multi sub to-str(@a) { @a».Str.join: ' '} +multi sub to-str($_) { .Str} +method name { $.cos.key } +method value { $.cos.value } +method text is also { to-str($.value) } +method gist { [~] '@', $.cos.key, '=', $.text } +method cos(--> Pair) is also { callsame() } + =begin pod diff --git a/lib/PDF/Tags/Elem.rakumod b/lib/PDF/Tags/Elem.rakumod index 8204498..96a11c9 100644 --- a/lib/PDF/Tags/Elem.rakumod +++ b/lib/PDF/Tags/Elem.rakumod @@ -1,380 +1,379 @@ -use PDF::Tags::Node::Parent :&att-owner; - #| represents one node in the structure tree. -class PDF::Tags::Elem - is PDF::Tags::Node::Parent { - - use PDF::COS; - use PDF::COS::Dict; - use PDF::COS::Name; - use PDF::COS::Stream; - use PDF::Content; - use PDF::Content::Canvas; - use PDF::Tags::Mark; - use PDF::Tags::Node :&build-node, :TagName; - use PDF::Tags::ObjRef; - use PDF::Tags::Text; - # PDF:Class - use PDF::Class::StructItem; - use PDF::Attributes; - use PDF::MCR; - use PDF::OBJR; - use PDF::Page; - use PDF::StructElem; - use PDF::XObject::Form; - use PDF::XObject::Image; - use PDF::XObject; - - method cos(--> PDF::StructElem) handles { callsame() } - has PDF::Tags::Node::Parent $.parent is rw = self.root; - has Hash $!attributes; - has TagName $.name is built; - has Str $.role is built; - has List $!classes; - - method classes { $!classes //= $.cos.class-map-keys } - - method name is rw { - Proxy.new( - FETCH => { $!name }, - STORE => -> $, $!name { - self.cos.tag = $!name; - } - ); - } +unit class PDF::Tags::Elem; - sub merge-atts(%atts, PDF::Attributes $a) { - if $a.Hash -> %a { - my $owner = $a.owner; - # guess owner, if not given - $owner //= att-owner($_).head - given %a.keys.head; - if $owner ~~ 'Layout'|'List'|'PrintField'|'Table' { - # standard owners (Table 341) with mutually distinct attribute names - %atts{.key} = .value for %a; - } - else { - %atts{$owner ~ ':' ~ .key} = .value for %a; - } +use PDF::Tags::Node::Parent :&att-owner; +also is PDF::Tags::Node::Parent; + +use PDF::COS; +use PDF::COS::Dict; +use PDF::COS::Name; +use PDF::COS::Stream; +use PDF::Content; +use PDF::Content::Canvas; +use PDF::Tags::Mark; +use PDF::Tags::Node :&build-node, :TagName; +use PDF::Tags::ObjRef; +use PDF::Tags::Text; +# PDF:Class +use PDF::Class::StructItem; +use PDF::Attributes; +use PDF::MCR; +use PDF::OBJR; +use PDF::Page; +use PDF::StructElem; +use PDF::XObject::Form; +use PDF::XObject::Image; +use PDF::XObject; + +method cos(--> PDF::StructElem) handles { callsame() } +has PDF::Tags::Node::Parent $.parent is rw = self.root; +has Hash $!attributes; +has TagName $.name is built; +has Str $.role is built; +has List $!classes; + +method classes { $!classes //= $.cos.class-map-keys } + +method name is rw { + Proxy.new( + FETCH => { $!name }, + STORE => -> $, $!name { + self.cos.tag = $!name; } - } - - method attributes { - $!attributes //= do { - my %atts; - for $.cos.attribute-dicts -> PDF::Attributes $atts { - merge-atts(%atts, $atts); - } - - unless %atts { - for @.classes { - with $.root.class-map{$_} -> PDF::Attributes $atts { - merge-atts(%atts, $atts); - } - } - } - - %atts = $_ with $!role; + ); +} - %atts; +sub merge-atts(%atts, PDF::Attributes $a) { + if $a.Hash -> %a { + my $owner = $a.owner; + # guess owner, if not given + $owner //= att-owner($_).head + given %a.keys.head; + if $owner ~~ 'Layout'|'List'|'PrintField'|'Table' { + # standard owners (Table 341) with mutually distinct attribute names + %atts{.key} = .value for %a; } - } - - method build-kid($) { - given callsame() { - if ! $.root.marks && $_ ~~ PDF::Tags::Mark && !.attributes { - # dereference marked content tags. just get the aggregate text - $.build-kid(.text); - } - else { - $_; - } + else { + %atts{$owner ~ ':' ~ .key} = .value for %a; } } +} - method text returns Str { self.cos.ActualText // $.kids».text.join } - - submethod TWEAK(Str :$Alt, Str :$ActualText, Str :$Lang, Str :$class, :%attributes) { - self.set-attributes(|%attributes) - if %attributes; - given self.cos -> $cos { - self.Pg = $_ with $cos.Pg; - $cos.Alt = $_ with $Alt; - $cos.ActualText = $_ with $ActualText; - $cos.Lang = $_ with $Lang; - $cos.C = PDF::COS::Name.COERCE($_) with $class; - my Str:D $tag = $cos.tag; - with self.root.role-map{$tag} { - $!role = $tag; - $!name = $_; - } - else { - $!name = $tag; - } +method attributes { + $!attributes //= do { + my %atts; + for $.cos.attribute-dicts -> PDF::Attributes $atts { + merge-atts(%atts, $atts); } - } - method mark(PDF::Tags::Elem:D $elem: PDF::Content $gfx, &action, :$name = self.name, |c --> PDF::Tags::Mark:D) { - temp $gfx.actual-text = ''; # Populated by PDF::Content.print() - - my PDF::Content::Tag $cos = $gfx.tag($name, &action, :mark, |c); - my PDF::Tags::Mark:D $mark = $elem.add-kid: :$cos; - $mark.actual-text = $gfx.actual-text; - # Register this mark in the parent tree - $.root.protect: { - given $gfx.canvas.StructParents -> $idx is rw { - $idx //= $.root.parent-tree.max-key + 1 - if $gfx.canvas ~~ PDF::Page; - $.root.parent-tree[$_+0][$mark.mcid] //= $elem.cos - with $idx; + unless %atts { + for @.classes { + with $.root.class-map{$_} -> PDF::Attributes $atts { + merge-atts(%atts, $atts); + } } } - $mark; - } + %atts = $_ with $!role; - # combined add-kid + mark - multi method add-kid(PDF::Content:D $gfx, &action, :$name!, Str :$Alt, Str :$Lang, Str :$ActualText, Str :$class, :%attributes, |c --> PDF::Tags::Elem:D) { - given self.add-kid(:$name, :$Alt, :$Lang, :$ActualText, :$class, :%attributes) { - .mark($gfx, &action, |c); - $_; - } + %atts; } +} - # combined add-kid + do - multi method add-kid(PDF::Content:D $gfx, PDF::XObject:D $xobj, :$name!, Str :$Alt, |c --> PDF::Tags::Elem:D) { - given self.add-kid(:$name, :$Alt) { - .do($gfx, $xobj, |c); +method build-kid($) { + given callsame() { + if ! $.root.marks && $_ ~~ PDF::Tags::Mark && !.attributes { + # dereference marked content tags. just get the aggregate text + $.build-kid(.text); + } + else { $_; } } +} - # combined add-kid + reference - multi method add-kid(PDF::Content:D $gfx, PDF::Class::StructItem:D $obj, :$name!, Str :$Alt, |c --> PDF::Tags::Elem:D) { - self.add-kid(:$name, :$Alt).reference($obj, :$gfx, |c); - } - - # copy intermediate node and descendants - multi method copy-tree(PDF::Tags::Elem:D $from-elem = self, PDF::XObject::Form:D :$Stm!, :$parent!) { - my PDF::StructElem $from-cos = $from-elem.cos; - my $S = $from-cos.S; - my PDF::StructElem $P = $parent.cos; - my PDF::StructElem() $to-cos = %( - :Type( :name ), - :$S, - :$P, - ); - for -> $k { - $to-cos{$k} = $_ with $from-cos{$k}; +method text returns Str { self.cos.ActualText // $.kids».text.join } + +submethod TWEAK(Str :$Alt, Str :$ActualText, Str :$Lang, Str :$class, :%attributes) { + self.set-attributes(|%attributes) + if %attributes; + given self.cos -> $cos { + self.Pg = $_ with $cos.Pg; + $cos.Alt = $_ with $Alt; + $cos.ActualText = $_ with $ActualText; + $cos.Lang = $_ with $Lang; + $cos.C = PDF::COS::Name.COERCE($_) with $class; + my Str:D $tag = $cos.tag; + with self.root.role-map{$tag} { + $!role = $tag; + $!name = $_; } - $to-cos = $_ with $.Pg // $from-cos; - my PDF::Tags::Elem $to-elem = build-node($to-cos, :$.root, :$parent); - for $from-elem.kids { - my PDF::Tags::Node:D $kid = $.copy-tree($_, :$Stm, :parent($to-elem)); - $to-elem.add-kid: :node($kid); + else { + $!name = $tag; } - $to-elem; } +} - # copy leaf node - multi method copy-tree(PDF::Tags::Mark $item, PDF::COS::Stream :$Stm!, PDF::Tags::Elem :$parent!) { - $item.clone: :$Stm, :$parent; +method mark(PDF::Tags::Elem:D $elem: PDF::Content $gfx, &action, :$name = self.name, |c --> PDF::Tags::Mark:D) { + temp $gfx.actual-text = ''; # Populated by PDF::Content.print() + + my PDF::Content::Tag $cos = $gfx.tag($name, &action, :mark, |c); + my PDF::Tags::Mark:D $mark = $elem.add-kid: :$cos; + $mark.actual-text = $gfx.actual-text; + # Register this mark in the parent tree + $.root.protect: { + given $gfx.canvas.StructParents -> $idx is rw { + $idx //= $.root.parent-tree.max-key + 1 + if $gfx.canvas ~~ PDF::Page; + $.root.parent-tree[$_+0][$mark.mcid] //= $elem.cos + with $idx; + } } - # copy reference - multi method copy-tree(PDF::Tags::ObjRef $ref, PDF::Tags::Elem :$parent!) { - $ref.clone: :$parent; + $mark; +} + +# combined add-kid + mark +multi method add-kid(PDF::Content:D $gfx, &action, :$name!, Str :$Alt, Str :$Lang, Str :$ActualText, Str :$class, :%attributes, |c --> PDF::Tags::Elem:D) { + given self.add-kid(:$name, :$Alt, :$Lang, :$ActualText, :$class, :%attributes) { + .mark($gfx, &action, |c); + $_; } +} - method !do-reference(PDF::Content $gfx, PDF::XObject $xobj, |c) { - my @rect = $gfx.do($xobj, |c); - self.reference($xobj, :$gfx); - self!bbox($gfx, @rect); - @rect; +# combined add-kid + do +multi method add-kid(PDF::Content:D $gfx, PDF::XObject:D $xobj, :$name!, Str :$Alt, |c --> PDF::Tags::Elem:D) { + given self.add-kid(:$name, :$Alt) { + .do($gfx, $xobj, |c); + $_; } +} + +# combined add-kid + reference +multi method add-kid(PDF::Content:D $gfx, PDF::Class::StructItem:D $obj, :$name!, Str :$Alt, |c --> PDF::Tags::Elem:D) { + self.add-kid(:$name, :$Alt).reference($obj, :$gfx, |c); +} - multi method do(PDF::Content $gfx, PDF::XObject::Image $img, *%o) { - self!do-reference($gfx, $img, |%o); +# copy intermediate node and descendants +multi method copy-tree(PDF::Tags::Elem:D $from-elem = self, PDF::XObject::Form:D :$Stm!, :$parent!) { + my PDF::StructElem $from-cos = $from-elem.cos; + my $S = $from-cos.S; + my PDF::StructElem $P = $parent.cos; + my PDF::StructElem() $to-cos = %( + :Type( :name ), + :$S, + :$P, + ); + for -> $k { + $to-cos{$k} = $_ with $from-cos{$k}; + } + $to-cos = $_ with $.Pg // $from-cos; + my PDF::Tags::Elem $to-elem = build-node($to-cos, :$.root, :$parent); + for $from-elem.kids { + my PDF::Tags::Node:D $kid = $.copy-tree($_, :$Stm, :parent($to-elem)); + $to-elem.add-kid: :node($kid); } + $to-elem; +} - multi method do(PDF::Content $gfx, PDF::XObject::Form $xobj, *%o) { - - if $.root.protect({$xobj.StructParents.defined || self!setup-parents($xobj)}) { - my @rect = $gfx.do($xobj, %o); - my PDF::Content::Canvas $canvas = $gfx.canvas; - my PDF::Page $Pg = $canvas - if $canvas ~~ PDF::Page; - - given $xobj.StructParents { - # potentially lossy. parent-tree only includes marked content references - $.root.protect: { - my Array $parents = self.root.parent-tree[$_+0]; - - for $parents.keys { - # copy sub-trees - my PDF::StructElem $cos = $parents[$_]; - my PDF::Tags::Elem $elem = build-node($cos, :$.root, :$Pg, :parent(self)); - my PDF::Tags::Node $node = $elem.copy-tree(:Stm($xobj), :parent(self)); - self.add-kid: :$node; - } +# copy leaf node +multi method copy-tree(PDF::Tags::Mark $item, PDF::COS::Stream :$Stm!, PDF::Tags::Elem :$parent!) { + $item.clone: :$Stm, :$parent; +} + +# copy reference +multi method copy-tree(PDF::Tags::ObjRef $ref, PDF::Tags::Elem :$parent!) { + $ref.clone: :$parent; +} + +method !do-reference(PDF::Content $gfx, PDF::XObject $xobj, |c) { + my @rect = $gfx.do($xobj, |c); + self.reference($xobj, :$gfx); + self!bbox($gfx, @rect); + @rect; +} + +multi method do(PDF::Content $gfx, PDF::XObject::Image $img, *%o) { + self!do-reference($gfx, $img, |%o); +} + +multi method do(PDF::Content $gfx, PDF::XObject::Form $xobj, *%o) { + + if $.root.protect({$xobj.StructParents.defined || self!setup-parents($xobj)}) { + my @rect = $gfx.do($xobj, %o); + my PDF::Content::Canvas $canvas = $gfx.canvas; + my PDF::Page $Pg = $canvas + if $canvas ~~ PDF::Page; + + given $xobj.StructParents { + # potentially lossy. parent-tree only includes marked content references + $.root.protect: { + my Array $parents = self.root.parent-tree[$_+0]; + + for $parents.keys { + # copy sub-trees + my PDF::StructElem $cos = $parents[$_]; + my PDF::Tags::Elem $elem = build-node($cos, :$.root, :$Pg, :parent(self)); + my PDF::Tags::Node $node = $elem.copy-tree(:Stm($xobj), :parent(self)); + self.add-kid: :$node; } } - self!bbox($gfx, @rect); - @rect; - } - else { - self!do-reference($gfx, $xobj, |%o); } + self!bbox($gfx, @rect); + @rect; } + else { + self!do-reference($gfx, $xobj, |%o); + } +} - # depth first search for referenced xobject - multi sub find-xobjects([]) { [] } - multi sub find-xobjects(@elems) { - my subset XObjRef of PDF::Tags::ObjRef where .cos.object ~~ PDF::XObject; - my PDF::XObject @xobjects = @elems.map: { - when PDF::Tags::Mark { .Stm } - when XObjRef { .cos.object } - default { Empty } - }; - - @xobjects ||= do { - my @kids; - @kids.append: .kids for @elems; - find-xobjects(@kids); - } +# depth first search for referenced xobject +multi sub find-xobjects([]) { [] } +multi sub find-xobjects(@elems) { + my subset XObjRef of PDF::Tags::ObjRef where .cos.object ~~ PDF::XObject; + my PDF::XObject @xobjects = @elems.map: { + when PDF::Tags::Mark { .Stm } + when XObjRef { .cos.object } + default { Empty } + }; + + @xobjects ||= do { + my @kids; + @kids.append: .kids for @elems; + find-xobjects(@kids); + } +} +# smart do on a sub-tree containing an x-object +multi method do(PDF::Tags::Elem:D $parent: PDF::Content $gfx, PDF::Tags::Elem:D $frag, *%o) { + my PDF::XObject @xobjects = find-xobjects([$frag]).unique + || die "no xobject found"; + die "element contains multiple xobjects" if @xobjects > 1; + my PDF::XObject:D $Stm = @xobjects[0]; + my PDF::Tags::Elem $node = $frag.copy-tree(:$Stm, :$parent); + if $node.name eq 'DocumentFragment' { + $parent.add-kid(:node($_)) for $node.kids; + } + else { + $parent.add-kid: :$node; } - # smart do on a sub-tree containing an x-object - multi method do(PDF::Tags::Elem:D $parent: PDF::Content $gfx, PDF::Tags::Elem:D $frag, *%o) { - my PDF::XObject @xobjects = find-xobjects([$frag]).unique - || die "no xobject found"; - die "element contains multiple xobjects" if @xobjects > 1; - my PDF::XObject:D $Stm = @xobjects[0]; - my PDF::Tags::Elem $node = $frag.copy-tree(:$Stm, :$parent); - if $node.name eq 'DocumentFragment' { - $parent.add-kid(:node($_)) for $node.kids; - } - else { - $parent.add-kid: :$node; - } - my @rect = $gfx.do($Stm, |%o); + my @rect = $gfx.do($Stm, |%o); - given $Stm { - when PDF::XObject::Form && !.StructParent.defined { - $.root.protect: { - unless .StructParents.defined { - $node!setup-parents($_) - // $node.reference($_, :$gfx); - } + given $Stm { + when PDF::XObject::Form && !.StructParent.defined { + $.root.protect: { + unless .StructParents.defined { + $node!setup-parents($_) + // $node.reference($_, :$gfx); } } - default { - $node.reference($_, :$gfx); - } } - - $parent!bbox($gfx, @rect); - @rect; + default { + $node.reference($_, :$gfx); + } } - multi sub find-parents(PDF::Tags::Elem $_, $xobj) { - my PDF::Tags::Elem @parents; - if .kids.first({ - $_ ~~ PDF::Tags::Mark && .Stm === $xobj - }) { - @parents.push: $_; - } - else { - @parents.append: find-parents($_, $xobj) - for .kids; - } + $parent!bbox($gfx, @rect); + @rect; +} - @parents; +multi sub find-parents(PDF::Tags::Elem $_, $xobj) { + my PDF::Tags::Elem @parents; + if .kids.first({ + $_ ~~ PDF::Tags::Mark && .Stm === $xobj + }) { + @parents.push: $_; } - multi sub find-parents($, $) is default { [] } - - # xobject form has marked content but no /StructParent(s) entries. Allow - # this as shortcut. Automatically wrap with elements and create a ParentTree entry - method !setup-parents(PDF::XObject::Form $xobj) { - my @parents = find-parents(self, $xobj); - if @parents { - my UInt $idx := $.root.parent-tree.max-key + 1; - $.root.parent-tree[$idx] = [ @parents».cos ]; - $xobj.StructParents = $idx; - } - else { - Nil; - } + else { + @parents.append: find-parents($_, $xobj) + for .kids; } - method set-attribute(Str() $key, Any:D $val) { - #Raku 2022.06+ my :($owner, $att) := att-owner($key); - my ($owner, $att) = att-owner($key); - fail "unable to determine owner for attribute: $key" - unless $owner; - $.cos.vivify-attributes(:$owner).set-attribute($att, $val); - self.attributes{$key} = $val; - callsame(); + @parents; +} +multi sub find-parents($, $) is default { [] } + +# xobject form has marked content but no /StructParent(s) entries. Allow +# this as shortcut. Automatically wrap with elements and create a ParentTree entry +method !setup-parents(PDF::XObject::Form $xobj) { + my @parents = find-parents(self, $xobj); + if @parents { + my UInt $idx := $.root.parent-tree.max-key + 1; + $.root.parent-tree[$idx] = [ @parents».cos ]; + $xobj.StructParents = $idx; } - method set-attributes(*%attributes) { - 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; - } - for %atts-by-owner.keys.sort -> $owner { - my $atts = $.cos.vivify-attributes(:$owner); - $atts.set-attribute(.key, .value) - for %atts-by-owner{$owner}.pairs; - } - $!attributes = Nil; # regen on next access + else { + Nil; } +} - method !bbox($gfx, @rect) { - self.set-bbox($gfx, @rect) - if self.name ~~ 'Figure'|'Form'|'Table'|'Formula'; +method set-attribute(Str() $key, Any:D $val) { + #Raku 2022.06+ my :($owner, $att) := att-owner($key); + my ($owner, $att) = att-owner($key); + fail "unable to determine owner for attribute: $key" + unless $owner; + $.cos.vivify-attributes(:$owner).set-attribute($att, $val); + self.attributes{$key} = $val; + callsame(); +} +method set-attributes(*%attributes) { + 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; } - - method set-bbox(PDF::Content $gfx, @rect) { - self.set-attribute('BBox', $gfx.base-coords(@rect).Array); + for %atts-by-owner.keys.sort -> $owner { + my $atts = $.cos.vivify-attributes(:$owner); + $atts.set-attribute(.key, .value) + for %atts-by-owner{$owner}.pairs; } + $!attributes = Nil; # regen on next access +} - multi method reference(PDF::Content $gfx, PDF::Class::StructItem $Obj) is DEPRECATED('reference($Obj, :$gfx)') { - $.reference($Obj, :$gfx); - } - multi method reference(PDF::Class::StructItem $Obj, PDF::Content:D :$gfx! ) { - my PDF::Page $Pg; - given $gfx.canvas { - when PDF::Page { $Pg = $_ } - } - $.reference($Obj, :$Pg); +method !bbox($gfx, @rect) { + self.set-bbox($gfx, @rect) + if self.name ~~ 'Figure'|'Form'|'Table'|'Formula'; +} + +method set-bbox(PDF::Content $gfx, @rect) { + self.set-attribute('BBox', $gfx.base-coords(@rect).Array); +} + +multi method reference(PDF::Content $gfx, PDF::Class::StructItem $Obj) is DEPRECATED('reference($Obj, :$gfx)') { + $.reference($Obj, :$gfx); +} +multi method reference(PDF::Class::StructItem $Obj, PDF::Content:D :$gfx! ) { + my PDF::Page $Pg; + given $gfx.canvas { + when PDF::Page { $Pg = $_ } } - multi method reference(PDF::Class::StructItem $Obj, PDF::Page :$Pg!) { - my PDF::OBJR() $cos = %( - :Type( :name ), - :$Obj, - ($Pg ?? :$Pg !! ()), - ); - - self.add-kid: :$cos; - - $.root.protect: { - without $Obj.StructParent { - $_ = $.root.parent-tree.max-key + 1; - $.root.parent-tree[$_ + 0] = self.cos; - } + $.reference($Obj, :$Pg); +} +multi method reference(PDF::Class::StructItem $Obj, PDF::Page :$Pg!) { + my PDF::OBJR() $cos = %( + :Type( :name ), + :$Obj, + ($Pg ?? :$Pg !! ()), + ); + + self.add-kid: :$cos; + + $.root.protect: { + without $Obj.StructParent { + $_ = $.root.parent-tree.max-key + 1; + $.root.parent-tree[$_ + 0] = self.cos; } - self; } + self; +} - method style { - callsame() //= do { - my $s = $.root.styler.tag-style($!name, |$.attributes); - $s.inherit($_) - with self.parent.?style; - $s; - } +method style { + callsame() //= do { + my $s = $.root.styler.tag-style($!name, |$.attributes); + $s.inherit($_) + with self.parent.?style; + $s; } } diff --git a/lib/PDF/Tags/Mark.rakumod b/lib/PDF/Tags/Mark.rakumod index 967216c..a9e0888 100644 --- a/lib/PDF/Tags/Mark.rakumod +++ b/lib/PDF/Tags/Mark.rakumod @@ -1,120 +1,119 @@ -use PDF::Tags::Node::Parent; - #| Marked content reference -class PDF::Tags::Mark - is PDF::Tags::Node::Parent { - - use PDF::COS; - use PDF::COS::TextString; - use PDF::Content::Tag; - use PDF::Content::Canvas; - # PDF::Class - use PDF::Page; - use PDF::MCR; - use PDF::XObject::Form; - - has PDF::Tags::Node::Parent $.parent is rw; - has %!attributes; - has Bool $!atts-built; - has Str $.actual-text is rw; - has PDF::Content::Canvas $.Stm; - has PDF::Content::Tag $.value is built handles; - - sub mcr(Int:D $MCID, :$Stm, :$Pg) { - my PDF::MCR() $cos = %( - :Type( :name ), - :$MCID, - ); - $cos = $_ with $Stm; - $cos = $_ with $Pg; - $cos; - } +unit class PDF::Tags::Mark; + +use PDF::Tags::Node::Parent; +also is PDF::Tags::Node::Parent; + +use PDF::COS; +use PDF::COS::TextString; +use PDF::Content::Tag; +use PDF::Content::Canvas; +# PDF::Class +use PDF::Page; +use PDF::MCR; +use PDF::XObject::Form; + +has PDF::Tags::Node::Parent $.parent is rw; +has %!attributes; +has Bool $!atts-built; +has Str $.actual-text is rw; +has PDF::Content::Canvas $.Stm; +has PDF::Content::Tag $.value is built handles; + +sub mcr(Int:D $MCID, :$Stm, :$Pg) { + my PDF::MCR() $cos = %( + :Type( :name ), + :$MCID, + ); + $cos = $_ with $Stm; + $cos = $_ with $Pg; + $cos; +} - method set-cos($!value) { - $!atts-built = False; - my $cos; - with $.mcid -> UInt $MCID { - $cos = $MCID; - my $referrer; - given $!value.canvas { - when PDF::XObject::Form { - $!Stm = $_; - $cos = mcr($MCID, :$!Stm, :$.Pg); +method set-cos($!value) { + $!atts-built = False; + my $cos; + with $.mcid -> UInt $MCID { + $cos = $MCID; + my $referrer; + given $!value.canvas { + when PDF::XObject::Form { + $!Stm = $_; + $cos = mcr($MCID, :$!Stm, :$.Pg); + } + when PDF::Page { + my $Pg = $_; + with self.parent.Pg { + $cos = mcr($MCID, :$Pg) + unless $_ === $Pg; } - when PDF::Page { - my $Pg = $_; - with self.parent.Pg { - $cos = mcr($MCID, :$Pg) - unless $_ === $Pg; - } - else { - $_ = $Pg; - } + else { + $_ = $Pg; } - # unlikely - default { warn "can mark object of type {.WHAT.raku}"; } } + # unlikely + default { warn "can mark object of type {.WHAT.raku}"; } } - callwith($cos); } + callwith($cos); +} - multi submethod TWEAK(PDF::Content::Tag:D :cos($_)!, Str :$!actual-text) { - self.set-cos($_); - } - multi submethod TWEAK(UInt:D :cos($mcid)!) { - with self.Stm // self.Pg -> PDF::Content::Canvas $_ { - with self.root.canvas-tags($_){$mcid} { - self.set-cos($_); - } - else { - die "unable to resolve MCID: $mcid"; - } +multi submethod TWEAK(PDF::Content::Tag:D :cos($_)!, Str :$!actual-text) { + self.set-cos($_); +} +multi submethod TWEAK(UInt:D :cos($mcid)!) { + with self.Stm // self.Pg -> PDF::Content::Canvas $_ { + with self.root.canvas-tags($_){$mcid} { + self.set-cos($_); } else { - die "no current marked-content page"; + die "unable to resolve MCID: $mcid"; } } - method attributes { - $!atts-built ||= do { - %!attributes = $!value.attributes; - True; - } - %!attributes; + else { + die "no current marked-content page"; } - method set-attribute(Str() $key, $val) { - fail "todo: update marked content attributes"; - callsame(); +} +method attributes { + $!atts-built ||= do { + %!attributes = $!value.attributes; + True; } + %!attributes; +} +method set-attribute(Str() $key, $val) { + fail "todo: update marked content attributes"; + callsame(); +} - sub sanitize(Str $_) { - # actual text sometimes have backspaces, etc? - .subst( - /<[ \x0..\x8 ]>/, - '', - :g - ); - } +sub sanitize(Str $_) { + # actual text sometimes have backspaces, etc? + .subst( + /<[ \x0..\x8 ]>/, + '', + :g + ); +} - method ActualText { - $.attributes unless $!atts-built; - $!actual-text //= sanitize PDF::COS::TextString.COERCE: $_ - with %!attributes; - $!actual-text; - } - method remove-actual-text is DEPRECATED { - with $.ActualText { - $!actual-text = Nil; - $!value.attributes:delete; - %!attributes:delete; - $_; - } - } - method text { $.ActualText // $.kids».text.join } - method AT-POS(UInt $i) { - fail "index out of range 0 .. $.elems: $i" unless 0 <= $i < $.elems; - self.kids-raw[$i] //= self.build-kid($!value.kids[$i]); +method ActualText { + $.attributes unless $!atts-built; + $!actual-text //= sanitize PDF::COS::TextString.COERCE: $_ + with %!attributes; + $!actual-text; +} +method remove-actual-text is DEPRECATED { + with $.ActualText { + $!actual-text = Nil; + $!value.attributes:delete; + %!attributes:delete; + $_; } } +method text { $.ActualText // $.kids».text.join } +method AT-POS(UInt $i) { + fail "index out of range 0 .. $.elems: $i" unless 0 <= $i < $.elems; + self.kids-raw[$i] //= self.build-kid($!value.kids[$i]); +} =begin pod diff --git a/lib/PDF/Tags/Node.rakumod b/lib/PDF/Tags/Node.rakumod index ac6b06b..e43df8c 100644 --- a/lib/PDF/Tags/Node.rakumod +++ b/lib/PDF/Tags/Node.rakumod @@ -1,70 +1,69 @@ #| Abstract Node -class PDF::Tags::Node { - - use PDF::COS; - use PDF::OBJR; # object reference - use PDF::MCR; # marked content reference - use PDF::Page; - use PDF::StructTreeRoot; - use PDF::StructElem; - use PDF::Content::Tag; - use PDF::Content::Canvas; - use PDF::Tags::Node::Root; - use Method::Also; - - my subset TagName of Str is export(:TagName) - where Str:U | /^$/; - - has PDF::Tags::Node::Root $.root is required handles; - has PDF::Page $.Pg; # current page scope - has $.cos is required; - method set-cos($!cos) { } - method Pg is rw { - Proxy.new( - FETCH => {$!Pg}, - STORE => -> $, $!Pg { - $!cos.Pg = $!Pg if $!cos ~~ PDF::OBJR|PDF::MCR|PDF::StructElem; - } - ); - } - - proto sub node-class($) is export(:node-class) {*} - multi sub node-class(PDF::StructTreeRoot) { require ::('PDF::Tags') } - multi sub node-class(PDF::StructElem) { require ::('PDF::Tags::Elem') } - multi sub node-class(PDF::OBJR) { require ::('PDF::Tags::ObjRef') } - multi sub node-class(PDF::MCR) { require ::('PDF::Tags::Mark') } - multi sub node-class(UInt) { require ::('PDF::Tags::Mark') } - multi sub node-class(PDF::Content::Tag) { require ::('PDF::Tags::Mark') } - multi sub node-class(Str) { require ::('PDF::Tags::Text') } - multi sub node-class(Pair) { require ::('PDF::Tags::Attr') } - - proto sub build-node($, |c --> PDF::Tags::Node) is export(:build-node) {*} - multi sub build-node(PDF::MCR $ref, PDF::Page :$Pg is copy, |c) { - my PDF::Content::Canvas $Stm = $_ with $ref.Stm; - my UInt:D $cos = $ref.MCID; - $Pg = $_ with $ref.Pg; - node-class($ref).new(:$cos, :$Pg, :$Stm, |c); - } - multi sub build-node(PDF::OBJR $cos, PDF::Page:D :$Pg = $cos.Pg, |c) { - node-class($cos).new( :$cos, :$Pg, |c) - } - multi sub build-node($cos, |c) { - node-class($cos).new: :$cos, |c; - } - - method xml(|c) is also { (require ::('PDF::Tags::XML-Writer')).new(|c).Str(self) ~ "\n" } - method text { '' } - - method xpath-context(PDF::Tags::Node:D $node:) handles { - (require ::('PDF::Tags::XPath')).new: :$node; - } - - multi method ACCEPTS(PDF::Tags::Node:D: Str $xpath) { - ? self.find($xpath); - } - multi method ACCEPTS(PDF::Tags::Node:D: Code $xpath) { - ? self.find($xpath); - } +unit class PDF::Tags::Node; + +use PDF::COS; +use PDF::OBJR; # object reference +use PDF::MCR; # marked content reference +use PDF::Page; +use PDF::StructTreeRoot; +use PDF::StructElem; +use PDF::Content::Tag; +use PDF::Content::Canvas; +use PDF::Tags::Node::Root; +use Method::Also; + +my subset TagName of Str is export(:TagName) + where Str:U | /^$/; + +has PDF::Tags::Node::Root $.root is required handles; +has PDF::Page $.Pg; # current page scope +has $.cos is required; +method set-cos($!cos) { } +method Pg is rw { + Proxy.new( + FETCH => {$!Pg}, + STORE => -> $, $!Pg { + $!cos.Pg = $!Pg if $!cos ~~ PDF::OBJR|PDF::MCR|PDF::StructElem; + } + ); +} + +proto sub node-class($) is export(:node-class) {*} +multi sub node-class(PDF::StructTreeRoot) { require ::('PDF::Tags') } +multi sub node-class(PDF::StructElem) { require ::('PDF::Tags::Elem') } +multi sub node-class(PDF::OBJR) { require ::('PDF::Tags::ObjRef') } +multi sub node-class(PDF::MCR) { require ::('PDF::Tags::Mark') } +multi sub node-class(UInt) { require ::('PDF::Tags::Mark') } +multi sub node-class(PDF::Content::Tag) { require ::('PDF::Tags::Mark') } +multi sub node-class(Str) { require ::('PDF::Tags::Text') } +multi sub node-class(Pair) { require ::('PDF::Tags::Attr') } + +proto sub build-node($, |c --> PDF::Tags::Node) is export(:build-node) {*} +multi sub build-node(PDF::MCR $ref, PDF::Page :$Pg is copy, |c) { + my PDF::Content::Canvas $Stm = $_ with $ref.Stm; + my UInt:D $cos = $ref.MCID; + $Pg = $_ with $ref.Pg; + node-class($ref).new(:$cos, :$Pg, :$Stm, |c); +} +multi sub build-node(PDF::OBJR $cos, PDF::Page:D :$Pg = $cos.Pg, |c) { + node-class($cos).new( :$cos, :$Pg, |c) +} +multi sub build-node($cos, |c) { + node-class($cos).new: :$cos, |c; +} + +method xml(|c) is also { (require ::('PDF::Tags::XML-Writer')).new(|c).Str(self) ~ "\n" } +method text { '' } + +method xpath-context(PDF::Tags::Node:D $node:) handles { + (require ::('PDF::Tags::XPath')).new: :$node; +} + +multi method ACCEPTS(PDF::Tags::Node:D: Str $xpath) { + ? self.find($xpath); +} +multi method ACCEPTS(PDF::Tags::Node:D: Code $xpath) { + ? self.find($xpath); } =begin pod diff --git a/lib/PDF/Tags/Node/Parent.rakumod b/lib/PDF/Tags/Node/Parent.rakumod index 41b36b1..77f85bb 100644 --- a/lib/PDF/Tags/Node/Parent.rakumod +++ b/lib/PDF/Tags/Node/Parent.rakumod @@ -1,189 +1,188 @@ -use PDF::Tags::Node :&node-class, :&build-node, :TagName; - #| Abstract non-leaf node -class PDF::Tags::Node::Parent - is PDF::Tags::Node { - - use Method::Also; - use PDF::COS; - use PDF::StructElem; - use PDF::Content::Tag :TagSet, :%TagAliases; - - has PDF::Tags::Node @.kids; - method kids-raw { @!kids } - has Hash $!store; - has Bool $!reified; - has UInt $!elems; - has $.style is rw; # Computed CSS style - - method elems(::?CLASS:D:) is also { - $!elems //= do with $.cos.kids { - when Hash { 1 } - default { .elems } - } // 0; - } +unit class PDF::Tags::Node::Parent; + +use PDF::Tags::Node :&node-class, :&build-node, :TagName; +also is PDF::Tags::Node; + +use Method::Also; +use PDF::COS; +use PDF::StructElem; +use PDF::Content::Tag :TagSet, :%TagAliases; + +has PDF::Tags::Node @.kids; +method kids-raw { @!kids } +has Hash $!store; +has Bool $!reified; +has UInt $!elems; +has $.style is rw; # Computed CSS style + +method elems(::?CLASS:D:) is also { + $!elems //= do with $.cos.kids { + when Hash { 1 } + default { .elems } + } // 0; +} - method node-path { - my $name = self.name; - my $xpath = $name; - - with self.parent { - $xpath = .node-path ~ '/' ~ $xpath - unless .name eq '#root'; - unless .kids == 1 { - # add [n] index - my Int $nth; - for .kids { - if .name eq $name { - $nth++; - if $_ === self { - $xpath ~= '[' ~ $nth ~ ']'; - last; - } +method node-path { + my $name = self.name; + my $xpath = $name; + + with self.parent { + $xpath = .node-path ~ '/' ~ $xpath + unless .name eq '#root'; + unless .kids == 1 { + # add [n] index + my Int $nth; + for .kids { + if .name eq $name { + $nth++; + if $_ === self { + $xpath ~= '[' ~ $nth ~ ']'; + last; } } } } - $xpath; } + $xpath; +} - method build-kid(::?CLASS:D: $cos, :$parent=self, :$Pg = $.Pg, |c) { - build-node($cos, :$parent, :$Pg, :$.root, |c); - } +method build-kid(::?CLASS:D: $cos, :$parent=self, :$Pg = $.Pg, |c) { + build-node($cos, :$parent, :$Pg, :$.root, |c); +} - method !adopt-node($node) { - # checks - die "unable to add fragment" - if $node.name eq 'DocumentFragment'; - die "unable to add a node to itself" - if $node === self || $node.cos === self.cos; +method !adopt-node($node) { + # checks + die "unable to add fragment" + if $node.name eq 'DocumentFragment'; + die "unable to add a node to itself" + if $node === self || $node.cos === self.cos; - with $node.parent { - die "node already parented" - unless $_ === self || .name eq 'DocumentFragment'; - } + with $node.parent { + die "node already parented" + unless $_ === self || .name eq 'DocumentFragment'; + } - # re-parent cos node - given self.cos.kids //= [] { - $_ = [$_] unless $_ ~~ List; - $node.cos.P = self.cos - if $node.cos ~~ PDF::StructElem; - .push($node.cos); - } + # re-parent cos node + given self.cos.kids //= [] { + $_ = [$_] unless $_ ~~ List; + $node.cos.P = self.cos + if $node.cos ~~ PDF::StructElem; + .push($node.cos); + } - # re-parent dom node - $node.parent = self; - @!kids.push: $node; + # re-parent dom node + $node.parent = self; + @!kids.push: $node; - # update caches - $!elems = Nil; - self.AT-POS($.elems-1) if $!reified; - .{$node.name}.push: $node with $!store; + # update caches + $!elems = Nil; + self.AT-POS($.elems-1) if $!reified; + .{$node.name}.push: $node with $!store; - $node; - } + $node; +} - multi method add-kid(PDF::Tags::Node:D :$node! --> PDF::Tags::Node:D) { - self!adopt-node($node); - } - multi method add-kid(Str:D :$name!, *%o --> PDF::Tags::Node:D) { - my $P := self.cos; - my PDF::StructElem() $cos = %( - :Type( :name ), - :S( :$name ), - :$P, - ); - self.add-kid(:$cos, |%o) - } - multi method add-kid(:$cos!, *%o --> PDF::Tags::Node:D) { - my PDF::Tags::Node $kid := self.build-kid($cos, |%o); - self!adopt-node($kid); - } +multi method add-kid(PDF::Tags::Node:D :$node! --> PDF::Tags::Node:D) { + self!adopt-node($node); +} +multi method add-kid(Str:D :$name!, *%o --> PDF::Tags::Node:D) { + my $P := self.cos; + my PDF::StructElem() $cos = %( + :Type( :name ), + :S( :$name ), + :$P, + ); + self.add-kid(:$cos, |%o) +} +multi method add-kid(:$cos!, *%o --> PDF::Tags::Node:D) { + my PDF::Tags::Node $kid := self.build-kid($cos, |%o); + self!adopt-node($kid); +} - multi method fragment(Str:D :$name!, *%o --> PDF::Tags::Node:D) { - my PDF::StructElem() $cos = %( - :Type( :name ), - :S( :$name ), - :P(Any), # tba - ); - my $parent = self.WHAT; - self.build-kid($cos, :$parent, |%o); - } - 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) - } - multi method FALLBACK(Str:D $_ where (%TagAliases{$_}:exists), |c) { - my Str:D $name = %TagAliases{$_}; - self.add-kid(:$name, |c) - } - multi method FALLBACK(Str:D $name where ($.role-map{$_}:exists), |c) { - self.add-kid(:$name, |c) - } - multi method FALLBACK($name, $?) { - die "Unknown tag $name"; - } - method AT-POS(UInt $i) { - fail "index out of range 0 .. $.elems: $i" unless 0 <= $i < $.elems; - @!kids[$i] //= self.build-kid($.cos.kids[$i]); - } - method Array { - $!reified ||= do { - self.AT-POS($_) for ^$.elems; - True; - } - @!kids; +multi method fragment(Str:D :$name!, *%o --> PDF::Tags::Node:D) { + my PDF::StructElem() $cos = %( + :Type( :name ), + :S( :$name ), + :P(Any), # tba + ); + my $parent = self.WHAT; + self.build-kid($cos, :$parent, |%o); +} +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) +} +multi method FALLBACK(Str:D $_ where (%TagAliases{$_}:exists), |c) { + my Str:D $name = %TagAliases{$_}; + self.add-kid(:$name, |c) +} +multi method FALLBACK(Str:D $name where ($.role-map{$_}:exists), |c) { + self.add-kid(:$name, |c) +} +multi method FALLBACK($name, $?) { + die "Unknown tag $name"; +} +method AT-POS(UInt $i) { + fail "index out of range 0 .. $.elems: $i" unless 0 <= $i < $.elems; + @!kids[$i] //= self.build-kid($.cos.kids[$i]); +} +method Array { + $!reified ||= do { + self.AT-POS($_) for ^$.elems; + True; } - method Hash handles { - $!store //= do { - my %h; - %h{.name}.push: $_ for self.Array; - if self.can('attributes') { - %h{'@' ~ .key} = .value - for self.attributes.pairs; - } - %h; + @!kids; +} +method Hash handles { + $!store //= do { + my %h; + %h{.name}.push: $_ for self.Array; + if self.can('attributes') { + %h{'@' ~ .key} = .value + for self.attributes.pairs; } + %h; } - method set-attribute(Str $key, $val) { - fail "attributes not applicable to objects of type {self.WHAT.raku}" - unless self.can('attributes'); - .{$key} = $val with $!store; - $val; - } - multi method AT-KEY(TagName:D $name) { - # tag name - @(self.Hash{$name} // []); - } - multi method AT-KEY(Str:D $xpath) is default { - $.xpath-context.AT-KEY($xpath); - } +} +method set-attribute(Str $key, $val) { + fail "attributes not applicable to objects of type {self.WHAT.raku}" + unless self.can('attributes'); + .{$key} = $val with $!store; + $val; +} +multi method AT-KEY(TagName:D $name) { + # tag name + @(self.Hash{$name} // []); +} +multi method AT-KEY(Str:D $xpath) is default { + $.xpath-context.AT-KEY($xpath); +} - method kids { - my class Kids does Iterable does Positional { - has PDF::Tags::Node $.node is required handles; - method Array handles { $!node.Array } - method iterator { - class Iteration does Iterator { - has UInt $!idx = 0; - has PDF::Tags::Node::Parent $.node is required; - method pull-one { - if $!idx < $!node.elems { - $!node.AT-POS($!idx++); - } - else { - IterationEnd; - } +method kids { + my class Kids does Iterable does Positional { + has PDF::Tags::Node $.node is required handles; + method Array handles { $!node.Array } + method iterator { + class Iteration does Iterator { + has UInt $!idx = 0; + has PDF::Tags::Node::Parent $.node is required; + method pull-one { + if $!idx < $!node.elems { + $!node.AT-POS($!idx++); + } + else { + IterationEnd; } } - Iteration.new: :$.node; } + Iteration.new: :$.node; } - Kids.new: :node(self); } - + Kids.new: :node(self); } constant ListAtts = set ; diff --git a/lib/PDF/Tags/ObjRef.rakumod b/lib/PDF/Tags/ObjRef.rakumod index 0cb20e8..132c18c 100644 --- a/lib/PDF/Tags/ObjRef.rakumod +++ b/lib/PDF/Tags/ObjRef.rakumod @@ -1,28 +1,28 @@ +#| Tagged object reference +unit class PDF::Tags::ObjRef; + use PDF::Tags::Node :&build-node; +also is PDF::Tags::Node; -#| Tagged object reference -class PDF::Tags::ObjRef - is PDF::Tags::Node { - use PDF::OBJR; - use PDF::StructElem; - use PDF::Tags::Node::Parent; - use PDF::Class::StructItem; - - submethod TWEAK { - self.Pg = $_ with self.cos.Pg; - } - has PDF::Tags::Node::Parent $.parent is rw; - has PDF::Tags::Node::Parent $!struct-parent; - method struct-parent { - $!struct-parent //= do with $.cos.object.struct-parent { - build-node($.root.parent-tree[$_+0], :$.Pg, :parent($.root)); - } - } - method cos(--> PDF::OBJR) { callsame() } +use PDF::OBJR; +use PDF::StructElem; +use PDF::Tags::Node::Parent; +use PDF::Class::StructItem; - method name { '#ref' } - method value(--> PDF::Class::StructItem) { $.cos.object } +submethod TWEAK { + self.Pg = $_ with self.cos.Pg; +} +has PDF::Tags::Node::Parent $.parent is rw; +has PDF::Tags::Node::Parent $!struct-parent; +method struct-parent { + $!struct-parent //= do with $.cos.object.struct-parent { + build-node($.root.parent-tree[$_+0], :$.Pg, :parent($.root)); + } } +method cos(--> PDF::OBJR) { callsame() } + +method name { '#ref' } +method value(--> PDF::Class::StructItem) { $.cos.object } =begin pod diff --git a/lib/PDF/Tags/Text.rakumod b/lib/PDF/Tags/Text.rakumod index f63a7f7..8683350 100644 --- a/lib/PDF/Tags/Text.rakumod +++ b/lib/PDF/Tags/Text.rakumod @@ -1,26 +1,28 @@ -use PDF::Tags::Node; #| Derived Text node -class PDF::Tags::Text - is PDF::Tags::Node { - use PDF::Tags::Node::Parent; - use Method::Also; - - =head2 Description - =para Objects of this class hold derived text. - =head2 Attributes and Methods +unit class PDF::Tags::Text; - submethod TWEAK(Str:D :$cos!) { - self.set-cos($cos); - } +use PDF::Tags::Node; +also is PDF::Tags::Node; - #| The parent node - has PDF::Tags::Node::Parent $.parent is rw; - =para of type PDF::Tags::Elem, or PDF::Tags::Mark +use PDF::Tags::Node::Parent; +use Method::Also; - #| Node name (always '#text') - method name { '#text' } +=head2 Description +=para Objects of this class hold derived text. +=head2 Attributes and Methods - #| Text content - method Str(--> Str) is also { $.cos() } +submethod TWEAK(Str:D :$cos!) { + self.set-cos($cos); } +#| The parent node +has PDF::Tags::Node::Parent $.parent is rw; +=para of type PDF::Tags::Elem, or PDF::Tags::Mark + +#| Node name (always '#text') +method name { '#text' } + +#| Text content +method Str(--> Str) is also { $.cos() } + + diff --git a/lib/PDF/Tags/XPath.rakumod b/lib/PDF/Tags/XPath.rakumod index 7e70d18..c192af1 100644 --- a/lib/PDF/Tags/XPath.rakumod +++ b/lib/PDF/Tags/XPath.rakumod @@ -1,43 +1,41 @@ #| Tiny XPath like search/navigation evaluator -class PDF::Tags::XPath { +unit class PDF::Tags::XPath; - use PDF::Tags; - use PDF::Tags::Node; - use PDF::Tags::XPath::Grammar; - use PDF::Tags::XPath::Actions; - use Method::Also; - - has PDF::Tags::Node $.node; - - method compile(Str:D $expr --> Code) { - my PDF::Tags::XPath::Actions $actions .= new; - fail "can't handle xpath: $expr" - unless PDF::Tags::XPath::Grammar.parse($expr, :$actions); - $/.ast; - } - - multi method find(Any $expr, PDF::Tags:D $dom) { - $!node = $dom.root; - self.find($expr); - } - - multi method find(Any $expr, PDF::Tags::Node:D $!node) { - self.find($expr); - } - - multi method find(Str:D $xpath) is also { - my PDF::Tags::XPath::Actions::Expression $expr := self.compile($xpath); - self.find($expr); - } - - multi method find(&expr --> Seq) { - &expr($!node).Seq; - } - - method first($expr is raw) { - self.find($expr)[0] // PDF::Tags::Node; - } +use PDF::Tags; +use PDF::Tags::Node; +use PDF::Tags::XPath::Grammar; +use PDF::Tags::XPath::Actions; +use Method::Also; + +has PDF::Tags::Node $.node; + +method compile(Str:D $expr --> Code) { + my PDF::Tags::XPath::Actions $actions .= new; + fail "can't handle xpath: $expr" + unless PDF::Tags::XPath::Grammar.parse($expr, :$actions); + $/.ast; +} + +multi method find(Any $expr, PDF::Tags:D $dom) { + $!node = $dom.root; + self.find($expr); +} + +multi method find(Any $expr, PDF::Tags::Node:D $!node) { + self.find($expr); +} + +multi method find(Str:D $xpath) is also { + my PDF::Tags::XPath::Actions::Expression $expr := self.compile($xpath); + self.find($expr); +} + +multi method find(&expr --> Seq) { + &expr($!node).Seq; +} +method first($expr is raw) { + self.find($expr)[0] // PDF::Tags::Node; } =begin pod diff --git a/t/threaded.t b/t/threaded.t index 75299de..f30b478 100644 --- a/t/threaded.t +++ b/t/threaded.t @@ -24,10 +24,10 @@ my PDF::Content::FontObj $font = $pdf.core-font: :family; my PDF::Content::FontObj $hdr-font = $pdf.core-font: :family, :weight; my PDF::Tags $tags .= create: :$pdf; -my @page-frags = (1..10).map: { PDF::Content::PageTree.pages-fragment() } -my @struct-frags = (1..10).map: { $tags.fragment(Division) }; +my @page-frags = (1..30).map: { PDF::Content::PageTree.pages-fragment() } +my @struct-frags = (1..30).map: { $tags.fragment(Division) }; -(1..10).race(:batch(1)).map: -> $chap-num { +(1..30).race(:batch(1)).map: -> $chap-num { # create a multi-page fragment for later assembly my PDF::Content::PageTree $pages = @page-frags[$chap-num-1]; # also a chapter tag for later assembly