H1: Creating New Filters

H2: Using Event Processing

SDF's event processing feature makes it easy to create
a new filter from an existing one. For example, the
following lines make {{grid}} a variation of the
{{FILT:table}} filter:

E:!on filter 'grid';; \
E:    $name='table'; $params.="; style='grid'"

H2: Using Perl

Perl subroutines can be used to implement filter logic.
An SDF filter called {{xxx}} is mapped to a Perl subroutine
called {{xxx}}_Filter within the SDF_USER package.
The subroutine has the following interface:

E:       xxx_Filter(*text, %param);

:where:

* {{@text}} is the text (original in and result out)
* {{%param}} is the set of parameters (name-value pairs).

For example, the Perl implementation of the {{FILT:appendix}} filter is:

!block Perl
sub appendix_Filter {
    local(*text, %param) = @_;

    # change headings
    unshift(@text, '!on paragraph \'H\d\'; __appendix; $style =~ s/H/A/');
    push   (@text, '!off paragraph __appendix');
}
!endblock

The {{FILE:perllib/sdf/filters.pl}} file within the SDF distribution
contains lots of examples of filters implemented as Perl subroutines.


H2: Adding Parameters

Parameters can be declared in a Perl array called _{{xxx}}_FilterParams.
The array is a table in {{FMT:TBL}} format which contains
the following fields:

!block table
Field   Description
Name    The parameter name
Type    The parameter type
Rule    The parameter validation rule, if any
!endblock

Parameter types and rules follow the same conventions
as macro argument types and rules. Refer to
{{SECT:Creating New Macros}} for details.

For example, the parameters for the {{FILT:example}} filter are declared
like this:

!block Perl
@_example_FilterParams = (
    'Name       Type        Rule',
    'skipheader boolean',
    'lang       string',
    'wide       boolean',
    'listitem   integer',
    'pure       boolean',
);
!endblock


H2: Arbitary Parameters

Occasionally, it is useful to support arbitary parameters.
To do this, specify ('ANY') as the value of the parameter table.
For example, the {{FILT:sdf}} filter can be used to apply an
arbitary set of attributes to a block of text. Its parameter table is:

!block Perl
@_sdf_FilterParams = ('ANY');
!endblock


H2: Data Models

If the text is a table, the filter typically has a data model
(i.e. validation table) called {{xxx_FilterModel}}.
To speed up processing, model validation is only done
in verbose mode. For example, the {{FILT:define}} filter
is defined as follows:

!block Perl
# define - variable definitions
@_define_FilterParams = (
    'Name       Type        Rule',
    'family     string      <\w+>',
    'export     boolean',
);
@_define_FilterModel = (
    'Field      Category    Rule',
    'Name       key         <\w+>',
    'Value      optional',
);
sub define_Filter {
    local(*text, %param) = @_;
    local(@tbl, @flds, $rec, %values);
    local($family, $export, $name);

    # Parse and validate the data
    @tbl = &::TableParse(@text);
    @text = ();
    &_FilterValidate(*tbl, *_define_FilterModel) if $validate;

    # Process the data
    $family = $param{'family'};
    $family .= '_' if $family ne '';
    $export = $param{'export'};
    (@flds) = &::TableFields(shift @tbl);
    for $rec (@tbl) {
        %values = &::TableRecSplit(*flds, $rec);
        $name = $family . $values{'Name'};
        push(@text, "!define $name $values{'Value'}");
        push(@text, "!export $name") if $export;
    }
}
!endblock


H2: Dynamic Filter Loading

Most filters are either built-in or defined in a library or
module which is loaded at the top of a document. The {{FILT:script}}
filter makes it easy to embed Perl code within an SDF file,
and library and module files are simply normal SDF files.

However, if SDF is asked to apply a filter which it does not know about,
it looks for a file called {{filter_name}}.sdp and dynamically
loads the filter from that file if it can. This feature is useful
for complex filters which are rarely used.

See the {{FILE:stdlib/module.sdp}} file for an example.
