Skip Menu |

This queue is for tickets about the Mxpress-PDF CPAN distribution.

Report information
The Basics
Id: 131739
Status: resolved
Priority: 0/
Queue: Mxpress-PDF

People
Owner: Nobody in particular
Requestors: perl [...] toby.ink
Cc:
AdminCc:

Bug Information
Severity: Unimportant
Broken in: (no value)
Fixed in: (no value)



Subject: weird usage of parameterized class
This seemed pretty cool so I had a look and noticed you were using a parameterized class a little strangely. Like there shouldn't be any need to grab the spec and manually fiddle with it. I thought perhaps you were working around a limitation in MooX::Pression, so I tried reworking it to see if there was any way I could add a feature to make this kind of thing easier, but I can't find any such limitation. Attached is a patch that moves the processing of @plugins into the parameterized class itself. I've run the test suite and it appears to produce a visually similar PDF, but other than that I haven't verified it performs as expected because I'm not 100% sure on what the $p method is SUPPOSED to do. If I'm misunderstanding what you were doing with the File class generator, please do clarify. I've not used class generators much, so it would be interesting to explore more about how they could be used, and ensure MooX::Pression can cover these use cases without needing to hack the hashrefs being passed around.
Subject: mxpress-pdf.patch
diff -urN Mxpress-PDF-0.02/lib/Mxpress/PDF.pm Mxpress-PDF-0.02-hacked/lib/Mxpress/PDF.pm --- Mxpress-PDF-0.02/lib/Mxpress/PDF.pm 2020-02-11 15:27:59.000000000 +0000 +++ Mxpress-PDF-0.02-hacked/lib/Mxpress/PDF.pm 2020-02-11 17:53:13.707890888 +0000 @@ -5,19 +5,32 @@ package Mxpress::PDF { our $VERSION = '0.02'; use MooX::Pression ( - version => '0.02', + version => '0.02', authority => 'cpan:LNATION', ); use Colouring::In; use constant mm => 25.4 / 72; use constant pt => 1; - class File () { + class File (HashRef $args) { + my @plugins = (qw/font boxed text title subtitle subsubtitle toc/, ($args->{plugins} ? @{$args->{plugins}} : ())); + for my $p (@plugins) { + my $meth = sprintf('_store_%s', $p); + has {$meth} ( type => Object ); + method {$p} () { + my $klass = $self->$meth; + if (!$klass) { + $klass = $class->FACTORY->$p($self, %{$args->{$p}}); + $self->$meth($klass); + } + return $klass; + } + } has file_name (type => Str, required => 1); has pdf (required => 1, type => Object); has pages (required => 1, type => ArrayRef); has page (type => Object); has page_args (type => HashRef); - has onsave_cbs (type => ArrayRef); + has onsave_cbs (type => ArrayRef); method add_page (Map %args) { my $page = $self->FACTORY->page( $self->pdf, @@ -29,7 +42,7 @@ push @{$self->pages}, $page; $self->page($page); $self->boxed->add( fill_colour => $page->background ) if $page->background; - $self->page->set_position($page->parse_position([])); + $self->page->set_position($page->parse_position([])); $self; } method save { @@ -60,7 +73,7 @@ has w (type => Num); has h (type => Num); factory page (Object $pdf, Map %args) { - my $page = $args{open} ? $pdf->openpage($args{num}) : $pdf->page($args{num}); + my $page = $args{open} ? $pdf->openpage($args{num}) : $pdf->page($args{num}); $page->mediabox($args{page_size}); my ($blx, $bly, $trx, $try) = $page->get_mediabox; my $new_page = $class->new( @@ -252,7 +265,7 @@ while (@paragraph and ($line_width + (scalar(@line) * $space_width) + $width{$paragraph[0]}) < $lw) { $line_width += $width{$paragraph[0]}; push @line, shift(@paragraph); - } + } my ($wordspace, $align); if ($self->align eq 'fulljustify' or $self->align eq 'justify' and @paragraph) { if (scalar(@line) == 1) { @@ -286,7 +299,7 @@ my $pad_end = $self->pad_end; my $pad = sprintf ("%s%s", $self->pad x int((( - (((($lw + $wordspace) - $line_width) - $text->advancewidth($self->pad . $pad_end)) - ($self->padding/mm)) + (((($lw + $wordspace) - $line_width) - $text->advancewidth($self->pad . $pad_end)) - ($self->padding/mm)) ) / $text->advancewidth($self->pad))), $pad_end ); @@ -305,7 +318,7 @@ return $self->file; } method _set_indent (Num $xpos, Num $w, Num $fl, Num $fp) { - if ($fl && $self->first_line_indent) { + if ($fl && $self->first_line_indent) { $xpos += $self->first_line_indent/mm; $w -= $self->first_line_indent/mm; } elsif ($fp && $self->first_paragraph_indent) { @@ -376,7 +389,7 @@ y => $y, children => [], level => $args{level} || 0, - title => $args{title}, + title => $args{title}, file => $file, page => $file->page, outline => $new, @@ -439,7 +452,7 @@ $self->file->subtitle->add($args{title} ? @{$args{title}} : 'Table of contents'); $self->toc_placeholder({ page => $self->file->page, - position => [$self->parse_position($args{position} || [])] + position => [$self->parse_position($args{position} || [])] }); $self->file->onsave('toc', 'render', %args); $self->file->add_page; @@ -483,7 +496,7 @@ my $total_height = ($self->count * $one_toc_link) - $h; while ($total_height > 0) { $args{page_offset}++; - $self->file->add_page(num => $placeholder->{page}->num + $args{page_offset}); + $self->file->add_page(num => $placeholder->{page}->num + $args{page_offset}); $total_height -= $self->file->page->h; } $self->file->page($placeholder->{page}); @@ -494,36 +507,9 @@ } } class Factory { - use PDF::API2; factory new_pdf (Str $name, Map %args) { - my @plugins = (qw/font boxed text title subtitle subsubtitle toc/, ($args{plugins} ? @{$args{plugins}} : ())); - my $spec = Mxpress::PDF::File->_generate_package_spec(); - for my $p (@plugins) { - my $meth = sprintf('_store_%s', $p); - $spec->{has}->{$meth} = { type => Object }; - $spec->{can}->{$p} = { - code => sub { - my $class = $_[0]->$meth; - if (!$class) { - $class = $factory->$p($_[0], %{$args{$p}}); - $_[0]->$meth($class) - } - return $class; - } - }; - } - return MooX::Press->generate_package( - 'class', - "Mxpress::PDF::File", - { - factory_package => $factory, - caller => $class, - prefix => $factory, - toolkit => 'Moo', - type_library => 'Mxpress::PDF::Types', - }, - $spec - )->new( + require PDF::API2; + $factory->generate_file( \%args )->new( file_name => $name, pages => [], num => 0, Binary files Mxpress-PDF-0.02/test.pdf and Mxpress-PDF-0.02-hacked/test.pdf differ
Subject: Re: [rt.cpan.org #131739] weird usage of parameterized class
Date: Wed, 12 Feb 2020 07:36:12 +0700
To: bug-Mxpress-PDF [...] rt.cpan.org
From: "LNATION ." <thisusedtobeanemail [...] gmail.com>
Thanks! Will take a look in my evening but this looks to be my failure - has {$meth} ( type => Object ); On Wed, Feb 12, 2020 at 12:58 AM Toby Inkster via RT < bug-Mxpress-PDF@rt.cpan.org> wrote: Show quoted text
> Tue Feb 11 12:58:31 2020: Request 131739 was acted upon. > Transaction: Ticket created by TOBYINK > Queue: Mxpress-PDF > Subject: weird usage of parameterized class > Broken in: (no value) > Severity: Unimportant > Owner: Nobody > Requestors: perl@toby.ink > Status: new > Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=131739 > > > > This seemed pretty cool so I had a look and noticed you were using a > parameterized class a little strangely. > > Like there shouldn't be any need to grab the spec and manually fiddle with > it. I thought perhaps you were working around a limitation in > MooX::Pression, so I tried reworking it to see if there was any way I could > add a feature to make this kind of thing easier, but I can't find any such > limitation. > > Attached is a patch that moves the processing of @plugins into the > parameterized class itself. I've run the test suite and it appears to > produce a visually similar PDF, but other than that I haven't verified it > performs as expected because I'm not 100% sure on what the $p method is > SUPPOSED to do. > > If I'm misunderstanding what you were doing with the File class generator, > please do clarify. I've not used class generators much, so it would be > interesting to explore more about how they could be used, and ensure > MooX::Pression can cover these use cases without needing to hack the > hashrefs being passed around. >
Allowing something like: has $xyz ( type => Object ); Would seem to make sense, but in the past, I treated `has $xyz` as a shortcut for something like: # Declares an attribute called "xyz" which is not allowed # to be a hashref or arrayref. # has xyz ( type => ~(ArrayRef|HashRef) ); I dropped support for that in an early release though. Ultimately if I do ever support `has $xyz` as a syntax again, it'll probably mean that each method in the class gets an implicit: \(my $xyz) = \( $self->{xyz} ); So that within methods, you can just use the $xyz variable instead of having to call $self->xyz. But it's hard to make that work nicely with type constraints, coercions, triggers, etc, unless you use ties which will be very slow.
Subject: Re: [rt.cpan.org #131739] weird usage of parameterized class
Date: Thu, 13 Feb 2020 00:43:16 +0700
To: bug-Mxpress-PDF [...] rt.cpan.org
From: "LNATION ." <thisusedtobeanemail [...] gmail.com>
I missed your release with PACKAGE_SPEC but I'm okay with this way just missed this {} syntax. One thing with MooX::Pression the errors/warnings seem to have downgraded since your re-write, line numbers seem to be redundant . On Wed, Feb 12, 2020 at 6:17 PM Toby Inkster via RT < bug-Mxpress-PDF@rt.cpan.org> wrote: Show quoted text
> Queue: Mxpress-PDF > Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=131739 > > > Allowing something like: > > has $xyz ( type => Object ); > > Would seem to make sense, but in the past, I treated `has $xyz` as a > shortcut for something like: > > # Declares an attribute called "xyz" which is not allowed > # to be a hashref or arrayref. > # > has xyz ( type => ~(ArrayRef|HashRef) ); > > I dropped support for that in an early release though. Ultimately if I do > ever support `has $xyz` as a syntax again, it'll probably mean that each > method in the class gets an implicit: > > \(my $xyz) = \( $self->{xyz} ); > > So that within methods, you can just use the $xyz variable instead of > having to call $self->xyz. But it's hard to make that work nicely with type > constraints, coercions, triggers, etc, unless you use ties which will be > very slow. > > >
Honestly, I haven't paid much attention to line numbers in error messages at all yet. It is certainly something it would be nice to get right. The way Keyword::Simple works, you grab a bunch of code after the keyword, parse it to find out what you need to find out, remove it from Perl's compilation buffer, and inject your own code into the compilation buffer. I think it ought to be possible to check how many lines are in the code being removed, how many lines are in the code being inserted, and if they mismatch, insert some extra blank lines first. I might have a go at that soon. That won't be perfect; won't cover code which isn't physically in the file being parsed. But it's a start.
I've pushed an improvement in line number reports to GitHub. Some might still be off by one or two, but it's overall a lot closer to correct.
Subject: Re: [rt.cpan.org #131739] weird usage of parameterized class
Date: Thu, 13 Feb 2020 08:38:38 +0700
To: bug-Mxpress-PDF [...] rt.cpan.org
From: "LNATION ." <thisusedtobeanemail [...] gmail.com>
Awesome, thanks! On Thu, Feb 13, 2020 at 2:10 AM Toby Inkster via RT < bug-Mxpress-PDF@rt.cpan.org> wrote: Show quoted text
> Queue: Mxpress-PDF > Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=131739 > > > I've pushed an improvement in line number reports to GitHub. Some might > still be off by one or two, but it's overall a lot closer to correct. >
FYI, I've renamed MooX::Pression to Zydeco because I wanted something fun and memorable like Moops.