File Coverage

blib/lib/App/optex/textconv/ooxml/xslt.pm
Criterion Covered Total %
statement 24 26 92.3
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 33 35 94.2


line stmt bran cond sub pod time code
1             package App::optex::textconv::ooxml::xslt;
2              
3             our $VERSION = '0.1401';
4              
5 1     1   12 use v5.14;
  1         4  
6 1     1   5 use warnings;
  1         3  
  1         40  
7 1     1   6 use Carp;
  1         2  
  1         53  
8 1     1   6 use utf8;
  1         2  
  1         5  
9 1     1   38 use Encode;
  1         3  
  1         84  
10 1     1   7 use Data::Dumper;
  1         2  
  1         50  
11              
12 1     1   6 use App::optex::textconv::Converter 'import';
  1         2  
  1         5  
13              
14             our @EXPORT_OK = qw(to_text get_list);
15              
16             our @CONVERTER = (
17             [ qr/\.doc[xm]$/ => \&to_text ],
18             [ qr/\.ppt[xm]$/ => \&to_text ],
19             # [ qr/\.xls[xm]$/ => \&to_text ],
20             );
21              
22             my %styles = (
23              
24             ##
25             ## Extract text from docx data.
26             ##
27             ## s/w:tabs/ /g;
28             ## for //w:p {
29             ## next unless .//w:t
30             ## for .//w:r {
31             ## for w:t {
32             ## s/w:tab/ /g;
33             ## print value;
34             ## }
35             ## }
36             ## print "\n\n";
37             ## }
38             ##
39             docx => q{
40            
41             xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
42            
43            
44            
45            
46            
47            
48            
49            
50            
51            
52            
53            
54            
55            
56            
57            
58            
59            
60            
61            
62            
63            
64             },
65              
66             ##
67             ## Extract text from pptx data.
68             ##
69             ## for //a:p {
70             ## next unless .//a:t
71             ## for a:r {
72             ## for a:t {
73             ## print value;
74             ## }
75             ## }
76             ## print "\n";
77             ## }
78             ##
79             pptx => q{
80            
81             xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
82             xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
83             xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
84            
85            
86            
87            
88            
89            
90            
91            
92            
93            
94            
95            
96            
97            
98            
99            
100             },
101              
102             ##
103             ## This code just extract all text with no space.
104             ##
105             xlsx => q{
106            
107             xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
108            
109            
110             },
111              
112             ##
113             ## This an experimental code for docx to extract table/picture information.
114             ##
115             descriptive_docx => q{
116            
117             xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
118            
119            
120            
121            
122            
123             [ TABLE START ]
124            
125             [ TABLE END ]
126            
127            
128            
129            
130            
131            
132            
133            
134            
135            
136            
137            
138            
139            
140            
141            
142            
143            
144             [ PICTURE START ]
145            
146             [ PICTURE END ]
147            
148            
149            
150             },
151              
152             ##
153             ## This is an original code included in Text::Distill module.
154             ##
155             Text_Distill_docx => q{
156            
157             xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
158            
159            
160            
161            
162            
163            
164            
165            
166            
167            
168            
169            
170            
171            
172            
173            
174            
175             },
176             );
177              
178             for (keys %styles) {
179             if (/^(...)x/) {
180             $styles{$1."m"} //= $styles{$_};
181             }
182             }
183              
184 1     1   691 use XML::LibXML;
  1         32915  
  1         6  
185 1     1   376 use XML::LibXSLT;
  0            
  0            
186              
187             my %LibXMLParserOptions = (
188             'no_network' => 1,
189             'expand_entities' => 0,
190             'load_ext_dtd' => 0,
191             );
192              
193             use Archive::Zip 1.37 qw( :ERROR_CODES :CONSTANTS );
194              
195             sub xml2text {
196             local $_ = shift;
197             my $type = shift;
198             my $xml_re = qr/(?=<\?xml\b[^>]*\?>\s*)/;
199             return $_ unless /$xml_re/;
200              
201             my @xml = grep { length } split /$xml_re/;
202             my @text = map { _xml2text($_, $type) } @xml;
203             join "\n", @text;
204             }
205              
206             sub _xml2text {
207             my $xml_document = shift;
208             my $type = shift;
209              
210             my $xml = XML::LibXML->new(%LibXMLParserOptions);
211             my $xslt = XML::LibXSLT->new();
212              
213             my $document = eval { $xml->parse_string($xml_document) };
214             if ($@) {
215             confess "[libxml2 error ". $@->code() ."] ". $@->message();
216             }
217              
218             my $style_doc = $xml->load_xml(string => $styles{$type});
219             my $style_sheet = $xslt->parse_stylesheet($style_doc);
220             my $transform = $style_sheet->transform($document);
221             my $result = $style_sheet->output_string($transform);
222              
223             $result;
224             }
225              
226             sub to_text {
227             my $file = shift;
228             my $type = ($file =~ /\.((?:doc|xls|ppt)[xm])$/)[0] or return;
229             my $zip = Archive::Zip->new($file) or die;
230             my @contents;
231             for my $entry (get_list($zip, $type)) {
232             my $member = $zip->memberNamed($entry) or next;
233             my $xml = $member->contents or next;
234             my $text = xml2text $xml, $type or next;
235             $file = encode 'utf8', $file if utf8::is_utf8($file);
236             push @contents, "[ \"$file\" $entry ]\n\n$text";
237             }
238             join "\n", @contents;
239             }
240              
241             sub get_list {
242             my($zip, $type) = @_;
243             if ($type =~ /^doc[xm]$/) {
244             map { "word/$_.xml" } qw(document endnotes footnotes);
245             }
246             elsif ($type =~ /^xls[xm]$/) {
247             map { "xl/$_.xml" } qw(sharedStrings);
248             }
249             elsif ($type =~ /^ppt[xm]$/) {
250             map { $_->[0] }
251             sort { $a->[1] <=> $b->[1] }
252             map { m{(ppt/slides/slide(\d+)\.xml)$} ? [ $1, $2 ] : () }
253             $zip->memberNames;
254             }
255             }
256              
257             1;