Class: Asciidoctor::Document
- Inherits:
-
AbstractBlock
- Object
- AbstractNode
- AbstractBlock
- Asciidoctor::Document
- Defined in:
- lib/asciidoctor/document.rb
Overview
can take the process to completion by calling the #convert method.
Defined Under Namespace
Classes: AttributeEntry, Author, Footnote, ImageReference, Title
Constant Summary
Constants included from Substitutors
Substitutors::CAN, Substitutors::CGI, Substitutors::DEL, Substitutors::ESC_R_SB, Substitutors::HighlightedPassSlotRx, Substitutors::PASS_END, Substitutors::PASS_START, Substitutors::PLUS, Substitutors::PassSlotRx, Substitutors::QuotedTextSniffRx, Substitutors::RS, Substitutors::R_SB, Substitutors::SUB_GROUPS, Substitutors::SUB_HINTS, Substitutors::SUB_OPTIONS, Substitutors::SpecialCharsRx, Substitutors::SpecialCharsTr
Instance Attribute Summary collapse
-
#backend ⇒ Object
readonly
Get the cached value of the backend attribute for this document.
-
#base_dir ⇒ Object
readonly
Get the String base directory for converting this document.
-
#catalog ⇒ Object
(also: #references)
readonly
Get the document catalog Hash.
-
#compat_mode ⇒ Object
readonly
Get the Boolean AsciiDoc compatibility mode.
-
#converter ⇒ Object
readonly
Get the Converter associated with this document.
-
#counters ⇒ Object
readonly
Get the Hash of document counters.
-
#doctype ⇒ Object
readonly
Get the cached value of the doctype attribute for this document.
-
#extensions ⇒ Object
readonly
Get the activated Extensions::Registry associated with this document.
-
#header ⇒ Object
readonly
Get the level-0 Section (i.e., doctitle).
-
#options ⇒ Object
readonly
Get the Hash of resolved options used to initialize this Document.
-
#outfilesuffix ⇒ Object
readonly
Get the outfilesuffix defined at the end of the header.
-
#parent_document ⇒ Object
readonly
Get a reference to the parent Document of this nested document.
-
#path_resolver ⇒ Object
readonly
Get/Set the PathResolver instance used to resolve paths in this Document.
-
#reader ⇒ Object
readonly
Get the Reader associated with this document.
-
#safe ⇒ Object
readonly
Public A read-only integer value indicating the level of security that should be enforced while processing this document.
-
#sourcemap ⇒ Object
Get or set the Boolean flag that indicates whether source map information should be tracked by the parser.
-
#syntax_highlighter ⇒ Object
readonly
Get the SyntaxHighlighter associated with this document.
Attributes inherited from AbstractBlock
#blocks, #caption, #content_model, #level, #numeral, #source_location, #style, #subs
Attributes inherited from AbstractNode
#attributes, #context, #document, #id, #node_name, #parent
Instance Method Summary collapse
-
#<<(block) ⇒ The
Append a content Block to this Document.
-
#attribute_locked?(name) ⇒ Boolean
Determine if the attribute has been locked by being assigned in document options.
-
#author ⇒ Object
Convenience method to retrieve the document attribute ‘author’.
-
#authors ⇒ Object
Convenience method to retrieve the authors of this document as an Array of Author objects.
- #basebackend?(base) ⇒ Boolean
- #callouts ⇒ Object
- #content ⇒ Object
-
#convert(opts = {}) ⇒ Object
(also: #render)
Convert the AsciiDoc document using the templates loaded by the Converter.
-
#counter(name, seed = nil) ⇒ Object
Get the named counter and take the next number in the sequence.
-
#delete_attribute(name) ⇒ Object
Delete the specified attribute from the document if the name is not locked.
-
#docinfo(location = :head, suffix = nil) ⇒ Object
Read the docinfo file(s) for inclusion in the document template.
- #docinfo_processors?(location = :head) ⇒ Boolean
-
#doctitle(opts = {}) ⇒ Title
(also: #name)
Resolves the primary title for the document.
- #embedded? ⇒ Boolean
- #extensions? ⇒ Boolean
- #first_section ⇒ Object
- #footnotes ⇒ Object
- #footnotes? ⇒ Boolean
- #header? ⇒ Boolean (also: #has_header?)
-
#increment_and_store_counter(counter_name, block) ⇒ Object
(also: #counter_increment)
Increment the specified counter and store it in the block’s attributes.
-
#initialize(data = nil, options = {}) ⇒ Document
constructor
Initialize a Document object.
- #nested? ⇒ Boolean
- #nofooter ⇒ Object
- #noheader ⇒ Object
- #notitle ⇒ Object
-
#parse(data = nil) ⇒ Document
Parse the AsciiDoc source stored in the Reader into an abstract syntax tree.
-
#parsed? ⇒ Boolean
Returns whether the source lines of the document have been parsed.
-
#playback_attributes(block_attributes) ⇒ Object
Replay attribute assignments at the block level.
-
#register(type, value) ⇒ Object
Register a reference in the document catalog.
-
#resolve_id(text) ⇒ Object
Scan registered references and return the ID of the first reference that matches the specified reference text.
-
#restore_attributes ⇒ Object
Restore the attributes to the previously saved state (attributes in header).
-
#revdate ⇒ Object
Convenience method to retrieve the document attribute ‘revdate’.
-
#set_attribute(name, value = '') ⇒ Object
Set the specified attribute on the document if the name is not locked.
-
#set_header_attribute(name, value = '', overwrite = true) ⇒ Boolean
Assign a value to the specified attribute in the document header.
-
#source ⇒ Object
Make the raw source for the Document available.
-
#source_lines ⇒ Object
Make the raw source lines for the Document available.
-
#title ⇒ String
Return the doctitle as a String.
-
#title=(title) ⇒ String
Set the title on the document header.
- #to_s ⇒ Object
-
#write(output, target) ⇒ void
Write the output to the specified file.
- #xreftext(xrefstyle = nil) ⇒ Object
Methods inherited from AbstractBlock
#alt, #assign_caption, #block?, #blocks?, #captioned_title, #context=, #file, #find_by, #inline?, #lineno, #list_marker_keyword, #next_adjacent_block, #number, #remove_sub, #sections, #sections?, #sub?, #title?
Methods inherited from AbstractNode
#add_role, #attr, #attr?, #block?, #enabled_options, #generate_data_uri, #generate_data_uri_from_uri, #has_role?, #icon_uri, #image_uri, #inline?, #is_uri?, #media_uri, #normalize_asset_path, #normalize_system_path, #normalize_web_path, #option?, #read_asset, #read_contents, #reftext, #reftext?, #remove_attr, #remove_role, #role, #role?, #roles, #set_attr, #set_option, #update_attributes
Methods included from Substitutors
#apply_header_subs, #apply_normal_subs, #apply_reftext_subs, #apply_subs, #expand_subs, #extract_passthroughs, #highlight_source, #resolve_block_subs, #resolve_lines_to_highlight, #resolve_pass_subs, #resolve_subs, #restore_passthroughs, #sub_attributes, #sub_callouts, #sub_macros, #sub_post_replacements, #sub_quotes, #sub_replacements, #sub_source, #sub_specialchars
Methods included from Logging
#logger, #message_with_context
Constructor Details
#initialize(data = nil, options = {}) ⇒ Document
Initialize a Asciidoctor::Document object.
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 |
# File 'lib/asciidoctor/document.rb', line 253 def initialize data = nil, = {} super self, :document if (parent_doc = .delete :parent) @parent_document = parent_doc [:base_dir] ||= parent_doc.base_dir [:catalog_assets] = true if parent_doc.[:catalog_assets] @catalog = parent_doc.catalog.merge footnotes: [] # QUESTION should we support setting attribute in parent document from nested document? # NOTE we must dup or else all the assignments to the overrides clobbers the real attributes @attribute_overrides = attr_overrides = parent_doc.attributes.merge parent_doctype = attr_overrides.delete 'doctype' attr_overrides.delete 'compat-mode' attr_overrides.delete 'toc' attr_overrides.delete 'toc-placement' attr_overrides.delete 'toc-position' @safe = parent_doc.safe @attributes['compat-mode'] = '' if (@compat_mode = parent_doc.compat_mode) @outfilesuffix = parent_doc.outfilesuffix @sourcemap = parent_doc.sourcemap @timings = nil @path_resolver = parent_doc.path_resolver @converter = parent_doc.converter initialize_extensions = nil @extensions = parent_doc.extensions @syntax_highlighter = parent_doc.syntax_highlighter else @parent_document = nil @catalog = { ids: {}, # deprecated; kept for backwards compatibility with converters refs: {}, footnotes: [], links: [], images: [], #indexterms: [], callouts: Callouts.new, includes: {}, } # copy attributes map and normalize keys # attribute overrides are attributes that can only be set from the commandline # a direct assignment effectively makes the attribute a constant # a nil value or name with leading or trailing ! will result in the attribute being unassigned @attribute_overrides = attr_overrides = {} ([:attributes] || {}).each do |key, val| if key.end_with? '@' if key.start_with? '!' key, val = (key.slice 1, key.length - 2), false elsif key.end_with? '!@' key, val = (key.slice 0, key.length - 2), false else key, val = key.chop, %(#{val}@) end elsif key.start_with? '!' key, val = (key.slice 1, key.length), val == '@' ? false : nil elsif key.end_with? '!' key, val = key.chop, val == '@' ? false : nil end attr_overrides[key.downcase] = val end if (to_file = [:to_file]) attr_overrides['outfilesuffix'] = Helpers.extname to_file end # safely resolve the safe mode from const, int or string if !(safe_mode = [:safe]) @safe = SafeMode::SECURE elsif ::Integer === safe_mode # be permissive in case API user wants to define new levels @safe = safe_mode else @safe = (SafeMode.value_for_name safe_mode) rescue SafeMode::SECURE end input_mtime = .delete :input_mtime @compat_mode = attr_overrides.key? 'compat-mode' @sourcemap = [:sourcemap] @timings = .delete :timings @path_resolver = PathResolver.new initialize_extensions = (defined? ::Asciidoctor::Extensions) ? true : nil @extensions = nil # initialize furthur down if initialize_extensions is true [:standalone] = [:header_footer] if (.key? :header_footer) && !(.key? :standalone) end @parsed = @reftexts = @header = @header_attributes = nil @counters = {} @attributes_modified = ::Set.new @docinfo_processor_extensions = {} standalone = [:standalone] ( = ).freeze attrs = @attributes #attrs['encoding'] = 'UTF-8' attrs['sectids'] = '' attrs['toc-placement'] = 'auto' if standalone attrs['copycss'] = '' # sync embedded attribute with :standalone option value attr_overrides['embedded'] = nil else attrs['notitle'] = '' # sync embedded attribute with :standalone option value attr_overrides['embedded'] = '' end attrs['stylesheet'] = '' attrs['webfonts'] = '' attrs['prewrap'] = '' attrs['attribute-undefined'] = Compliance.attribute_undefined attrs['attribute-missing'] = Compliance.attribute_missing attrs['iconfont-remote'] = '' # language strings # TODO load these based on language settings attrs['caution-caption'] = 'Caution' attrs['important-caption'] = 'Important' attrs['note-caption'] = 'Note' attrs['tip-caption'] = 'Tip' attrs['warning-caption'] = 'Warning' attrs['example-caption'] = 'Example' attrs['figure-caption'] = 'Figure' #attrs['listing-caption'] = 'Listing' attrs['table-caption'] = 'Table' attrs['toc-title'] = 'Table of Contents' #attrs['preface-title'] = 'Preface' attrs['section-refsig'] = 'Section' attrs['part-refsig'] = 'Part' attrs['chapter-refsig'] = 'Chapter' attrs['appendix-caption'] = attrs['appendix-refsig'] = 'Appendix' attrs['untitled-label'] = 'Untitled' attrs['version-label'] = 'Version' attrs['last-update-label'] = 'Last updated' attr_overrides['asciidoctor'] = '' attr_overrides['asciidoctor-version'] = ::Asciidoctor::VERSION attr_overrides['safe-mode-name'] = (safe_mode_name = SafeMode.name_for_value @safe) attr_overrides["safe-mode-#{safe_mode_name}"] = '' attr_overrides['safe-mode-level'] = @safe # the only way to set the max-include-depth attribute is via the API; default to 64 like AsciiDoc Python attr_overrides['max-include-depth'] ||= 64 # the only way to set the allow-uri-read attribute is via the API; disabled by default attr_overrides['allow-uri-read'] ||= nil attr_overrides['user-home'] = USER_HOME # remap legacy attribute names attr_overrides['sectnums'] = attr_overrides.delete 'numbered' if attr_overrides.key? 'numbered' attr_overrides['hardbreaks-option'] = attr_overrides.delete 'hardbreaks' if attr_overrides.key? 'hardbreaks' # If the base_dir option is specified, it overrides docdir and is used as the root for relative # paths. Otherwise, the base_dir is the directory of the source file (docdir), if set, otherwise # the current directory. if (base_dir_val = [:base_dir]) @base_dir = (attr_overrides['docdir'] = ::File. base_dir_val) elsif attr_overrides['docdir'] @base_dir = attr_overrides['docdir'] else #logger.warn 'setting base_dir is recommended when working with string documents' unless nested? @base_dir = attr_overrides['docdir'] = ::Dir.pwd end # allow common attributes backend and doctype to be set using options hash, coerce values to string if (backend_val = [:backend]) attr_overrides['backend'] = %(#{backend_val}) end if (doctype_val = [:doctype]) attr_overrides['doctype'] = %(#{doctype_val}) end if @safe >= SafeMode::SERVER # restrict document from setting copycss, source-highlighter and backend attr_overrides['copycss'] ||= nil attr_overrides['source-highlighter'] ||= nil attr_overrides['backend'] ||= DEFAULT_BACKEND # restrict document from seeing the docdir and trim docfile to relative path if !parent_doc && attr_overrides.key?('docfile') attr_overrides['docfile'] = attr_overrides['docfile'][(attr_overrides['docdir'].length + 1)..-1] end attr_overrides['docdir'] = '' attr_overrides['user-home'] = '.' if @safe >= SafeMode::SECURE attr_overrides['max-attribute-value-size'] = 4096 unless attr_overrides.key? 'max-attribute-value-size' # assign linkcss (preventing css embedding) unless explicitly disabled from the commandline or API #attr_overrides['linkcss'] = (attr_overrides.fetch 'linkcss', '') || nil attr_overrides['linkcss'] = '' unless attr_overrides.key? 'linkcss' # restrict document from enabling icons attr_overrides['icons'] ||= nil end end # the only way to set the max-attribute-value-size attribute is via the API; disabled by default @max_attribute_value_size = (size = (attr_overrides['max-attribute-value-size'] ||= nil)) ? size.to_i.abs : nil attr_overrides.delete_if do |key, val| if val # a value ending in @ allows document to override value if ::String === val && (val.end_with? '@') val, verdict = val.chop, true end attrs[key] = val else # a nil or false value both unset the attribute; only a nil value locks it attrs.delete key verdict = val == false end verdict end if parent_doc @backend = attrs['backend'] # reset doctype unless it matches the default value unless (@doctype = attrs['doctype'] = parent_doctype) == DEFAULT_DOCTYPE update_doctype_attributes DEFAULT_DOCTYPE end # don't need to do the extra processing within our own document # FIXME line info isn't reported correctly within include files in nested document @reader = Reader.new data, [:cursor] @source_location = @reader.cursor if @sourcemap # Now parse the lines in the reader into blocks # Eagerly parse (for now) since a subdocument is not a publicly accessible object Parser.parse @reader, self # should we call some sort of post-parse function? restore_attributes @parsed = true else # setup default backend and doctype @backend = nil if (initial_backend = attrs['backend'] || DEFAULT_BACKEND) == 'manpage' @doctype = attrs['doctype'] = attr_overrides['doctype'] = 'manpage' else @doctype = (attrs['doctype'] ||= DEFAULT_DOCTYPE) end update_backend_attributes initial_backend, true # dynamic intrinstic attribute values #attrs['indir'] = attrs['docdir'] #attrs['infile'] = attrs['docfile'] # fallback directories attrs['stylesdir'] ||= '.' attrs['iconsdir'] ||= %(#{attrs.fetch 'imagesdir', './images'}/icons) fill_datetime_attributes attrs, input_mtime if initialize_extensions if (ext_registry = [:extension_registry]) # QUESTION should we warn if the value type of this option is not a registry if Extensions::Registry === ext_registry || ((defined? ::AsciidoctorJ::Extensions::ExtensionRegistry) && ::AsciidoctorJ::Extensions::ExtensionRegistry === ext_registry) @extensions = ext_registry.activate self end elsif ::Proc === (ext_block = [:extensions]) @extensions = Extensions.create(&ext_block).activate self elsif !Extensions.groups.empty? @extensions = Extensions::Registry.new.activate self end end @reader = PreprocessorReader.new self, data, (Reader::Cursor.new attrs['docfile'], @base_dir), normalize: true @source_location = @reader.cursor if @sourcemap end end |
Instance Attribute Details
#backend ⇒ Object (readonly)
Get the cached value of the backend attribute for this document
190 191 192 |
# File 'lib/asciidoctor/document.rb', line 190 def backend @backend end |
#base_dir ⇒ Object (readonly)
Get the String base directory for converting this document.
Defaults to directory of the source file. If the source is a string, defaults to the current directory.
214 215 216 |
# File 'lib/asciidoctor/document.rb', line 214 def base_dir @base_dir end |
#catalog ⇒ Object (readonly) Also known as: references
Get the document catalog Hash
199 200 201 |
# File 'lib/asciidoctor/document.rb', line 199 def catalog @catalog end |
#compat_mode ⇒ Object (readonly)
Get the Boolean AsciiDoc compatibility mode
enabling this attribute activates the following syntax changes:
* single quotes as constrained emphasis formatting marks
* single backticks parsed as inline literal, formatted as monospace
* single plus parsed as constrained, monospaced inline formatting
* double plus parsed as constrained, monospaced inline formatting
187 188 189 |
# File 'lib/asciidoctor/document.rb', line 187 def compat_mode @compat_mode end |
#converter ⇒ Object (readonly)
Get the Converter associated with this document
232 233 234 |
# File 'lib/asciidoctor/document.rb', line 232 def converter @converter end |
#counters ⇒ Object (readonly)
Get the Hash of document counters
205 206 207 |
# File 'lib/asciidoctor/document.rb', line 205 def counters @counters end |
#doctype ⇒ Object (readonly)
Get the cached value of the doctype attribute for this document
193 194 195 |
# File 'lib/asciidoctor/document.rb', line 193 def doctype @doctype end |
#extensions ⇒ Object (readonly)
Get the activated Extensions::Registry associated with this document.
238 239 240 |
# File 'lib/asciidoctor/document.rb', line 238 def extensions @extensions end |
#header ⇒ Object (readonly)
Get the level-0 Section (i.e., doctitle). (Only stores the title, not the header attributes).
208 209 210 |
# File 'lib/asciidoctor/document.rb', line 208 def header @header end |
#options ⇒ Object (readonly)
Get the Hash of resolved options used to initialize this Document
217 218 219 |
# File 'lib/asciidoctor/document.rb', line 217 def end |
#outfilesuffix ⇒ Object (readonly)
Get the outfilesuffix defined at the end of the header.
220 221 222 |
# File 'lib/asciidoctor/document.rb', line 220 def outfilesuffix @outfilesuffix end |
#parent_document ⇒ Object (readonly)
Get a reference to the parent Document of this nested document.
223 224 225 |
# File 'lib/asciidoctor/document.rb', line 223 def parent_document @parent_document end |
#path_resolver ⇒ Object (readonly)
Get/Set the PathResolver instance used to resolve paths in this Document.
229 230 231 |
# File 'lib/asciidoctor/document.rb', line 229 def path_resolver @path_resolver end |
#reader ⇒ Object (readonly)
Get the Reader associated with this document
226 227 228 |
# File 'lib/asciidoctor/document.rb', line 226 def reader @reader end |
#safe ⇒ Object (readonly)
Public A read-only integer value indicating the level of security that should be enforced while processing this document. The value must be set in the Document constructor using the :safe option.
A value of 0 (UNSAFE) disables any of the security features enforced by Asciidoctor (Ruby is still subject to its own restrictions).
A value of 1 (SAFE) closely parallels safe mode in AsciiDoc. In particular, it prevents access to files which reside outside of the parent directory of the source file and disables any macro other than the include directive.
A value of 10 (SERVER) disallows the document from setting attributes that would affect the conversion of the document, in addition to all the security features of SafeMode::SAFE. For instance, this level forbids changing the backend or source-highlighter using an attribute defined in the source document header. This is the most fundamental level of security for server deployments (hence the name).
A value of 20 (SECURE) disallows the document from attempting to read files from the file system and including the contents of them into the document, in addition to all the security features of SafeMode::SECURE. In particular, it disallows use of the include::[] directive and the embedding of binary content (data uri), stylesheets and JavaScripts referenced by the document. (Asciidoctor and trusted extensions may still be allowed to embed trusted content into the document).
Since Asciidoctor is aiming for wide adoption, 20 (SECURE) is the default value and is recommended for server deployments.
A value of 100 (PARANOID) is planned to disallow the use of passthrough macros and prevents the document from setting any known attributes in addition to all the security features of SafeMode::SECURE. Please note that this level is not currently implemented (and therefore not enforced)!
176 177 178 |
# File 'lib/asciidoctor/document.rb', line 176 def safe @safe end |
#sourcemap ⇒ Object
Get or set the Boolean flag that indicates whether source map information should be tracked by the parser
196 197 198 |
# File 'lib/asciidoctor/document.rb', line 196 def sourcemap @sourcemap end |
#syntax_highlighter ⇒ Object (readonly)
Get the SyntaxHighlighter associated with this document
235 236 237 |
# File 'lib/asciidoctor/document.rb', line 235 def syntax_highlighter @syntax_highlighter end |
Instance Method Details
#<<(block) ⇒ The
Append a content Block to this Document.
If the child block is a Section, assign an index to it.
802 803 804 805 |
# File 'lib/asciidoctor/document.rb', line 802 def << block assign_numeral block if block.context == :section super end |
#attribute_locked?(name) ⇒ Boolean
Determine if the attribute has been locked by being assigned in document options
895 896 897 |
# File 'lib/asciidoctor/document.rb', line 895 def attribute_locked?(name) @attribute_overrides.key?(name) end |
#author ⇒ Object
Convenience method to retrieve the document attribute ‘author’
returns the full name of the author as a String
742 743 744 |
# File 'lib/asciidoctor/document.rb', line 742 def @attributes['author'] end |
#authors ⇒ Object
Convenience method to retrieve the authors of this document as an Array of Author objects.
This method is backed by the author-related attributes on the document.
returns the authors of this document as an Array
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 |
# File 'lib/asciidoctor/document.rb', line 751 def if (attrs = @attributes).key? 'author' = [(Author.new attrs['author'], attrs['firstname'], attrs['middlename'], attrs['lastname'], attrs['authorinitials'], attrs['email'])] if ( = attrs['authorcount'] || 0) > 1 idx = 1 while idx < idx += 1 << (Author.new attrs[%(author_#{idx})], attrs[%(firstname_#{idx})], attrs[%(middlename_#{idx})], attrs[%(lastname_#{idx})], attrs[%(authorinitials_#{idx})], attrs[%(email_#{idx})]) end end else [] end end |
#basebackend?(base) ⇒ Boolean
671 672 673 |
# File 'lib/asciidoctor/document.rb', line 671 def basebackend? base @attributes['basebackend'] == base end |
#callouts ⇒ Object
645 646 647 |
# File 'lib/asciidoctor/document.rb', line 645 def callouts @catalog[:callouts] end |
#content ⇒ Object
def convert_to target, opts = {}
start = ::Time.now.to_f if (monitor = opts[:monitor])
output = (r = converter opts).convert
monitor[:convert] = ::Time.now.to_f - start if monitor
unless target.respond_to? :write
@attributes['outfile'] = target = ::File. target
@attributes['outdir'] = ::File.dirname target
end
start = ::Time.now.to_f if monitor
r.write output, target
monitor[:write] = ::Time.now.to_f - start if monitor
output
end
1017 1018 1019 1020 1021 |
# File 'lib/asciidoctor/document.rb', line 1017 def content # NOTE per AsciiDoc-spec, remove the title before converting the body @attributes.delete('title') super end |
#convert(opts = {}) ⇒ Object Also known as: render
Convert the AsciiDoc document using the templates loaded by the Converter. If a :template_dir is not specified, or a template is missing, the converter will fall back to using the appropriate built-in template.
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 |
# File 'lib/asciidoctor/document.rb', line 924 def convert opts = {} @timings.start :convert if @timings parse unless @parsed unless @safe >= SafeMode::SERVER || opts.empty? # QUESTION should we store these on the Document object? @attributes.delete 'outfile' unless (@attributes['outfile'] = opts['outfile']) @attributes.delete 'outdir' unless (@attributes['outdir'] = opts['outdir']) end # QUESTION should we add extensions that execute before conversion begins? if doctype == 'inline' if (block = @blocks[0] || @header) if block.content_model == :compound || block.content_model == :empty logger.warn 'no inline candidate; use the inline doctype to convert a single paragragh, verbatim, or raw block' else output = block.content end end else if opts.key? :standalone transform = opts[:standalone] ? 'document' : 'embedded' elsif opts.key? :header_footer transform = opts[:header_footer] ? 'document' : 'embedded' else transform = [:standalone] ? 'document' : 'embedded' end output = @converter.convert self, transform end unless @parent_document if (exts = @extensions) && exts.postprocessors? exts.postprocessors.each do |ext| output = ext.process_method[self, output] end end end @timings.record :convert if @timings output end |
#counter(name, seed = nil) ⇒ Object
Get the named counter and take the next number in the sequence.
578 579 580 581 582 583 584 585 586 587 |
# File 'lib/asciidoctor/document.rb', line 578 def counter name, seed = nil return @parent_document.counter name, seed if @parent_document if (attr_seed = !(attr_val = @attributes[name]).nil_or_empty?) && (@counters.key? name) @attributes[name] = @counters[name] = Helpers.nextval attr_val elsif seed @attributes[name] = @counters[name] = seed == seed.to_i.to_s ? seed.to_i : seed else @attributes[name] = @counters[name] = Helpers.nextval attr_seed ? attr_val : 0 end end |
#delete_attribute(name) ⇒ Object
Delete the specified attribute from the document if the name is not locked
If the attribute is locked, false is returned. Otherwise, the attribute is deleted.
880 881 882 883 884 885 886 887 888 |
# File 'lib/asciidoctor/document.rb', line 880 def delete_attribute(name) if attribute_locked?(name) false else @attributes.delete(name) @attributes_modified << name true end end |
#docinfo(location = :head, suffix = nil) ⇒ Object
Read the docinfo file(s) for inclusion in the document template
If the docinfo1 attribute is set, read the docinfo.ext file. If the docinfo attribute is set, read the doc-name.docinfo.ext file. If the docinfo2 attribute is set, read both files in that order.
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 |
# File 'lib/asciidoctor/document.rb', line 1035 def docinfo location = :head, suffix = nil if safe < SafeMode::SECURE qualifier = %(-#{location}) unless location == :head suffix = @outfilesuffix unless suffix if (docinfo = @attributes['docinfo']).nil_or_empty? if @attributes.key? 'docinfo2' docinfo = ['private', 'shared'] elsif @attributes.key? 'docinfo1' docinfo = ['shared'] else docinfo = docinfo ? ['private'] : nil end else docinfo = docinfo.split(',').map {|it| it.strip } end if docinfo content = [] docinfo_file, docinfo_dir, docinfo_subs = %(docinfo#{qualifier}#{suffix}), @attributes['docinfodir'], resolve_docinfo_subs unless (docinfo & ['shared', %(shared-#{location})]).empty? docinfo_path = normalize_system_path docinfo_file, docinfo_dir # NOTE normalizing the lines is essential if we're performing substitutions if (shared_docinfo = read_asset docinfo_path, normalize: true) content << (apply_subs shared_docinfo, docinfo_subs) end end unless @attributes['docname'].nil_or_empty? || (docinfo & ['private', %(private-#{location})]).empty? docinfo_path = normalize_system_path %(#{@attributes['docname']}-#{docinfo_file}), docinfo_dir # NOTE normalizing the lines is essential if we're performing substitutions if (private_docinfo = read_asset docinfo_path, normalize: true) content << (apply_subs private_docinfo, docinfo_subs) end end end end # TODO allow document to control whether extension docinfo is contributed if @extensions && (docinfo_processors? location) ((content || []).concat @docinfo_processor_extensions[location].map {|ext| ext.process_method[self] }.compact).join LF elsif content content.join LF else '' end end |
#docinfo_processors?(location = :head) ⇒ Boolean
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 |
# File 'lib/asciidoctor/document.rb', line 1083 def docinfo_processors?(location = :head) if @docinfo_processor_extensions.key?(location) # false means we already performed a lookup and didn't find any @docinfo_processor_extensions[location] != false elsif @extensions && @document.extensions.docinfo_processors?(location) !!(@docinfo_processor_extensions[location] = @document.extensions.docinfo_processors(location)) else @docinfo_processor_extensions[location] = false end end |
#doctitle(opts = {}) ⇒ Title Also known as: name
Resolves the primary title for the document
Searches the locations to find the first non-empty value:
* document-level attribute named title
* header title (known as the document title)
* title of the first section
* document-level attribute named untitled-label (if :use_fallback option is set)
If no value can be resolved, nil is returned.
If the :partition attribute is specified, the value is parsed into an Document::Title object. If the :sanitize attribute is specified, XML elements are removed from the value.
TODO separate sanitization by type (:cdata for HTML/XML, :plain_text for non-SGML, false for none)
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 |
# File 'lib/asciidoctor/document.rb', line 716 def doctitle opts = {} unless (val = @attributes['title']) if (sect = first_section) val = sect.title elsif !(opts[:use_fallback] && (val = @attributes['untitled-label'])) return end end if (separator = opts[:partition]) Title.new val, opts.merge({ separator: (separator == true ? @attributes['title-separator'] : separator) }) elsif opts[:sanitize] && val.include?('<') val.gsub(XmlSanitizeRx, '').squeeze(' ').strip else val end end |
#embedded? ⇒ Boolean
653 654 655 |
# File 'lib/asciidoctor/document.rb', line 653 def @attributes.key? 'embedded' end |
#extensions? ⇒ Boolean
657 658 659 |
# File 'lib/asciidoctor/document.rb', line 657 def extensions? @extensions ? true : false end |
#first_section ⇒ Object
786 787 788 |
# File 'lib/asciidoctor/document.rb', line 786 def first_section @header || @blocks.find {|e| e.context == :section } end |
#footnotes ⇒ Object
641 642 643 |
# File 'lib/asciidoctor/document.rb', line 641 def footnotes @catalog[:footnotes] end |
#footnotes? ⇒ Boolean
637 638 639 |
# File 'lib/asciidoctor/document.rb', line 637 def footnotes? @catalog[:footnotes].empty? ? false : true end |
#header? ⇒ Boolean Also known as: has_header?
790 791 792 |
# File 'lib/asciidoctor/document.rb', line 790 def header? @header ? true : false end |
#increment_and_store_counter(counter_name, block) ⇒ Object Also known as: counter_increment
Increment the specified counter and store it in the block’s attributes
595 596 597 |
# File 'lib/asciidoctor/document.rb', line 595 def increment_and_store_counter counter_name, block ((AttributeEntry.new counter_name, (counter counter_name)).save_to block.attributes).value end |
#nested? ⇒ Boolean
649 650 651 |
# File 'lib/asciidoctor/document.rb', line 649 def nested? @parent_document ? true : false end |
#nofooter ⇒ Object
782 783 784 |
# File 'lib/asciidoctor/document.rb', line 782 def @attributes.key? 'nofooter' end |
#noheader ⇒ Object
778 779 780 |
# File 'lib/asciidoctor/document.rb', line 778 def noheader @attributes.key? 'noheader' end |
#notitle ⇒ Object
774 775 776 |
# File 'lib/asciidoctor/document.rb', line 774 def notitle !@attributes.key?('showtitle') && @attributes.key?('notitle') end |
#parse(data = nil) ⇒ Document
Parse the AsciiDoc source stored in the Reader into an abstract syntax tree.
If the data parameter is not nil, create a new PreprocessorReader and assigned it to the reader property of this object. Otherwise, continue with the reader that was created in #initialize. Pass the reader to Parser.parse to parse the source data into an abstract syntax tree.
If parsing has already been performed, this method returns without performing any processing.
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 |
# File 'lib/asciidoctor/document.rb', line 531 def parse data = nil if @parsed self else doc = self # create reader if data is provided (used when data is not known at the time the Document object is created) if data @reader = PreprocessorReader.new doc, data, (Reader::Cursor.new @attributes['docfile'], @base_dir), normalize: true @source_location = @reader.cursor if @sourcemap end if (exts = @parent_document ? nil : @extensions) && exts.preprocessors? exts.preprocessors.each do |ext| @reader = ext.process_method[doc, @reader] || @reader end end # Now parse the lines in the reader into blocks Parser.parse @reader, doc, header_only: [:parse_header_only] # should we call sort of post-parse function? restore_attributes if exts && exts.tree_processors? exts.tree_processors.each do |ext| if (result = ext.process_method[doc]) && Document === result && result != doc doc = result end end end @parsed = true doc end end |
#parsed? ⇒ Boolean
Returns whether the source lines of the document have been parsed.
568 569 570 |
# File 'lib/asciidoctor/document.rb', line 568 def parsed? @parsed end |
#playback_attributes(block_attributes) ⇒ Object
Replay attribute assignments at the block level
820 821 822 823 824 825 826 827 828 829 830 831 832 833 |
# File 'lib/asciidoctor/document.rb', line 820 def playback_attributes(block_attributes) if block_attributes.key? :attribute_entries block_attributes[:attribute_entries].each do |entry| name = entry.name if entry.negate @attributes.delete name @compat_mode = false if name == 'compat-mode' else @attributes[name] = entry.value @compat_mode = true if name == 'compat-mode' end end end end |
#register(type, value) ⇒ Object
Register a reference in the document catalog
602 603 604 605 606 607 608 609 610 611 612 613 614 |
# File 'lib/asciidoctor/document.rb', line 602 def register type, value case type when :ids # deprecated register :refs, [(id = value[0]), (Inline.new self, :anchor, value[1], type: :ref, id: id)] when :refs @catalog[:refs][value[0]] ||= (ref = value[1]) ref when :footnotes @catalog[type] << value else @catalog[type] << (type == :images ? (ImageReference.new value, @attributes['imagesdir']) : value) if [:catalog_assets] end end |
#resolve_id(text) ⇒ Object
Scan registered references and return the ID of the first reference that matches the specified reference text.
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 |
# File 'lib/asciidoctor/document.rb', line 621 def resolve_id text if @reftexts @reftexts[text] elsif @parsed # @reftexts is set eagerly to prevent nested lazy init (@reftexts = {}).tap {|accum| @catalog[:refs].each {|id, ref| accum[ref.xreftext] ||= id } }[text] else # @reftexts is set eagerly to prevent nested lazy init resolved_id = nil # NOTE short-circuit early since we're throwing away this table (@reftexts = {}).tap {|accum| @catalog[:refs].each {|id, ref| (xreftext = ref.xreftext) == text ? (break (resolved_id = id)) : (accum[xreftext] ||= id) } } @reftexts = nil resolved_id end end |
#restore_attributes ⇒ Object
Restore the attributes to the previously saved state (attributes in header)
836 837 838 839 |
# File 'lib/asciidoctor/document.rb', line 836 def restore_attributes @catalog[:callouts].rewind unless @parent_document @attributes.replace @header_attributes end |
#revdate ⇒ Object
Convenience method to retrieve the document attribute ‘revdate’
returns the date of last revision for the document as a String
770 771 772 |
# File 'lib/asciidoctor/document.rb', line 770 def revdate @attributes['revdate'] end |
#set_attribute(name, value = '') ⇒ Object
Set the specified attribute on the document if the name is not locked
If the attribute is locked, false is returned. Otherwise, the value is assigned to the attribute name after first performing attribute substitutions on the value. If the attribute name is ‘backend’ or ‘doctype’, then the value of backend-related attributes are updated.
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 |
# File 'lib/asciidoctor/document.rb', line 852 def set_attribute name, value = '' unless attribute_locked? name value = apply_attribute_value_subs value unless value.empty? # NOTE if @header_attributes is set, we're beyond the document header if @header_attributes @attributes[name] = value else case name when 'backend' update_backend_attributes value, (@attributes_modified.delete? 'htmlsyntax') && value == @backend when 'doctype' update_doctype_attributes value else @attributes[name] = value end @attributes_modified << name end value end end |
#set_header_attribute(name, value = '', overwrite = true) ⇒ Boolean
Assign a value to the specified attribute in the document header.
The assignment will be visible when the header attributes are restored, typically between processor phases (e.g., between parse and convert).
910 911 912 913 914 915 916 917 918 |
# File 'lib/asciidoctor/document.rb', line 910 def set_header_attribute name, value = '', overwrite = true attrs = @header_attributes || @attributes if overwrite == false && (attrs.key? name) false else attrs[name] = value true end end |
#source ⇒ Object
Make the raw source for the Document available.
662 663 664 |
# File 'lib/asciidoctor/document.rb', line 662 def source @reader.source if @reader end |
#source_lines ⇒ Object
Make the raw source lines for the Document available.
667 668 669 |
# File 'lib/asciidoctor/document.rb', line 667 def source_lines @reader.source_lines if @reader end |
#title ⇒ String
Return the doctitle as a String
678 679 680 |
# File 'lib/asciidoctor/document.rb', line 678 def title doctitle end |
#title=(title) ⇒ String
Set the title on the document header
Set the title of the document header to the specified value. If the header does not exist, it is first created.
690 691 692 693 694 695 |
# File 'lib/asciidoctor/document.rb', line 690 def title= title unless (sect = @header) (sect = (@header = Section.new self, 0)).sectname = 'header' end sect.title = title end |
#to_s ⇒ Object
1094 1095 1096 |
# File 'lib/asciidoctor/document.rb', line 1094 def to_s %(#<#{self.class}@#{object_id} {doctype: #{doctype.inspect}, doctitle: #{(@header != nil ? @header.title : nil).inspect}, blocks: #{@blocks.size}}>) end |
#write(output, target) ⇒ void
This method returns an undefined value.
Write the output to the specified file
If the converter responds to :write, delegate the work of writing the file to that method. Otherwise, write the output the specified file.
975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 |
# File 'lib/asciidoctor/document.rb', line 975 def write output, target @timings.start :write if @timings if Writer === @converter @converter.write output, target else if target.respond_to? :write # QUESTION should we set encoding using target.set_encoding? unless output.nil_or_empty? target.write output.chomp # ensure there's a trailing endline target.write LF end else ::File.write target, output, mode: FILE_WRITE_MODE end if @backend == 'manpage' && ::String === target && (@converter.class.respond_to? :write_alternate_pages) @converter.class.write_alternate_pages @attributes['mannames'], @attributes['manvolnum'], target end end @timings.record :write if @timings nil end |
#xreftext(xrefstyle = nil) ⇒ Object
735 736 737 |
# File 'lib/asciidoctor/document.rb', line 735 def xreftext xrefstyle = nil (val = reftext) && !val.empty? ? val : title end |