home *** CD-ROM | disk | FTP | other *** search
- package RISCOS::DrawFile::TextArea::Parser;
- use Carp;
-
- use strict;
- use vars qw ($VERSION @ISA %parser $lax $mode);
- use RISCOS::Units qw(millipoint2draw draw2millipoint point2draw);
- require RISCOS::DrawFile::TextArea;
- require RISCOS::Font;
-
- $VERSION = 0.02;
- @ISA = 'RISCOS::DrawFile::TextArea';
-
- # Definately not threadable.
- $mode = '' unless defined $mode;
- # '' for native
- # 'D' for Draw emulation
- # '+' for DrawPlus emulation
- # 'F' for DrawFile module emulation
- # 'L' for Librarian emulation
-
- sub new {
- my $proto = shift;
- my $class = ref($proto) || $proto;
-
- my $self = RISCOS::DrawFile::TextArea->new (@_);
- $self = $self->[0] if ref ($self) eq 'ARRAY';
-
- bless ($self, $class);
-
- my $output = [];
- # foreach (@{$self->Cols()}) {
- # push @$output, scalar &RISCOS::DrawFile::Path::Rectangle($_, undef, [255, 0, 0]);
- # }
-
- $self->{'__MODE'} = $mode;
- $self->{'__ALIGN'} = 'L';
- $self->{'__FONT'} = {};
- $self->{'__PLEAD'} = $self->{'__LEAD'} = point2draw 10;
- $self->StartY() if ($self->{'__MODE'} eq 'F');
- # Bozo DrawFile module interprets the spec rather strictly and *always*
- # starts the first line at \L10
-
-
- $self->Margins (1, 1);
- # $self->{'__STARTY'} is undefined, and read as __LEAD first time
- # Oh, unless we're the Drawfile module in which case it is 10 points first
- # time.
-
- my $text = \$self->{'__TEXT'};
-
- unless ($$text =~ s/^\\! 1\n//s or $lax) {
- $$text =~ /^(.{1,5})/s;
- warn "TextArea starts with '$1' not '\\! 1\n'";
- }
- $$text =~ tr/\t\n -\377//cd; # Delete unrecognised control chars.
-
- my $current = $self->reset_block(undef, undef, undef);
- my @line = $current;
- my $seen_printing;
- if ($self->{'__MODE'} eq 'L') {
- # Some sort of cacky definition of a no-show.
- # return ()
- # if $self->{'__FORE'} eq 'ÿÿÿÿ' and $self->{'__BACK'} eq 'ÿÿÿÿ';
- $self->{'__FORE'} = "\0\0\0\0";
- $self->{'__BACK'} = "\0ÿÿÿ";
- }
- # 0 action (default 0x118 - underline break, text vertical and
- # horizontal) shift
- # Not sure whether to believe this, as rechecking what differs
- # allows more sophisticated agglomeration if (say) UL is changed
- # to same thickness.
- # 1 x offset from left margin (draw units)
- # undef means read left margin for first, or carry on using
- # previous entry width
- # 2 y offset from previous x,y; (draw units)
- # 3 width of this object (unjustified) (draw units)
- # 4 font fore
- # 5 font back
- # 6 font object (name, width, height)
- # 7 underline position
- # 8 underline thickness
- # 9 text
- # 10 position (relative to left margin)
- # undef if it needs recalculating
- # 11 position + width
- # 12 number of spaces
- while (length $$text) {
- my ($action, $words, $para) = 0;
- if ($$text =~ /^\\(.)/s) {
- my $length = length $$text;
- # print STDERR "$1\n"; # \ Command
- if (defined (my $sub = $parser{$1})) {
- ($action, $words) = &$sub ($self, $text);
- if (length ($$text) == $length) {
- $$text =~ /^(.*)/m;
- die "Mangled command in '$1'";
- }
- } else {
- $$text =~ /^(.*)/m;
- die "Undefined command in '$1'";
- }
- } elsif ($$text =~ s/^(\n+)//s) {
- $para = length ($1) - 1;
- if ($$text =~ /^[\t ]/) {
- # As far as Draw is concerned "\n\t" or "\n " *is* a single
- # paragraph break
- $para = 1 if not $para and $self->{'__MODE'} eq 'D';
- # DrawFile module attempts to emulate Draw's bug whereby
- # \n\t is treated as a paragraph break, but in the
- # process intruduces its own. D'oh
- $para++ if $self->{'__MODE'} eq 'F';
- }
- if ($para) {
- # print "paragraph $para \n";
- $action = 0x20000;
- # Bleurgh. Draw breaks the spec.
- $para = int ((2 + $para) / 2) if $self->{'__MODE'} eq 'D';
- } else {
- # print "Embedded newline\n";
- # Ingore it as per spec if it is followed by space
- $words = ($$text =~ /^[\t ]/) ? '' : ' ';
- # DrawFile module eats final single newline
- $words = '' if $$text eq '' and $self->{'__MODE'} eq 'F';
- if (not $seen_printing and not $current->[6]) {
- die "No font set before single newline encountered\n";
- }
- }
- } else {
- $$text =~ s/^([^\\\n]*)//s;
- ($words = $1) =~ tr/\t/ /;
- # Remove a single newline following text that ends with a space.
- $$text =~ s/^\n(?![\n\t ])// if $words =~ / $/;
- }
- if (defined $words and length $words) {
- if (not $seen_printing and not $current->[6]) {
- carp "No font set before text '$words' encountered";
- return ();
- }
- $seen_printing = 1;
- $current->[3] # Width
- = millipoint2draw
- $current->[6]->StringBBox($current->[9] .= $words)->[2];
- # Increase the right bbox edge if it is already calculated.
- # No bozo, you can't remove this
- $current->[11] = $current->[10] + $current->[3]
- if defined $current->[10];
- }
-
- my $restore = 0;
- if (!length $current->[9]) {
- # $current points to an empty thing
- pop @line;
- $restore = 1;
- }
-
- if (@line) {
- WRAP: while (1) {
- # We have text. Check how much overflows margins
- # Hike off what we can if we overflow a line
- my $which = 0;
- # 1 hour later - don't let it auto-vivify
- while (defined $line[$which]
- and defined $line[$which]->[10]) {
- $which++
- }
-
- # This loop may run zero times
- while (defined $line[$which]) {
- # First see if we live at an absolute x offset
- unless (defined ($line[$which]->[10]
- = $line[$which]->[1])) {
- # First object is at left margin (ie offset zero from it)
- # Rest are previous object offset + previous object width
- # print STDERR "'$words' $which $line[$which-1]->[10]\n";
- $line[$which]->[10] = $which ? ($line[$which-1]->[10]
- + $line[$which-1]->[3])
- : 0;
- }
-
- $line[$which]->[11] = $line[$which]->[10]
- + $line[$which]->[3];
- $which++;
- }
- # Right, everything has a position
- my $width = $self->Width();
- for ( $which = 0 ; $which < @line ; $which++ ) {
- if ($line[$which]->[11] > $width) {
- # Wrap time
- my $victim = $line[$which];
- # print "$victim->[11] > $width\n";
- # print "Overflow with $which $victim->[10] "
- # ."'$victim->[9]'\n";
- my ($split) =
- $victim->[6]->Split($victim->[9], ' ', 0,
- # Remaining width
- draw2millipoint $width - $victim->[10]);
-
- # OK, life gets messy here.
- # Need to find the last space that fits in the column
- # (if any).
- # If $split is not '' then it's in $victim
- # Otherwise seach back in the line until we hit the last
- # space.
- #
- # Then need to run forward from the position of the
- # space checking all positions of soft hyphens until
- # we either find
- # 1 another space [this cuts search of early]
- # 2 a soft hyphen that doesn't fit
- # 3 we run out of text
- # hangon - actually, that means that we hit the
- # end of $victim, as we know that $victim won't
- # all fit, so there is no way that $victim plus
- # some more stuff will.
- # hangon, I don't think that we can meet a space
- # until we are in $victim again.
- #
- # each time that we find a soft hyphen that fits record
- # its position
- #
- # So we have variables:
- # object containing last known space/soft hyphen
- # $found
- # offset of (start of) last known space/soft hypen
- # $split
- # flag ('-' for soft hyphen, '' for space)
- # $flag
- # object we are currently searching.
- # $which
- #
- # soft hyphens are not marked known until we verify that
- # they fit on the line
- my ($length, $found);
- if (length $split) {
- $found = $which;
- # print "Splitting on space '$split'\n";
- } else {
- # OK, got to find that space
- while ($which) {
- if ($line[--$which]->[9] =~ /(.*) /) {
- $split = $1;
- $found = $which;
- last;
- }
- }
- }
- # Doubt this will really help until there are more
- # than about 3 soft hyphens in one word.
- # (Units in millipoints)
- # my $remains = $width
- # - $victim->[6]->CharBBox('-')->[2];
-
- # Now loop round the possible soft hypen positions
- # If you are Draw or DrawPlus you let soft hyphens
- # impinge into the right margin (but not go outside the
- # column)
-
- # Skip stuff before first space
- $length = length $split;
- my ($offset, $flag) = (0, '');
- OBJECTS: while (1) {
- # Assign to pos to start matching after prefix
- # (or to make damn sure we aren't confused later on
- # if this position happens to fail a soft hyphen
- # check and then we return here for a soft hyphen
- # check on the next line)
- pos $line[$which]->[9] = $length;
- while ($line[$which]->[9] =~ /\025-\n/sg) {
- last OBJECTS if ($width < (
- $line[$which]->[10] + # Start coord
- millipoint2draw
- $line[$which]->[6]->StringBBox("$`-")->[2]));
- # Wah, it's loads of -> -> ->
- # get here if it doesn't overflow
- $split = $`;
- $flag = '-';
- $found = $which;
- # print "Found - in $found '$split'\n";
- }
- # Got back to the object we started at, and we know
- # for sure that it doesn't fit
- last if $line[$which] eq $victim;
- # OK, starting a new object, so there is no prefix
- # text to skip
- $length = 0;
- $which++;
- }
-
- # OK.
- # $found is undef if we really haven't been able to
- # split anywhere. In that case split victim at any
- # character
- # otherwise
- # $found is the index of the object to split
- # $split is the text to strip from the front of it
- # $flag = '-' if we're splitting on a soft hyphen:
- # add '-' to $split
- # strip soft hyphen from front of remainder
- # else strip leading whitespace
- if (defined $found) {
- # Fake it as if we'd terminated at this point in the
- # first place.
- $which = $found;
- $victim = $line[$which];
- $victim->[9] = substr ($victim->[9], length $split);
- if ($flag) {
- # Soft hyphen found
- $victim->[9] =~ s/^\025-\n//s;
- $split .= $flag;
- } else {
- $victim->[9] =~ s/^ *//s;
- }
- # print "'$split' '$victim->[9]'\n";
- } else {
- die '!' unless $victim eq $line[$which];
- # Waah. Single word too long and must be split
- ($split)
- = $victim->[6]->Split($victim->[9], '', 0,
- draw2millipoint
- $width - $victim->[10]);
- unless (length $split) {
- $victim->[6] =~ /^([^ ]+)/;
- die 'First character of word \'' . $1 .
- '\' too wide for margins';
- }
- $victim->[9] = substr ($victim->[9], length $split);
- }
- # print "Loose '$split' keep '$victim->[9]'\n";
- my @process;
- if (length $victim->[9]) {
- # There is some text left
- $victim->[3] = millipoint2draw
- $victim->[6]->StringBBox($victim->[9])->[2];
- # print "Now $victim->[3]\n";
- # Remove all items up to item before the split item.
- @process = splice @line, 0, $which;
- # Copy the split item. The copy in @line has
- # the second half of the text and the correct
- # width
- # Our copy referenced via $vicitm can now be
- # ammended without affecting the details in @line
- $victim = [@$victim];
- } else {
- # Remove all items including the "split" item, which
- # appears to have no printable characters after
- # split point.
- # I don't think that we should ever end up in here.
- # print "You aren't here with $which '$split' "
- # ."'$victim->[9]' '$line[$which+1]->[9]'\n";
- # Wrong. \C255 0 0/swings \B0 255 0
- # splits on the space after swings. The space is
- # then eaten by the s///; above.
- # So we need to remove the empty last element, as it
- # is a reference to the same array as $victim.
- # (and hence when we assign to it (below) we'll be
- # writing to two places)
- @process = splice @line, 0, $which;
- shift @line; # Remove this empty entry
- unless (@line or $restore) {
- # Make sure there will be at least one entry
- # in the line accumulator next time round
- @line = $current = $self->reset_block();
- # 0x800000 hack was here. It doesn't work :-)
- }
- }
- if (@line) {
- # However, if there was a vertical move, we have it
- # in our copy, so the original doesn't need it
- $line[0]->[2] = 0;
- # And the original is on a new line, so it can't be
- # appended to the previous text.
- $line[0]->[0] |= 0x101;
- }
-
- # Remove all the calculated x positions on the remaining
- # items, so that next time round the loop they get
- # recalculated.
- foreach (@line) {
- undef $_->[10];
- }
- # Copy the first half text into vicitm and write its
- # width
- $victim->[3] = millipoint2draw
- $victim->[6]->StringBBox($victim->[9] = $split)->[2];
- $victim->[11] = $victim->[10] + $victim->[3];
- push @$output, &__doaline ($self, $self->{'__ALIGN'},
- @process, $victim);
- $self->PendingMargins();
- return $output
- unless defined $self->LeadBy($self->{'__LEAD'});
- redo WRAP;
- }
- }
- last WRAP;
- }
- }
- # warn "$restore $#line $current $line[$#line]";
- # warn "$restore $#line $current $line[$#line]";
- # Force final flush
- $action |= 0x10000 unless length ($$text);
-
- if ($action) {
- # Do it
- # (unless we're being the DrawFile module and \A)
- if ($action & 0x7F0000) {
- # Line or paragraph
- # printf "Flushing %8X:\n", $action;
- # foreach my $chunk (@line) {
- # print "\t$chunk->[8]\n";
- # }
- # print "\n";
-
- if ((($action & 0x7F0000) == 0x40000)
- and $self->{'__MODE'} eq 'F') {
- $action = 0
- } else {
- # Why this not work?
- # unless ($action & 0x40000 and not @line) {
- unless ($action & 0x40000 and not $seen_printing) {
- my $align = $self->{'__ALIGN'};
- $align = 'L' if $align eq 'D';
- push @$output, &__doaline ($self, $align, @line);
- @line = ();
- if ($action & 0x20000) {
- $self->LeadBy($self->{'__LEAD'}
- + $para * $self->{'__PLEAD'})
- } else {
- $self->LeadBy($self->{'__LEAD'})
- }
- $action |= 0x101;
- # Force text and underline breaks.
- }
- }
- # warn 'Bailout' if $self->{'__BAILOUT'};
- return $output if $self->{'__BAILOUT'};
- if (defined $self->{'__PENDINGALIGN'}) {
- $self->{'__ALIGN'} = $self->{'__PENDINGALIGN'};
- delete $self->{'__PENDINGALIGN'};
- }
- }
- if ($action & 0xFFFFFF) {
- # Making a new array current
- $restore = 1;
- $current = $self->reset_block(($seen_printing ? $action
- : undef),
- undef, undef, $current->[2]);
- }
- }
- # Do the pending margins unless we have some printing text.
- $self->PendingMargins() unless @line;
- push @line, $current if $restore;
- }
- $output
- }
-
-
- # Self
- # Alignment
- # Array of things
- sub __doaline {
- my $self = shift;
- my $align = shift;
- return () unless @_;
- my ($left, $right) = ($self->StartX(), $self->EndX());
- my ($extra, $x, $y) = (0, 0, $self->StartY());
-
- # Fake the margins to acchieve the desired alignment.
- if ($align eq 'D') {
- # $spaces = 0;
- my $j_from = @_; # Object number
- my ($spaces, $width);
-
- while ($j_from) {
- last if defined $_[--$j_from][1];
- }
- # $object >= 0
- my @done = splice @_, 0, $j_from;
- # @_ now contains only the objects that need justifying.
- my $j_left = $left + ($_[0][1] || 0);
-
- @_ = (@done, map {
- my $current = $_;
- my $flag = $$current[0];
- my $starty = $$current[2];
- my @list = ();
- $$current[0] = 0x20; # Text horizontal shift break
- $$current[2] = 0; # no vertical move
- while (length $$current[9] and $$current[9] =~ s/^([^ ]*)//) {
- my $bit = [@$current];
- $$bit[9] = $1;
- if ($$current[9] =~ s/^( +)//) {
- # Find if there are any trailing spaces and if so append
- # Write the space count for this object.
- $spaces += $$bit[12] = length $1;
- $$bit[9] .= $1;
- }
- if (length $$bit[9]) {
- $width += $$bit[3]
- = millipoint2draw $$bit[6]->StringBBox($$bit[9])->[2];
- push @list, $bit
- } else {
- confess "Error with '$$current[9]' remaining";
- }
- }
- $list[0][0] = $flag; # Restore true flags for first object
- $list[0][2] = $starty; # Restore true y offset for first object
- @list;
- } @_);
- # Spaces = 0 is equivalent to left justification.
- # oops. that's what happens anyway :-)
-
- # $width gives right edge of last thing
- $extra = (($right - $j_left - $width) / $spaces) if $spaces;
- # Split extra space equally for each space, even if they are different
- # font sizes.
- # print "$spaces $extra\n" if defined $spaces;
- } elsif ($align eq 'L') {
- } elsif ($align eq 'R') {
- # $width = $_[$#_]->[11];
- $left = $right - $_[$#_]->[11];
- } elsif ($align eq 'C') {
- my $centre = ($left + $right) / 2;
- my $width = $_[$#_]->[11];
- $left = $centre - $width / 2;
- $right = $centre + $width / 2;
- } else {
- confess "Unknown alignment code '$align'";
- }
-
- my ($starty, $nextx, @output);
- $starty = $y;
- $nextx = 0;
- my @text;
- # start x, start y, length, colour, thickness
- # length is left edge of next text object.
- my @underline;
-
- foreach my $object (@_) {
- $object->[9] =~ s/\025-\n//gs; # Strip still-soft hyphens
-
- $x = defined ($object->[1]) ? $object->[1] : $nextx;
-
- if ($object->[0] & 255) {
- # Break
- if (@text) {
- if (my $output = RISCOS::DrawFile::Text->new (\@text, 7)) {
- push @output, ref ($output) eq 'ARRAY' ? @$output : $output;
- }
- }
-
- $y -= $object->[2];
-
- # fore, back, font, h, w, x, y, text, kern, r2l, trans
- # colours already packed so pass a reference
- @text = (\$object->[4], \$object->[5], $object->[6], undef, undef,
- $left + $x, $y, $object->[9]);
- } else {
- $text[7] .= $object->[9];
- }
- $nextx = $x + $object->[3] + $extra * ($object->[12] || 0);
- # x still holds start of this object
- # nextx has x for next object (possibly corrected for justification
- if ($object->[0] & 0xFF00) {
- # Underline break
- push @output, scalar RISCOS::DrawFile::Path->new (
- # Move to the start, line to the width
- [[[2, $underline[0], $underline[1]],
- [8, $underline[2], $underline[1]]],
- # Fill, line colours, width
- undef, $underline[3], $underline[4]]) if @underline;
-
- if ($object->[8]) {
- # DrawPlus is just plain inconsistent here - change the zoom!
- @underline = ($left + $x, $y + $object->[7] - $object->[8] / 2,
- $left + $nextx, \$object->[4], $object->[8]);
- } else {
- @underline = ();
- }
- }
- elsif ($object->[8]) {
- $underline[2] = $left + $nextx;
- }
- }
- if (@text) {
- if (my $output = RISCOS::DrawFile::Text->new (\@text, 7)) {
- push @output, ref ($output) eq 'ARRAY' ? @$output : $output;
- }
- }
- push @output, scalar RISCOS::DrawFile::Path->new (
- [[[2, $underline[0], $underline[1]],
- [8, $underline[2], $underline[1]]],
- # Fill, line colours, width
- undef, $underline[3], $underline[4]]) if @underline;
- $starty -= $y;
- # Remove this for DrawPlus-like behaviour
- $self->LeadBy($starty) if $starty and ($self->{'__MODE'} ne '+' or
- $self->{'__MODE'} ne 'F');
-
- @output
- }
-
-
- sub reset_block ($;$$$) {
- my ($self, $action, $x, $y) = @_;
- # $x = $self->{'__LEFTM'} unless (@_ > 1); Don't set this.
-
- $y = 0 unless defined $y;
- if (defined ($self->{'__VERT'})) {
- $y += $self->{'__VERT'};
- undef $self->{'__VERT'};
- }
- $action = 0x118 unless defined $action;
-
- [$action, $x, $y, 0, $self->{'__FORE'}, $self->{'__BACK'},
- $self->CurrentFont(), $self->{'__UPOS'}, $self->{'__UWIDTH'}, ''];
- }
-
- sub CurrentColumn {
- my $self = shift;
- my $cc = \$self->{'__CURCOL'};
- return $$cc if defined $$cc;
- unless (defined ($$cc = $self->ShiftCols())) {
- $self->{'__BAILOUT'} = 1;
- } else {
- $self->{'__STARTX'} = $$$cc[0] + $self->{'__LEFTM'}
- if defined $self->{'__LEFTM'};
- if (defined $self->{'__RIGHTM'}) {
- $self->{'__ENDX'} = $$$cc[2] - $self->{'__RIGHTM'};
- $self->{'__LINEWIDTH'} = $self->{'__ENDX'} - $self->{'__STARTX'}
- if defined $self->{'__LEFTM'};
- }
- }
- $$cc;
- }
- sub StartY {
- my $self = shift;
- if (defined $self->{'__CURCOL'}) {
- my $y = $self->{'__STARTY'};
- return $y if defined $y;
- }
- return undef unless defined (my $cc = $self->CurrentColumn());
- $self->{'__STARTY'} = $cc->[3] - $self->{'__LEAD'};
- }
- sub PendingMargins() {
- return unless $_[0]->{'__PENDINGM'};
- $_[0]->Margins(@{$_[0]->{'__PENDINGM'}});
- delete $_[0]->{'__PENDINGM'};
- }
- sub Margins ($$$) {
- my ($self, $left, $right) = @_;
- my $col = $self->CurrentColumn();
- $self->{'__STARTX'} = $$col[0] + ($self->{'__LEFTM'} = point2draw $left);
- $self->{'__ENDX'} = $$col[2] - ($self->{'__RIGHTM'} = point2draw $right);
- $self->{'__LINEWIDTH'} = $self->{'__ENDX'} - $self->{'__STARTX'};
- }
- sub StartX ($) {
- $_[0]->{'__STARTX'};
- }
- sub EndX ($) {
- $_[0]->{'__ENDX'};
- }
- sub Width ($) {
- $_[0]->{'__LINEWIDTH'}
- }
- sub LeadBy ($$) {
- my ($self, $by) = @_;
- return undef if $self->{'__BAILOUT'};
- # This makes sure that __STARTY is initialised if a line break is found
- # before any printing text
- my $y = $self->{'__STARTY'} = $self->StartY() - $by;
- return $y if ($y >= $self->CurrentColumn()->[1]);
- # print "lead by $by drops off end\n";
- undef $self->{'__CURCOL'}; # New column
- $self->StartY();
- }
- sub CurrentFont {
- my $self = shift;
- my $font = $self->{'__FONT'}->{$self->{'__CURFONT'}}
- if defined $self->{'__CURFONT'};
- if (ref ($font) eq 'ARRAY') {
- # Find the font now. Don't bother finding fonts that are declared but
- # never used in the text area.
- # Don't leave the font as an array to pass to string width because this
- # will result in tons of calls to find and loose it for each call.
- # Remember that the destructor looses this object correctly :-)
- $font = RISCOS::Font->new(@$font);
- unless ($font) {
- warn $^E;
- $font = RISCOS::Font->new('Trinity.Medium', 12);
- }
- $self->{'__FONT'}->{$self->{'__CURFONT'}} = $font if defined $font;
- }
- $font;
- }
-
- # Pass in self, reference to scalar
- # 0x01000000 line format change (margins, centring, tabstops, leading)
- # 0x00800000 internal hack to force regeneration of block
- # 0x00040000 line break unless at start (or end) of line (\A)
- # 0x00020000 new paragraph
- # 0x00010000 forces line break (not new paragraph)
- # 0x00000100 underline break
- # 0x00000020 text horizontal shift break
- # 0x00000010 text vertical shift break
- # 0x00000008 text colour break
- # 0x00000004 text size break
- # 0x00000002 text font break
- # 0x00000001 text new paragraph
- # return param 2 (if defined) is text to add to output string
- # CHECK that input string is actually shorter - fatal error if it is not.
-
- sub A ($$) { # PRM wrong
- my $self = shift;
- ${$_[0]} =~ s#^\\A(.)[\n/]?##s; # \n eaten here
- $self->{'__PENDINGALIGN'} = $1; # align code is case sensitive
- 0x01040000 # Force a *conditional* line break (PRM wrong)
- }
-
- sub BC {
- ${$_[1]} =~ s#^\\[BC]\s*(\d+)\s+(\d+)\s+(\d+)[/\n]##s;
- pack 'I', ($1 << 8) | ($2 << 16) | ($3 << 24)
- }
- sub B ($$) { # PRM wrong
- my $self = $_[0];
- $self->{'__BACK'} = BC (@_);
- 8 # Force a text break
- }
- sub C ($$) { # PRM wrong
- my $self = $_[0];
- $self->{'__FORE'} = BC (@_);
- 8 # Force a text break
- }
-
- # Don't think that we actually pay attention to this
- sub D ($$) { # PRM wrong
- my $self = shift;
- ${$_[0]} =~ s#^\\D\s*(\d+)[/\n]##s;
- ($self->{'__COLNUM'}) = $1;
- undef
- }
-
- sub F ($$) { # PRM wrong
- my $self = shift;
- my $fonthash = $self->{'__FONT'};
- # And the PRM says:
- # ${$_[0]} =~ s#^\\F(\d\d?)(\S+)\s+(\d+)(\s+(\d+))?[/\n]##s;
- # And the truth is:
- ${$_[0]} =~ s#^\\F\s*(\d\d?)\s*(\S+)\s+([\d.]+)(\s+([\d.]+))?[/\n]##s;
- # ($num, $name, $width, $size)
- $fonthash->{$1} = [$2, $5, $3]; # 5 because 4 is the outer nesting ()
- 2 # Don't carry on
- # Pathalogical programmers write
- # \0Hello\F0 Trinity.Bold 12/ W\F0 Homerton.Medium 12/orld
- }
-
- sub digit ($$) { # PRM wrong
- my $self = shift;
- ${$_[0]} =~ s#^\\(\d\d?)[\n/]?##s; # \n eaten here
- # One point where I don't want undef == 0
- return 0 if defined ($self->{'__CURFONT'}) and $self->{'__CURFONT'} == $1;
- # Force a canonical numeric form, so that "03" and "3" hash identically
- $self->{'__CURFONT'} = 0 + $1;
- 0x006 # Text break. Underline does *not* change if font size changes
- }
-
- sub L ($$) { # PRM wrong
- my $self = shift;
- ${$_[0]} =~ s#^\\L\s*(\d+)[/\n]##s;
- $self->{'__LEAD'} = point2draw (0 + $1); # Mustn't pass in raw $1
- 0 # Carry on.
- }
-
- sub M ($$) { # PRM wrong
- my $self = shift;
- ${$_[0]} =~ s#^\\M\s*(\d+)\s+(\d+)[/\n]##s;
- $self->{'__PENDINGM'} = [$1, $2];
- 0x01000000 # Carry on. Mad! Well, it would be except that Draw interprets
- # M commands as being relevant to the next line, not this one.
- }
-
- sub P ($$) { # PRM wrong
- my $self = shift;
- ${$_[0]} =~ s#^\\P\s*(\d+)[/\n]##s;
- $self->{'__PLEAD'} = point2draw (0 + $1);
- 0x01000000 # Carry on.
- }
-
- sub U ($$) { # PRM wrong
- my $self = shift;
- if (${$_[0]} =~ s#^\\U\.[/]?##) { # This one is strict
- undef $self->{'__UPOS'};
- undef $self->{'__UWIDTH'};
- } else {
- my $size = point2draw ($self->CurrentFont()->PointY()) / 256;
- ${$_[0]} =~ s#^\\U\s*((-?[\d]+)\s+(\d+))[/\n]##s; # Definately \d
- # ($pos, $width)
- $self->{'__UPOS'} = $2 * $size;
- $self->{'__UWIDTH'} = $3 * $size;
- }
- 0x100 # Underline break.
- }
-
-
- sub V ($$) { # PRM wrong
- my $self = shift;
- ${$_[0]} =~ s#^\\V\s*(-?\d+)/?##s; # definately \d - decimals not allowed
- # for text - is up, but for paper co-ordinates y positive is upwards
- $self->{'__UPOS'} += $self->{'__VERT'} = point2draw (-$1); # - ensures copy
- # underline does not shift!
- 0x010 # Text break, not underline break
- }
-
- sub hyphen ($$) { # minus/hyphen soft hyphen PRM wrong
- ${$_[1]} =~ s#^\\-/?##s;
- # See XFont_Paint - this is comment. But perl can scan for it at split time.
- (0, "\25-\n") # Carry on.
- }
-
- sub newline ($$) { # newline
- ${$_[1]} =~ s#^\\\n##s;
- 0x10000 # Force line break
- }
-
- sub backslash ($$) { # backslash
- ${$_[1]} =~ s#^\\\\##s;
- (0, '\\') # Carry on.
- }
-
- sub semicolon ($$) { # semicolon comment
- ${$_[1]} =~ s#^\\;.*\n##;
- 0 # Carry on.
- }
-
- %parser = (
- 'A' => \&A,
- 'B' => \&B,
- 'C' => \&C,
- 'D' => \&D,
- 'F' => \&F,
- 'L' => \&L,
- 'M' => \&M,
- 'P' => \&P,
- 'U' => \&U,
- 'V' => \&V,
-
- '-' => \&hyphen,
- "\n" => \&newline,
- '\\' => \&backslash,
- ';' => \&semicolon,
- );
-
- $parser{0} = $parser{1} = $parser{2} = $parser{3} = $parser{4} =
- $parser{5} = $parser{6} = $parser{7} = $parser{8} = $parser{9} = \&digit;
-
- if ($mode eq 'L') {
- $lax = 1;
- $parser{'G'} = \&G,
- $parser{'T'} = \&T;
- $parser{'I'} = \&I;
- }
-
- # I
- # \IP/ text to here is index to page.
- # \II 4/ hidden page target
-
- sub I ($$) {
- my $self = shift;
- ${$_[0]} =~ s#^\\IP[/\n]|^\\II\s*(\S+?)[/\n]##s;
- 0 # Carry on.
- }
-
-
- # T
- # \T./ clear tabs?
- # \TL 172/ set tabstop?
- # \TR 351/ ditto
- # \TC 1/
-
-
- sub T ($$) {
- my $self = shift;
- undef $self->{'__TAB_S'};
- if (${$_[0]} =~ s#^\\T\s*\.[/\n]##) {
- undef $self->{'__TAB'};
- } else {
- ${$_[0]} =~ s#^\\T([LRC])\s*(\d+)[/\n]##s;
- $self->{'__TAB'}->{point2draw (0 + $2)} = $1 . $2;
- }
- 0
- }
-
- sub G ($$) {
- my $self = shift;
- ${$_[0]} =~ s#^\\G\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)[/\n]##s;
- $3 ne '0' ? (0, " «Insert illustration $4 at $1, $2» ")
- : (0, " «Insert illustration $4 at $1, $2 ¿$3?» ")
- }
-
- # G
- # \G 357 156 0 ID0500000001
- # x, y, dunno, ID never seen dunno != 0
- 1;
- __END__
-
- =head1 NAME
-
- RISCOS::Drawfile::TextArea::Parser
-
- =head1 SYNOPSIS
-
- =head1 DESCRIPTION
-
- =head1 BUGS
-
- =head1 AUTHOR
-
- Nicholas Clark <F<nick@unfortu.net>>
-