File Coverage

blib/lib/Text/Md2Inao.pm
Criterion Covered Total %
statement 85 85 100.0
branch 15 16 93.7
condition 2 3 66.6
subroutine 18 18 100.0
pod 0 8 0.0
total 120 130 92.3


line stmt bran cond sub pod time code
1             package Text::Md2Inao;
2 29     29   746737 use utf8;
  29         89  
  29         231  
3 29     29   1177 use strict;
  29         57  
  29         898  
4 29     29   164 use warnings;
  29         59  
  29         1750  
5              
6             our $VERSION = '0.11';
7              
8 29     29   156 use Carp;
  29         50  
  29         5035  
9 29     29   48933 use Class::Accessor::Fast qw/antlers/;
  29         548460  
  29         598  
10 29     29   16107 use Encode;
  29         191840  
  29         3472  
11 29     29   52605 use HTML::TreeBuilder;
  29         1952423  
  29         442  
12 29     29   35126 use Text::Markdown::Hoedown;
  29         217524  
  29         5932  
13              
14 29     29   253385 use Text::Md2Inao::Director;
  29         100  
  29         223  
15 29     29   20844 use Text::Md2Inao::Builder::Inao;
  29         100  
  29         364  
16              
17             # デフォルトのリストスタイル
18             # disc: 黒丸
19             # square: 四角
20             # circle: 白丸
21             # alpha: アルファベット
22             has default_list => ( is => 'rw', isa => 'Str' );
23              
24             # リストの文字数上限
25             # WEB+DB PRESSの場合、リストは、1行63桁(文字)まで
26             # 書籍の場合、リストは1行69桁(文字)まで
27             has max_list_length => ( is => 'rw', isa => 'Num' );
28              
29             # 本文埋め込みリストの文字数上限
30             # WEB+DB PRESSの場合、本文リストは1行55桁(文字)まで
31             # 書籍の場合、本文リストは1行73桁(文字)まで
32             has max_inline_list_length => ( is => 'rw', isa => 'Num' );
33              
34             # 空行のスタイル
35             # half | full
36             has blank_style => ( is => 'rw', isa => 'Str' );
37              
38             # コンテキスト判定のための属性
39             has in_footnote => (is => 'rw', isa => 'Bool');
40             has in_column => (is => 'rw', isa => 'Bool');
41             has in_code_block => (is => 'rw', isa => 'Bool');
42             has in_list => (is => 'rw', isa => 'Bool');
43             has in_quote_block => (is => 'rw', isa => 'Bool');
44              
45             has director => ( is => 'rw' );
46             has builder => ( is => 'rw' );
47             has metadata => ( is => 'rw' );
48              
49             sub use_special_italic {
50 32     32 0 52     my $self = shift;
51 32 100       910     return 1 if $self->in_column;
52 27 50       215     return 1 if $self->in_code_block;
53 27 100       171     return 1 if $self->in_list;
54 22 100       163     return 1 if $self->in_quote_block;
55 17         132     return;
56             }
57              
58             sub prepare_text_for_markdown {
59 206     206 0 337     my $text = shift;
60              
61             ## Work Around: 先頭空白は字下げとみなし全角空白に置き換える (issue #4)
62 206         957     $text =~ s/^[ ]{1,3}([^ <])/ $1/mg;
63              
64             ## Work Around: リストの後にコードブロックが続くとだめな問題 (issue #6)
65 206         755     $text =~ s!([-*+] .*?)\n\n !$1\n\n \n\n !g;
66              
67 206         448     return $text;
68             }
69              
70             sub prepare_html_for_inao {
71 206     206 0 351     my $html = shift;
72             ## Work Around: リスト周りと inao 記法の相性が悪い
73             # see also: t/07_list.t
74 206         689     $html =~ s!<li><p>(.+)</p></li>\n!<li>$1</li>\n!g;
75              
76             ## 段落切り替えの意図が見えるのにそうならないケースを補正 (issue #8)
77 206         839     $html =~ s!<p>(.*)\n !<p>$1</p>\n<p> !g;
78 206         465     return $html;
79             }
80              
81             sub to_html_tree {
82 206     206 0 339     my $text = shift;
83              
84 206         559     $text = prepare_text_for_markdown($text);
85 206         1051     my $html = markdown($text,
86                     extensions => HOEDOWN_EXT_FENCED_CODE,
87                 );
88 206         10993     $html = prepare_html_for_inao($html);
89              
90 206         1174     my $tree = HTML::TreeBuilder->new;
91 206         62018     $tree->no_space_compacting(1);
92 206         2467     $tree->parse_content(\$html);
93             }
94              
95             sub parse_element {
96 650     650 0 303780     my ($self, $elem) = @_;
97 650         1933     my @out = map { $self->director->process($self, $_) } $elem->content_list;
  943         6736  
98 650         6096     return join '', @out;
99             }
100              
101             sub parse_markdown {
102 206     206 0 407     my ($self, $md) = @_;
103 206         616     return $self->parse_element(to_html_tree($md)->find('body'));
104             }
105              
106             sub parse_metadata {
107 202     202 0 528     my ($self, $in) = @_;
108 202 100       2518     if ($in =~ /^[\w\(\)]+?:.+\n/m) {
109 17         31         my $has_metadata;
110 17         117         my @lines = split /\n/, $in;
111 17         39         my %meta;
112              
113 17         48         for (@lines) {
114 42 100       259             if (m/^([\w\(\)]+?):(.+)/) {
115 36         102                 my ($k, $v) = ($1, $2);
116 36         181                 $v =~ s/^\s+//;
117 36         113                 $v =~ s/\s+$//;
118 36         661                 $meta{lc $k} = $v;
119 36         19545                 next;
120                         }
121 6 100       23             if ($_ eq '') { # 区切りの空行
122 2         4                 $has_metadata = 1;
123 2         13                 $self->metadata(\%meta);
124 2         17                 last;
125                         }
126                         else {
127 4         9                 last;
128                         }
129                     }
130 17 100       90         if ($has_metadata) {
131 2         28             $in =~ s/^.+?\n\n//s;
132 2         13             return $in;
133                     }
134                 }
135 200         452     return $in;
136             }
137              
138             sub parse {
139 202     202 0 623767     my ($self, $md) = @_;
140 202   66     1116     my $builder = $self->builder || Text::Md2Inao::Builder::Inao->new;
141 202         1969     $self->director( Text::Md2Inao::Director->new($builder) );
142 202         3893     $md = $self->parse_metadata($md);
143 202         685     $md = $self->director->process_before_filter($self, $md);
144 202         761     my $out = $self->parse_markdown($md);
145 202         4428     return $self->director->process_after_filter($self, $out);
146             }
147              
148             1;
149              
150             =head1 NAME
151            
152             Text::Md2Inao - Convert markdown text to Inao-format
153            
154             =head1 SYNOPSIS
155            
156             my $p = Text::Md2Inao->new({
157             default_list => 'disc',
158             max_list_length => 63,
159             max_inline_list_length => 55,
160             });
161            
162             print encode_utf8 $p->parse($markdown_text);
163            
164             =head1 DESCRIPTION
165            
166             This is a text converter for WEB+DB PRESS articles.
167            
168             =head1 AUTHOR
169            
170             Naoya Ito E<lt>i.naoya@gmail.comE<gt>
171            
172             =head1 LICENSE
173            
174             This library is free software; you can redistribute it and/or modify
175             it under the same terms as Perl itself.
176            
177             =cut
178