File Coverage

blib/lib/App/optex/textconv.pm
Criterion Covered Total %
statement 32 91 35.1
branch 0 28 0.0
condition n/a
subroutine 11 22 50.0
pod 0 9 0.0
total 43 150 28.6


line stmt bran cond sub pod time code
1             package App::optex::textconv;
2              
3             our $VERSION = '0.13';
4              
5 1     1   837 use v5.14;
  1         5  
6 1     1   6 use warnings;
  1         6  
  1         25  
7 1     1   578 use Encode;
  1         10661  
  1         88  
8              
9             =encoding utf-8
10              
11             =head1 NAME
12              
13             textconv - optex module to replace document file by its text contents
14              
15             =head1 VERSION
16              
17             Version 0.13
18              
19             =head1 SYNOPSIS
20              
21             optex command -Mtextconv
22              
23             optex command -Mtc (alias module)
24              
25             optex command -Mtextconv::load=pandoc
26              
27             =head1 DESCRIPTION
28              
29             This module replaces several sort of filenames by node representing
30             its text information. File itself is not altered.
31              
32             For example, you can check the text difference between MS word files
33             like this:
34              
35             $ optex diff -Mtextconv OLD.docx NEW.docx
36              
37             If you have symbolic link named B to B, and following
38             setting in your F<~/.optex.d/diff.rc>:
39              
40             option default --textconv
41             option --textconv -Mtextconv $
42              
43             Next command simply produces the same result.
44              
45             $ diff OLD.docx NEW.docx
46              
47             =head2 FILE FORMATS
48              
49             =over 7
50              
51             =item msdoc
52              
53             Microsoft office format files in XML (.docx, .pptx, .xlsx, .docm,
54             .pptm, .xlsm).
55             See L.
56              
57             =item pdf
58              
59             Use L command to covert PDF format.
60             See L.
61              
62             =item jpeg
63              
64             JPEG files is converted to their exif information (.jpeg, .jpg).
65              
66             =item http
67              
68             Name start with C or C is converted to text data
69             translated by L command.
70              
71             =item pandoc
72              
73             Use L command to translate Microsoft
74             office document in XML format.
75             See L.
76              
77             =item tika
78              
79             Use L command to translate
80             Microsoft office document in XML and non-XML format.
81             See L.
82              
83             =back
84              
85             =head1 MICROSOFT DOCUMENTS
86              
87             Microsoft office document in XML format (.docx, .pptx, .xlsx) is
88             converted to plain text by original code implemented in
89             L module. Algorithm used in this module
90             is extremely simple, and consequently runs fast.
91              
92             Two module are included in this distribution to use other external
93             converter program, B and B, those implement much more
94             serious algorithm. They can be invoked by calling B function
95             with module declaration like:
96              
97             optex -Mtextconv::load=pandoc
98              
99             optex -Mtextconv::load=tika
100              
101             =head1 INSTALL
102              
103             =head2 CPANM
104              
105             $ cpanm App::optex::textconv
106             or
107             $ curl -sL http://cpanmin.us | perl - App::optex::textconv
108              
109             =head2 GIT
110              
111             These are sample configurations using L in git
112             environment.
113              
114             ~/.gitconfig
115             [diff "msdoc"]
116             textconv = optex -Mtextconv cat
117             [diff "pdf"]
118             textconv = optex -Mtextconv cat
119             [diff "jpg"]
120             textconv = optex -Mtextconv cat
121              
122             ~/.config/git/attributes
123             *.docx diff=msdoc
124             *.pptx diff=msdoc
125             *.xlmx diff=msdoc
126             *.pdf diff=pdf
127             *.jpg diff=jpg
128              
129             About other GIT related setting, see
130             L.
131              
132             =head1 SEE ALSO
133              
134             L
135              
136             L
137              
138             L
139              
140             L
141              
142             =head1 AUTHOR
143              
144             Kazumasa Utashiro
145              
146             =head1 LICENSE
147              
148             Copyright 2019-2021 Kazumasa Utashiro.
149              
150             This library is free software; you can redistribute it and/or modify
151             it under the same terms as Perl itself.
152              
153             =cut
154              
155 1     1   602 use Data::Dumper;
  1         6365  
  1         64  
