asciidoctor-epub3 1.5.0.alpha.15 → 1.5.0.alpha.16

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4404201f6335840b7c6bb344eed4870654ae7132a3a63309d3a3f64536da62f2
4
- data.tar.gz: 252e5083cc9a5e1d589181ee0b141414f7182867ba763a1927271d72779ab4e2
3
+ metadata.gz: bbc1a47f9a011cc194ab8cd731ec3d6121079f0a49e32540329d8b30fb390ecc
4
+ data.tar.gz: c02ceac6c1c9259ff70a3b799ceb9ed70f5af5e5b233914882332b560f95259d
5
5
  SHA512:
6
- metadata.gz: e85119f2680e17c119d1997e9272e14630c64caae39c27a2055ec60cf617399d95154e1cf3de41f2eedebc9b73a373f2028c3c854cb1d779da3adfde3c79c0f8
7
- data.tar.gz: ef099d1233ad69d99e0985ba6f9580d99f80ccdc824d036505f5507adf18fad79a8643684f111665d086147558a41eec4513db95be5d93d6e4d1bf46d35a3dba
6
+ metadata.gz: 409a0b3d28aa559a14f33fd042b739595b1be0625b18044c308b088f3396479fc3859eece4d81bf9439d9c10e815061143eaed29506266154b9c22e6b2fd8100
7
+ data.tar.gz: d1e8a6c56803400dc9181bbaacec99df81a522a24301e96adb40f19456066e16d27202da6ce8e48081ee177e51c166916ef951eefc3119aba3ea02235b1b3628
@@ -5,6 +5,14 @@
5
5
  This document provides a high-level view of the changes to the {project-name} by release.
6
6
  For a detailed view of what has changed, refer to the {uri-repo}/commits/master[commit history] on GitHub.
7
7
 
