zine_brewer 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6e2d08e05252737a218c8293c1b3f0961e6e5ccc686b7618c7d1f003a5ee344b
4
+ data.tar.gz: 74c096aa7daaea76924e5f5ca526d14903b2b4b452e7a4de145dbcd5048caf42
5
+ SHA512:
6
+ metadata.gz: cf2f41012a5fbdbbeffe777fa8b46031536469cb10dab13c50c52491bfe76bdd1e042f7fac7ad4418c53320e1c35bfa30c40cf49b61854298b31a973453243ec
7
+ data.tar.gz: 74c92ca207131f8b1742d06effa4597b98d3fc9fd1bcaa38f549bb85f1ec9d7c4b2b4c7ce75d8ac13f125c45a6bd1e6d10ddcae8355d05c638100e0b43da8a1e
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at akinori.ichigo@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in zine_brewer.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Akinori Ichigo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,31 @@
1
+ # ZineBrewer
2
+
3
+ ZineBrewer converts Kramdown (=exhanced Markdown) document to HTML for Shoeisha Web Media.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'zine_brewer'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install zine_brewer
20
+
21
+ ## Usage
22
+
23
+ $ zine_brewer *kramdown document*
24
+
25
+ ## License
26
+
27
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
28
+
29
+ ## Code of Conduct
30
+
31
+ Everyone interacting in the ZineBrewer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/akinori-ichigo/zine_brewer/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "zine_brewer"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "zine_brewer"
4
+
5
+ begin
6
+ zine = ZineBrewer::Application.new(ARGV[0])
7
+ zine.write_out
8
+ puts 'Conversion from Markdown to HTML is done. The output files (header.txt / body.txt) are in proof directory.'
9
+ rescue => e
10
+ puts 'Something wrong...'
11
+ p e
12
+ end
13
+
@@ -0,0 +1,2 @@
1
+ require "zine_brewer/version"
2
+ require "zine_brewer/main"
@@ -0,0 +1,152 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2015 Thomas Leitner <t_leitner@gmx.at>
5
+ # Copyright (C) 2016 Akinori Ichigo <ichigo@shoeisha.co.jp>
6
+ #
7
+ # This file is part of kramdown which is licensed under the MIT.
8
+ #++
9
+ #
10
+
11
+ require 'kramdown'
12
+ require 'kramdown/parser'
13
+ require 'kramdown/converter'
14
+ require 'kramdown/utils'
15
+
16
+ module Kramdown
17
+ module Converter
18
+ class SeHtml < Html
19
+
20
+ def convert_img(el, indent)
21
+ if %r{^common} =~ File.dirname(el.attr['src'])
22
+ el.attr['src'] = "/static/images/article/common/#{File.basename(el.attr['src'])}"
23
+ end
24
+ "<img#{html_attributes(el.attr)} />"
25
+ end
26
+
27
+ def convert_codeblock(el, indent)
28
+ raw_caption = el.attr.delete('caption')
29
+ caption = if raw_caption.nil?
30
+ ''
31
+ else
32
+ el_caption = Document.new(raw_caption, {:auto_ids => false, :input => 'sekd'}).root.children[0]
33
+ format_as_block_html('div', {'class' => 'caption'}, inner(el_caption, indent), indent + 2)
34
+ end
35
+
36
+ result = escape_html(el.value)
37
+ result.chomp!
38
+ if el.attr['class'].to_s =~ /\bshow-whitespaces\b/
39
+ result.gsub!(/(?:(^[ \t]+)|([ \t]+$)|([ \t]+))/) do |m|
40
+ suffix = ($1 ? '-l' : ($2 ? '-r' : ''))
41
+ m.scan(/./).map do |c|
42
+ case c
43
+ when "\t" then "<span class=\"ws-tab#{suffix}\">\t</span>"
44
+ when " " then "<span class=\"ws-space#{suffix}\">&#8901;</span>"
45
+ end
46
+ end.join('')
47
+ end
48
+ end
49
+
50
+ format_as_indented_block_html('div', {'class' => 'src_frame'},
51
+ caption + ' ' + format_as_indented_block_html('pre', {'class' => el.attr['class']}, result, indent),
52
+ indent)
53
+ end
54
+
55
+ def convert_column(el, indent)
56
+ format_as_indented_block_html('section', el.attr, inner(el, indent), indent)
57
+ end
58
+
59
+ # Makes caption element from caption attr of table element.
60
+ def table_caption(raw_caption)
61
+ el_caption = Document.new(raw_caption, {:auto_ids => false, :input => 'sekd'}).root.children[0]
62
+ format_as_block_html('caption', {}, inner(el_caption, indent), indent)
63
+ end
64
+
65
+ def convert_table(el, indent)
66
+ raw_caption = el.attr.delete('caption')
67
+ table = super(el, indent)
68
+ if !raw_caption.nil?
69
+ table.sub!(/\A(<table.*?>)/, "\\1\n#{table_caption(raw_caption)}\n")
70
+ end
71
+ table.gsub(/^\s*<\/?t(head|body|foot)>\n/, '')
72
+ .gsub(/ style="text-align: left"/, '')
73
+ .gsub(/ style="text-align: center"/, ' class="txtC"')
74
+ .gsub(/ style="text-align: right"/, ' class="txtR"')
75
+ end
76
+
77
+ def convert_definition_table(el, indent)
78
+ raw_caption = el.attr.delete('caption')
79
+ rows = raw_caption.nil? ? '' : table_caption(raw_caption)
80
+ inner(el.children.first, indent).scan(/(<dt>.+?<\/dt>|<dd>.+?<\/dd>)/m).
81
+ map{|x| x.first.sub(/\A<dt>(.+)<\/dt>\z/m, "<th>\\1</th>")}.
82
+ map{|x| x.sub(/\A<dd>(.+)<\/dd>\z/m, "<td>\\1</td>")}.
83
+ each_slice(2) do |x|
84
+ row = x.map{|c| (' '*(indent + 2)) + c}.join("\n")
85
+ rows += format_as_indented_block_html('tr', {}, "#{row}\n", indent + 2)
86
+ end
87
+ format_as_indented_block_html('table', el.attr, rows, indent)
88
+ end
89
+
90
+ def convert_div(el, indent)
91
+ format_as_indented_block_html('div', el.attr, inner(el, indent), indent)
92
+ end
93
+
94
+ def convert_page(el, indent)
95
+ el.value + "\n"
96
+ end
97
+
98
+ def convert_footnote_marker_sekd(el, indent)
99
+ %!<sup id="fnref:#{el.value}"><a href="#fn:#{el.value}">[#{el.value}]</a></sup>!
100
+ end
101
+
102
+ def convert_footnote_definition_sekd(el, indent)
103
+ format_as_block_html('section', el.attr, "\n <h4>注</h4>\n" + inner(el, indent), indent)
104
+ end
105
+
106
+ def convert_a(el, indent)
107
+ res = inner(el, indent)
108
+ attr = el.attr.dup
109
+ if attr['href'].start_with?('mailto:')
110
+ mail_addr = attr['href'][7..-1]
111
+ attr['href'] = obfuscate('mailto') << ":" << obfuscate(mail_addr)
112
+ res = obfuscate(res) if res == mail_addr
113
+ else
114
+ attr['target'] = '_blank' unless attr['href'].start_with?('#')
115
+ end
116
+ format_as_span_html(el.type, attr, res)
117
+ end
118
+
119
+ def convert_span(el, indent)
120
+ format_as_span_html('span', el.attr, inner(el, indent))
121
+ end
122
+
123
+ def convert_em(el, indent)
124
+ format_as_span_html('strong', el.attr, inner(el, indent))
125
+ end
126
+
127
+ def convert_math(el, indent)
128
+ if (result = format_math(el, :indent => indent))
129
+ result
130
+ elsif el.options[:category] == :block
131
+ format_as_block_html('pre', el.attr, "$$\n#{el.value}\n$$", indent)
132
+ else
133
+ format_as_span_html('math', el.attr, "$#{el.value}$")
134
+ end
135
+ end
136
+
137
+ def convert_root(el, indent)
138
+ result = inner(el, indent)
139
+ if @toc_code
140
+ toc_tree = generate_toc_tree(@toc, @toc_code[0], @toc_code[1] || {})
141
+ text = if toc_tree.children.size > 0
142
+ convert(toc_tree, 0)
143
+ else
144
+ ''
145
+ end
146
+ result.sub!(/#{@toc_code.last}/, text.gsub(/\\/, "\\\\\\\\"))
147
+ end
148
+ result
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,161 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2017 Akinori Ichigo <akinori.ichigo@gmail.com>
4
+ #
5
+ # This source code has written to extend and modify kramdown for
6
+ # web media published by SHOEISHA Co., Ltd.
7
+ #
8
+
9
+ require 'kramdown'
10
+ require 'kramdown/parser'
11
+
12
+ module Kramdown
13
+ module Parser
14
+ class Sekd < Kramdown
15
+
16
+ def initialize(source, options)
17
+ super
18
+
19
+ parsers_replacing = {
20
+ :@block_parsers => {:codeblock_fenced => :codeblock_fenced_sekd,
21
+ :footnote_definition => :footnote_definition_sekd},
22
+ :@span_parsers => {:footnote_marker => :footnote_marker_sekd}
23
+ }
24
+ parsers_replacing.each do |target, definitions|
25
+ target_instance = instance_variable_get(target)
26
+ definitions.each do |current, replacement|
27
+ target_instance[target_instance.index(current)] = replacement
28
+ end
29
+ end
30
+
31
+ @block_parsers.insert(5, :column, :definition_table, :page)
32
+ @span_parsers.insert(5, :span)
33
+
34
+ @page = 0
35
+ @fn_counter = 0
36
+ @fn_number = Hash.new{|h, k| h[k] = (@fn_counter += 1).to_s }
37
+ end
38
+
39
+ # Parse the fenced codeblock at the current location
40
+ # code highlighting by Google Prettify.
41
+ def parse_codeblock_fenced_sekd
42
+ if @src.check(self.class::FENCED_CODEBLOCK_MATCH)
43
+ start_line_number = @src.current_line_number
44
+ @src.pos += @src.matched_size
45
+ el = new_block_el(:codeblock, @src[4].chomp, nil, :location => start_line_number)
46
+ lang, linenums = @src[3].to_s.strip.split(/:/)
47
+ lang_ext = LANG_BY_EXT[lang] || lang
48
+ el.attr['class'] = "prettyprint" + (lang_ext.nil? ? ' nocode' : " lang-#{lang_ext}")
49
+ el.attr['class'] += " linenums:#{linenums}" unless linenums.nil?
50
+ @tree.children << el
51
+ true
52
+ else
53
+ false
54
+ end
55
+ end
56
+ FENCED_CODEBLOCK_MATCH = /^(([~`]){3,})\s*?(\w[\d:\w-]*)?\s*?\n(.*?)^\1\2*\s*?\n/m
57
+ define_parser(:codeblock_fenced_sekd, /^[~`]{3,}/, nil, 'parse_codeblock_fenced_sekd')
58
+
59
+ LANG_BY_EXT = {
60
+ "bash"=>"bsh", "clojure"=>"clj", "csharp"=>"cs", "c#"=>"cs", "el"=>"lisp",
61
+ "erl"=>"erlang", "golang"=>"go", "haskell"=>"hs", "javascript"=>"js", "obj-c"=>"m",
62
+ "objective-c"=>"m", "objc"=>"m", "pas"=>"pascal", "python"=>"py", "rlang"=>"r",
63
+ "ruby"=>"rb", "sc"=>"scala", "visualbasic"=>"vb"
64
+ }.merge(Hash[*%w!
65
+ apollo basic bsh c cc cpp clj cs csh css cyc cv dart erlang go hs htm html java js
66
+ llvm m matlab ml mumps mxml lisp lua n pascal perl pl pm proto py r rb rd scala sh
67
+ sql tcl tex vb vhdl wiki xhtml xml xq xsl yaml yml
68
+ !.map{|i| [i, i]}.flatten])
69
+
70
+ def parse_column
71
+ if @src.check(self.class::COLUMN_MATCH)
72
+ start_line_number = @src.current_line_number
73
+ @src.pos += @src.matched_size
74
+ el = Element.new(:column, nil, {'class' => 'columnSection'}, :location => start_line_number)
75
+ parse_blocks(el, @src[1])
76
+ update_attr_with_ial(el.attr, @block_ial) unless @block_ial.nil?
77
+ @tree.children << el
78
+ true
79
+ else
80
+ false
81
+ end
82
+ end
83
+
84
+ COLUMN_MATCH = /^={3,}\s*?column\s*?\n(.*?)^={2,}\/column\s*?\n/mi
85
+ define_parser(:column, /^={3,}co/i, nil, 'parse_column')
86
+
87
+ def parse_definition_table
88
+ if @src.check(self.class::DEFINITION_TABLE_MATCH)
89
+ start_line_number = @src.current_line_number
90
+ @src.pos += @src.matched_size
91
+ el = Element.new(:definition_table, nil, nil, :location => start_line_number)
92
+ parse_blocks(el, @src[1])
93
+ update_attr_with_ial(el.attr, @block_ial) unless @block_ial.nil?
94
+ @tree.children << el
95
+ true
96
+ else
97
+ false
98
+ end
99
+ end
100
+
101
+ DEFINITION_TABLE_MATCH = /^={3,}\s*?dtable\s*?\n\n*(.*?)^={2,}\/dtable\s*?\n/mi
102
+ define_parser(:definition_table, /^={3,}dt/i, nil, 'parse_definition_table')
103
+
104
+ @page = 0
105
+ def parse_page
106
+ if @src.check(self.class::PAGE_MATCH)
107
+ start_line_number = @src.current_line_number
108
+ @src.pos += @src.matched_size
109
+ page = (@page > 0 ? "</div>\n<!-- page_delimiter -->\n" : '') + "<div id=\"p#{@page+=1}\">"
110
+ @tree.children << new_block_el(:page, page, nil, :location => start_line_number)
111
+ true
112
+ else
113
+ false
114
+ end
115
+ end
116
+
117
+ PAGE_MATCH = /^<%-*\s*page\s*-*>/
118
+ define_parser(:page, /^<%/, nil, 'parse_page')
119
+
120
+ def parse_footnote_definition_sekd
121
+ start_line_number = @src.current_line_number
122
+ @src.pos += @src.matched_size
123
+ if @fn_number.has_key?(@src[1])
124
+ warning("Duplicate footnote name '#{@src[1]}' on line #{start_line_number} - overwriting")
125
+ end
126
+ a = %!<a href="#fnref:#{@fn_number[@src[1]]}">[#{@fn_number[@src[1]]}]</a>: #{@src[2].strip}!
127
+ p = new_block_el(:p, nil, {'id' => "fn:#{@fn_number[@src[1]]}"})
128
+ p.children << new_block_el(:raw_text, a, nil)
129
+ if @tree.children.last.type == :footnote_definition_sekd
130
+ @tree.children.last.children << p
131
+ elsif [-1, -2].map{|i| @tree.children[i].type } == [:blank, :footnote_definition_sekd]
132
+ @tree.children.pop
133
+ @tree.children.last.children << p
134
+ else
135
+ f = new_block_el(:footnote_definition_sekd, nil, {'class' => 'columnSection footnotes'},
136
+ :location => start_line_number)
137
+ f.children << p
138
+ @tree.children << f
139
+ end
140
+ true
141
+ end
142
+
143
+ FOOTNOTE_DEFINITION_START = /^#{OPT_SPACE}\[\^(#{ALD_ID_NAME})\]:\s*?(.*?\n#{CODEBLOCK_MATCH})/
144
+ define_parser(:footnote_definition_sekd, FOOTNOTE_DEFINITION_START, /^#{OPT_SPACE}\[^/, 'parse_footnote_definition_sekd')
145
+
146
+ def parse_footnote_marker_sekd
147
+ start_line_number = @src.current_line_number
148
+ @src.pos += @src.matched_size
149
+ unless @fn_number.has_key?(@src[1])
150
+ warning("No footnote marker '#{@src[1]}' on line #{start_line_number} - missing")
151
+ end
152
+ @tree.children << new_block_el(:footnote_marker_sekd, @fn_number[@src[1]], nil,
153
+ :location => start_line_number)
154
+ end
155
+
156
+ FOOTNOTE_MARKER_START = /\[\^(#{ALD_ID_NAME})\]/
157
+ define_parser(:footnote_marker_sekd, FOOTNOTE_MARKER_START, '\[', 'parse_footnote_marker_sekd')
158
+
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,128 @@
1
+ # coding: utf-8
2
+
3
+ require 'kconv'
4
+ require 'darkmouun'
5
+
6
+ require_relative 'kramdown/parser/sekd'
7
+ require_relative 'kramdown/converter/sehtml'
8
+
9
+ module ZineBrewer
10
+
11
+ class Application
12
+
13
+ Commons = "[hlink]: common/hlink.svg"
14
+
15
+ attr_reader :corner, :title, :lead, :pic, :author, :css, :converted
16
+
17
+ def initialize(path)
18
+
19
+ begin
20
+ Encoding.default_external = Kconv.guess(File.open(path, 'r:BINARY').read)
21
+ input_data = File.open(path, 'rt').read.encode('UTF-8')
22
+ rescue
23
+ puts 'ERROR: There is not the input file. Check the input file.'
24
+ exit
25
+ end
26
+
27
+ @dir = File.dirname(path)
28
+
29
+ @article_id = begin
30
+ File.open("#{@dir}/id.txt", "rt").read.chomp
31
+ rescue
32
+ docdir = File.basename(@dir)
33
+ /^\d+$/ =~ docdir ? docdir : '■記事ID■'
34
+ end
35
+
36
+ header, body = /\A((?:.|\n)*?)<%-- page -->/.match(input_data).yield_self do |m|
37
+ if m.nil?
38
+ ['', "\n\n" + Commons + "\n\n" + input_data]
39
+ else
40
+ [m[1], '<%-- page -->' + "\n\n" + Commons + "\n\n" + m.post_match]
41
+ end
42
+ end
43
+
44
+ @article_id = $+ if header.sub!(/^■記事ID■[^0-9]+(\d+)$/, '')
45
+ h = header.strip.split(/\n\n+/)
46
+ @corner = set_header_item(h[0], 'IT人材ラボの記事・ニュース')
47
+ @title = set_header_item(h[1], 'タイトル' )
48
+ @lead = set_header_item(h[2], 'リード')
49
+ @pic = set_header_item(h[3], 'dummy.png')
50
+ @author = set_header_item(h[4], '著者 クレジット')
51
+ @css = set_header_item(h[5], '')
52
+
53
+ @converted = convert(body)
54
+ end
55
+
56
+ def set_header_item(target, default)
57
+ if /\A[\-%]+\Z/ =~ target || target.nil?
58
+ default.define_singleton_method(:is_complete?){ false }
59
+ default
60
+ else
61
+ target.define_singleton_method(:is_complete?){ true }
62
+ target
63
+ end
64
+ end
65
+
66
+ ## Writing out header and body to each file
67
+ def write_out
68
+ make_proof_directory
69
+ write_proof_header
70
+ write_proof_body
71
+ end
72
+
73
+ def make_proof_directory
74
+ @proof_dir = @dir + '/proof'
75
+ begin
76
+ Dir.mkdir(@proof_dir)
77
+ rescue Errno::EEXIST
78
+ end
79
+ end
80
+
81
+ def write_proof_header
82
+ header_output = ""
83
+ header_output << "[コーナー]\n#{@corner}\n\n" if @corner.is_complete?
84
+ header_output << "[タイトル]\n#{@title}\n\n" if @title.is_complete?
85
+ header_output << "[リード]\n<p>#{@lead}</p>\n\n" if @lead.is_complete?
86
+ header_output << "[タイトル画像]\n#{@pic}\n\n" if @pic.is_complete?
87
+ header_output << "[著者クレジット]\n#{@author}\n\n" if @author.is_complete?
88
+ header_output << "[追加CSS]\n#{@css}\n\n" if @css.is_complete?
89
+ File.open("#{@proof_dir}/header.txt", 'wb') do |f|
90
+ f.write(header_output)
91
+ end
92
+ end
93
+
94
+ def write_proof_body
95
+ File.open("#{@proof_dir}/body.txt", 'wb') do |f|
96
+ f.write(@converted.gsub("<!-- page_delimiter -->\n", ''))
97
+ end
98
+ end
99
+
100
+ ## Converts markdown to html and returns body
101
+ def convert(body)
102
+ dkmn = Darkmouun.document.new(body, {:auto_ids => false, :input => 'sekd'}, :se_html)
103
+
104
+ ### Sets templates
105
+ dkmn.add_templates "#{__dir__}/templates/", *Dir['*.rb',base:"#{__dir__}/templates/"]
106
+
107
+ ### Sets pre process
108
+ dkmn.pre_process = lambda do |t|
109
+ t.gsub!(/\n\K={2,}div/, "<div markdown=\"1\">")
110
+ t.gsub!(/\n\K={2,}\/div/, "</div>")
111
+ end
112
+
113
+ ### Sets post process
114
+ dkmn.post_process = lambda do |t|
115
+ t.gsub!('(((BR)))', '<br/>')
116
+ t.gsub!(/■記事ID■/, @article_id)
117
+ t.gsub!(/—/, "―")
118
+ t.gsub!(/[‘’]/, "'")
119
+ t.gsub!(/<li>\\/, '<li>')
120
+ t << "</div>" if /<div id="p1">/ =~ t
121
+ end
122
+
123
+ ### Converts a markdown document and returns that converted body
124
+ dkmn.convert
125
+ end
126
+ end
127
+ end
128
+
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+
3
+ class BookRanking < Mustache
4
+
5
+ # <<BookRanking>>
6
+ # url: AmazonのURL
7
+ # title: 書名
8
+ # src: 書影ファイル名
9
+ # desc: 説明
10
+
11
+ @template = <<EOT
12
+ <div class="imgLRBlock cf">
13
+ <figure class="imgR">
14
+ <a href="{{url}}" target="_blank"><img alt="{{title}}" src="{{cover}}" style="border:1px solid #808080; width:100px;" title="{{title}}" /></a></figure>
15
+ <p markdown="1"> {{& description}}</p>
16
+ </div>
17
+ EOT
18
+
19
+ def description
20
+ Mustache.new.render(desc, {book: "『[#{title}](#{url})』"})
21
+ end
22
+
23
+ def cover
24
+ case File.dirname(src)
25
+ when ".", "images"
26
+ "/static/images/article/■記事ID■/#{File.basename(src)}"
27
+ when "common"
28
+ "/static/images/article/common/#{File.basename(src)}"
29
+ else
30
+ src
31
+ end
32
+ end
33
+
34
+ end
35
+
@@ -0,0 +1,104 @@
1
+ # coding: utf-8
2
+
3
+ class Casts < Mustache
4
+
5
+ # <<Casts>>
6
+ # casts:
7
+ # src: 写真ファイル名
8
+ # name: 姓 名
9
+ # huri: ふり がな
10
+ # cap: プロフィール
11
+
12
+ # CSSに下記の登録が必要
13
+ # article#contents div.article div.casts>div ~ div { margin-top:8px; }
14
+
15
+ @template = <<EOT
16
+ <div class="casts" style="margin-bottom:30px; padding:13px 13px 3px; border:solid 2px #eee;">
17
+ {{#prof_list}}
18
+ <div class="imgLRBlock cf">
19
+ <figure class="imgL">
20
+ <img src="{{fig_src}}" alt="{{name}}" style="height:135px;" />
21
+ </figure>
22
+ <p class="ovh" markdown="span" style="font-size:14px; line-height:1.7; margin-bottom:10px;"><strong>{{name}}({{huri}})氏</strong><br />{{& caption}}</p>
23
+ </div>
24
+ {{/prof_list}}
25
+ </div>
26
+ EOT
27
+
28
+ def prof_list
29
+ result = []
30
+ (casts rescue a_cast_param).each do |h|
31
+ a_cast = {}
32
+ raise "Error: No src:" if h["src"].nil?
33
+ a_cast[:fig_src] = make_src(h["src"])
34
+ a_cast[:name] = h["name"]
35
+ a_cast[:huri] = h["huri"]
36
+ a_cast[:caption] = make_caption(h["cap"])
37
+ result << a_cast
38
+ end
39
+ result
40
+ end
41
+
42
+ private
43
+ def a_cast_param
44
+ [{"src" => (src rescue nil),
45
+ "name" => (name rescue nil),
46
+ "huri" => (huri rescue nil),
47
+ "cap" => (cap rescue nil)}]
48
+ end
49
+
50
+ def make_src(l_src)
51
+ case File.dirname(l_src)
52
+ when ".", "images"
53
+ "/static/images/article/■記事ID■/#{File.basename(l_src)}"
54
+ when "common"
55
+ "/static/images/article/common/#{File.basename(l_src)}"
56
+ else
57
+ l_src
58
+ end
59
+ end
60
+
61
+ def make_caption(l_cap)
62
+ begin
63
+ l_cap.chomp.sub(/^\\/, '').gsub(/\n/, '(((BR)))')
64
+ rescue
65
+ nil
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ __END__
72
+
73
+ {:.casts %margin-bottom:45px; %padding:13px 13px 3px; %border:solid 2px #eee;}
74
+ ===div
75
+
76
+ {:.imgLRBlock .cf}
77
+ ===div
78
+ {:.imgL}
79
+ <<Fig_N>>
80
+ src: 2096_misonou_p.jpg
81
+ height: 135px
82
+
83
+ {:.ovh markdown="1" %font-size:14px; %line-height:1.7; %margin-bottom:10px;}
84
+ **御園生 銀平(みそのう ぎんぺい)氏**<br/>
85
+ ソフトバンク株式会社[[ ]]{:%font-size:3px;}人事本部[[ ]]{:%font-size:3px;}戦略企画統括部[[ ]]{:%font-size:3px;}人材戦略部[[ ]]{:%font-size:3px;}デジタルHR推進課。<br/>
86
+ (※御園生様のプロフィールを100〜150字程度でお願いいたします)。
87
+ ==/div
88
+
89
+ {:.imgLRBlock .cf}
90
+ ===div
91
+ {:.imgL}
92
+ <<Fig_N>>
93
+ src: 2096_shikauchi_p.jpg
94
+ height: 135px
95
+
96
+ {:.ovh markdown="1" %font-size:14px; %line-height:1.7; %margin-bottom:10px;}
97
+ **鹿内 学(しかうち まなぶ)氏**<br/>
98
+ 博士(理学)。株式会社シンギュレイト 代表。<br/>
99
+ 働く中でのコミュニケーション・データから関係性に注目した次世代ピープルアナリティクスにとりくむ。代表を務めるシンギュレイトでは1 on 1や会議で利用できる可視化ツールを提供中。働く組織の科学と実用をめざす。情報量規準が好き、サッカー好き、漫画好き。
100
+ ==/div
101
+
102
+ ==/div
103
+
104
+
@@ -0,0 +1,68 @@
1
+ # coding: utf-8
2
+
3
+ module Fig_00
4
+
5
+ def imgs_list
6
+ result = []
7
+ (imgs rescue a_img_param).each do |h|
8
+ a_img = {}
9
+ raise "Error: No src:" if h["src"].nil?
10
+ a_img[:fig_src] = make_src(h["src"])
11
+ a_img[:href] = h["href"]
12
+ a_img[:alt] = make_alt(h["alt"])
13
+ a_img[:img_style] = make_img_style(h["width"], h["height"]) \
14
+ unless h["width"].nil? && h["height"].nil?
15
+ result << a_img
16
+ end
17
+ result
18
+ end
19
+
20
+ def caption
21
+ begin
22
+ cap.chomp.sub(/^\\/, '').gsub(/\n/,'(((BR)))')
23
+ rescue
24
+ nil
25
+ end
26
+ end
27
+
28
+ private
29
+ def a_img_param
30
+ [{"src" => (src rescue nil),
31
+ "href" => (href rescue nil),
32
+ "alt" => (alt rescue caption),
33
+ "width" => (width rescue nil),
34
+ "height" => (height rescue nil)}]
35
+ end
36
+
37
+ def make_src(l_src)
38
+ case File.dirname(l_src)
39
+ when ".", "images"
40
+ f = File.basename(l_src)
41
+ "/static/images/article/■記事ID■/#{/^\d+_/ =~ f ? f : '■記事ID■_' + f}"
42
+ when "common"
43
+ "/static/images/article/common/#{File.basename(l_src)}"
44
+ else
45
+ l_src
46
+ end
47
+ end
48
+
49
+ def make_alt(l_alt)
50
+ begin
51
+ l_alt.gsub(/\[(.+?)\]\(.+?\)/, '\1').gsub(/\n/, '')
52
+ rescue
53
+ nil
54
+ end
55
+ end
56
+
57
+ def make_img_style(l_width, l_height)
58
+ s = []
59
+ s << "width:#{l_width};" unless l_width.nil?
60
+ s << "height:#{l_height};" unless l_height.nil?
61
+ unless s.all?{|v| v.nil? }
62
+ %Q{style="#{s.join(' ').strip}" }
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+
@@ -0,0 +1,15 @@
1
+ # coding: utf-8
2
+
3
+ module Fig_01
4
+
5
+ def open_which
6
+ defined?(href)
7
+ end
8
+
9
+ def figcaption_sw
10
+ defined?(cap)
11
+ end
12
+
13
+ end
14
+
15
+
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+
3
+ require_relative 'fig/module_fig_base'
4
+ require_relative 'fig/module_fig_plus'
5
+
6
+ class Fig_A < Mustache
7
+
8
+ include Fig_00, Fig_01
9
+
10
+ # <<Fig_A>>
11
+ # src: 画像ファイル名
12
+ # width: 画像幅
13
+ # height: 画像高さ
14
+ # cap: キャプション
15
+
16
+ @template = <<EOT
17
+ <figure>
18
+ {{#imgs_list}}
19
+ <a href="{{fig_src}}" target="_blank"><img src="{{fig_src}}" alt="{{alt}}" {{& img_style}}/></a>
20
+ {{/imgs_list}}
21
+ <figcaption markdown="span">{{#figcaption_sw}}{{& caption}}<br/>{{/figcaption_sw}}[画像クリックで拡大表示]</figcaption>
22
+ </figure>
23
+ EOT
24
+
25
+ end
26
+
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+
3
+ require_relative 'fig/module_fig_base'
4
+ require_relative 'fig/module_fig_plus'
5
+
6
+ class Fig_H < Mustache
7
+
8
+ include Fig_00, Fig_01
9
+
10
+ # <<Fig_H>>
11
+ # src: 画像ファイル名
12
+ # href: 画像クリックで遷移するURL
13
+ # width: 画像幅
14
+ # height: 画像高さ
15
+ # cap: キャプション
16
+
17
+ @template = <<EOT
18
+ <figure>
19
+ {{#imgs_list}}
20
+ {{#href}}<a href="{{href}}" target="_blank">{{/href}}<img src="{{fig_src}}" alt="{{alt}}" {{& img_style}}/>{{#href}}</a>{{/href}}
21
+ {{/imgs_list}}
22
+ {{#figcaption_sw}}<figcaption markdown="span">{{& caption}}</figcaption>{{/figcaption_sw}}
23
+ </figure>
24
+ EOT
25
+
26
+ end
27
+
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+
3
+ require_relative 'fig/module_fig_base'
4
+ require_relative 'fig/module_fig_plus'
5
+
6
+ class Fig_N < Mustache
7
+
8
+ include Fig_00, Fig_01
9
+
10
+ # <<Fig_N>>
11
+ # src: 画像ファイル名
12
+ # width: 画像幅
13
+ # height: 画像高さ
14
+ # cap: キャプション
15
+ # from: 出典 << 出典URL
16
+
17
+ @template = <<EOT
18
+ <figure>
19
+ {{#imgs_list}}
20
+ <img src="{{fig_src}}" alt="{{alt}}" {{& img_style}}/>
21
+ {{/imgs_list}}
22
+ {{#figcaption_sw}}<figcaption markdown="span">{{caption}}{{& cited_from}}</figcaption>{{/figcaption_sw}}
23
+ </figure>
24
+ EOT
25
+
26
+ end
27
+
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+
3
+ require_relative 'fig/module_fig_base'
4
+
5
+ class Fig_P < Mustache
6
+
7
+ include Fig_00
8
+
9
+ # <<Fig_P>>
10
+ # src: 画像ファイル名
11
+ # name: 姓 名
12
+ # huri: ふり・がな
13
+ # width: 画像幅
14
+ # height: 画像高さ
15
+ # cap: |
16
+ # キャプション
17
+
18
+ @template = <<EOT
19
+ <figure>
20
+ {{#imgs_list}}
21
+ <img src="{{fig_src}}" alt="{{name}}氏" {{& img_style}} />
22
+ {{/imgs_list}}
23
+ <div style="text-align:left; padding:0em 2em;">
24
+ <figcaption><strong>{{name}}({{huri}})氏</strong></figcaption>
25
+ <figcaption markdown="span">{{caption}}</figcaption>
26
+ </div>
27
+ </figure>
28
+ EOT
29
+
30
+ end
31
+
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+
3
+ require_relative 'fig/module_fig_base'
4
+ require_relative 'fig/module_fig_plus'
5
+
6
+ class Fig_Z < Mustache
7
+
8
+ include Fig_00, Fig_01
9
+
10
+ # <<Fig_Z>>
11
+ # src: 画像ファイル名
12
+ # width: 画像幅
13
+ # height: 画像高さ
14
+ # cap: キャプション
15
+
16
+ @template = <<EOT
17
+ <figure>
18
+ {{#imgs_list}}
19
+ <a href="{{fig_src}}" rel="lightbox" target="_blank"><img src="{{fig_src}}" alt="{{alt}}" {{& img_style}}/></a>
20
+ {{/imgs_list}}
21
+ <figcaption markdown="span">{{#figcaption_sw}}{{& caption}}<br/>{{/figcaption_sw}}[画像クリックで拡大表示]</figcaption>
22
+ </figure>
23
+ EOT
24
+
25
+ end
26
+
@@ -0,0 +1,3 @@
1
+ module ZineBrewer
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'lib/zine_brewer/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "zine_brewer"
5
+ spec.version = ZineBrewer::VERSION
6
+ spec.authors = ["Akinori Ichigo"]
7
+ spec.email = ["akinori.ichigo@gmail.com"]
8
+
9
+ spec.summary = %q{Kramdown to HTML converter for Shoeisha Web Media}
10
+ spec.homepage = "https://github.com/akinori-ichigo/zine_brewer"
11
+ spec.license = "MIT"
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
13
+
14
+ spec.metadata["homepage_uri"] = spec.homepage
15
+ spec.metadata["source_code_uri"] = "https://github.com/akinori-ichigo/zine_brewer"
16
+ spec.metadata["changelog_uri"] = "https://github.com/akinori-ichigo/zine_brewer"
17
+
18
+ # Specify which files should be added to the gem when it is released.
19
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
21
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
+ end
23
+ spec.bindir = "exe"
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ["lib"]
26
+
27
+ spec.add_runtime_dependency "darkmouun"
28
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zine_brewer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Akinori Ichigo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-10-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: darkmouun
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
28
+ email:
29
+ - akinori.ichigo@gmail.com
30
+ executables:
31
+ - zine_brewer
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - CODE_OF_CONDUCT.md
36
+ - Gemfile
37
+ - LICENSE.txt
38
+ - README.md
39
+ - Rakefile
40
+ - bin/console
41
+ - bin/setup
42
+ - exe/zine_brewer
43
+ - lib/zine_brewer.rb
44
+ - lib/zine_brewer/kramdown/converter/sehtml.rb
45
+ - lib/zine_brewer/kramdown/parser/sekd.rb
46
+ - lib/zine_brewer/main.rb
47
+ - lib/zine_brewer/templates/bookranking.rb
48
+ - lib/zine_brewer/templates/casts.rb
49
+ - lib/zine_brewer/templates/fig/module_fig_base.rb
50
+ - lib/zine_brewer/templates/fig/module_fig_plus.rb
51
+ - lib/zine_brewer/templates/fig_a.rb
52
+ - lib/zine_brewer/templates/fig_h.rb
53
+ - lib/zine_brewer/templates/fig_n.rb
54
+ - lib/zine_brewer/templates/fig_p.rb
55
+ - lib/zine_brewer/templates/fig_z.rb
56
+ - lib/zine_brewer/version.rb
57
+ - zine_brewer.gemspec
58
+ homepage: https://github.com/akinori-ichigo/zine_brewer
59
+ licenses:
60
+ - MIT
61
+ metadata:
62
+ homepage_uri: https://github.com/akinori-ichigo/zine_brewer
63
+ source_code_uri: https://github.com/akinori-ichigo/zine_brewer
64
+ changelog_uri: https://github.com/akinori-ichigo/zine_brewer
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 2.3.0
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubygems_version: 3.1.4
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Kramdown to HTML converter for Shoeisha Web Media
84
+ test_files: []