156 1     1   8 use List::Util 1.45 qw(first);
  1         26  
  1         100  
157              
158             our @CONVERTER;
159 1     1   446 use App::optex::textconv::default;
  1         15  
  1         6  
160 1     1   480 use App::optex::textconv::msdoc;
  1         4  
  1         9  
161              
162 1     1   11 use Exporter 'import';
  1         2  
  1         832  
163              
164             our @EXPORT = ();
165             our @EXPORT_OK = qw(initialize finalize load);
166             our %EXPORT_TAGS = ( alias => \@EXPORT_OK );
167              
168             my($mod, $argv);
169             sub initialize {
170 0     0 0   ($mod, $argv) = @_;
171             }
172              
173             sub finalize {
174 0     0 0   textconv();
175             }
176              
177             sub argv (&) {
178 0     0 0   my $sub = shift;
179 0           @$argv = $sub->(@$argv);
180             }
181              
182             sub hit {
183 0     0 0   local $_ = shift;
184 0           my $check = shift;
185 0 0         if (ref $check eq 'CODE') {
186 0           $check->();
187             } else {
188 0           /$check/;
189             }
190             }
191              
192             sub converter {
193 0     0 0   my $filename = shift;
194 0 0   0     if (my $ent = first { hit $filename, $_->[0] } @CONVERTER) {
  0            
195 0           return $ent->[1];
196             }
197 0           undef;
198             }
199              
200             sub exec_command {
201 0     0 0   my($format, $file) = @_;
202 0           my $exec = sprintf $format, $file;
203 0           qx($exec);
204             }
205              
206             sub load_module {
207 0     0 0   my $name = shift;
208 0           my $module = __PACKAGE__ . "::$name";
209 0           eval "use $module";
210 0 0         if ($@) {
211 0 0         warn $@ unless $@ =~ /Can't locate/;
212 0           return 0;
213             }
214 0           $module;
215             }
216              
217             sub load {
218 0     0 0   while (my($mod, $val) = splice(@_, 0, 2)) {
219 0 0         load_module $mod if $val;
220             }
221             }
222              
223             my @persist;
224              
225             sub textconv {
226             argv {
227             ARGV:
228 0     0     for (@_) {
229             # check file existence
230 0           do {{
231 0 0         m[^https?://] and last; # skip URL
  0            
232 0 0         -f or next ARGV;
233             }};
234 0           my($suffix) = map { lc } /\.(\w+)$/x;
  0            
235 0           my $func = do {
236 0 0         if (my $converter = converter $_) {
    0          
237 0 0         if (ref $converter eq 'CODE') {
238 0           $converter;
239             }
240             else {
241 0           sub { exec_command $converter, $_ };
  0            
242             }
243             }
244             elsif ($suffix) {
245 0           state %loaded;
246 0           my $state = \$loaded{$suffix};
247 0           my $to_text = join '::', __PACKAGE__, $suffix, 'to_text';
248 0 0         if (not defined $$state) {
    0          
249 0           $$state = 0;
250 0 0         load_module $suffix or next;
251 0 0         $$state = 1 if defined &{$to_text};
  0            
252 0           redo;
253             } elsif ($$state) {
254 0           $to_text;
255             } else {
256 0           next;
257             }
258             } else {
259 0           next;
260             }
261             };
262 1     1   510 use App::optex::Tmpfile;
  1         1016  
  1         54  
263 0           my $tmp = $persist[@persist] = App::optex::Tmpfile->new;
264 0           my $data = do {
265 1     1   7 no strict 'refs';
  1         2  
  1         43  
266 1     1   1997 use charnames ':full';
  1         40717  
  1         7  
267 0           local $_ = decode 'utf8', &$func($_);
268 0           s/[\p{Private_Use}\p{Unassigned}]/\N{GETA MARK}/g;
269 0           encode 'utf8', $_;
270             };
271 0           $_ = $tmp->write($data)->rewind->path;
272             }
273 0           @_;
274 0     0 0   };
275             }
276              
277             1;
278              
279             __DATA__