8
+ == 1.5.0.alpha.16 (2020-04-26) - @slonopotamus
9
+
10
+ * add basic audio and video support (#9)
11
+ * add support for `[horizontal]` definition list (#165)
12
+ * add proper handling of `:data-uri:` document attribute (#324)
13
+ * add support for customizable document splitting into chapters via `epub-chapter-level` attribute (#327)
14
+ * avoid outputting 'true' for unsupported blocks (#332)
15
+
8
16
  == 1.5.0.alpha.15 (2020-03-11) - @slonopotamus
9
17
 
10
18
  * support section numbering and captions (#20)
@@ -1,6 +1,6 @@
1
1
  = {project-name}: A _native_ EPUB3 converter for AsciiDoc
2
2
  Dan Allen <https://github.com/mojavelinux[@mojavelinux]>; Sarah White <https://github.com/graphitefriction[@graphitefriction]>
3
- v1.5.0.alpha.15, 2020-03-11
3
+ v1.5.0.alpha.16, 2020-04-26
4
4
  // Settings:
5
5
  :experimental:
6
6
  :idprefix:
@@ -140,6 +140,7 @@ Like other converters, Asciidoctor EPUB3 handles this chunking task by automatic
140
140
 
141
141
  When `doctype` attribute is set to `book`, each top-level section will become a separate ebook "chapter" file.
142
142
  This includes preface, bibliography, appendix, etc.
143
+ This behavior can be configured via `epub-chapter-level` document attribute.
143
144
 
144
145
  Otherwise, whole document is converted to a single ebook chapter.
145
146
 
@@ -298,6 +299,13 @@ The recommended practice is to identify the referenced resource by means of a st
298
299
  |An optional override of the properties attribute for this document's item in the manifest.
299
300
  _Only applies to a chapter document._
300
301
 
302
+ |epub-chapter-level
303
+ |Specify the section level at which to split the EPUB into separate "chapter" files.
304
+ This attribute only affects documents with `:doctype: book`.
305
+ The default is to split into chapters at level-1 sections.
306
+ This attribute only affects the internal composition of the EPUB, not the way chapters and sections are displayed to users.
307
+ Some readers may be slow if the chapter files are too large, so for large documents with few level-1 headings, one might want to use a chapter level of 2 or 3.
308
+
301
309
  |series-name, series-volume, series-id
302
310
  |Populates the series statements (`belongs-to-collection`) in the package metadata.
303
311
  Volume is a number, ID probably a UUID that is constant for all volumes in the series.
@@ -36,9 +36,10 @@ An extension for Asciidoctor that converts AsciiDoc documents to EPUB3 and KF8/M
36
36
  s.add_development_dependency 'rake', '~> 13.0.0'
37
37
  s.add_development_dependency 'rouge', '~> 3.0'
38
38
  s.add_development_dependency 'rspec', '~> 3.9.0'
39
- s.add_development_dependency 'rubocop', '~> 0.80.0'
39
+ s.add_development_dependency 'rubocop', '~> 0.81.0'
40
40
  s.add_development_dependency 'rubocop-rspec', '~> 1.38.0'
41
41
 
42
42
  s.add_runtime_dependency 'asciidoctor', '>= 1.5.6', '< 3.0.0'
43
43
  s.add_runtime_dependency 'gepub', '~> 1.0.0'
44
+ s.add_runtime_dependency 'mime-types', '~> 3.0'
44
45
  end
@@ -357,6 +357,11 @@ dl dd {
357
357
  margin-top: 0.25em;
358
358
  }
359
359
 
360
+ td.hdlist1 {
361
+ font-weight: bold;
362
+ padding-right: 0.625em;
363
+ }
364
+
360
365
  div.callout-list {
361
366
  margin-top: 0.5em;
362
367
  }
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'mime/types'
3
4
  require 'open3'
4
5
  require_relative 'font_icon_map'
5
6
 
@@ -29,7 +30,7 @@ module Asciidoctor
29
30
  unless (entry_dir = ::File.dirname entry.name) == '.' || (::File.directory? entry_dir)
30
31
  ::FileUtils.mkdir_p entry_dir
31
32
  end
32
- entry.extract
33
+ entry.extract entry.name
33
34
  end
34
35
  end
35
36
  end
@@ -104,7 +105,8 @@ module Asciidoctor
104
105
  if respond_to? method_name
105
106
  send method_name, node
106
107
  else
107
- logger.warn %(conversion missing in backend #{@backend} for #{name})
108
+ logger.warn %(#{::File.basename node.attr('docfile')}: conversion missing in backend #{@backend} for #{name})
109
+ nil
108
110
  end
109
111
  end
110
112
 
@@ -114,7 +116,8 @@ module Asciidoctor
114
116
  return Asciidoctor::Document === node ? node.attr('docname') || node.id : nil
115
117
  end
116
118
  return (node.id || 'preamble') if node.context == :preamble && node.level == 0
117
- Asciidoctor::Section === node && node.level <= 1 ? node.id : nil
119
+ chapter_level = [node.document.attr('epub-chapter-level', 1).to_i, 1].max
120
+ Asciidoctor::Section === node && node.level <= chapter_level ? node.id : nil
118
121
  end
119
122
 
120
123
  def get_numbered_title node
@@ -150,7 +153,7 @@ module Asciidoctor
150
153
  @epubcheck_path = node.attr 'ebook-epubcheck-path'
151
154
  @xrefs_seen = ::Set.new
152
155
  @icon_names = []
153
- @images = []
156
+ @media_files = []
154
157
  @footnotes = []
155
158
 
156
159
  @book = GEPUB::Book.new 'EPUB/package.opf'
@@ -221,14 +224,7 @@ module Asciidoctor
221
224
  add_front_matter_page node
222
225
 
223
226
  if node.doctype == 'book'
224
- toc_items = []
225
- node.sections.each do |section|
226
- toc_items << section
227
- section.sections.each do |subsection|
228
- next if get_chapter_name(node).nil?
229
- toc_items << subsection
230
- end
231
- end
227
+ toc_items = node.sections
232
228
  node.content
233
229
  else
234
230
  toc_items = [node]
@@ -245,13 +241,16 @@ module Asciidoctor
245
241
  docimagesdir = (node.attr 'imagesdir', '.').chomp '/'
246
242
  docimagesdir = (docimagesdir == '.' ? nil : %(#{docimagesdir}/))
247
243
 
248
- @images.each do |image|
249
- if image[:name].start_with? %(#{docimagesdir}jacket/cover.)
250
- logger.warn %(image path is reserved for cover artwork: #{image[:name]}; skipping image found in content)
251
- elsif ::File.readable? image[:path]
252
- @book.add_item image[:name], content: image[:path]
244
+ @media_files.each do |file|
245
+ if file[:name].start_with? %(#{docimagesdir}jacket/cover.)
246
+ logger.warn %(path is reserved for cover artwork: #{file[:name]}; skipping file found in content)
247
+ elsif ::File.readable? file[:path]
248
+ mime_types = MIME::Types.type_for file[:name]
249
+ mime_types.delete_if {|x| x.media_type != file[:media_type] }
250
+ preferred_mime_type = mime_types.empty? ? nil : mime_types[0].content_type
251
+ @book.add_item file[:name], content: file[:path], media_type: preferred_mime_type
253
252
  else
254
- logger.error %(#{File.basename node.attr('docfile')}: image not found or not readable: #{image[:path]})
253
+ logger.error %(#{File.basename node.attr('docfile')}: media file not found or not readable: #{file[:path]})
255
254
  end
256
255
  end
257
256
 
@@ -565,7 +564,7 @@ document.addEventListener('DOMContentLoaded', function(event, reader) {
565
564
  end
566
565
  figure_classes = ['listing']
567
566
  figure_classes << 'coalesce' if node.option? 'unbreakable'
568
- title_div = node.title? ? %(<figcaption>#{get_numbered_title node}</figcaption>) : ''
567
+ title_div = node.title? ? %(<figcaption>#{node.captioned_title}</figcaption>) : ''
569
568
  %(<figure class="#{figure_classes * ' '}">#{title_div}
570
569
  #{syntax_hl ? (syntax_hl.format node, lang, opts) : pre_open + (node.content || '') + pre_close}
571
570
  </figure>)
@@ -757,16 +756,30 @@ document.addEventListener('DOMContentLoaded', function(event, reader) {
757
756
  # TODO: add complex class if list has nested blocks
758
757
  def convert_dlist node
759
758
  lines = []
759
+ id_attribute = node.id ? %( id="#{node.id}") : ''
760
+
761
+ classes = case node.style
762
+ when 'horizontal'
763
+ ['hdlist', node.role]
764
+ when 'itemized', 'ordered'
765
+ # QUESTION should we just use itemized-list and ordered-list as the class here? or just list?
766
+ ['dlist', %(#{node.style}-list), node.role]
767
+ else
768
+ ['description-list']
769
+ end.compact
770
+
771
+ class_attribute = %( class="#{classes.join ' '}")
772
+
773
+ lines << %(<div#{id_attribute}#{class_attribute}>)
774
+ lines << %(<div class="title">#{node.title}</div>) if node.title?
775
+
760
776
  case (style = node.style)
761
777
  when 'itemized', 'ordered'
762
778
  list_tag_name = style == 'itemized' ? 'ul' : 'ol'
763
779
  role = node.role
764
780
  subject_stop = node.attr 'subject-stop', (role && (node.has_role? 'stack') ? nil : ':')
765
- # QUESTION should we just use itemized-list and ordered-list as the class here? or just list?
766
- div_classes = [%(#{style}-list), role].compact
767
781
  list_class_attr = (node.option? 'brief') ? ' class="brief"' : ''
768
- lines << %(<div class="#{div_classes * ' '}">
769
- <#{list_tag_name}#{list_class_attr}#{list_tag_name == 'ol' && (node.option? 'reversed') ? ' reversed="reversed"' : ''}>)
782
+ lines << %(<#{list_tag_name}#{list_class_attr}#{list_tag_name == 'ol' && (node.option? 'reversed') ? ' reversed="reversed"' : ''}>)
770
783
  node.items.each do |subjects, dd|
771
784
  # consists of one term (a subject) and supporting content
772
785
  subject = [*subjects].first.text
@@ -782,11 +795,40 @@ document.addEventListener('DOMContentLoaded', function(event, reader) {
782
795
  end
783
796
  lines << '</li>'
784
797
  end
785
- lines << %(</#{list_tag_name}>
786
- </div>)
798
+ lines << %(</#{list_tag_name}>)
799
+ when 'horizontal'
800
+ lines << '<table>'
801
+ if (node.attr? 'labelwidth') || (node.attr? 'itemwidth')
802
+ lines << '<colgroup>'
803
+ col_style_attribute = (node.attr? 'labelwidth') ? %( style="width: #{(node.attr 'labelwidth').chomp '%'}%;") : ''
804
+ lines << %(<col#{col_style_attribute} />)
805
+ col_style_attribute = (node.attr? 'itemwidth') ? %( style="width: #{(node.attr 'itemwidth').chomp '%'}%;") : ''
806
+ lines << %(<col#{col_style_attribute} />)
807
+ lines << '</colgroup>'
808
+ end
809
+ node.items.each do |terms, dd|
810
+ lines << '<tr>'
811
+ lines << %(<td class="hdlist1#{(node.option? 'strong') ? ' strong' : ''}">)
812
+ first_term = true
813
+ terms.each do |dt|
814
+ lines << %(<br />) unless first_term
815
+ lines << '<p>'
816
+ lines << dt.text
817
+ lines << '</p>'
818
+ first_term = nil
819
+ end
820
+ lines << '</td>'
821
+ lines << '<td class="hdlist2">'
822
+ if dd
823
+ lines << %(<p>#{dd.text}</p>) if dd.text?
824
+ lines << dd.content if dd.blocks?
825
+ end
826
+ lines << '</td>'
827
+ lines << '</tr>'
828
+ end
829
+ lines << '</table>'
787
830
  else
788
- lines << '<div class="description-list">
789
- <dl>'
831
+ lines << '<dl>'
790
832
  node.items.each do |terms, dd|
791
833
  [*terms].each do |dt|
792
834
  lines << %(<dt>
@@ -803,9 +845,10 @@ document.addEventListener('DOMContentLoaded', function(event, reader) {
803
845
  end
804
846
  lines << '</dd>'
805
847
  end
806
- lines << '</dl>
807
- </div>'
848
+ lines << '</dl>'
808
849
  end
850
+
851
+ lines << '</div>'
809
852
  lines * LF
810
853
  end
811
854
 
@@ -879,14 +922,16 @@ document.addEventListener('DOMContentLoaded', function(event, reader) {
879
922
  document
880
923
  end
881
924
 
882
- def register_image node, target
883
- if target.end_with? '.svg'
925
+ def register_media_file node, target, media_type
926
+ if target.end_with?('.svg') || target.start_with?('data:image/svg+xml')
884
927
  chapter = get_enclosing_chapter node
885
928
  chapter.set_attr 'epub-properties', [] unless chapter.attr? 'epub-properties'
886
929
  epub_properties = chapter.attr 'epub-properties'
887
930
  epub_properties << 'svg' unless epub_properties.include? 'svg'
888
931
  end
889
932
 
933
+ return if target.start_with? 'data:'
934
+
890
935
  out_dir = node.attr('outdir', nil, true) || doc_option(node.document, :to_dir)
891
936
  fs_path = (::File.join out_dir, target)
892
937
  unless ::File.exist? fs_path
@@ -894,7 +939,7 @@ document.addEventListener('DOMContentLoaded', function(event, reader) {
894
939
  fs_path = ::File.join base_dir, target
895
940
  end
896
941
  # We need *both* virtual and physical image paths. Unfortunately, references[:images] only has one of them.
897
- @images << { name: target, path: fs_path }
942
+ @media_files << { name: target, path: fs_path, media_type: media_type }
898
943
  end
899
944
 
900
945
  def resolve_image_attrs node
@@ -911,16 +956,81 @@ document.addEventListener('DOMContentLoaded', function(event, reader) {
911
956
  img_attrs
912
957
  end
913
958
 
959
+ def convert_audio node
960
+ id_attr = node.id ? %( id="#{node.id}") : ''
961
+ target = node.media_uri node.attr 'target'
962
+ register_media_file node, target, 'audio'
963
+ title_element = node.title? ? %(\n<figcaption>#{node.captioned_title}</figcaption>) : ''
964
+
965
+ autoplay_attr = (node.option? 'autoplay') ? ' autoplay="autoplay"' : ''
966
+ controls_attr = (node.option? 'nocontrols') ? '' : ' controls="controls"'
967
+ loop_attr = (node.option? 'loop') ? ' loop="loop"' : ''
968
+
969
+ start_t = node.attr 'start'
970
+ end_t = node.attr 'end'
971
+ if start_t || end_t
972
+ time_anchor = %(#t=#{start_t || ''}#{end_t ? ",#{end_t}" : ''})
973
+ else
974
+ time_anchor = ''
975
+ end
976
+
977
+ %(<figure#{id_attr} class="audioblock#{prepend_space node.role}">#{title_element}
978
+ <div class="content">
979
+ <audio src="#{target}#{time_anchor}"#{autoplay_attr}#{controls_attr}#{loop_attr}>
980
+ <div>Your Reading System does not support (this) audio.</div>
981
+ </audio>
982
+ </div>
983
+ </figure>)
984
+ end
985
+
986
+ # TODO: Support multiple video files in different formats for a single video
987
+ def convert_video node
988
+ id_attr = node.id ? %( id="#{node.id}") : ''
989
+ target = node.media_uri node.attr 'target'
990
+ register_media_file node, target, 'video'
991
+ title_element = node.title? ? %(\n<figcaption>#{node.captioned_title}</figcaption>) : ''
992
+
993
+ width_attr = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : ''
994
+ height_attr = (node.attr? 'height') ? %( height="#{node.attr 'height'}") : ''
995
+ autoplay_attr = (node.option? 'autoplay') ? ' autoplay="autoplay"' : ''
996
+ controls_attr = (node.option? 'nocontrols') ? '' : ' controls="controls"'
997
+ loop_attr = (node.option? 'loop') ? ' loop="loop"' : ''
998
+
999
+ start_t = node.attr 'start'
1000
+ end_t = node.attr 'end'
1001
+ if start_t || end_t
1002
+ time_anchor = %(#t=#{start_t || ''}#{end_t ? ",#{end_t}" : ''})
1003
+ else
1004
+ time_anchor = ''
1005
+ end
1006
+
1007
+ if (poster = node.attr 'poster').nil_or_empty?
1008
+ poster_attr = ''
1009
+ else
1010
+ poster = node.media_uri poster
1011
+ register_media_file node, poster, 'image'
1012
+ poster_attr = %( poster="#{poster}")
1013
+ end
1014
+
1015
+ %(<figure#{id_attr} class="video#{prepend_space node.role}">#{title_element}
1016
+ <div class="content">
1017
+ <video src="#{target}#{time_anchor}"#{width_attr}#{height_attr}#{autoplay_attr}#{poster_attr}#{controls_attr}#{loop_attr}>
1018
+ <div>Your Reading System does not support (this) video.</div>
1019
+ </video>
1020
+ </div>
1021
+ </figure>)
1022
+ end
1023
+
914
1024
  def convert_image node
915
1025
  target = node.image_uri node.attr 'target'
916
- register_image node, target
1026
+ register_media_file node, target, 'image'
917
1027
  id_attr = node.id ? %( id="#{node.id}") : ''
1028
+ title_element = node.title? ? %(\n<figcaption>#{node.captioned_title}</figcaption>) : ''
918
1029
  img_attrs = resolve_image_attrs node
919
1030
  %(<figure#{id_attr} class="image#{prepend_space node.role}">
920
1031
  <div class="content">
921
1032
  <img src="#{target}"#{prepend_space img_attrs * ' '} />
922
- </div>#{node.title? ? %(
923
- <figcaption>#{node.captioned_title}</figcaption>) : ''}
1033
+ </div>#{title_element}
924
1034
  </figure>)
925
1035
  end
926
1036
 
@@ -1025,7 +1135,7 @@ document.addEventListener('DOMContentLoaded', function(event, reader) {
1025
1135
  %(<i class="#{i_classes * ' '}"></i>)
1026
1136
  else
1027
1137
  target = node.image_uri node.target
1028
- register_image node, target
1138
+ register_media_file node, target, 'image'
1029
1139
 
1030
1140
  img_attrs = resolve_image_attrs node
1031
1141
  img_attrs << %(class="inline#{prepend_space node.role}")
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Asciidoctor
4
4
  module Epub3
5
- VERSION = '1.5.0.alpha.15'
5
+ VERSION = '1.5.0.alpha.16'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asciidoctor-epub3
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0.alpha.15
4
+ version: 1.5.0.alpha.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Allen
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-03-10 00:00:00.000000000 Z
12
+ date: 2020-04-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: asciidoctor-diagram
@@ -107,14 +107,14 @@ dependencies:
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 0.80.0
110
+ version: 0.81.0
111
111
  type: :development
112
112
  prerelease: false
113
113
  version_requirements: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 0.80.0
117
+ version: 0.81.0
118
118
  - !ruby/object:Gem::Dependency
119
119
  name: rubocop-rspec
120
120
  requirement: !ruby/object:Gem::Requirement
@@ -163,6 +163,20 @@ dependencies:
163
163
  - - "~>"
164
164
  - !ruby/object:Gem::Version
165
165
  version: 1.0.0
166
+ - !ruby/object:Gem::Dependency
167
+ name: mime-types
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '3.0'
173
+ type: :runtime
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '3.0'
166
180
  description: 'An extension for Asciidoctor that converts AsciiDoc documents to EPUB3
167
181
  and KF8/MOBI (Kindle) e-book archives.
168
182