File Coverage

blib/lib/Apache/Filter/HanConvert.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             # $File: //member/autrijus/Apache-Filter-HanConvert/HanConvert.pm $ $Author: autrijus $
2             # $Revision: #4 $ $Change: 2690 $ $DateTime: 2002/12/12 06:47:15 $
3              
4             package Apache::Filter::HanConvert;
5             $Apache::Filter::HanConvert::VERSION = '0.02';
6              
7 1     1   9596 use strict;
  1         3  
  1         107  
8 1     1   6 use warnings;
  1         3  
  1         44  
9              
10             =head1 NAME
11              
12             Apache::Filter::HanConvert - Filter between Chinese variant and encodings
13              
14             =head1 VERSION
15              
16             This document describes version 0.02 of Apache::Filter::HanConvert, released
17             December 12, 2002.
18              
19             =head1 SYNOPSIS
20              
21             In F:
22              
23             PerlModule Apache::Filter::HanConvert
24             PerlOutputFilterHandler Apache::Filter::HanConvert
25             PerlSetVar HanConvertFromVariant "traditional"
26              
27             =head1 DESCRIPTION
28              
29             This module utilizes the B module with B's
30             output filtering mechanism, to provide a flexible and customizable
31             solution for serving multiple encoding/variants from the same source
32             documents.
33              
34             From the settings in L, the server would negotiate with the
35             client's browser about their C preference (C and
36             C means Simplified, other C means Traditional), as well as the
37             preferred C setting (defaults to C if nothing
38             was explicitly specified).
39              
40             The C header will be rewritten to reflect the final
41             encoding used.
42              
43             If you want to use other encodings, try adding these lines:
44              
45             PerlSetVar HanConvertFromEncoding "UTF-8"
46             PerlSetVar HanConvertToEncodingTraditional "big5"
47             PerlSetVar HanConvertToEncodingSimplified "gbk"
48              
49             Finally, if you'd like to dictate it to always convert to a specific
50             variant/encoding, use this:
51              
52             PerlSetVar HanConvertToVariant "simplified"
53             PerlSetVar HanConvertToEncoding "gbk"
54              
55             =head1 CAVEATS
56              
57             The C config probably could take multiple
58             encodings and apply L to find out the correct source
59             encoding.
60              
61             Currently this module does not work with C, so the server's
62             C setting won't be honored. Patches welcome!
63              
64             =cut
65              
66 1     1   4036 use Encode ();
  1         21689  
  1         29  
67 1     1   973 use Encode::HanConvert 0.10 ();
  1         6447  
  1         17928  
68              
69 1     1   1933 use Apache2 ();
  0            
  0            
