diff options
| -rw-r--r-- | lib/asciidoctor/pdf/converter.rb | 155 | ||||
| -rw-r--r-- | lib/asciidoctor/pdf/formatted_text/transform.rb | 38 | ||||
| -rw-r--r-- | lib/asciidoctor/pdf/theme_loader.rb | 14 |
3 files changed, 110 insertions, 97 deletions
diff --git a/lib/asciidoctor/pdf/converter.rb b/lib/asciidoctor/pdf/converter.rb index 56db85fc..1f4a4f90 100644 --- a/lib/asciidoctor/pdf/converter.rb +++ b/lib/asciidoctor/pdf/converter.rb @@ -717,7 +717,7 @@ module Asciidoctor when 'page' pagenums = term.dests.uniq {|dest| dest[:page] }.map {|dest| %(<a anchor="#{dest[:anchor]}">#{dest[:page]}</a>) } when 'range' - first_anchor_per_page = term.dests.each_with_object({}) {|dest, accum| accum[dest[:page]] ||= dest[:anchor] } + first_anchor_per_page = {}.tap {|accum| term.dests.each {|dest| accum[dest[:page]] ||= dest[:anchor] } } pagenums = (consolidate_ranges first_anchor_per_page.keys).map do |range| anchor = first_anchor_per_page[(range.include? '-') ? (range.partition '-')[0] : range] %(<a anchor="#{anchor}">#{range}</a>) @@ -1081,7 +1081,7 @@ module Asciidoctor highlight_lines = nil else pg_highlight_bg_color = pg_block_styles[:highlight_background_color] - highlight_lines = highlight_lines.map {|linenum| [linenum, pg_highlight_bg_color] }.to_h + highlight_lines = {}.tap {|accum| highlight_lines.each {|linenum| accum[linenum] = pg_highlight_bg_color } } end end if (node.option? 'linenums') || (node.attr? 'linenums') @@ -1120,7 +1120,7 @@ module Asciidoctor lexer ||= ::Rouge::Lexers::PlainText source_string, conum_mapping = extract_conums source_string if callouts_enabled if (node.attr? 'highlight') && !(hl_lines = (node.resolve_lines_to_highlight source_string, (node.attr 'highlight'))).empty? - formatter_opts[:highlight_lines] = hl_lines.map {|linenum| [linenum, true] }.to_h + formatter_opts[:highlight_lines] = {}.tap {|accum| hl_lines.each {|linenum| accum[linenum] = true } } end fragments = formatter.format (lexer.lex source_string), formatter_opts rescue [text: source_string] source_chunks = conum_mapping ? (restore_conums fragments, conum_mapping) : fragments @@ -3261,64 +3261,70 @@ module Asciidoctor end end - colspec_dict = PageSides.each_with_object({}) do |side, acc| - side_trim_content_width = trim_content_width[side] - if (custom_colspecs = @theme[%(#{periphery}_#{side}_columns)] || @theme[%(#{periphery}_columns)]) - case (colspecs = (custom_colspecs.to_s.tr ',', ' ').split).size - when 0, 1 - colspecs = { left: '0', center: colspecs[0] || '100', right: '0' } - when 2 - colspecs = { left: colspecs[0], center: '0', right: colspecs[1] } - else # 3 - colspecs = { left: colspecs[0], center: colspecs[1], right: colspecs[2] } - end - tot_width = 0 - side_colspecs = colspecs.map do |col, spec| - if (alignment_char = spec.chr).to_i.to_s == alignment_char - alignment = :left - rel_width = spec.to_f - else - alignment = AlignmentTable[alignment_char] - rel_width = (spec.slice 1, spec.length).to_f + colspec_dict = {}.tap do |acc| + PageSides.each do |side| + side_trim_content_width = trim_content_width[side] + if (custom_colspecs = @theme[%(#{periphery}_#{side}_columns)] || @theme[%(#{periphery}_columns)]) + case (colspecs = (custom_colspecs.to_s.tr ',', ' ').split).size + when 0, 1 + colspecs = { left: '0', center: colspecs[0] || '100', right: '0' } + when 2 + colspecs = { left: colspecs[0], center: '0', right: colspecs[1] } + else # 3 + colspecs = { left: colspecs[0], center: colspecs[1], right: colspecs[2] } end - tot_width += rel_width - [col, align: alignment, width: rel_width, x: 0] - end.to_h - # QUESTION: should we allow the columns to overlap (capping width at 100%)? - side_colspecs.each {|_, colspec| colspec[:width] = (colspec[:width] / tot_width) * side_trim_content_width } - side_colspecs[:right][:x] = (side_colspecs[:center][:x] = side_colspecs[:left][:width]) + side_colspecs[:center][:width] - acc[side] = side_colspecs - else - acc[side] = { - left: { align: :left, width: side_trim_content_width, x: 0 }, - center: { align: :center, width: side_trim_content_width, x: 0 }, - right: { align: :right, width: side_trim_content_width, x: 0 }, - } + tot_width = 0 + side_colspecs = {}.tap do |accum| + colspecs.each do |col, spec| + if (alignment_char = spec.chr).to_i.to_s == alignment_char + alignment = :left + rel_width = spec.to_f + else + alignment = AlignmentTable[alignment_char] + rel_width = (spec.slice 1, spec.length).to_f + end + tot_width += rel_width + accum[col] = { align: alignment, width: rel_width, x: 0 } + end + end + # QUESTION: should we allow the columns to overlap (capping width at 100%)? + side_colspecs.each {|_, colspec| colspec[:width] = (colspec[:width] / tot_width) * side_trim_content_width } + side_colspecs[:right][:x] = (side_colspecs[:center][:x] = side_colspecs[:left][:width]) + side_colspecs[:center][:width] + acc[side] = side_colspecs + else + acc[side] = { + left: { align: :left, width: side_trim_content_width, x: 0 }, + center: { align: :center, width: side_trim_content_width, x: 0 }, + right: { align: :right, width: side_trim_content_width, x: 0 }, + } + end end end - content_dict = PageSides.each_with_object({}) do |side, acc| - side_content = {} - ColumnPositions.each do |position| - next if (val = @theme[%(#{periphery}_#{side}_#{position}_content)]).nil_or_empty? - val = val.to_s unless ::String === val - if (val.include? ':') && val =~ ImageAttributeValueRx - attrlist = $2 - image_attrs = (AttributeList.new attrlist).parse %w(alt width) - image_path, image_format = ::Asciidoctor::Image.target_and_format $1, image_attrs - if (image_path = resolve_image_path doc, image_path, image_format, @themesdir) && (::File.readable? image_path) - image_opts = resolve_image_options image_path, image_format, image_attrs, container_size: [colspec_dict[side][position][:width], trim_content_height[side]] - side_content[position] = [image_path, image_opts, image_attrs['link']] + content_dict = {}.tap do |acc| + PageSides.each do |side| + side_content = {} + ColumnPositions.each do |position| + next if (val = @theme[%(#{periphery}_#{side}_#{position}_content)]).nil_or_empty? + val = val.to_s unless ::String === val + if (val.include? ':') && val =~ ImageAttributeValueRx + attrlist = $2 + image_attrs = (AttributeList.new attrlist).parse %w(alt width) + image_path, image_format = ::Asciidoctor::Image.target_and_format $1, image_attrs + if (image_path = resolve_image_path doc, image_path, image_format, @themesdir) && (::File.readable? image_path) + image_opts = resolve_image_options image_path, image_format, image_attrs, container_size: [colspec_dict[side][position][:width], trim_content_height[side]] + side_content[position] = [image_path, image_opts, image_attrs['link']] + else + # NOTE: allows inline image handler to report invalid reference and replace with alt text + side_content[position] = %(image:#{image_path}[#{attrlist}]) + end else - # NOTE: allows inline image handler to report invalid reference and replace with alt text - side_content[position] = %(image:#{image_path}[#{attrlist}]) + side_content[position] = val end - else - side_content[position] = val end - end - acc[side] = side_content + acc[side] = side_content + end end if (trim_bg_color = trim_styles[:bg_color]) || trim_bg_image || trim_border_width > 0 @@ -3866,18 +3872,17 @@ module Asciidoctor def register_fonts font_catalog, fonts_dir return unless font_catalog - dirs = (fonts_dir.split ValueSeparatorRx, -1).map do |dir| - dir == 'GEM_FONTS_DIR' || dir.empty? ? ThemeLoader::FontsDir : dir - end + dirs = (fonts_dir.split ValueSeparatorRx, -1).map {|dir| dir == 'GEM_FONTS_DIR' || dir.empty? ? ThemeLoader::FontsDir : dir } font_catalog.each do |key, styles| - styles = styles.each_with_object({}) do |(style, path), accum| - found = dirs.any? do |dir| - resolved_font_path = font_path path, dir - accum[style.to_sym] = resolved_font_path if ::File.readable? resolved_font_path + register_font key => ({}.tap do |accum| + styles.each do |style, path| + found = dirs.any? do |dir| + resolved_font_path = font_path path, dir + accum[style.to_sym] = resolved_font_path if ::File.readable? resolved_font_path + end + raise ::Errno::ENOENT, ((File.absolute_path? path) ? %(#{path} not found) : %(#{path} not found in #{fonts_dir.gsub ValueSeparatorRx, ' or '})) unless found end - raise ::Errno::ENOENT, ((File.absolute_path? path) ? %(#{path} not found) : %(#{path} not found in #{fonts_dir.gsub ValueSeparatorRx, ' or '})) unless found - end - register_font key => styles + end) end end @@ -4578,14 +4583,16 @@ module Asciidoctor def consolidate_ranges nums if nums.size > 1 prev = nil - nums.each_with_object [] do |num, accum| + accum = [] + nums.each do |num| if prev && (prev.to_i + 1) == num.to_i accum[-1][1] = num else accum << [num] end prev = num - end.map {|range| range.join '-' } + end + accum.map {|range| range.join '-' } else nums end @@ -4930,14 +4937,16 @@ module Asciidoctor result = yield else email = nil - original_attrs = AuthorAttributeNames.each_with_object({}) do |(prop_name, attr_name), accum| - accum[attr_name] = doc.attr attr_name - if (val = author[prop_name]) - doc.set_attr attr_name, val - # NOTE: email attribute could be a url - email = val if prop_name == :email - else - doc.remove_attr attr_name + original_attrs = {}.tap do |accum| + AuthorAttributeNames.each do |prop_name, attr_name| + accum[attr_name] = doc.attr attr_name + if (val = author[prop_name]) + doc.set_attr attr_name, val + # NOTE: email attribute could be a url + email = val if prop_name == :email + else + doc.remove_attr attr_name + end end end doc.set_attr 'url', ((email.include? '@') ? %(mailto:#{email}) : email) if email diff --git a/lib/asciidoctor/pdf/formatted_text/transform.rb b/lib/asciidoctor/pdf/formatted_text/transform.rb index 0dde0237..882fc2c9 100644 --- a/lib/asciidoctor/pdf/formatted_text/transform.rb +++ b/lib/asciidoctor/pdf/formatted_text/transform.rb @@ -95,22 +95,24 @@ module Asciidoctor styles: (to_styles theme.menu_font_style), }.compact, } - revise_roles = [].to_set - theme.each_pair.each_with_object @theme_settings do |(key, val), accum| - next unless (key = key.to_s).start_with? 'role_' - role, key = (key.slice 5, key.length).split '_', 2 - if (prop = ThemeKeyToFragmentProperty[key]) - (accum[role] ||= {})[prop] = val - #elsif key == 'font_kerning' - # unless (resolved_val = val == 'none' ? false : (val == 'normal' ? true : nil)).nil? - # (accum[role] ||= {})[:kerning] = resolved_val - # end - elsif key == 'font_style' || key == 'text_decoration' - revise_roles << role + @theme_settings.tap do |accum| + revise_roles = [].to_set + theme.each_pair do |key, val| + next unless (key = key.to_s).start_with? 'role_' + role, key = (key.slice 5, key.length).split '_', 2 + if (prop = ThemeKeyToFragmentProperty[key]) + (accum[role] ||= {})[prop] = val + #elsif key == 'font_kerning' + # unless (resolved_val = val == 'none' ? false : (val == 'normal' ? true : nil)).nil? + # (accum[role] ||= {})[:kerning] = resolved_val + # end + elsif key == 'font_style' || key == 'text_decoration' + revise_roles << role + end + end + revise_roles.each do |role| + (accum[role] ||= {})[:styles] = to_styles theme[%(role_#{role}_font_style)], theme[%(role_#{role}_text_decoration)] end - end - revise_roles.each_with_object @theme_settings do |role, accum| - (accum[role] ||= {})[:styles] = to_styles theme[%(role_#{role}_font_style)], theme[%(role_#{role}_text_decoration)] end @theme_settings['line-through'] = { styles: [:strikethrough].to_set } unless @theme_settings.key? 'line-through' @theme_settings['underline'] = { styles: [:underline].to_set } unless @theme_settings.key? 'underline' @@ -276,8 +278,10 @@ module Asciidoctor when '#' # hex string (e.g., #FF0000) fragment[:color] = value.length == 7 ? (value.slice 1, 6) : (value.slice 1, 3).each_char.map {|c| c * 2 }.join if HexColorRx.match? value when '[' # CMYK array (e.g., [50, 100, 0, 0]) - fragment[:color] = ((((value.slice 1, value.length).chomp ']').split ', ', 4).each_with_object ::Array.new 4, 0).with_index do |(it, accum), idx| - accum[idx] = (ival = it.to_i) == (fval = it.to_f) ? ival : fval + fragment[:color] = [0, 0, 0, 0].tap do |accum| + (((value.slice 1, value.length).chomp ']').split ', ', 4).each_with_index do |it, idx| + accum[idx] = (ival = it.to_i) == (fval = it.to_f) ? ival : fval + end end else # assume a 6-character hex color (internal only) fragment[:color] = value diff --git a/lib/asciidoctor/pdf/theme_loader.rb b/lib/asciidoctor/pdf/theme_loader.rb index 245f7184..71c13ee5 100644 --- a/lib/asciidoctor/pdf/theme_loader.rb +++ b/lib/asciidoctor/pdf/theme_loader.rb @@ -15,9 +15,7 @@ module Asciidoctor BaseThemePath = ::File.join ThemesDir, 'base-theme.yml' BundledThemeNames = (::Dir.children ThemesDir).map {|it| it.slice 0, it.length - 10 } DeprecatedCategoryKeys = { 'blockquote' => 'quote', 'key' => 'kbd', 'literal' => 'codespan', 'outline_list' => 'list' } - DeprecatedKeys = %w(base heading heading_h1 heading_h2 heading_h3 heading_h4 heading_h5 heading_h6 title_page abstract abstract_title admonition_label sidebar_title toc_title).each_with_object({ 'table_caption_side' => 'table_caption_end' }) do |prefix, accum| - accum[%(#{prefix}_align)] = %(#{prefix}_text_align) - end + DeprecatedKeys = { 'table_caption_side' => 'table_caption_end' }.tap {|accum| %w(base heading heading_h1 heading_h2 heading_h3 heading_h4 heading_h5 heading_h6 title_page abstract abstract_title admonition_label sidebar_title toc_title).each {|prefix| accum[%(#{prefix}_align)] = %(#{prefix}_text_align) } } PaddingBottomHackKeys = %w(example_padding quote_padding sidebar_padding verse_padding) VariableRx = /\$([a-z0-9_-]+)/ @@ -159,10 +157,12 @@ module Asciidoctor elsif key == 'font_fallbacks' data[key] = ::Array === val ? val.map {|name| expand_vars name.to_s, data } : [] elsif key.start_with? 'admonition_icon_' - data[key] = val.map do |(key2, val2)| - key2 = key2.tr '-', '_' if key2.include? '-' - [key2.to_sym, (key2.end_with? '_color') ? (to_color evaluate val2, data) : (evaluate val2, data)] - end.to_h if val + data[key] = {}.tap do |accum| + val.each do |key2, val2| + key2 = key2.tr '-', '_' if key2.include? '-' + accum[key2.to_sym] = (key2.end_with? '_color') ? (to_color evaluate val2, data) : (evaluate val2, data) + end + end if val elsif ::Hash === val if (rekey = DeprecatedCategoryKeys[key]) logger.warn %(the #{key.tr '_', '-'} theme category is deprecated; use the #{rekey.tr '_', '-'} category instead) |
