This is an automated email from the git hooks/post-receive script. abe pushed a commit to branch master in repository librtf-document-perl.
commit 975b9339c9b378f91d50977e81a602e63fedee26 Author: Axel Beckert <a...@deuxchevaux.org> Date: Tue Jul 21 21:52:55 2015 +0200 Fix line encodings of Document.pm --- Document.pm | 2392 ++++++++++++++++---------------- debian/changelog | 1 + debian/patches/01_make_it_strict.patch | 438 +++--- 3 files changed, 1417 insertions(+), 1414 deletions(-) diff --git a/Document.pm b/Document.pm index a21b986..2912269 100644 --- a/Document.pm +++ b/Document.pm @@ -1,1196 +1,1196 @@ -package RTF::Document; -require 5.005; -require Exporter; - -use vars qw( - $VERSION - %DOCINFO %PROPERTIES - %FONTCLASSES %FONTPITCH %COLORNAMES %STYLETYPES %NUMSTYLES -); -$VERSION = "0.64"; - -@ISA = qw(Exporter); -@EXPORT = qw(); -@EXPORT_OK = qw(); - -use Carp; -use POSIX qw(floor ceil); -use Convert::Units::Type 0.33; - -%NUMSTYLES = ( - '1' => '\pndec', - 'a' => '\pnlcltr', - 'i' => '\pnlcrm', - 'A' => '\pnucltr', - 'I' => '\pnucrm', - '1st' => '\pnord' -); - -sub _prop_list -{ - my ($self, $code, $properties) = @_; - my ($result, $level, $style); - - if ($properties eq "off") { - return '\pard'; - } - - $result = $self->new_group( '\*', '\pn' ); - - ${$properties}{level} = ${$properties}{style}, if (${$properties}{style} eq "bullet"); - - if (${$properties}{level}) { - $level = ${$properties}{level}; - $level = "blt", if ($level eq "bullet"); - if ((($level<1) or ($level>11)) and ($level ne "blt")) - { - carp "List level \`$level\' is out of range"; - $level = 'body'; - } - } else { - $level = 'body'; - } - - $self->add_raw ($result, '\pnlvl'.$level); - - if ($level eq "body") { - $style = $NUMSTYLES{${$properties}{style}} || '\pndec'; - $self->add_raw ($result, $style); - } - - if (defined(${$properties}{font})) - { - $self->add_raw ($result, '\pnf'.${$properties}{font}); - } - - if (defined(${$properties}{color})) - { - $self->add_raw ($result, '\pncf'.${$properties}{color}); - } - - if (defined(${$properties}{before})) - { - my $group = $self->add_group($result); - $self->add_raw ($group, '\pntxtb '.escape_simple(${$properties}{before}) ); - } - - if (defined(${$properties}{after})) - { - my $group = $self->add_group($result); - $self->add_raw ($group, '\pntxta '.escape_simple(${$properties}{after}) ); - } - - if (${$properties}{across}) - { - $self->add_raw ($result, '\pnacross'); - } - - if (defined(${$properties}{indent})) - { - $self->add_raw ($result, '\pnindent'.POSIX::floor( - Convert::Units::Type::convert(${$properties}{indent}, "twips") - )); - } - - if (defined(${$properties}{space})) - { - $self->add_raw ($result, '\pnsp'.POSIX::floor( - Convert::Units::Type::convert(${$properties}{space}, "twips") - )); - } - - if (${$properties}{hang}) - { - $self->add_raw ($result, '\pnhang'); - } - - if (defined(${$properties}{start})) - { - $self->add_raw ($result, '\pnstart'.${$properties}{start}); - } - - - return ($result); -} - -# $arg is a key to RTF control in hash value -sub _prop_decode -{ - my ($self, $hash, $arg) = @_; - my $result = ${$hash}{$arg}; - - unless (defined($result)) { - carp "Don\'t know how to handle value \`$arg\'"; - } - - return ("\\".$result); -} - -sub _prop_style { - my ($self, $code, $arg) = @_; - $code = decode_stylename($arg, '\s222'); - my $formatting, $style_properties; - - if (defined($code)) { - $formatting = $self->new_group(); - %{$style_properties} = %{$self->{styles}->{$code}}; - - if (${$style_properties}{secd}) { - $self->add_raw($formatting, '\secd'); - delete ${$style_properties}{secd}; - } - if (${$style_properties}{pard}) { - $self->add_raw($formatting, '\pard'); - delete ${$style_properties}{pard}; - } - if (${$style_properties}{plain}) { - $self->add_raw($formatting, '\plain'); - delete ${$style_properties}{plain}; - } - - $self->set_properties( \%PROPERTIES, $style_properties, $formatting); - unless (@{$formatting}) { - carp "Style \`$arg\' is not defined"; - $code = decode_stylename("none"); - } - } - return ($code, @{$formatting} ); -} - -# $arg is a unit of type (points, picas, inches) converted to twips -sub _prop_twips { - my ($self, $code, $arg) = @_; - return ("\\".$code.POSIX::floor(Convert::Units::Type::convert($arg, "twips"))); -} - -# $arg is a unit of type (points, picas, inches) converted to half-points -sub _prop_halfpts { - my ($self, $code, $arg) = @_; - return ("\\".$code.POSIX::floor(Convert::Units::Type::convert($arg, "half-points"))); -} - -# $arg is a string (which may need to be escaped) -sub _prop_pcdata { - my ($self, $code, $arg) = @_; - $arg =~ s/([\\\{\}])/\\$1/g; - return ("\\".$code, escape_simple($arg)); -} - -# $arg is a raw value -sub _prop_raw { - my ($self, $code, $arg) = @_; - return ("\\".$code.$arg); -} - -# $arg is a an on/off indicator (0 = off, NZ = on) -sub _prop_onoff { - my ($self, $code, $arg) = @_; - if ($arg) - { - return ("\\".$code); - } - else - { - return ("\\".$code."0"); - } -} - -# $arg is a an emit/don't emit indicator (0 = don't emit control, NZ = emit) -sub _prop_on { - my ($self, $code, $arg) = @_; - if ($arg) - { - return ("\\".$code); - } - else - { - return undef; - } -} - -# Synopsis of %DOCINFO and %PROPERTIES -# property => [ where, control, group, function ] -# property = name of the property -# where = what section of the document this control is usually applied to -# control = the control word used (if a hash, how to decode various controls) -# group = if non-zero, emit this as part of a group -# function = what function to use to process this property -# Most properties follow the following naming scheme: -# doc = document-wide properties (should be set only once) -# sec = section properties -# col = column properties (within a section) -# par = paragraph properties - -%DOCINFO = ( - # --- Document summary information - 'doc_title' => [ 'info', 'title', 1, \&_prop_pcdata ], - 'doc_author' => [ 'info', 'author', 1, \&_prop_pcdata ], - 'doc_subject' => [ 'info', 'subject', 1, \&_prop_pcdata ], - 'doc_manager' => [ 'info', 'manager', 1, \&_prop_pcdata ], - 'doc_company' => [ 'info', 'company', 1, \&_prop_pcdata ], - 'doc_operator' => [ 'info', 'operator', 1, \&_prop_pcdata ], - 'doc_category' => [ 'info', 'category', 1, \&_prop_pcdata ], - 'doc_keywords' => [ 'info', 'keywords', 1, \&_prop_pcdata ], - 'doc_summary' => [ 'info', 'doccomm', 1, \&_prop_pcdata ], - 'doc_comment' => [ 'text', '*\comment', 1, \&_prop_pcdata ], - 'doc_base_href' => [ 'info', 'hlinkbase', 1, \&_prop_pcdata ], - 'doc_version' => [ 'info', 'version', 0, \&_prop_raw ], - 'doc_time_created' => [ 'creatim' ], - - 'doc_from_text' => [ 'text', 'fromtext', 0, \&_prop_on ], - 'doc_make_backup' => [ 'text', 'makebackup', 0, \&_prop_on ], - 'doc_rtf_def' => [ 'text', 'defformat', 0, \&_prop_on ], - - # --- Page sizes, margins, etc. - doc_page_width => [ text, paperw, 0, \&_prop_twips ], - doc_page_height => [ text, paperh, 0, \&_prop_twips ], - doc_landscape => [ text, landscape, 0, \&_prop_on ], - doc_facing => [ text, facingp, 0, \&_prop_on ], - doc_margin_left => [ text, margl, 0, \&_prop_twips ], - doc_margin_right => [ text, margr, 0, \&_prop_twips ], - doc_margin_top => [ text, margt, 0, \&_prop_twips ], - doc_margin_bottom => [ text, margb, 0, \&_prop_twips ], - doc_margin_mirror=> [ text, margmirror, 0, \&_prop_on ], - doc_gutter => [ text, gutter, 0, \&_prop_twips ], - - # --- Hyphenation - doc_hyphen_auto => [ 'text', 'hyphauto', 0, \&_prop_onoff ], - doc_hyphen_caps => [ 'text', 'hyphcaps', 0, \&_prop_onoff ], - doc_hyphen_lines => [ 'text', 'hyphconsec', 0, \&_prop_onoff ], - doc_hyphen_zone => [ 'text', 'hyphhotz', 0, \&_prop_twips ], - - # --- Views - doc_view_scale => [ text, viewscale, 0, \&_prop_raw ], - doc_view_zoom => [ text, { none=>'viewzk0', 'full-page'=>'viewzk1', - 'best-fit'=>'viewzk1' }, 0, \&_prop_decode ], - doc_view_caption => [ text, windowcaption, 1, , \&_prop_pcdata ], - 'doc_view_mode' => [ 'text', { 'none'=>'viewkind0', 'layout'=>'viewkind1', - 'outline'=>'viewkind2', 'master'=>'viewkind3', - - - - - 'normal'=>'viewkind4', 'online'=>'viewkind5'}, 0, \&_prop_decode ], - - # --- Character set - 'doc_charset' => [ 'charset' ], - - # --- Widow/orphan controls - doc_widow_cntrl => [ text, widowctrl, 0, \&_prop_on ], - - # --- Tabs - tabs_default => [ 'text', 'deftab', 0, \&_prop_twips ], - -); - -%PROPERTIES = ( - - # --- New section, paragraph, line - 'sec' => [ 'text', 'sect', 0, \&_prop_on ], - 'par' => [ 'text', 'par', 0, \&_prop_on ], - 'line' => [ 'text', 'line', 0, \&_prop_on ], - 'line_soft' => [ 'text', 'softline', 0, \&_prop_on ], - - # --- Sections.... - 'sec_brk' => [ 'text', { 'none'=>'sbknone', 'column'=>'sbkcol', - 'page'=>'sbkpage', 'even'=>'sbkeven', 'odd'=>'sbkodd'}, 0, \&_prop_decode ], - - # --- Columns - col => [ text, 'colulmn', 0, \&_prop_on ], - col_soft => [ text, 'softcol', 0, \&_prop_on ], - col_num => [ text, 'cols', 0, \&_prop_raw ], - col_space => [ text, 'colsx', 0, \&_prop_twips ], - col_select => [ text, 'colno', 0, \&_prop_raw ], - col_padding_right => [ text, 'colsr', 0, \&_prop_twips ], - col_width => [ text, 'colw', 0, \&_prop_twips ], - col_line => [ text, 'linebetcol', 0, \&_prop_on ], - - 'page_brk' => [ 'text', 'page', 0, \&_prop_on ], - 'page_softbrk' => [ 'text', 'softpage', 0, \&_prop_on ], - - # --- Forms.... - 'sec_unlock' => [ 'text', 'sectunlocked', 0, \&_prop_on ], - - # --- Footsnotes, endnotes stuff - 'sec_endnotes_here' => [ 'text', 'endnhere', 0, \&_prop_on ], - - # --- Alignment - 'par_align' => [ 'text', { left=>'ql', right=>'qr', center=>'qc', justify=>'qj' }, 0, \&_prop_decode ], - 'sec_vert_align' => [ 'text', { top=>vertalt, bottom=>vertalb, center=>vertalc }, 0, \&_prop_decode ], - - # --- Indentation - 'par_indent_first' => [ 'text', 'fi', 0, \&_prop_twips ], - 'par_indent_left' => [ 'text', 'li', 0, \&_prop_twips ], - 'par_indent_right' => [ 'text', 'ri', 0, \&_prop_twips ], - 'par_outline_level' => [ 'text', 'outlinelevel', 0, \&_prop_raw ], - - 'par_number_text' => [ 'text', 'pntext', 1, \&_prop_pcdata ], - 'par_number' => [ 'text', 'pn', 0, \&_prop_list ], - - # --- Style - 'style' => [ 'text', 's', 0, \&_prop_style ], - 'style_default' => [ 'text', { 'character'=>'plain', 'paragraph'=>'pard', - 'section'=>'secd', 'row'=>'trowd', 'cell'=>'tcelld' }, 0, \&_prop_decode ], - - # --- Paragraph spacing - 'par_space_before' => [ 'text', 'sb', 0, \&_prop_twips ], - 'par_space_after' => [ 'text', 'sa', 0, \&_prop_twips ], - 'par_space_lines' => [ 'text', 'sl', 0, \&_prop_raw ], - 'par_space_lines_mult' => [ 'text', 'slmult', 0, \&_prop_raw ], - - # --- Character formatting - 'bold' => [ 'text', 'b', 0, \&_prop_onoff ], - 'italic' => [ 'text', 'i', 0, \&_prop_onoff ], - 'caps' => [ 'text', 'caps', 0, \&_prop_onoff ], - 'caps_small' => [ 'text', 'scaps', 0, \&_prop_onoff ], - 'underline' => [ 'text', { 'off'=>'ul0', 'continuous'=>'ul', 'dotted'=>'uld', - 'dash'=>'uldash', 'dot-dash'=>'uldashd', 'dot-dot-dash'=>'uldashdd', - 'double'=>'ulb', 'none'=>'ulnone', 'thick'=>'ulth', 'word'=>'ulw', - 'wave'=>'ulwave' }, 0, \&_prop_decode ], - 'hidden' => [ 'text', 'v', 0, \&_prop_onoff ], - - # --- Colors - 'color_foreground' => [ 'text', 'cf', 0, \&_prop_raw ], - 'color_background' => [ 'text', 'cb', 0, \&_prop_raw ], - 'highlight' => [ 'text', 'highlight', 0, \&_prop_raw ], - - # --- Fonts - 'font' => [ 'text', 'f', 0, \&_prop_raw ], - 'font_size' => [ 'text', 'fs', 0, \&_prop_halfpts ], - 'font_scale' => [ 'text', 'charscalex', 0, \&_prop_raw ], - - # --- Page sizes, margins, etc. - 'sec_page_width' => [ 'text', 'pgwsxn', 0, \&_prop_twips ], - 'sec_page_height' => [ 'text', 'pghsxn', 0, \&_prop_twips ], - 'sec_landscape' => [ 'text', 'lndscpsxn', 0, \&_prop_on ], - 'sec_margin_left' => [ 'text', 'marglsxn', 0, \&_prop_twips ], - 'sec_margin_right' => [ 'text', 'margrsxn', 0, \&_prop_twips ], - 'sec_margin_top' => [ 'text', 'margtsxn', 0, \&_prop_twips ], - 'sec_margin_bottom' => [ 'text', 'margbsxn', 0, \&_prop_twips ], - 'sec_margin_mirror' => [ 'text', 'margmirsxn', 0, \&_prop_on ], - 'sec_gutter' => [ 'text', 'guttersxn', 0, \&_prop_twips ], - - 'sec_title_pg' => [ 'text', 'titlepg', 0, \&_prop_on ], - 'sec_header_margin' => [ 'text', 'headery', 0, \&_prop_twips ], - 'sec_footer_margin' => [ 'text', 'footery', 0, \&_prop_twips ], - - # --- Hyphenation - 'par_hyphen' => [ 'text', 'hyphpar', 0, \&_prop_onoff ], - - # --- Widow/orphan controls - 'par_widow_cntrl' => [ text, { 0=>nowidctlpar, 1=>widctlpar }, 0, \&_prop_decode ], - 'par_intact' => [ 'text', 'keep', 0, \&_prop_on ], - 'par_keep_next' => [ 'text', 'keepn', 0, \&_prop_on ], - - 'par_pgbrk_before' => [ 'text', 'pagebb', 0, \&_prop_on ], - - # --- Page numbering - 'pg_num_start' => [ 'text', 'pgnstart', 0, \&_prop_raw ], - 'pg_num_cont' => [ 'text', 'pgncont', 0, \&_prop_on ], - 'pg_num_restart' => [ 'text', 'pgnrestart', 0, \&_prop_on ], - - 'sec_title_pg' => [ 'text', 'titlepg', 0, \&_prop_on ] -); - -sub set_properties -{ - my $self = shift; - - my $table = shift, - $settings = shift, - $destination = shift; - my ($property, $value, $where, $what, $arg, $default); - - local ($_); - - foreach $property (keys %{$settings}) { - if (defined(${$table}{$property})) - { - ($where, $what, $group, $default, $arg) = @{${$table}{$property}}; - - if (defined($destination)) - { - carp "\`$property\' is not a section, paragraph or character property", - if ($where ne "text"); - $where = $destination; - } else { - $where = $self->{$where}, if (defined($what)); - } - - if (defined($what)) - { - $value = ${$settings}{$property}; - my @controls = $self->$default($what, $value, $arg); - - if (@controls) - { - if ($group) { - my $subgroup = $self->add_group($where); - $self->add_raw ($subroup, @controls ); - } else { - $self->add_raw ($where, @controls ); - } - } - } else { - $self->{$where} = ${$settings}{$property}; - } - - } else { - carp "Don\'t know how to handle property: \`$property\'"; - } - } -} - -sub initialize -{ - my $self = shift; - $self->{charset} = "ansi"; # Character Set - - # --- Document Header - $self->{DOCUMENT} = $self->new_group( '\rtf', $self->{charset} ); - - $self->{fonttbl} = $self->add_group($self->{DOCUMENT}); - $self->{fontCnt} = 0; - - $self->{colortbl} = $self->add_group($self->{DOCUMENT}); - $self->{colorCnt} = 0; # count of colors in table - - $self->{styletbl} = $self->add_group($self->{DOCUMENT}); - $self->{styleCnt} = 0; # count of styles defined - - $self->{text} = $self->add_group($self->{DOCUMENT}); - - $self->{info} = $self->add_group(); - $self->add_raw ( $self->{info}, '\info' ); - $self->{creatim} = time(); -} - -sub import { - my $self = shift; - $self->set_properties (\%DOCINFO, @_); - - $self->splice_raw ($self->{DOCUMENT}, 1, 1, "\\".$self->{charset}); - - # --- Insert creation time in Information Group - if ($self->{creatim}) - { - my ($ss, $mn, $hr, $dd, $mm, $yy) = localtime($self->{creatim}); - $yy+=1900; $mm++; - - my $creatim = $self->add_group($self->{info}); - - $self->add_raw( $creatim, '\creatim', - "\\yr$yy", "\\mo$mm", "\\dy$dd", "\\hr$hr", "\\min$mn", "\\sec$ss" - ); - $self->{creatim} = 0; - }; -} - -sub new -{ - my $this = shift; - my $class = ref($this) || $this; - my $self = {}; - bless $self, $class; - $self->initialize(); - $self->import(@_); - return $self; -} - -sub emit_group { - local ($el, $data); - - unless (@_) { - return undef; - } - - $data = "\{"; - - foreach $el (@_) - { - if (ref($el) eq ARRAY) { - $data .= emit_group(@$el); - } else { - if (($el !~ m/^[\\\;\{\}]/) and (substr($data, length($data)-1) !~ m/[\}\{\s]/)) { - $data .= " "; - } - $data .= $el; - } - } - $data .= "\}"; - return $data; - -} - - -%FONTCLASSES = ( - 'swiss' => 'swiss', - 'sans-serif' => 'swiss', - 'roman' => 'roman', - 'serif' => 'roman', - 'modern' => 'modern', - 'monospace' => 'modern', - 'script' => 'script', - 'decor' => 'decor', - 'fantasy' => 'decor', - 'tech' => 'tech', - 'symbol' => 'tech', - 'bidi' => 'bidi' -); -%FONTPITCH = ( - 'default' => 0, - 'fixed' => 1, - 'variable' => 2 -); -sub add_font -{ - local ($_); - my $self = shift; - - my $name = shift, - $attributes = shift; - - my $class = $FONTCLASSES{${$attributes}{family}}; - - unless (defined($class)) { - $class = "nil"; - carp "Unknown font family \`${$attributes}{family}\'"; - } - - unless ($self->{fontCnt}) { - $self->add_raw ($self->{fonttbl}, '\fonttbl'); - $self->splice_raw ($self->{DOCUMENT}, 2, 0, "\\deff".$self->{fontCnt}); - } - - my $fattr = $self->add_group($self->{fonttbl}); - - $self->add_raw ($fattr, ('\f'.$self->{fontCnt}, '\f'.$class) ); - - if (defined(my $pitch = ${$attributes}{pitch})) - { - $self->add_raw ($fattr, '\fprq'. ($FONTPITCH{ $pitch } - or carp "Don\'t know how to handle \`pitch => $pitch\'" ) - ); - } - - if (defined(my $actual = ${$attributes}{name})) # non-tagged name (is this correct?) - { - $self->add_raw ($fattr, ['\*\fname '.escape_simple($actual) ] ); - } - - $self->add_raw ($fattr, escape_simple($name) ); - - my @alternates = @{${$attributes}{alternates}}; - if (@alternates) { - while ($_ = shift @alternates) { - $self->add_raw ($fattr, [ '\*\falt '.escape_simple($_) ] ); - } - } - - $self->add_raw ($fattr, ';' ); - - if (${$attributes}{default}) { - carp "Default font redefined", - if (@{$self->{DOCUMENT}}[2] ne "\\deff0"); - @{$self->{DOCUMENT}}[2] = "\\deff".$self->{fontCnt}; - } - - return $self->{fontCnt}++; -} - -sub decode_stylename -{ - my $name = shift; - my $current = shift || '\s0'; - - $current =~ m/^\\[cd]?s(\d+)/; - my ($next, $last) = ($1+1, $1-1); - $last = 222, if ($last<0); - - return '\s222', if ($name eq "none"); - return '\s0', if ($name eq "default"); - return $current, if ($name eq "self"); - return '\s'.$next, if ($name eq "next"); - return '\s'.$last, if ($name eq "last"); - return $name; -} - -%STYLETYPES = ( - 'character' => '\*\cs', - 'paragraph' => '\s', - 'section' => '\ds' -); - -sub add_style -{ - my $self = shift; - my ($name, $formatting, $attributes) = @_; - - unless (@{$self->{styletbl}}) { - $self->add_raw ( $self->{styletbl}, '\stylesheet'); - } - - $type = ${$attributes}{type} || "paragraph"; - my $code = $STYLETYPES{$type}; - unless (defined($code)) { - carp "Don\'t know how to handle a \`$type\' style"; - } - - my $style; - if (${$attributes}{default}) { - carp "Default style\'s type must be \`paragraph\'", if ($type ne "paragraph"); - $code = "\\s0"; - $style = $code; - $self->{$style} = $self->new_group();; - } else { - $code .= ++$self->{styleCnt}; - ($style = $code) =~ s/^\\\*//; - $self->{$style} = $self->new_group( $code ); - } - - $self->set_properties( \%PROPERTIES, $formatting, $self->{$style} ); - - carp "Warning: next attribute for style sheets is not used", - if (defined(${$attributes}{next})); - - my $sbasedon = ${$attributes}{basedon} || "none", - $snext = ${$attributes}{next} || "self"; - - $sbasedon = decode_stylename($sbasedon, $style); - $snext = decode_stylename($snext, $style); - - # --- Inherit stylesheet from "basedon" - if ($sbasedon ne '\s222') { - %{$self->{styles}->{$style}} = %{$self->{styles}->{$sbasedon}}; - } else { - $self->{styles}->{$style} = {}; - } - - foreach my $aux (keys %{$formatting}) { - ${$self->{styles}->{$style}}{$aux} = ${$formatting}{$aux}; - } - - $sbasedon =~ s/^\\[dc]?s//; $snext =~ s/^\\[dc]?s//; - - push @{$self->{$style}}, ('\sbasedon'.$sbasedon), if (defined(${$attributes}{basedon})); - push @{$self->{$style}}, ('\snext'.$snext), if (defined(${$attributes}{next})); - - push @{$self->{$style}}, ('\shidden'), if (${$attributes}{hidden}); - push @{$self->{$style}}, ('\sautoupd'), if (${$attributes}{autoupdate}); - - if ($type eq "character") { - if (${$attributes}{additive}) { - push @{$self->{$style}}, '\additive'; - } else { - ${$self->{styles}->{$style}}{plain} = 1; - } - } else { - ${$self->{styles}->{$style}}{plain} = 1; - ${$self->{styles}->{$style}}{pard} = 1; - if ($type eq "section") { - ${$self->{styles}->{$style}}{secd} = 1; - } - } - - push @{$self->{$style}}, escape_simple($name).";"; - - if (${$attributes}{default}) { - $self->splice_raw($self->{styletbl}, 1, 0, $self->{$style}); - } else { - $self->add_raw($self->{styletbl}, $self->{$style}); - } - - return $style; -} - -# --- These are the color names used in the HTML 4.0 spec. WordPad also uses these -# names too. However, Microsoft's RTF 1.5 spec uses different color names. - -%COLORNAMES = ( - 'black' => [0, 0, 0], - 'blue' => [0, 0, 255], - 'aqua' => [0, 255, 255], - 'lime' => [0, 255, 0], - 'fuscia' => [255, 0, 255], - 'red' => [255, 0, 0], - 'yellow' => [255, 255, 0], - 'white' => [255, 255, 255], - 'navy' => [0, 0, 128], - 'teal' => [0, 128, 128], - 'green' => [0, 128, 0], - 'purple' => [128, 0, 128], - 'maroon' => [128, 0, 0], - 'olive' => [128, 128, 0], - 'gray' => [128, 128, 128], - 'silver' => [192, 192, 192], -); - -sub parse_value -{ - local ($_) = shift; - $_ = $1 * 2.55, if (m/\-?(\d+(\.\d*)?)\s*\%$/); - return POSIX::ceil($_); -} - -sub add_color -{ - my $self = shift; - my $attributes = shift; - my ($red, $grn, $blu); - - if (defined(${$attributes}{name})) { - my $name = ${$attributes}{name}; - ($red, $grn, $blu) = @{$COLORNAMES{$name}}; - carp "Unrecognized color name \`$name\'", - unless (defined($COLORNAMES{$name})); - } else { - $red = parse_value(${$attributes}{red}); - $grn = parse_value(${$attributes}{green}); - $blu = parse_value(${$attributes}{blue}); - } - - if (${$attributes}{gray}) { - ($red, $grn, $blu) = (255, 255, 255), unless ($red+$grn+$blu); - - $red = POSIX::ceil(parse_value(${$attributes}{gray}) / 255 * $red); - $grn = POSIX::ceil(parse_value(${$attributes}{gray}) / 255 * $grn); - $blu = POSIX::ceil(parse_value(${$attributes}{gray}) / 255 * $blu); - } - - unless ($self->{colorCnt}) { - $self->add_raw ($self->{colortbl}, ('\colortbl', ';')); - } - - foreach ($red, $grn, $blu) { - carp "Invalid color value: $_.", if ($_<0) or ($_>255); - } - - if (${$attributes}{default}) { - carp "Default color will not used by most RTF readers"; - $self->splice_raw ($self->{colortbl}, 1, 1, ("\\red$red", "\\green$grn", "\\blue$blu;") ); - return 0; - } else { - $self->add_raw ($self->{colortbl}, ("\\red$red", "\\green$grn", "\\blue$blu;") ); - return ++$self->{colorCnt}; - } -} - -sub new_group { - my $self = shift; - my $group = []; - push @{$group}, @_; - return $group; -} - -sub add_group { - my $self = shift; - my $section = shift || $self->root(); - my $group = shift || $self->new_group(); - $self->add_raw ($section, $group); - return $group; -} - -sub root { - my $self = shift; - return $self->{text}; -} - -sub splice_raw # splice a raw value into a section -{ - my $self = shift; - my $section = shift; - my $position = shift; - my $length = shift; - - splice @{$section}, $position, $length, @_; -} - -sub add_raw # add a raw value to a section -{ - my $self = shift; - my $section = shift; - - push @{$section}, @_ ; -} - - -# --- Escape brackets, backslashes and 8-bit characters -sub escape_simple { - local ($_) = shift; - s/([\\\{\}])/\\$1/g; - s/([\x80-\xff])/sprintf("\\\'\%02x", ord($1))/eg; - return $_; -} - -# --- Escapes special characters to common RTF controls -sub escape_text -{ - local ($_) = escape_simple(shift); - s/\r/\\par/g; # carriage returns = new paragraphs - s/\n/\\line/g; # escape newlines - s/\t/\\tab/g; # escape tabs - return $_; -} - -sub split_text # splits special characters and regular text into list items -{ - my ($line) = shift; - - $line =~ s/\r//g; # remove carriage returns - $line =~ s/\n\n/\r/g; # change double-newlines to new carriage returns - - my (@list) = (); - local($_); - - foreach (split /(?<=[\n\r\t\\\{\}])|(?=[\n\r\t\\\{\}])/, $line) { - push @list, escape_text ($_); - } - return @list; -} - -sub add_text { - my $self = shift; - my $group = shift || $self->root(); - my ($arg, $rarg); - - while ($arg = shift) { - $rarg = ref($arg); - if ($rarg eq HASH) - { - $self->set_properties (\%PROPERTIES, $arg, $group); - } - elsif ($rarg eq ARRAY) - { - my $subgroup = $self->add_group($group); - $self->add_text ($subgroup, @{$arg} ); - } - elsif ($rarg eq SCALAR) - { - $self->add_text (${$arg}); - } - else - { - $self->add_raw ($group, split_text($arg)); - } - } -} - -sub rtf -{ - my $self = shift; - - unless ($self->{fontCnt}) { - carp "No default font has been specified"; - } - - return emit_group @{$self->{DOCUMENT}}; -} - -1; - -__END__ - -=head1 NAME - -RTF::Document - Perl extension for generating Rich Text (RTF) Files - -=head1 DESCRIPTION - -RTF::Document is a module for generating Rich Text Format (RTF) documents -that can be used by most text converters and word processors. - -For a listing of properties, consult the %DOCINFO and %PROPERTIES hashes -in the source code. - -=head1 REQUIRED MODULES - - Carp - POSIX - Convert::Units::Type 0.33 - -=head1 EXAMPLE - - use RTF::Document; - - # Document properties - - $rtf = new RTF::Document( - { - doc_page_width => '8.5in', - doc_page_height => '11in' - } - ); - - # Font definitions - - $fAvantGarde = $rtf->add_font ("AvantGarde", - { family=>swiss, - default=>1 - } ); - $fCourier = $rtf->add_font ("Courier", - { family=>monospace, pitch=>fixed, - alternates=>["Courier New", "American Typewriter"] - } ); - - # Color definitions - - $cRed = $rtf->add_color ( { red=>255 } ); - $cGreen = $rtf->add_color ( { green=>128 } ); - $cCustm = $rtf->add_color ( { red=>0x66, blue=>0x33, green=>0x33 } ); - - $cBlack = $rtf->add_color ( { name=>'black' } ); - $cWhite = $rtf->add_color ( { gray=>'100%' } ); - - $cNavy = $rtf->add_color ( { blue=>'100%', gray=>'50%' } ); - - # style definitions - - $sNormal = $rtf->add_style( "Normal", - { font=>$fAvantGarde, font_size=>'12pt', - color_foreground=>$cBlack }, - { type=>paragraph, default=>1 } - ); - - $sGreen = $rtf->add_style( "Green", - { color_foreground=>$cGreen }, - { type=>character, additive=>1 } - ); - - # Mix any combo of properties and text... - - $rtf->add_text( $rtf->root(), - "Default text\n\n", - - { bold=>1, underline=>continuous }, - "Bold/Underlined Text\n\n", - - { font_size=>'20pt', font=>$fCourier, - color_foreground=>$cRed }, - "Bigger, Red and Monospaced.\n\n", - - { style_default=>paragraph }, - { style_default=>character }, - - "This is ", - [ { style=>$sGreen }, "green" ], - " styled.\n\n" - - ); - - open FILE, ">MyFile.rtf"; - binmode FILE; - print FILE $rtf->rtf(); - close FILE; - -=head1 DOCUMENT STRUCTURE - -For purposes of using this module, a Rich Text (RTF) Document can be subdivided into -I<groups>. Groups can be considered containers for I<text> and I<controls> (controlling -document and text properties). - -For all intents and purposes, a group limits the scope of controls. So if we set -the "bold" character property within a group, the text will be bold only within -that group (until it is turned off within that group). - -When generating a RTF document using this module, we are only concerned with the -I<root> group (also called the "Document Area"). (The "Header" groups are taken -care of automatically by this module.) - -The Document Area is subdivided into I<sections>. Each section is subdivided into -I<paragraphs>. - -=head1 METHODS - -Some of the methods are documented below. (Methods not documented here may -be changed in future versions.) - -=head2 new - - $rtf = new RTF::Document( \%DocumentProperties ); - -Creates a new RTF document object. - -=head2 root - - $gRoot = $rtf->root(); - -Returns the "root" group in the RTF document. - -=head2 new_group - - $gMyGroup = $rtf->new_group(); - -Creates a new group (not inside of the RTF document). - -=head2 add_group - - $gChildOfRoot = $rtf->add_group(); - $gChildOfChild = $rtf->add_group( $gChild ); - -Adds a child group to the specfied group. If no group is specified, the "root" -group is assumed. - - $rtf->add_group( $rtf->root(), $gMyGroup ); - -Adds a group to the specified parent group (in this case, the root group). - -=head2 add_raw - - $rtf->add_raw( $group, '\par', "Some Text" ); - -Adds raw controls and text to the group. This method is intended for internal -use only. - -=head2 add_text - - $rtf->add_text( $group, "Some text ", { bold=>1 }, "more text" ); - -Adds text and controls to a group. Text is escaped. - -=head2 add_font - -=head2 add_color - -=head2 add_style - -=head1 PROPERTIES - -=head2 Document Properties - -=head2 Section Properties - -=head2 Paragraph Properties - -=head2 Character Properties - -=head1 KNOWN ISSUES - -This module should be considered in the "alpha" stage. Use at your own risk. - -There are no default document or style properties produced by this module, -with the exception of the character set. If you want to make sure that a -I<specific> font, color, or style is used, you must specify it. Otherwise -you rely on the assumptions of whatever RTF reader someone is using. - -This module does not insert newlines anywhere in the text, even though some -RTF writers break lines before they exceed 225 characters. This may or may -not be an issue with some reader software. - -Unknown font or style properties will generally be ignored without warning. - -This module supports some newer RTF controls (used in Word 95/Word 97) that -may are not understood by older RTF readers. - -Once a Font, Color or Style is added, it cannot be changed. No checking -for redundant entries is done. - -Generally, it is not possible to reference a not-yet-created Style with the -next or basedon attributes. However, you can use the constances "last", -"self" or "next" to reference the last style added, the current style -being added, or the next style that will be added, respectively. - -Specifying properties in a particular order within a group does not -guarantee that they will be emitted in that order. If order matters, -specify them separetly. For instance, - - $rtf->add_text($rtf->root, { style_default=>character, bold=>1 } ); - -should be (if you want to ensure character styles are reset before setting -bold text): - - $rtf->add_text($rtf->root, { style_default=>character }, { bold=>1 } ); - -Also note that duplicate properties within the same group won't work. i.e., -If you want to set "style_default" for both paragraphs and characters, you -must do so in separate groups. - -This isn't so much as a bug as the way Perl handles hashes. - -=head2 Unimplemented Features - -A rather large number of features and control words are not handled in this -version. Among the major features: - -=over - -=item Annotations and Comments - -=item Bookmarks - -=item Bullets and Paragraph Numbering - -Some support has been added. The backwards-compatability controls for numbered -paragraphs used by older readers has not been added because it is not properly -handled by newer readers. - -=item Character Sets and Internationalization - -Non-"ANSI" character sets (i.e., Macintosh) and Unicode character -sets are not supported (at least not intentionally). There is no -support for Asian character sets in this version of the module. - -Unicode character escapes are not implemented. - -Language codes (defining a default language, or a language for a -group of characters) are not implemented. - -Bi-directional and text-flow controls are not implemented. - -=item Embedded Images and OLE Objects - -=item File Tables - -=item Footnotes and Endnotes - -=item Forms - -=item Headers and Footers - -=item Hyphenation Control - -Some minimal controls have been added. - -=item Lists and List Tables - -Not implemented: List Tables are really a kind of style sheet for lists. -Priority will be given to support generic bullets and paragraph numbering. - -=item Page Numbering - -Minimal definition, untested. - -=item Printer Bin Controls - -=item Revision Tables - -=item Special Characters and Document Variables - -Most special characters not not implemented, with the exception of tabs. Double -newline characters are converted to a new paragraph control, and single newlines -are converted to a new line control. - -=item Tabs - -=item Tables and Frames - -=back - -=head1 SEE ALSO - -Microsoft Technical Support and Application Note, "Rich Text Format (RTF) -Specification and Sample Reader Program", Version 1.5. - -I<Convert::Units::Type>. - -=head1 AUTHOR - -Robert Rothenberg <wlkng...@unix.asb.com> - -=head1 LICENSE - -Copyright (c) 1999-2000 Robert Rothenberg. All rights reserved. -This program is free software; you can redistribute it and/or -modify it under the same terms as Perl itself. - -=cut - +package RTF::Document; +require 5.005; +require Exporter; + +use vars qw( + $VERSION + %DOCINFO %PROPERTIES + %FONTCLASSES %FONTPITCH %COLORNAMES %STYLETYPES %NUMSTYLES +); +$VERSION = "0.64"; + +@ISA = qw(Exporter); +@EXPORT = qw(); +@EXPORT_OK = qw(); + +use Carp; +use POSIX qw(floor ceil); +use Convert::Units::Type 0.33; + +%NUMSTYLES = ( + '1' => '\pndec', + 'a' => '\pnlcltr', + 'i' => '\pnlcrm', + 'A' => '\pnucltr', + 'I' => '\pnucrm', + '1st' => '\pnord' +); + +sub _prop_list +{ + my ($self, $code, $properties) = @_; + my ($result, $level, $style); + + if ($properties eq "off") { + return '\pard'; + } + + $result = $self->new_group( '\*', '\pn' ); + + ${$properties}{level} = ${$properties}{style}, if (${$properties}{style} eq "bullet"); + + if (${$properties}{level}) { + $level = ${$properties}{level}; + $level = "blt", if ($level eq "bullet"); + if ((($level<1) or ($level>11)) and ($level ne "blt")) + { + carp "List level \`$level\' is out of range"; + $level = 'body'; + } + } else { + $level = 'body'; + } + + $self->add_raw ($result, '\pnlvl'.$level); + + if ($level eq "body") { + $style = $NUMSTYLES{${$properties}{style}} || '\pndec'; + $self->add_raw ($result, $style); + } + + if (defined(${$properties}{font})) + { + $self->add_raw ($result, '\pnf'.${$properties}{font}); + } + + if (defined(${$properties}{color})) + { + $self->add_raw ($result, '\pncf'.${$properties}{color}); + } + + if (defined(${$properties}{before})) + { + my $group = $self->add_group($result); + $self->add_raw ($group, '\pntxtb '.escape_simple(${$properties}{before}) ); + } + + if (defined(${$properties}{after})) + { + my $group = $self->add_group($result); + $self->add_raw ($group, '\pntxta '.escape_simple(${$properties}{after}) ); + } + + if (${$properties}{across}) + { + $self->add_raw ($result, '\pnacross'); + } + + if (defined(${$properties}{indent})) + { + $self->add_raw ($result, '\pnindent'.POSIX::floor( + Convert::Units::Type::convert(${$properties}{indent}, "twips") + )); + } + + if (defined(${$properties}{space})) + { + $self->add_raw ($result, '\pnsp'.POSIX::floor( + Convert::Units::Type::convert(${$properties}{space}, "twips") + )); + } + + if (${$properties}{hang}) + { + $self->add_raw ($result, '\pnhang'); + } + + if (defined(${$properties}{start})) + { + $self->add_raw ($result, '\pnstart'.${$properties}{start}); + } + + + return ($result); +} + +# $arg is a key to RTF control in hash value +sub _prop_decode +{ + my ($self, $hash, $arg) = @_; + my $result = ${$hash}{$arg}; + + unless (defined($result)) { + carp "Don\'t know how to handle value \`$arg\'"; + } + + return ("\\".$result); +} + +sub _prop_style { + my ($self, $code, $arg) = @_; + $code = decode_stylename($arg, '\s222'); + my $formatting, $style_properties; + + if (defined($code)) { + $formatting = $self->new_group(); + %{$style_properties} = %{$self->{styles}->{$code}}; + + if (${$style_properties}{secd}) { + $self->add_raw($formatting, '\secd'); + delete ${$style_properties}{secd}; + } + if (${$style_properties}{pard}) { + $self->add_raw($formatting, '\pard'); + delete ${$style_properties}{pard}; + } + if (${$style_properties}{plain}) { + $self->add_raw($formatting, '\plain'); + delete ${$style_properties}{plain}; + } + + $self->set_properties( \%PROPERTIES, $style_properties, $formatting); + unless (@{$formatting}) { + carp "Style \`$arg\' is not defined"; + $code = decode_stylename("none"); + } + } + return ($code, @{$formatting} ); +} + +# $arg is a unit of type (points, picas, inches) converted to twips +sub _prop_twips { + my ($self, $code, $arg) = @_; + return ("\\".$code.POSIX::floor(Convert::Units::Type::convert($arg, "twips"))); +} + +# $arg is a unit of type (points, picas, inches) converted to half-points +sub _prop_halfpts { + my ($self, $code, $arg) = @_; + return ("\\".$code.POSIX::floor(Convert::Units::Type::convert($arg, "half-points"))); +} + +# $arg is a string (which may need to be escaped) +sub _prop_pcdata { + my ($self, $code, $arg) = @_; + $arg =~ s/([\\\{\}])/\\$1/g; + return ("\\".$code, escape_simple($arg)); +} + +# $arg is a raw value +sub _prop_raw { + my ($self, $code, $arg) = @_; + return ("\\".$code.$arg); +} + +# $arg is a an on/off indicator (0 = off, NZ = on) +sub _prop_onoff { + my ($self, $code, $arg) = @_; + if ($arg) + { + return ("\\".$code); + } + else + { + return ("\\".$code."0"); + } +} + +# $arg is a an emit/don't emit indicator (0 = don't emit control, NZ = emit) +sub _prop_on { + my ($self, $code, $arg) = @_; + if ($arg) + { + return ("\\".$code); + } + else + { + return undef; + } +} + +# Synopsis of %DOCINFO and %PROPERTIES +# property => [ where, control, group, function ] +# property = name of the property +# where = what section of the document this control is usually applied to +# control = the control word used (if a hash, how to decode various controls) +# group = if non-zero, emit this as part of a group +# function = what function to use to process this property +# Most properties follow the following naming scheme: +# doc = document-wide properties (should be set only once) +# sec = section properties +# col = column properties (within a section) +# par = paragraph properties + +%DOCINFO = ( + # --- Document summary information + 'doc_title' => [ 'info', 'title', 1, \&_prop_pcdata ], + 'doc_author' => [ 'info', 'author', 1, \&_prop_pcdata ], + 'doc_subject' => [ 'info', 'subject', 1, \&_prop_pcdata ], + 'doc_manager' => [ 'info', 'manager', 1, \&_prop_pcdata ], + 'doc_company' => [ 'info', 'company', 1, \&_prop_pcdata ], + 'doc_operator' => [ 'info', 'operator', 1, \&_prop_pcdata ], + 'doc_category' => [ 'info', 'category', 1, \&_prop_pcdata ], + 'doc_keywords' => [ 'info', 'keywords', 1, \&_prop_pcdata ], + 'doc_summary' => [ 'info', 'doccomm', 1, \&_prop_pcdata ], + 'doc_comment' => [ 'text', '*\comment', 1, \&_prop_pcdata ], + 'doc_base_href' => [ 'info', 'hlinkbase', 1, \&_prop_pcdata ], + 'doc_version' => [ 'info', 'version', 0, \&_prop_raw ], + 'doc_time_created' => [ 'creatim' ], + + 'doc_from_text' => [ 'text', 'fromtext', 0, \&_prop_on ], + 'doc_make_backup' => [ 'text', 'makebackup', 0, \&_prop_on ], + 'doc_rtf_def' => [ 'text', 'defformat', 0, \&_prop_on ], + + # --- Page sizes, margins, etc. + doc_page_width => [ text, paperw, 0, \&_prop_twips ], + doc_page_height => [ text, paperh, 0, \&_prop_twips ], + doc_landscape => [ text, landscape, 0, \&_prop_on ], + doc_facing => [ text, facingp, 0, \&_prop_on ], + doc_margin_left => [ text, margl, 0, \&_prop_twips ], + doc_margin_right => [ text, margr, 0, \&_prop_twips ], + doc_margin_top => [ text, margt, 0, \&_prop_twips ], + doc_margin_bottom => [ text, margb, 0, \&_prop_twips ], + doc_margin_mirror=> [ text, margmirror, 0, \&_prop_on ], + doc_gutter => [ text, gutter, 0, \&_prop_twips ], + + # --- Hyphenation + doc_hyphen_auto => [ 'text', 'hyphauto', 0, \&_prop_onoff ], + doc_hyphen_caps => [ 'text', 'hyphcaps', 0, \&_prop_onoff ], + doc_hyphen_lines => [ 'text', 'hyphconsec', 0, \&_prop_onoff ], + doc_hyphen_zone => [ 'text', 'hyphhotz', 0, \&_prop_twips ], + + # --- Views + doc_view_scale => [ text, viewscale, 0, \&_prop_raw ], + doc_view_zoom => [ text, { none=>'viewzk0', 'full-page'=>'viewzk1', + 'best-fit'=>'viewzk1' }, 0, \&_prop_decode ], + doc_view_caption => [ text, windowcaption, 1, , \&_prop_pcdata ], + 'doc_view_mode' => [ 'text', { 'none'=>'viewkind0', 'layout'=>'viewkind1', + 'outline'=>'viewkind2', 'master'=>'viewkind3', + + + + + 'normal'=>'viewkind4', 'online'=>'viewkind5'}, 0, \&_prop_decode ], + + # --- Character set + 'doc_charset' => [ 'charset' ], + + # --- Widow/orphan controls + doc_widow_cntrl => [ text, widowctrl, 0, \&_prop_on ], + + # --- Tabs + tabs_default => [ 'text', 'deftab', 0, \&_prop_twips ], + +); + +%PROPERTIES = ( + + # --- New section, paragraph, line + 'sec' => [ 'text', 'sect', 0, \&_prop_on ], + 'par' => [ 'text', 'par', 0, \&_prop_on ], + 'line' => [ 'text', 'line', 0, \&_prop_on ], + 'line_soft' => [ 'text', 'softline', 0, \&_prop_on ], + + # --- Sections.... + 'sec_brk' => [ 'text', { 'none'=>'sbknone', 'column'=>'sbkcol', + 'page'=>'sbkpage', 'even'=>'sbkeven', 'odd'=>'sbkodd'}, 0, \&_prop_decode ], + + # --- Columns + col => [ text, 'colulmn', 0, \&_prop_on ], + col_soft => [ text, 'softcol', 0, \&_prop_on ], + col_num => [ text, 'cols', 0, \&_prop_raw ], + col_space => [ text, 'colsx', 0, \&_prop_twips ], + col_select => [ text, 'colno', 0, \&_prop_raw ], + col_padding_right => [ text, 'colsr', 0, \&_prop_twips ], + col_width => [ text, 'colw', 0, \&_prop_twips ], + col_line => [ text, 'linebetcol', 0, \&_prop_on ], + + 'page_brk' => [ 'text', 'page', 0, \&_prop_on ], + 'page_softbrk' => [ 'text', 'softpage', 0, \&_prop_on ], + + # --- Forms.... + 'sec_unlock' => [ 'text', 'sectunlocked', 0, \&_prop_on ], + + # --- Footsnotes, endnotes stuff + 'sec_endnotes_here' => [ 'text', 'endnhere', 0, \&_prop_on ], + + # --- Alignment + 'par_align' => [ 'text', { left=>'ql', right=>'qr', center=>'qc', justify=>'qj' }, 0, \&_prop_decode ], + 'sec_vert_align' => [ 'text', { top=>vertalt, bottom=>vertalb, center=>vertalc }, 0, \&_prop_decode ], + + # --- Indentation + 'par_indent_first' => [ 'text', 'fi', 0, \&_prop_twips ], + 'par_indent_left' => [ 'text', 'li', 0, \&_prop_twips ], + 'par_indent_right' => [ 'text', 'ri', 0, \&_prop_twips ], + 'par_outline_level' => [ 'text', 'outlinelevel', 0, \&_prop_raw ], + + 'par_number_text' => [ 'text', 'pntext', 1, \&_prop_pcdata ], + 'par_number' => [ 'text', 'pn', 0, \&_prop_list ], + + # --- Style + 'style' => [ 'text', 's', 0, \&_prop_style ], + 'style_default' => [ 'text', { 'character'=>'plain', 'paragraph'=>'pard', + 'section'=>'secd', 'row'=>'trowd', 'cell'=>'tcelld' }, 0, \&_prop_decode ], + + # --- Paragraph spacing + 'par_space_before' => [ 'text', 'sb', 0, \&_prop_twips ], + 'par_space_after' => [ 'text', 'sa', 0, \&_prop_twips ], + 'par_space_lines' => [ 'text', 'sl', 0, \&_prop_raw ], + 'par_space_lines_mult' => [ 'text', 'slmult', 0, \&_prop_raw ], + + # --- Character formatting + 'bold' => [ 'text', 'b', 0, \&_prop_onoff ], + 'italic' => [ 'text', 'i', 0, \&_prop_onoff ], + 'caps' => [ 'text', 'caps', 0, \&_prop_onoff ], + 'caps_small' => [ 'text', 'scaps', 0, \&_prop_onoff ], + 'underline' => [ 'text', { 'off'=>'ul0', 'continuous'=>'ul', 'dotted'=>'uld', + 'dash'=>'uldash', 'dot-dash'=>'uldashd', 'dot-dot-dash'=>'uldashdd', + 'double'=>'ulb', 'none'=>'ulnone', 'thick'=>'ulth', 'word'=>'ulw', + 'wave'=>'ulwave' }, 0, \&_prop_decode ], + 'hidden' => [ 'text', 'v', 0, \&_prop_onoff ], + + # --- Colors + 'color_foreground' => [ 'text', 'cf', 0, \&_prop_raw ], + 'color_background' => [ 'text', 'cb', 0, \&_prop_raw ], + 'highlight' => [ 'text', 'highlight', 0, \&_prop_raw ], + + # --- Fonts + 'font' => [ 'text', 'f', 0, \&_prop_raw ], + 'font_size' => [ 'text', 'fs', 0, \&_prop_halfpts ], + 'font_scale' => [ 'text', 'charscalex', 0, \&_prop_raw ], + + # --- Page sizes, margins, etc. + 'sec_page_width' => [ 'text', 'pgwsxn', 0, \&_prop_twips ], + 'sec_page_height' => [ 'text', 'pghsxn', 0, \&_prop_twips ], + 'sec_landscape' => [ 'text', 'lndscpsxn', 0, \&_prop_on ], + 'sec_margin_left' => [ 'text', 'marglsxn', 0, \&_prop_twips ], + 'sec_margin_right' => [ 'text', 'margrsxn', 0, \&_prop_twips ], + 'sec_margin_top' => [ 'text', 'margtsxn', 0, \&_prop_twips ], + 'sec_margin_bottom' => [ 'text', 'margbsxn', 0, \&_prop_twips ], + 'sec_margin_mirror' => [ 'text', 'margmirsxn', 0, \&_prop_on ], + 'sec_gutter' => [ 'text', 'guttersxn', 0, \&_prop_twips ], + + 'sec_title_pg' => [ 'text', 'titlepg', 0, \&_prop_on ], + 'sec_header_margin' => [ 'text', 'headery', 0, \&_prop_twips ], + 'sec_footer_margin' => [ 'text', 'footery', 0, \&_prop_twips ], + + # --- Hyphenation + 'par_hyphen' => [ 'text', 'hyphpar', 0, \&_prop_onoff ], + + # --- Widow/orphan controls + 'par_widow_cntrl' => [ text, { 0=>nowidctlpar, 1=>widctlpar }, 0, \&_prop_decode ], + 'par_intact' => [ 'text', 'keep', 0, \&_prop_on ], + 'par_keep_next' => [ 'text', 'keepn', 0, \&_prop_on ], + + 'par_pgbrk_before' => [ 'text', 'pagebb', 0, \&_prop_on ], + + # --- Page numbering + 'pg_num_start' => [ 'text', 'pgnstart', 0, \&_prop_raw ], + 'pg_num_cont' => [ 'text', 'pgncont', 0, \&_prop_on ], + 'pg_num_restart' => [ 'text', 'pgnrestart', 0, \&_prop_on ], + + 'sec_title_pg' => [ 'text', 'titlepg', 0, \&_prop_on ] +); + +sub set_properties +{ + my $self = shift; + + my $table = shift, + $settings = shift, + $destination = shift; + my ($property, $value, $where, $what, $arg, $default); + + local ($_); + + foreach $property (keys %{$settings}) { + if (defined(${$table}{$property})) + { + ($where, $what, $group, $default, $arg) = @{${$table}{$property}}; + + if (defined($destination)) + { + carp "\`$property\' is not a section, paragraph or character property", + if ($where ne "text"); + $where = $destination; + } else { + $where = $self->{$where}, if (defined($what)); + } + + if (defined($what)) + { + $value = ${$settings}{$property}; + my @controls = $self->$default($what, $value, $arg); + + if (@controls) + { + if ($group) { + my $subgroup = $self->add_group($where); + $self->add_raw ($subroup, @controls ); + } else { + $self->add_raw ($where, @controls ); + } + } + } else { + $self->{$where} = ${$settings}{$property}; + } + + } else { + carp "Don\'t know how to handle property: \`$property\'"; + } + } +} + +sub initialize +{ + my $self = shift; + $self->{charset} = "ansi"; # Character Set + + # --- Document Header + $self->{DOCUMENT} = $self->new_group( '\rtf', $self->{charset} ); + + $self->{fonttbl} = $self->add_group($self->{DOCUMENT}); + $self->{fontCnt} = 0; + + $self->{colortbl} = $self->add_group($self->{DOCUMENT}); + $self->{colorCnt} = 0; # count of colors in table + + $self->{styletbl} = $self->add_group($self->{DOCUMENT}); + $self->{styleCnt} = 0; # count of styles defined + + $self->{text} = $self->add_group($self->{DOCUMENT}); + + $self->{info} = $self->add_group(); + $self->add_raw ( $self->{info}, '\info' ); + $self->{creatim} = time(); +} + +sub import { + my $self = shift; + $self->set_properties (\%DOCINFO, @_); + + $self->splice_raw ($self->{DOCUMENT}, 1, 1, "\\".$self->{charset}); + + # --- Insert creation time in Information Group + if ($self->{creatim}) + { + my ($ss, $mn, $hr, $dd, $mm, $yy) = localtime($self->{creatim}); + $yy+=1900; $mm++; + + my $creatim = $self->add_group($self->{info}); + + $self->add_raw( $creatim, '\creatim', + "\\yr$yy", "\\mo$mm", "\\dy$dd", "\\hr$hr", "\\min$mn", "\\sec$ss" + ); + $self->{creatim} = 0; + }; +} + +sub new +{ + my $this = shift; + my $class = ref($this) || $this; + my $self = {}; + bless $self, $class; + $self->initialize(); + $self->import(@_); + return $self; +} + +sub emit_group { + local ($el, $data); + + unless (@_) { + return undef; + } + + $data = "\{"; + + foreach $el (@_) + { + if (ref($el) eq ARRAY) { + $data .= emit_group(@$el); + } else { + if (($el !~ m/^[\\\;\{\}]/) and (substr($data, length($data)-1) !~ m/[\}\{\s]/)) { + $data .= " "; + } + $data .= $el; + } + } + $data .= "\}"; + return $data; + +} + + +%FONTCLASSES = ( + 'swiss' => 'swiss', + 'sans-serif' => 'swiss', + 'roman' => 'roman', + 'serif' => 'roman', + 'modern' => 'modern', + 'monospace' => 'modern', + 'script' => 'script', + 'decor' => 'decor', + 'fantasy' => 'decor', + 'tech' => 'tech', + 'symbol' => 'tech', + 'bidi' => 'bidi' +); +%FONTPITCH = ( + 'default' => 0, + 'fixed' => 1, + 'variable' => 2 +); +sub add_font +{ + local ($_); + my $self = shift; + + my $name = shift, + $attributes = shift; + + my $class = $FONTCLASSES{${$attributes}{family}}; + + unless (defined($class)) { + $class = "nil"; + carp "Unknown font family \`${$attributes}{family}\'"; + } + + unless ($self->{fontCnt}) { + $self->add_raw ($self->{fonttbl}, '\fonttbl'); + $self->splice_raw ($self->{DOCUMENT}, 2, 0, "\\deff".$self->{fontCnt}); + } + + my $fattr = $self->add_group($self->{fonttbl}); + + $self->add_raw ($fattr, ('\f'.$self->{fontCnt}, '\f'.$class) ); + + if (defined(my $pitch = ${$attributes}{pitch})) + { + $self->add_raw ($fattr, '\fprq'. ($FONTPITCH{ $pitch } + or carp "Don\'t know how to handle \`pitch => $pitch\'" ) + ); + } + + if (defined(my $actual = ${$attributes}{name})) # non-tagged name (is this correct?) + { + $self->add_raw ($fattr, ['\*\fname '.escape_simple($actual) ] ); + } + + $self->add_raw ($fattr, escape_simple($name) ); + + my @alternates = @{${$attributes}{alternates}}; + if (@alternates) { + while ($_ = shift @alternates) { + $self->add_raw ($fattr, [ '\*\falt '.escape_simple($_) ] ); + } + } + + $self->add_raw ($fattr, ';' ); + + if (${$attributes}{default}) { + carp "Default font redefined", + if (@{$self->{DOCUMENT}}[2] ne "\\deff0"); + @{$self->{DOCUMENT}}[2] = "\\deff".$self->{fontCnt}; + } + + return $self->{fontCnt}++; +} + +sub decode_stylename +{ + my $name = shift; + my $current = shift || '\s0'; + + $current =~ m/^\\[cd]?s(\d+)/; + my ($next, $last) = ($1+1, $1-1); + $last = 222, if ($last<0); + + return '\s222', if ($name eq "none"); + return '\s0', if ($name eq "default"); + return $current, if ($name eq "self"); + return '\s'.$next, if ($name eq "next"); + return '\s'.$last, if ($name eq "last"); + return $name; +} + +%STYLETYPES = ( + 'character' => '\*\cs', + 'paragraph' => '\s', + 'section' => '\ds' +); + +sub add_style +{ + my $self = shift; + my ($name, $formatting, $attributes) = @_; + + unless (@{$self->{styletbl}}) { + $self->add_raw ( $self->{styletbl}, '\stylesheet'); + } + + $type = ${$attributes}{type} || "paragraph"; + my $code = $STYLETYPES{$type}; + unless (defined($code)) { + carp "Don\'t know how to handle a \`$type\' style"; + } + + my $style; + if (${$attributes}{default}) { + carp "Default style\'s type must be \`paragraph\'", if ($type ne "paragraph"); + $code = "\\s0"; + $style = $code; + $self->{$style} = $self->new_group();; + } else { + $code .= ++$self->{styleCnt}; + ($style = $code) =~ s/^\\\*//; + $self->{$style} = $self->new_group( $code ); + } + + $self->set_properties( \%PROPERTIES, $formatting, $self->{$style} ); + + carp "Warning: next attribute for style sheets is not used", + if (defined(${$attributes}{next})); + + my $sbasedon = ${$attributes}{basedon} || "none", + $snext = ${$attributes}{next} || "self"; + + $sbasedon = decode_stylename($sbasedon, $style); + $snext = decode_stylename($snext, $style); + + # --- Inherit stylesheet from "basedon" + if ($sbasedon ne '\s222') { + %{$self->{styles}->{$style}} = %{$self->{styles}->{$sbasedon}}; + } else { + $self->{styles}->{$style} = {}; + } + + foreach my $aux (keys %{$formatting}) { + ${$self->{styles}->{$style}}{$aux} = ${$formatting}{$aux}; + } + + $sbasedon =~ s/^\\[dc]?s//; $snext =~ s/^\\[dc]?s//; + + push @{$self->{$style}}, ('\sbasedon'.$sbasedon), if (defined(${$attributes}{basedon})); + push @{$self->{$style}}, ('\snext'.$snext), if (defined(${$attributes}{next})); + + push @{$self->{$style}}, ('\shidden'), if (${$attributes}{hidden}); + push @{$self->{$style}}, ('\sautoupd'), if (${$attributes}{autoupdate}); + + if ($type eq "character") { + if (${$attributes}{additive}) { + push @{$self->{$style}}, '\additive'; + } else { + ${$self->{styles}->{$style}}{plain} = 1; + } + } else { + ${$self->{styles}->{$style}}{plain} = 1; + ${$self->{styles}->{$style}}{pard} = 1; + if ($type eq "section") { + ${$self->{styles}->{$style}}{secd} = 1; + } + } + + push @{$self->{$style}}, escape_simple($name).";"; + + if (${$attributes}{default}) { + $self->splice_raw($self->{styletbl}, 1, 0, $self->{$style}); + } else { + $self->add_raw($self->{styletbl}, $self->{$style}); + } + + return $style; +} + +# --- These are the color names used in the HTML 4.0 spec. WordPad also uses these +# names too. However, Microsoft's RTF 1.5 spec uses different color names. + +%COLORNAMES = ( + 'black' => [0, 0, 0], + 'blue' => [0, 0, 255], + 'aqua' => [0, 255, 255], + 'lime' => [0, 255, 0], + 'fuscia' => [255, 0, 255], + 'red' => [255, 0, 0], + 'yellow' => [255, 255, 0], + 'white' => [255, 255, 255], + 'navy' => [0, 0, 128], + 'teal' => [0, 128, 128], + 'green' => [0, 128, 0], + 'purple' => [128, 0, 128], + 'maroon' => [128, 0, 0], + 'olive' => [128, 128, 0], + 'gray' => [128, 128, 128], + 'silver' => [192, 192, 192], +); + +sub parse_value +{ + local ($_) = shift; + $_ = $1 * 2.55, if (m/\-?(\d+(\.\d*)?)\s*\%$/); + return POSIX::ceil($_); +} + +sub add_color +{ + my $self = shift; + my $attributes = shift; + my ($red, $grn, $blu); + + if (defined(${$attributes}{name})) { + my $name = ${$attributes}{name}; + ($red, $grn, $blu) = @{$COLORNAMES{$name}}; + carp "Unrecognized color name \`$name\'", + unless (defined($COLORNAMES{$name})); + } else { + $red = parse_value(${$attributes}{red}); + $grn = parse_value(${$attributes}{green}); + $blu = parse_value(${$attributes}{blue}); + } + + if (${$attributes}{gray}) { + ($red, $grn, $blu) = (255, 255, 255), unless ($red+$grn+$blu); + + $red = POSIX::ceil(parse_value(${$attributes}{gray}) / 255 * $red); + $grn = POSIX::ceil(parse_value(${$attributes}{gray}) / 255 * $grn); + $blu = POSIX::ceil(parse_value(${$attributes}{gray}) / 255 * $blu); + } + + unless ($self->{colorCnt}) { + $self->add_raw ($self->{colortbl}, ('\colortbl', ';')); + } + + foreach ($red, $grn, $blu) { + carp "Invalid color value: $_.", if ($_<0) or ($_>255); + } + + if (${$attributes}{default}) { + carp "Default color will not used by most RTF readers"; + $self->splice_raw ($self->{colortbl}, 1, 1, ("\\red$red", "\\green$grn", "\\blue$blu;") ); + return 0; + } else { + $self->add_raw ($self->{colortbl}, ("\\red$red", "\\green$grn", "\\blue$blu;") ); + return ++$self->{colorCnt}; + } +} + +sub new_group { + my $self = shift; + my $group = []; + push @{$group}, @_; + return $group; +} + +sub add_group { + my $self = shift; + my $section = shift || $self->root(); + my $group = shift || $self->new_group(); + $self->add_raw ($section, $group); + return $group; +} + +sub root { + my $self = shift; + return $self->{text}; +} + +sub splice_raw # splice a raw value into a section +{ + my $self = shift; + my $section = shift; + my $position = shift; + my $length = shift; + + splice @{$section}, $position, $length, @_; +} + +sub add_raw # add a raw value to a section +{ + my $self = shift; + my $section = shift; + + push @{$section}, @_ ; +} + + +# --- Escape brackets, backslashes and 8-bit characters +sub escape_simple { + local ($_) = shift; + s/([\\\{\}])/\\$1/g; + s/([\x80-\xff])/sprintf("\\\'\%02x", ord($1))/eg; + return $_; +} + +# --- Escapes special characters to common RTF controls +sub escape_text +{ + local ($_) = escape_simple(shift); + s/\r/\\par/g; # carriage returns = new paragraphs + s/\n/\\line/g; # escape newlines + s/\t/\\tab/g; # escape tabs + return $_; +} + +sub split_text # splits special characters and regular text into list items +{ + my ($line) = shift; + + $line =~ s/\r//g; # remove carriage returns + $line =~ s/\n\n/\r/g; # change double-newlines to new carriage returns + + my (@list) = (); + local($_); + + foreach (split /(?<=[\n\r\t\\\{\}])|(?=[\n\r\t\\\{\}])/, $line) { + push @list, escape_text ($_); + } + return @list; +} + +sub add_text { + my $self = shift; + my $group = shift || $self->root(); + my ($arg, $rarg); + + while ($arg = shift) { + $rarg = ref($arg); + if ($rarg eq HASH) + { + $self->set_properties (\%PROPERTIES, $arg, $group); + } + elsif ($rarg eq ARRAY) + { + my $subgroup = $self->add_group($group); + $self->add_text ($subgroup, @{$arg} ); + } + elsif ($rarg eq SCALAR) + { + $self->add_text (${$arg}); + } + else + { + $self->add_raw ($group, split_text($arg)); + } + } +} + +sub rtf +{ + my $self = shift; + + unless ($self->{fontCnt}) { + carp "No default font has been specified"; + } + + return emit_group @{$self->{DOCUMENT}}; +} + +1; + +__END__ + +=head1 NAME + +RTF::Document - Perl extension for generating Rich Text (RTF) Files + +=head1 DESCRIPTION + +RTF::Document is a module for generating Rich Text Format (RTF) documents +that can be used by most text converters and word processors. + +For a listing of properties, consult the %DOCINFO and %PROPERTIES hashes +in the source code. + +=head1 REQUIRED MODULES + + Carp + POSIX + Convert::Units::Type 0.33 + +=head1 EXAMPLE + + use RTF::Document; + + # Document properties + + $rtf = new RTF::Document( + { + doc_page_width => '8.5in', + doc_page_height => '11in' + } + ); + + # Font definitions + + $fAvantGarde = $rtf->add_font ("AvantGarde", + { family=>swiss, + default=>1 + } ); + $fCourier = $rtf->add_font ("Courier", + { family=>monospace, pitch=>fixed, + alternates=>["Courier New", "American Typewriter"] + } ); + + # Color definitions + + $cRed = $rtf->add_color ( { red=>255 } ); + $cGreen = $rtf->add_color ( { green=>128 } ); + $cCustm = $rtf->add_color ( { red=>0x66, blue=>0x33, green=>0x33 } ); + + $cBlack = $rtf->add_color ( { name=>'black' } ); + $cWhite = $rtf->add_color ( { gray=>'100%' } ); + + $cNavy = $rtf->add_color ( { blue=>'100%', gray=>'50%' } ); + + # style definitions + + $sNormal = $rtf->add_style( "Normal", + { font=>$fAvantGarde, font_size=>'12pt', + color_foreground=>$cBlack }, + { type=>paragraph, default=>1 } + ); + + $sGreen = $rtf->add_style( "Green", + { color_foreground=>$cGreen }, + { type=>character, additive=>1 } + ); + + # Mix any combo of properties and text... + + $rtf->add_text( $rtf->root(), + "Default text\n\n", + + { bold=>1, underline=>continuous }, + "Bold/Underlined Text\n\n", + + { font_size=>'20pt', font=>$fCourier, + color_foreground=>$cRed }, + "Bigger, Red and Monospaced.\n\n", + + { style_default=>paragraph }, + { style_default=>character }, + + "This is ", + [ { style=>$sGreen }, "green" ], + " styled.\n\n" + + ); + + open FILE, ">MyFile.rtf"; + binmode FILE; + print FILE $rtf->rtf(); + close FILE; + +=head1 DOCUMENT STRUCTURE + +For purposes of using this module, a Rich Text (RTF) Document can be subdivided into +I<groups>. Groups can be considered containers for I<text> and I<controls> (controlling +document and text properties). + +For all intents and purposes, a group limits the scope of controls. So if we set +the "bold" character property within a group, the text will be bold only within +that group (until it is turned off within that group). + +When generating a RTF document using this module, we are only concerned with the +I<root> group (also called the "Document Area"). (The "Header" groups are taken +care of automatically by this module.) + +The Document Area is subdivided into I<sections>. Each section is subdivided into +I<paragraphs>. + +=head1 METHODS + +Some of the methods are documented below. (Methods not documented here may +be changed in future versions.) + +=head2 new + + $rtf = new RTF::Document( \%DocumentProperties ); + +Creates a new RTF document object. + +=head2 root + + $gRoot = $rtf->root(); + +Returns the "root" group in the RTF document. + +=head2 new_group + + $gMyGroup = $rtf->new_group(); + +Creates a new group (not inside of the RTF document). + +=head2 add_group + + $gChildOfRoot = $rtf->add_group(); + $gChildOfChild = $rtf->add_group( $gChild ); + +Adds a child group to the specfied group. If no group is specified, the "root" +group is assumed. + + $rtf->add_group( $rtf->root(), $gMyGroup ); + +Adds a group to the specified parent group (in this case, the root group). + +=head2 add_raw + + $rtf->add_raw( $group, '\par', "Some Text" ); + +Adds raw controls and text to the group. This method is intended for internal +use only. + +=head2 add_text + + $rtf->add_text( $group, "Some text ", { bold=>1 }, "more text" ); + +Adds text and controls to a group. Text is escaped. + +=head2 add_font + +=head2 add_color + +=head2 add_style + +=head1 PROPERTIES + +=head2 Document Properties + +=head2 Section Properties + +=head2 Paragraph Properties + +=head2 Character Properties + +=head1 KNOWN ISSUES + +This module should be considered in the "alpha" stage. Use at your own risk. + +There are no default document or style properties produced by this module, +with the exception of the character set. If you want to make sure that a +I<specific> font, color, or style is used, you must specify it. Otherwise +you rely on the assumptions of whatever RTF reader someone is using. + +This module does not insert newlines anywhere in the text, even though some +RTF writers break lines before they exceed 225 characters. This may or may +not be an issue with some reader software. + +Unknown font or style properties will generally be ignored without warning. + +This module supports some newer RTF controls (used in Word 95/Word 97) that +may are not understood by older RTF readers. + +Once a Font, Color or Style is added, it cannot be changed. No checking +for redundant entries is done. + +Generally, it is not possible to reference a not-yet-created Style with the +next or basedon attributes. However, you can use the constances "last", +"self" or "next" to reference the last style added, the current style +being added, or the next style that will be added, respectively. + +Specifying properties in a particular order within a group does not +guarantee that they will be emitted in that order. If order matters, +specify them separetly. For instance, + + $rtf->add_text($rtf->root, { style_default=>character, bold=>1 } ); + +should be (if you want to ensure character styles are reset before setting +bold text): + + $rtf->add_text($rtf->root, { style_default=>character }, { bold=>1 } ); + +Also note that duplicate properties within the same group won't work. i.e., +If you want to set "style_default" for both paragraphs and characters, you +must do so in separate groups. + +This isn't so much as a bug as the way Perl handles hashes. + +=head2 Unimplemented Features + +A rather large number of features and control words are not handled in this +version. Among the major features: + +=over + +=item Annotations and Comments + +=item Bookmarks + +=item Bullets and Paragraph Numbering + +Some support has been added. The backwards-compatability controls for numbered +paragraphs used by older readers has not been added because it is not properly +handled by newer readers. + +=item Character Sets and Internationalization + +Non-"ANSI" character sets (i.e., Macintosh) and Unicode character +sets are not supported (at least not intentionally). There is no +support for Asian character sets in this version of the module. + +Unicode character escapes are not implemented. + +Language codes (defining a default language, or a language for a +group of characters) are not implemented. + +Bi-directional and text-flow controls are not implemented. + +=item Embedded Images and OLE Objects + +=item File Tables + +=item Footnotes and Endnotes + +=item Forms + +=item Headers and Footers + +=item Hyphenation Control + +Some minimal controls have been added. + +=item Lists and List Tables + +Not implemented: List Tables are really a kind of style sheet for lists. +Priority will be given to support generic bullets and paragraph numbering. + +=item Page Numbering + +Minimal definition, untested. + +=item Printer Bin Controls + +=item Revision Tables + +=item Special Characters and Document Variables + +Most special characters not not implemented, with the exception of tabs. Double +newline characters are converted to a new paragraph control, and single newlines +are converted to a new line control. + +=item Tabs + +=item Tables and Frames + +=back + +=head1 SEE ALSO + +Microsoft Technical Support and Application Note, "Rich Text Format (RTF) +Specification and Sample Reader Program", Version 1.5. + +I<Convert::Units::Type>. + +=head1 AUTHOR + +Robert Rothenberg <wlkng...@unix.asb.com> + +=head1 LICENSE + +Copyright (c) 1999-2000 Robert Rothenberg. All rights reserved. +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=cut + diff --git a/debian/changelog b/debian/changelog index f4552af..ec48c43 100644 --- a/debian/changelog +++ b/debian/changelog @@ -11,6 +11,7 @@ librtf-document-perl (0.64-11) UNRELEASED; urgency=medium + Add debian/source/format + Drop build-dependency on dpatch + Drop dependencies on patch target from debian/rules. + * Drop complete Diff of Document.pm by fixing the line-endings. -- Axel Beckert <a...@debian.org> Tue, 21 Jul 2015 21:03:51 +0200 diff --git a/debian/patches/01_make_it_strict.patch b/debian/patches/01_make_it_strict.patch index f1122bb..51c08e3 100644 --- a/debian/patches/01_make_it_strict.patch +++ b/debian/patches/01_make_it_strict.patch @@ -1,238 +1,240 @@ -Author: -Description: ---- librtf-document-perl.orig/Document.pm -+++ librtf-document-perl/Document.pm +Author: +Description: +Index: librtf-document-perl/Document.pm +=================================================================== +--- librtf-document-perl.orig/Document.pm 2015-07-21 21:55:14.839491051 +0200 ++++ librtf-document-perl/Document.pm 2015-07-21 21:56:00.519375457 +0200 @@ -14,6 +14,7 @@ - @EXPORT_OK = qw(); - - use Carp; -+use strict 'vars', 'subs'; - use POSIX qw(floor ceil); - use Convert::Units::Type 0.33; - + @EXPORT_OK = qw(); + + use Carp; ++use strict 'vars', 'subs'; + use POSIX qw(floor ceil); + use Convert::Units::Type 0.33; + @@ -129,7 +130,7 @@ - sub _prop_style { - my ($self, $code, $arg) = @_; - $code = decode_stylename($arg, '\s222'); -- my $formatting, $style_properties; -+ my ($formatting, $style_properties); - - if (defined($code)) { - $formatting = $self->new_group(); + sub _prop_style { + my ($self, $code, $arg) = @_; + $code = decode_stylename($arg, '\s222'); +- my $formatting, $style_properties; ++ my ($formatting, $style_properties); + + if (defined($code)) { + $formatting = $self->new_group(); @@ -242,16 +243,16 @@ - 'doc_rtf_def' => [ 'text', 'defformat', 0, \&_prop_on ], - - # --- Page sizes, margins, etc. -- doc_page_width => [ text, paperw, 0, \&_prop_twips ], -- doc_page_height => [ text, paperh, 0, \&_prop_twips ], -- doc_landscape => [ text, landscape, 0, \&_prop_on ], -- doc_facing => [ text, facingp, 0, \&_prop_on ], -- doc_margin_left => [ text, margl, 0, \&_prop_twips ], -- doc_margin_right => [ text, margr, 0, \&_prop_twips ], -- doc_margin_top => [ text, margt, 0, \&_prop_twips ], -- doc_margin_bottom => [ text, margb, 0, \&_prop_twips ], -- doc_margin_mirror=> [ text, margmirror, 0, \&_prop_on ], -- doc_gutter => [ text, gutter, 0, \&_prop_twips ], -+ doc_page_width => [ 'text', 'paperw', 0, \&_prop_twips ], -+ doc_page_height => [ 'text', 'paperh', 0, \&_prop_twips ], -+ doc_landscape => [ 'text', 'landscape', 0, \&_prop_on ], -+ doc_facing => [ 'text', 'facingp', 0, \&_prop_on ], -+ doc_margin_left => [ 'text', 'margl', 0, \&_prop_twips ], -+ doc_margin_right => [ 'text', 'margr', 0, \&_prop_twips ], -+ doc_margin_top => [ 'text', 'margt', 0, \&_prop_twips ], -+ doc_margin_bottom => [ 'text', 'margb', 0, \&_prop_twips ], -+ doc_margin_mirror=> [ 'text', 'margmirror', 0, \&_prop_on ], -+ doc_gutter => [ 'text', 'gutter', 0, \&_prop_twips ], - - # --- Hyphenation - doc_hyphen_auto => [ 'text', 'hyphauto', 0, \&_prop_onoff ], + 'doc_rtf_def' => [ 'text', 'defformat', 0, \&_prop_on ], + + # --- Page sizes, margins, etc. +- doc_page_width => [ text, paperw, 0, \&_prop_twips ], +- doc_page_height => [ text, paperh, 0, \&_prop_twips ], +- doc_landscape => [ text, landscape, 0, \&_prop_on ], +- doc_facing => [ text, facingp, 0, \&_prop_on ], +- doc_margin_left => [ text, margl, 0, \&_prop_twips ], +- doc_margin_right => [ text, margr, 0, \&_prop_twips ], +- doc_margin_top => [ text, margt, 0, \&_prop_twips ], +- doc_margin_bottom => [ text, margb, 0, \&_prop_twips ], +- doc_margin_mirror=> [ text, margmirror, 0, \&_prop_on ], +- doc_gutter => [ text, gutter, 0, \&_prop_twips ], ++ doc_page_width => [ 'text', 'paperw', 0, \&_prop_twips ], ++ doc_page_height => [ 'text', 'paperh', 0, \&_prop_twips ], ++ doc_landscape => [ 'text', 'landscape', 0, \&_prop_on ], ++ doc_facing => [ 'text', 'facingp', 0, \&_prop_on ], ++ doc_margin_left => [ 'text', 'margl', 0, \&_prop_twips ], ++ doc_margin_right => [ 'text', 'margr', 0, \&_prop_twips ], ++ doc_margin_top => [ 'text', 'margt', 0, \&_prop_twips ], ++ doc_margin_bottom => [ 'text', 'margb', 0, \&_prop_twips ], ++ doc_margin_mirror=> [ 'text', 'margmirror', 0, \&_prop_on ], ++ doc_gutter => [ 'text', 'gutter', 0, \&_prop_twips ], + + # --- Hyphenation + doc_hyphen_auto => [ 'text', 'hyphauto', 0, \&_prop_onoff ], @@ -260,10 +261,10 @@ - doc_hyphen_zone => [ 'text', 'hyphhotz', 0, \&_prop_twips ], - - # --- Views -- doc_view_scale => [ text, viewscale, 0, \&_prop_raw ], -- doc_view_zoom => [ text, { none=>'viewzk0', 'full-page'=>'viewzk1', -+ doc_view_scale => [ 'text', 'viewscale', 0, \&_prop_raw ], -+ doc_view_zoom => [ 'text', { none=>'viewzk0', 'full-page'=>'viewzk1', - 'best-fit'=>'viewzk1' }, 0, \&_prop_decode ], -- doc_view_caption => [ text, windowcaption, 1, , \&_prop_pcdata ], -+ doc_view_caption => [ 'text', 'windowcaption', 1, , \&_prop_pcdata ], - 'doc_view_mode' => [ 'text', { 'none'=>'viewkind0', 'layout'=>'viewkind1', - 'outline'=>'viewkind2', 'master'=>'viewkind3', - + doc_hyphen_zone => [ 'text', 'hyphhotz', 0, \&_prop_twips ], + + # --- Views +- doc_view_scale => [ text, viewscale, 0, \&_prop_raw ], +- doc_view_zoom => [ text, { none=>'viewzk0', 'full-page'=>'viewzk1', ++ doc_view_scale => [ 'text', 'viewscale', 0, \&_prop_raw ], ++ doc_view_zoom => [ 'text', { none=>'viewzk0', 'full-page'=>'viewzk1', + 'best-fit'=>'viewzk1' }, 0, \&_prop_decode ], +- doc_view_caption => [ text, windowcaption, 1, , \&_prop_pcdata ], ++ doc_view_caption => [ 'text', 'windowcaption', 1, , \&_prop_pcdata ], + 'doc_view_mode' => [ 'text', { 'none'=>'viewkind0', 'layout'=>'viewkind1', + 'outline'=>'viewkind2', 'master'=>'viewkind3', + @@ -276,7 +277,7 @@ - 'doc_charset' => [ 'charset' ], - - # --- Widow/orphan controls -- doc_widow_cntrl => [ text, widowctrl, 0, \&_prop_on ], -+ doc_widow_cntrl => [ 'text', 'widowctrl', 0, \&_prop_on ], - - # --- Tabs - tabs_default => [ 'text', 'deftab', 0, \&_prop_twips ], + 'doc_charset' => [ 'charset' ], + + # --- Widow/orphan controls +- doc_widow_cntrl => [ text, widowctrl, 0, \&_prop_on ], ++ doc_widow_cntrl => [ 'text', 'widowctrl', 0, \&_prop_on ], + + # --- Tabs + tabs_default => [ 'text', 'deftab', 0, \&_prop_twips ], @@ -296,14 +297,14 @@ - 'page'=>'sbkpage', 'even'=>'sbkeven', 'odd'=>'sbkodd'}, 0, \&_prop_decode ], - - # --- Columns -- col => [ text, 'colulmn', 0, \&_prop_on ], -- col_soft => [ text, 'softcol', 0, \&_prop_on ], -- col_num => [ text, 'cols', 0, \&_prop_raw ], -- col_space => [ text, 'colsx', 0, \&_prop_twips ], -- col_select => [ text, 'colno', 0, \&_prop_raw ], -- col_padding_right => [ text, 'colsr', 0, \&_prop_twips ], -- col_width => [ text, 'colw', 0, \&_prop_twips ], -- col_line => [ text, 'linebetcol', 0, \&_prop_on ], -+ col => [ 'text', 'colulmn', 0, \&_prop_on ], -+ col_soft => [ 'text', 'softcol', 0, \&_prop_on ], -+ col_num => [ 'text', 'cols', 0, \&_prop_raw ], -+ col_space => [ 'text', 'colsx', 0, \&_prop_twips ], -+ col_select => [ 'text', 'colno', 0, \&_prop_raw ], -+ col_padding_right => [ 'text', 'colsr', 0, \&_prop_twips ], -+ col_width => [ 'text', 'colw', 0, \&_prop_twips ], -+ col_line => [ 'text', 'linebetcol', 0, \&_prop_on ], - - 'page_brk' => [ 'text', 'page', 0, \&_prop_on ], - 'page_softbrk' => [ 'text', 'softpage', 0, \&_prop_on ], + 'page'=>'sbkpage', 'even'=>'sbkeven', 'odd'=>'sbkodd'}, 0, \&_prop_decode ], + + # --- Columns +- col => [ text, 'colulmn', 0, \&_prop_on ], +- col_soft => [ text, 'softcol', 0, \&_prop_on ], +- col_num => [ text, 'cols', 0, \&_prop_raw ], +- col_space => [ text, 'colsx', 0, \&_prop_twips ], +- col_select => [ text, 'colno', 0, \&_prop_raw ], +- col_padding_right => [ text, 'colsr', 0, \&_prop_twips ], +- col_width => [ text, 'colw', 0, \&_prop_twips ], +- col_line => [ text, 'linebetcol', 0, \&_prop_on ], ++ col => [ 'text', 'colulmn', 0, \&_prop_on ], ++ col_soft => [ 'text', 'softcol', 0, \&_prop_on ], ++ col_num => [ 'text', 'cols', 0, \&_prop_raw ], ++ col_space => [ 'text', 'colsx', 0, \&_prop_twips ], ++ col_select => [ 'text', 'colno', 0, \&_prop_raw ], ++ col_padding_right => [ 'text', 'colsr', 0, \&_prop_twips ], ++ col_width => [ 'text', 'colw', 0, \&_prop_twips ], ++ col_line => [ 'text', 'linebetcol', 0, \&_prop_on ], + + 'page_brk' => [ 'text', 'page', 0, \&_prop_on ], + 'page_softbrk' => [ 'text', 'softpage', 0, \&_prop_on ], @@ -316,7 +317,7 @@ - - # --- Alignment - 'par_align' => [ 'text', { left=>'ql', right=>'qr', center=>'qc', justify=>'qj' }, 0, \&_prop_decode ], -- 'sec_vert_align' => [ 'text', { top=>vertalt, bottom=>vertalb, center=>vertalc }, 0, \&_prop_decode ], -+ 'sec_vert_align' => [ 'text', { top=>'vertalt', bottom=>'vertalb', center=>'vertalc' }, 0, \&_prop_decode ], - - # --- Indentation - 'par_indent_first' => [ 'text', 'fi', 0, \&_prop_twips ], + + # --- Alignment + 'par_align' => [ 'text', { left=>'ql', right=>'qr', center=>'qc', justify=>'qj' }, 0, \&_prop_decode ], +- 'sec_vert_align' => [ 'text', { top=>vertalt, bottom=>vertalb, center=>vertalc }, 0, \&_prop_decode ], ++ 'sec_vert_align' => [ 'text', { top=>'vertalt', bottom=>'vertalb', center=>'vertalc' }, 0, \&_prop_decode ], + + # --- Indentation + 'par_indent_first' => [ 'text', 'fi', 0, \&_prop_twips ], @@ -378,7 +379,7 @@ - 'par_hyphen' => [ 'text', 'hyphpar', 0, \&_prop_onoff ], - - # --- Widow/orphan controls -- 'par_widow_cntrl' => [ text, { 0=>nowidctlpar, 1=>widctlpar }, 0, \&_prop_decode ], -+ 'par_widow_cntrl' => [ 'text', { 0=>'nowidctlpar', 1=>'widctlpar' }, 0, \&_prop_decode ], - 'par_intact' => [ 'text', 'keep', 0, \&_prop_on ], - 'par_keep_next' => [ 'text', 'keepn', 0, \&_prop_on ], - + 'par_hyphen' => [ 'text', 'hyphpar', 0, \&_prop_onoff ], + + # --- Widow/orphan controls +- 'par_widow_cntrl' => [ text, { 0=>nowidctlpar, 1=>widctlpar }, 0, \&_prop_decode ], ++ 'par_widow_cntrl' => [ 'text', { 0=>'nowidctlpar', 1=>'widctlpar' }, 0, \&_prop_decode ], + 'par_intact' => [ 'text', 'keep', 0, \&_prop_on ], + 'par_keep_next' => [ 'text', 'keepn', 0, \&_prop_on ], + @@ -397,9 +398,9 @@ - my $self = shift; - - my $table = shift, -- $settings = shift, -- $destination = shift; -- my ($property, $value, $where, $what, $arg, $default); -+ my $settings = shift, -+ my $destination = shift; -+ my ($property, $value, $where, $what, $group, $arg, $default); - - local ($_); - + my $self = shift; + + my $table = shift, +- $settings = shift, +- $destination = shift; +- my ($property, $value, $where, $what, $arg, $default); ++ my $settings = shift, ++ my $destination = shift; ++ my ($property, $value, $where, $what, $group, $arg, $default); + + local ($_); + @@ -426,7 +427,7 @@ - { - if ($group) { - my $subgroup = $self->add_group($where); -- $self->add_raw ($subroup, @controls ); -+ $self->add_raw ($subgroup, @controls ); - } else { - $self->add_raw ($where, @controls ); - } + { + if ($group) { + my $subgroup = $self->add_group($where); +- $self->add_raw ($subroup, @controls ); ++ $self->add_raw ($subgroup, @controls ); + } else { + $self->add_raw ($where, @controls ); + } @@ -469,7 +470,7 @@ - my $self = shift; - $self->set_properties (\%DOCINFO, @_); - -- $self->splice_raw ($self->{DOCUMENT}, 1, 1, "\\".$self->{charset}); -+ $self->splice_raw ($self->{DOCUMENT}, 1, 1, "\\".$self->{charset}) if ( defined $self->{charset} ); - - # --- Insert creation time in Information Group - if ($self->{creatim}) + my $self = shift; + $self->set_properties (\%DOCINFO, @_); + +- $self->splice_raw ($self->{DOCUMENT}, 1, 1, "\\".$self->{charset}); ++ $self->splice_raw ($self->{DOCUMENT}, 1, 1, "\\".$self->{charset}) if ( defined $self->{charset} ); + + # --- Insert creation time in Information Group + if ($self->{creatim}) @@ -498,7 +499,7 @@ - } - - sub emit_group { -- local ($el, $data); -+ my ($el, $data); - - unless (@_) { - return undef; + } + + sub emit_group { +- local ($el, $data); ++ my ($el, $data); + + unless (@_) { + return undef; @@ -508,7 +509,7 @@ - - foreach $el (@_) - { -- if (ref($el) eq ARRAY) { -+ if (ref($el) eq "ARRAY") { - $data .= emit_group(@$el); - } else { - if (($el !~ m/^[\\\;\{\}]/) and (substr($data, length($data)-1) !~ m/[\}\{\s]/)) { + + foreach $el (@_) + { +- if (ref($el) eq ARRAY) { ++ if (ref($el) eq "ARRAY") { + $data .= emit_group(@$el); + } else { + if (($el !~ m/^[\\\;\{\}]/) and (substr($data, length($data)-1) !~ m/[\}\{\s]/)) { @@ -548,7 +549,7 @@ - my $self = shift; - - my $name = shift, -- $attributes = shift; -+ my $attributes = shift; - - my $class = $FONTCLASSES{${$attributes}{family}}; - + my $self = shift; + + my $name = shift, +- $attributes = shift; ++ my $attributes = shift; + + my $class = $FONTCLASSES{${$attributes}{family}}; + @@ -580,7 +581,8 @@ - - $self->add_raw ($fattr, escape_simple($name) ); - -- my @alternates = @{${$attributes}{alternates}}; -+ my @alternates = (); -+ @alternates = @{${$attributes}{alternates}} if ( defined @{${$attributes}{alternates}} ); - if (@alternates) { - while ($_ = shift @alternates) { - $self->add_raw ($fattr, [ '\*\falt '.escape_simple($_) ] ); + + $self->add_raw ($fattr, escape_simple($name) ); + +- my @alternates = @{${$attributes}{alternates}}; ++ my @alternates = (); ++ @alternates = @{${$attributes}{alternates}} if ( defined @{${$attributes}{alternates}} ); + if (@alternates) { + while ($_ = shift @alternates) { + $self->add_raw ($fattr, [ '\*\falt '.escape_simple($_) ] ); @@ -630,7 +632,7 @@ - $self->add_raw ( $self->{styletbl}, '\stylesheet'); - } - -- $type = ${$attributes}{type} || "paragraph"; -+ my $type = ${$attributes}{type} || "paragraph"; - my $code = $STYLETYPES{$type}; - unless (defined($code)) { - carp "Don\'t know how to handle a \`$type\' style"; + $self->add_raw ( $self->{styletbl}, '\stylesheet'); + } + +- $type = ${$attributes}{type} || "paragraph"; ++ my $type = ${$attributes}{type} || "paragraph"; + my $code = $STYLETYPES{$type}; + unless (defined($code)) { + carp "Don\'t know how to handle a \`$type\' style"; @@ -654,7 +656,7 @@ - if (defined(${$attributes}{next})); - - my $sbasedon = ${$attributes}{basedon} || "none", -- $snext = ${$attributes}{next} || "self"; -+ my $snext = ${$attributes}{next} || "self"; - - $sbasedon = decode_stylename($sbasedon, $style); - $snext = decode_stylename($snext, $style); + if (defined(${$attributes}{next})); + + my $sbasedon = ${$attributes}{basedon} || "none", +- $snext = ${$attributes}{next} || "self"; ++ my $snext = ${$attributes}{next} || "self"; + + $sbasedon = decode_stylename($sbasedon, $style); + $snext = decode_stylename($snext, $style); @@ -802,7 +804,7 @@ - my $position = shift; - my $length = shift; - -- splice @{$section}, $position, $length, @_; -+ splice @{$section}, $position, $length, @_ ; - } - - sub add_raw # add a raw value to a section + my $position = shift; + my $length = shift; + +- splice @{$section}, $position, $length, @_; ++ splice @{$section}, $position, $length, @_ ; + } + + sub add_raw # add a raw value to a section @@ -855,16 +857,16 @@ - - while ($arg = shift) { - $rarg = ref($arg); -- if ($rarg eq HASH) -+ if ($rarg eq "HASH") - { - $self->set_properties (\%PROPERTIES, $arg, $group); - } -- elsif ($rarg eq ARRAY) -+ elsif ($rarg eq "ARRAY") - { - my $subgroup = $self->add_group($group); - $self->add_text ($subgroup, @{$arg} ); - } -- elsif ($rarg eq SCALAR) -+ elsif ($rarg eq "SCALAR") - { - $self->add_text (${$arg}); - } + + while ($arg = shift) { + $rarg = ref($arg); +- if ($rarg eq HASH) ++ if ($rarg eq "HASH") + { + $self->set_properties (\%PROPERTIES, $arg, $group); + } +- elsif ($rarg eq ARRAY) ++ elsif ($rarg eq "ARRAY") + { + my $subgroup = $self->add_group($group); + $self->add_text ($subgroup, @{$arg} ); + } +- elsif ($rarg eq SCALAR) ++ elsif ($rarg eq "SCALAR") + { + $self->add_text (${$arg}); + } @@ -1192,5 +1194,9 @@ - This program is free software; you can redistribute it and/or - modify it under the same terms as Perl itself. - --=cut -+=head1 FIXES - -+Some bugs have been fixed by nmag only <n...@softhome.net>, now -+the code is clean and under strict directives. -+ -+=cut + This program is free software; you can redistribute it and/or + modify it under the same terms as Perl itself. + +-=cut ++=head1 FIXES + ++Some bugs have been fixed by nmag only <n...@softhome.net>, now ++the code is clean and under strict directives. ++ ++=cut -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-perl/packages/librtf-document-perl.git _______________________________________________ Pkg-perl-cvs-commits mailing list Pkg-perl-cvs-commits@lists.alioth.debian.org http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-perl-cvs-commits