From 334fb7359aad7d88747adbc29dbed86b1013b69f Mon Sep 17 00:00:00 2001 From: wajasu Date: Tue, 5 Mar 2013 17:53:28 -0600 Subject: [PATCH] Bug 2499: add support for text wrapping using proportional font metrics Currently when labels are printed and the text would render past the bounding label definition, the number of chars that can be written per row is based off of a fixed size for a character in that font. This change performs text wrapping based on each character's character width metric contained in the specified font. Once truncation is detected, it scans back and breaks on a space, and continues with the next words on subsequent lines. It will allow more characters or words from a 'title' to be written per line when possible. Signed-off-by: wajasu --- C4/Labels/Label.pm | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/C4/Labels/Label.pm b/C4/Labels/Label.pm index 760bacf..851ae8f 100644 --- a/C4/Labels/Label.pm +++ b/C4/Labels/Label.pm @@ -3,7 +3,6 @@ package C4::Labels::Label; use strict; use warnings; -use Text::Wrap; use Algorithm::CheckDigits; use Text::CSV_XS; use Data::Dumper; @@ -381,6 +380,71 @@ sub create_label { return; } +sub wrap_fields_proportional { + my ($self, %params) = @_; + my $font = $params{'font'}; + my $font_size = $params{'font_size'}; + my $field_data = $params{'field_data'}; + my @lines; + + my $field_length = length $field_data; + my @fieldchar_map; + my @fieldwidth_map; + my @fieldcharwidthsum_map; + my $fieldcharwidthsum = 0.0; # adjusted for margins to align with draw_label_text + my $line_startpos = 0; + my $ignore_subsequent_lines = 0; + + # build lookup maps and use them to scan backward to break on space when truncation would be detected. + my $idx = 0; + for ( $idx=0; $idx <= $field_length; $idx++ ) { + my $fieldchar = substr $field_data, $idx, 1; + $fieldchar_map[$idx] = $fieldchar; + + my $fieldchar_width = C4::Creators::PDF->StrWidth($fieldchar, $font, $self->{'font_size'}); + $fieldcharwidthsum = $fieldcharwidthsum + $fieldchar_width; + $fieldcharwidthsum_map[$idx] = $fieldcharwidthsum; # accumulation of field widths for this field_data + + if ( $fieldcharwidthsum > ($self->{'width'} - $self->{'left_text_margin'}) ) { # truncation detect + + # search backward until space to break text on. + my $space_start_idx = $idx - 1; # start from current position - 1 + + SPACESCAN: for ( my $space_idx = $space_start_idx; $space_idx >= 0; $space_idx--) { # in reverse + + if ( $fieldchar_map[$space_idx] eq ' ' ) { + my $line_length = $space_idx - $line_startpos; + my $text_line = substr $field_data, $line_startpos, $line_length; + push @lines, $text_line; + + if ( ! $self->{'text_wrap_cols'} ) { $ignore_subsequent_lines = 1; } # so we don't write more + + $line_startpos = $space_idx + 1; + + # re-adjust fieldcharwidthsum to start from line_startpos thru current idx of master loop + $fieldcharwidthsum = 0.0; + for ( my $widthsum_idx = $line_startpos; $widthsum_idx <= $idx; $widthsum_idx++ ) { + my $fc = substr $field_data, $widthsum_idx, 1; + my $fcw = C4::Creators::PDF->StrWidth($fc, $font, $self->{'font_size'}); + $fieldcharwidthsum = $fieldcharwidthsum + $fcw; + } + + last SPACESCAN; + } + } + } + } + + # flush what didn't get flushed from truncation if we haven't written anything yet. + if ( $ignore_subsequent_lines == 0 ) { + my $line_length = $field_length - $line_startpos; + my $text_line = substr $field_data, $line_startpos, $line_length; + push @lines, $text_line; + } + + return \@lines; +} + sub draw_label_text { my ($self, %params) = @_; my @label_text = (); @@ -430,8 +494,13 @@ sub draw_label_text { $field_data =~ s/\(/\\\(/g; # Escape '(' and ')' for the pdf object stream... $field_data =~ s/\)/\\\)/g; } - eval{$Text::Wrap::columns = $self->{'text_wrap_cols'};}; - my @line = split(/\n/ ,wrap('', '', $field_data)); + + my @line = @{ wrap_fields_proportional( $self, + font => $font, + font_size => $self->{'font_size'}, + field_data => $field_data + ) }; + # If this is a title field, limit to two lines; all others limit to one... FIXME: this is rather arbitrary if ($field->{'code'} eq 'title' && scalar(@line) >= 2) { while (scalar(@line) > 2) { @@ -452,9 +521,9 @@ sub draw_label_text { $text_llx = $params{'llx'} + $self->{'width'} - ($self->{'left_text_margin'} + $string_width); } elsif($self->{'justify'} eq 'C') { - # some code to try and center each line on the label based on font size and string point width... - my $whitespace = ($self->{'width'} - ($string_width + (2 * $self->{'left_text_margin'}))); - $text_llx = (($whitespace / 2) + $params{'llx'} + $self->{'left_text_margin'}); + # some code to try and center each line on the label based on font size and string point width... + my $whitespace = ($self->{'width'} - ($string_width + (2 * $self->{'left_text_margin'}))); + $text_llx = (($whitespace / 2) + $params{'llx'} + $self->{'left_text_margin'}); } else { $text_llx = ($params{'llx'} + $self->{'left_text_margin'}); -- 1.8.1.5