Method: Asciidoctor.convert

Defined in:
lib/asciidoctor/convert.rb

.convert(input, options = {}) ⇒ Object Also known as: render

Parse the AsciiDoc source input into an Asciidoctor::Document and convert it to the specified backend format.

Accepts input as an IO (or StringIO), String or String Array object. If the input is a File, the object is expected to be opened for reading and is not closed afterwards by this method. Information about the file (filename, directory name, etc) gets assigned to attributes on the Document object.

If the :to_file option is true, and the input is a File, the output is written to a file adjacent to the input file, having an extension that corresponds to the backend format. Otherwise, if the :to_file option is specified, the file is written to that file. If :to_file is not an absolute path, it is resolved relative to :to_dir, if given, otherwise the Document#base_dir. If the target directory does not exist, it will not be created unless the :mkdirs option is set to true. If the file cannot be written because the target directory does not exist, or because it falls outside of the Document#base_dir in safe mode, an IOError is raised.

If the output is going to be written to a file, the header and footer are included unless specified otherwise (writing to a file implies creating a standalone document). Otherwise, the header and footer are not included by default and the converted result is returned.

Parameters:

  • input

    the String AsciiDoc source filename

  • options (defaults to: {})

    a String, Array or Hash of options to control processing (default: {}) String and Array values are converted into a Hash. See Asciidoctor::Document#initialize for details about options.

Returns:

  • the Document object if the converted String is written to a file, otherwise the converted String



34
35
36
37
38
39
40
41
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/asciidoctor/convert.rb', line 34

def convert input, options = {}
  (options = options.merge).delete :parse
  to_dir = options.delete :to_dir
  mkdirs = options.delete :mkdirs

  case (to_file = options.delete :to_file)
  when true, nil
    unless (write_to_target = to_dir)
      sibling_path = ::File.absolute_path input.path if ::File === input
    end
    to_file = nil
  when false
    to_file = nil
  when '/dev/null'
    return load input, options
  else
    options[:to_file] = write_to_target = to_file unless (stream_output = to_file.respond_to? :write)
  end

  unless options.key? :standalone
    if sibling_path || write_to_target
      options[:standalone] = options.fetch :header_footer, true
    elsif options.key? :header_footer
      options[:standalone] = options[:header_footer]
    end
  end

  # NOTE outfile may be controlled by document attributes, so resolve outfile after loading
  if sibling_path
    options[:to_dir] = outdir = ::File.dirname sibling_path
  elsif write_to_target
    if to_dir
      if to_file
        options[:to_dir] = ::File.dirname ::File.expand_path to_file, to_dir
      else
        options[:to_dir] = ::File.expand_path to_dir
      end
    elsif to_file
      options[:to_dir] = ::File.dirname ::File.expand_path to_file
    end
  end

  # NOTE :to_dir is always set when outputting to a file
  # NOTE :to_file option only passed if assigned an explicit path
  doc = load input, options

  if sibling_path # write to file in same directory
    outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix})
    raise ::IOError, %(input file and output file cannot be the same: #{outfile}) if outfile == sibling_path
  elsif write_to_target # write to explicit file or directory
    working_dir = (options.key? :base_dir) ? (::File.expand_path options[:base_dir]) : ::Dir.pwd
    # QUESTION should the jail be the working_dir or doc.base_dir???
    jail = doc.safe >= SafeMode::SAFE ? working_dir : nil
    if to_dir
      outdir = doc.normalize_system_path(to_dir, working_dir, jail, target_name: 'to_dir', recover: false)
      if to_file
        outfile = doc.normalize_system_path(to_file, outdir, nil, target_name: 'to_dir', recover: false)
        # reestablish outdir as the final target directory (in the case to_file had directory segments)
        outdir = ::File.dirname outfile
      else
        outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix})
      end
    elsif to_file
      outfile = doc.normalize_system_path(to_file, working_dir, jail, target_name: 'to_dir', recover: false)
      # establish outdir as the final target directory (in the case to_file had directory segments)
      outdir = ::File.dirname outfile
    end

    if ::File === input && outfile == (::File.absolute_path input.path)
      raise ::IOError, %(input file and output file cannot be the same: #{outfile})
    end

    if mkdirs
      Helpers.mkdir_p outdir
    else
      # NOTE we intentionally refer to the directory as it was passed to the API
      raise ::IOError, %(target directory does not exist: #{to_dir} (hint: set :mkdirs option)) unless ::File.directory? outdir
    end
  else # write to stream
    outfile = to_file
    outdir = nil
  end

  if outfile && !stream_output
    output = doc.convert 'outfile' => outfile, 'outdir' => outdir
  else
    output = doc.convert
  end

  if outfile
    doc.write output, outfile

    # NOTE document cannot control this behavior if safe >= SafeMode::SERVER
    # NOTE skip if stylesdir is a URI
    if !stream_output && doc.safe < SafeMode::SECURE && (doc.attr? 'linkcss') && (doc.attr? 'copycss') &&
        (doc.basebackend? 'html') && !((stylesdir = (doc.attr 'stylesdir')) && (Helpers.uriish? stylesdir))
      if (stylesheet = doc.attr 'stylesheet')
        if DEFAULT_STYLESHEET_KEYS.include? stylesheet
          copy_asciidoctor_stylesheet = true
        elsif !(Helpers.uriish? stylesheet)
          copy_user_stylesheet = true
        end
      end
      copy_syntax_hl_stylesheet = (syntax_hl = doc.syntax_highlighter) && (syntax_hl.write_stylesheet? doc)
      if copy_asciidoctor_stylesheet || copy_user_stylesheet || copy_syntax_hl_stylesheet
        stylesoutdir = doc.normalize_system_path(stylesdir, outdir, doc.safe >= SafeMode::SAFE ? outdir : nil)
        if mkdirs
          Helpers.mkdir_p stylesoutdir
        else
          raise ::IOError, %(target stylesheet directory does not exist: #{stylesoutdir} (hint: set :mkdirs option)) unless ::File.directory? stylesoutdir
        end

        if copy_asciidoctor_stylesheet
          Stylesheets.instance.write_primary_stylesheet stylesoutdir
        # FIXME should Stylesheets also handle the user stylesheet?
        elsif copy_user_stylesheet
          if (stylesheet_src = doc.attr 'copycss') == '' || stylesheet_src == true
            stylesheet_src = doc.normalize_system_path stylesheet
          else
            # NOTE in this case, copycss is a source location (but cannot be a URI)
            stylesheet_src = doc.normalize_system_path stylesheet_src.to_s
          end
          stylesheet_dest = doc.normalize_system_path stylesheet, stylesoutdir, (doc.safe >= SafeMode::SAFE ? outdir : nil)
          # NOTE don't warn if src can't be read and dest already exists (see #2323)
          if stylesheet_src != stylesheet_dest && (stylesheet_data = doc.read_asset stylesheet_src,
              warn_on_failure: !(::File.file? stylesheet_dest), label: 'stylesheet')
            if (stylesheet_outdir = ::File.dirname stylesheet_dest) != stylesoutdir && !(::File.directory? stylesheet_outdir)
              if mkdirs
                Helpers.mkdir_p stylesheet_outdir
              else
                raise ::IOError, %(target stylesheet directory does not exist: #{stylesheet_outdir} (hint: set :mkdirs option))
              end
            end
            ::File.write stylesheet_dest, stylesheet_data, mode: FILE_WRITE_MODE
          end
        end
        syntax_hl.write_stylesheet doc, stylesoutdir if copy_syntax_hl_stylesheet
      end
    end
    doc
  else
    output
  end
end