70             use Apache::Filter ();
71             use Apache::RequestRec ();
72              
73             use APR::Brigade ();
74             use APR::Bucket ();
75              
76             use Apache::Const -compile => qw(OK DECLINED);
77             use APR::Const -compile => ':common';
78              
79             my %variants = (
80             'TS' => 'trad-simp',
81             'ST' => 'simp-trad',
82             'XS' => 'trad-simp',
83             'XT' => 'simp-trad',
84             );
85              
86             my %encodings = (
87             'T' => 'HanConvertToEncodingTraditional',
88             'S' => 'HanConvertToEncodingSimplified',
89             );
90              
91             my %charsets = (
92             'T' => qr{
93             big-?5 |
94             big5-?et(:en)? |
95             (?:tca|tw)[-_]?big5 |
96             big5-?hk(?:scs)? |
97             hk(?:scs)?[-_]?big5 |
98             MacChineseTrad |
99             cp950 |
100             (?:x-)winddows-950 |
101             (?:cmex[-_]|tw[-_])?big5-?e(?:xt)? |
102             (?:cmex[-_]|tw[-_])?big5-?p(?:lus)? |
103             (?:cmex[-_]|tw[-_])?big5\+ |
104             (?:ccag[-_])?cccii |
105             euc[-_]tw |
106             tw[-_]euc |
107             utf-?8 |
108             ucs-?2[bl]e |
109             utf-?(?:16|32)(?:[bl]e)?
110             }x,
111             'S' => qr{
112             euc[-_]cn |
113             cn[-_]euc |
114             iso-ir-165 |
115             MacChineseSimp |
116             cp936 |
117             (?:x-)winddows-936 |
118             hz |
119             gb[-_ ]?2312(?:\D+)? |
120             gb[-_ ]?18030 |
121             utf-?8 |
122             ucs-?2[bl]e |
123             utf-?(?:16|32)(?:[bl]e)?
124             }x
125             );
126              
127             sub Apache::Filter::HanConvert::handler {
128             my($filter, $bb) = @_;
129              
130             my $r = $filter->r;
131             my $content_type = $r->content_type;
132              
133             return Apache::DECLINED
134             if defined( $content_type ) and $content_type !~ m|^text/|io;
135              
136             my $from_variant = uc(substr($r->dir_config("HanConvertFromVariant"), 0, 1)) || 'X';
137             my $from_encoding = $r->dir_config("HanConvertFromEncoding");
138             my $to_variant = uc(substr($r->dir_config("HanConvertToVariant"), 0, 1));
139             my $to_encoding = $r->dir_config("HanConvertToEncoding");
140              
141             if (!$to_variant) {
142             my $langs = $r->headers_in->get('Accept-Language');
143              
144             $to_variant = (($1 and $1 ne 'cn') ? 'T' : 'S')
145             if $langs =~ /\bzh(?:-(tw|cn|hk|sg))?\b/;
146             }
147              
148             return Apache::DECLINED unless $to_variant;
149              
150             $to_encoding ||= $r->dir_config($encodings{$to_variant});
151            
152             if (!$to_encoding) {
153             my $chars = $r->headers_in->get('Accept-Charset');
154              
155             $to_encoding = $1
156             if $chars =~ /\b($charsets{$to_variant})\b/i;
157             }
158              
159             my $var_enc = $variants{"$from_variant$to_variant"} || 'utf8';
160             $from_encoding = Encode::resolve_alias($from_encoding) || 'utf8';
161             $to_encoding = Encode::resolve_alias($to_encoding) || 'utf8';
162              
163             return Apache::DECLINED if $from_encoding eq $to_encoding
164             and $from_variant eq $to_variant;
165              
166             my $charset = ($to_encoding eq 'utf8' ? 'utf-8' : $to_encoding);
167             $content_type =~ s/(?:;charset=[^;]+(.*))?$/;charset=$charset$1/;
168             $r->content_type($content_type);
169              
170             my $c = $filter->c;
171             my $bb_ctx = APR::Brigade->new($c->pool, $c->bucket_alloc);
172             my $data = '';
173              
174             while (!$bb->empty) {
175             my $bucket = $bb->first;
176              
177             $bucket->remove;
178              
179             if ($bucket->is_eos) {
180             $bb_ctx->insert_tail($bucket);
181             last;
182             }
183              
184             my $buffer;
185             my $status = $bucket->read($buffer);
186             return $status unless $status == APR::SUCCESS;
187              
188             Encode::from_to($buffer, $from_encoding => 'utf8', Encode::FB_HTMLCREF)
189             if $from_encoding ne 'utf8';
190              
191             if ($var_enc eq $to_encoding) {
192             $bucket = APR::Bucket->new( $buffer );
193             }
194             elsif ($data .= $buffer) {
195             $bucket = APR::Bucket->new( Encode::encode(
196             $to_encoding, Encode::decode($var_enc, $data, Encode::FB_QUIET)
197             ) );
198             }
199              
200             $bb_ctx->insert_tail($bucket);
201             }
202              
203             my $rv = $filter->next->pass_brigade($bb_ctx);
204             return $rv unless $rv == APR::SUCCESS;
205              
206             Apache::OK;
207             }
208              
209             1;
210              
211             __END__