\n'
content += code
content += ''
content += 'asciidoctor-plantuml-0.1.1/ 0000755 0000041 0000041 00000000000 14235144530 015677 5 ustar www-data www-data asciidoctor-plantuml-0.1.1/test/ 0000755 0000041 0000041 00000000000 14235144530 016656 5 ustar www-data www-data asciidoctor-plantuml-0.1.1/test/fixtures/ 0000755 0000041 0000041 00000000000 14235144530 020527 5 ustar www-data www-data asciidoctor-plantuml-0.1.1/test/fixtures/config.puml 0000644 0000041 0000041 00000000131 14235144530 022666 0 ustar www-data www-data skinparam monochrome true skinparam backgroundColor transparent skinparam style strictuml asciidoctor-plantuml-0.1.1/test/fixtures/test.puml 0000644 0000041 0000041 00000000110 14235144530 022375 0 ustar www-data www-data @startuml User -> (Start) User --> (Use the application) : Label @enduml asciidoctor-plantuml-0.1.1/test/test_plantuml.rb 0000644 0000041 0000041 00000035246 14235144530 022110 0 ustar www-data www-data # frozen_string_literal: true require 'test/unit' require 'asciidoctor' require 'stringio' require 'nokogiri' require 'asciidoctor-plantuml' DOC_BASIC = <<~ENDOFSTRING = Hello PlantUML! [plantuml, format="png"] .Title Of this ---- User -> (Start) User --> (Use the application) : Label ---- ENDOFSTRING DOC_BASIC_LITERAL = <<~ENDOFSTRING = Hello PlantUML! [plantuml, format="png"] .Title Of this .... User -> (Start) User --> (Use the application) : Label .... ENDOFSTRING DOC_BASIC2 = <<~ENDOFSTRING = Hello PlantUML! [plantuml, format="png"] .Title Of this [[fig-xref]] ---- @startuml User -> (Start) User --> (Use the application) : Label @enduml ---- ENDOFSTRING DOC_BASIC2_LITERAL = <<~ENDOFSTRING = Hello PlantUML! [plantuml, format="png"] .Title Of this [[fig-xref]] .... @startuml User -> (Start) User --> (Use the application) : Label @enduml .... ENDOFSTRING DOC_BASIC3 = <<~ENDOFSTRING = Hello Compound PlantUML! [plantuml, format="png"] ---- [COMP1] [COMP2] [COMP1] -> [COMP2] [COMP2] --> [COMP3] ---- ENDOFSTRING DOC_BASIC3_LITERAL = <<~ENDOFSTRING = Hello Compound PlantUML! [plantuml, format="png"] .... [COMP1] [COMP2] [COMP1] -> [COMP2] [COMP2] --> [COMP3] .... ENDOFSTRING DOC_ID = <<~ENDOFSTRING = Hello PlantUML! [plantuml, format="png", id="myId"] ---- User -> (Start) User --> (Use the application) : Label ---- ENDOFSTRING DOC_DIM = <<~ENDOFSTRING = Hello PlantUML! [plantuml, format="png", width="100px", height="50px"] ---- User -> (Start) User --> (Use the application) : Label ---- ENDOFSTRING DOC_ALT = <<~ENDOFSTRING = Hello PlantUML! [plantuml, format="png", alt="alt"] ---- User -> (Start) User --> (Use the application) : Label ---- ENDOFSTRING DOC_BAD_FORMAT = <<~ENDOFSTRING = Hello PlantUML! [plantuml, format="jpg"] ---- User -> (Start) User --> (Use the application) : Label ---- ENDOFSTRING DOC_MULTI = <<~ENDOFSTRING = Hello PlantUML! [plantuml, format="png"] ---- User -> (Start) User --> (Use the application) : Label ---- [plantuml, format="png"] ---- User -> (Start) User --> (Use the application) : Label ---- [plantuml, format="txt"] ---- User -> (Start) User --> (Use the application) : Label ---- ENDOFSTRING DOC_MULTI_LITERAL = <<~ENDOFSTRING = Hello PlantUML! [plantuml, format="png"] .... User -> (Start) User --> (Use the application) : Label .... [plantuml, format="png"] .... User -> (Start) User --> (Use the application) : Label .... [plantuml, format="txt"] .... User -> (Start) User --> (Use the application) : Label .... ENDOFSTRING DOC_TXT = <<~ENDOFSTRING = Hello PlantUML! [plantuml, format="txt"] ---- User -> (Start) User --> (Use the application) : Label ---- ENDOFSTRING DOC_SVG = <<~ENDOFSTRING = Hello PlantUML! [plantuml, format="svg"] ---- User -> (Start) User --> (Use the application) : Label ---- ENDOFSTRING DOC_BLOCK_MACRO = <<~ENDOFSTRING = Hello PlantUML! .Title Of this plantuml::test/fixtures/test.puml[] ENDOFSTRING DOC_BLOCK_MACRO_MISSING_FILE = <<~ENDOFSTRING = Hello PlantUML! .Title Of this plantuml::test/fixtures/missing.puml[] ENDOFSTRING DOC_BLOCK_MACRO_INSECURE_FILE = <<~ENDOFSTRING = Hello PlantUML! .Title Of this plantuml::/etc/passwd[] ENDOFSTRING DOC_SUBS_ATTRIBUTES = <<~ENDOFSTRING = Hello PlantUML! :text: Label [plantuml, format="png", subs="attributes+"] .Title Of this ---- User -> (Start) User --> (Use the application) : {text} ---- ENDOFSTRING DOC_CONFIG_INCLUDE = <<~ENDOFSTRING = Hello PlantUML! :plantuml-include: test/fixtures/config.puml [plantuml, format="png"] .Title Of this ---- User -> (Start) User --> (Use the application) : Label ---- ENDOFSTRING DOC_CONFIG_INCLUDE_MISSING_FILE = <<~ENDOFSTRING = Hello PlantUML! :plantuml-include: test/fixtures/missing.puml [plantuml, format="png"] .Title Of this ---- User -> (Start) User --> (Use the application) : Label ---- ENDOFSTRING DOC_CONFIG_INCLUDE_INSECURE_FILE = <<~ENDOFSTRING = Hello PlantUML! :plantuml-include: /etc/passwd [plantuml, format="png"] .Title Of this ---- User -> (Start) User --> (Use the application) : Label ---- ENDOFSTRING DOC_CONFIG_INCLUDE_MACRO_BLOCK = <<~ENDOFSTRING = Hello PlantUML! :plantuml-include: test/fixtures/config.puml [plantuml, format="png"] plantuml::test/fixtures/test.puml[] ENDOFSTRING class PlantUmlTest < Test::Unit::TestCase GENURL = 'http://localhost:8080/plantuml/png/U9npA2v9B2efpStX2YrEBLBGjLFG20Q9Q4Bv804WIw4a8rKXiQ0W9pCviIGpFqzJmKh19p4fDOVB8JKl1QWT05kd5wq0' GENURL2 = 'http://localhost:8080/plantuml/png/U9npA2v9B2efpStXYdRszmqmZ8NGHh4mleAkdGAAa15G22Pc7Clba9gN0jGE00W75Cm0' GENURL_ENCODING = 'http://localhost:8080/plantuml/png/~1U9npA2v9B2efpStX2YrEBLBGjLFG20Q9Q4Bv804WIw4a8rKXiQ0W9pCviIGpFqzJmKh19p4fDOVB8JKl1QWT05kd5wq0' GENURL_CONFIG = 'http://localhost:8080/plantuml/png/~1U9nDZJ4Emp08HVUSWh4PUe4ELQIktQeUW3YeiMA31NZexKEg3bc-Fly1Vp97zLxBO5lcXeeLgh2aLQKIk7OwaHdJzb7fl3oaY0P6ja34Vjeo_nOArPn-dzz62jSxN5v7r_YVZo0S-4g0hPMSqBFm23Tuuanbc8YNEDy1SzOwlG00' SVGGENURL = 'http://localhost:8080/plantuml/svg/~1U9npA2v9B2efpStX2YrEBLBGjLFG20Q9Q4Bv804WIw4a8rKXiQ0W9pCviIGpFqzJmKh19p4fDOVB8JKl1QWT05kd5wq0' def setup Asciidoctor::PlantUml.configure do |c| c.url = 'http://localhost:8080/plantuml' c.txt_enable = true c.png_enable = true c.svg_enable = true end end def test_plantuml_block_processor html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal GENURL, element['src'] end def test_plantuml_block_literal_processor html = ::Asciidoctor.convert( StringIO.new(DOC_BASIC_LITERAL), backend: 'html5' ) page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal GENURL, element['src'] end def test_plantuml_block_processor2 html = ::Asciidoctor.convert( StringIO.new(DOC_BASIC2), backend: 'html5' ) page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal GENURL, element['src'] end def test_plantuml_block_literal_processor2 html = ::Asciidoctor.convert( StringIO.new(DOC_BASIC2_LITERAL), backend: 'html5' ) page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal GENURL, element['src'] end def test_plantuml_block_processor3 html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC3), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal GENURL2, element['src'] end def test_plantuml_block_literal_processor3 html = ::Asciidoctor.convert( StringIO.new(DOC_BASIC3_LITERAL), backend: 'html5' ) page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal GENURL2, element['src'] end def test_plantuml_block_processor_encoding Asciidoctor::PlantUml.configure do |c| c.encoding = 'deflate' end html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal GENURL_ENCODING, element['src'] end def test_plantuml_block_macro_processor html = ::Asciidoctor.convert(StringIO.new(DOC_BLOCK_MACRO), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal GENURL, element['src'] end def test_should_show_file_error html = ::Asciidoctor.convert(StringIO.new(DOC_BLOCK_MACRO_MISSING_FILE), backend: 'html5', safe: :secure) page = Nokogiri::HTML(html) elements = page.css('pre.plantuml-error') assert_equal elements.size, 1 assert_includes html, 'No such file or directory' end def test_should_show_insecure_error html = ::Asciidoctor.convert(StringIO.new(DOC_BLOCK_MACRO_INSECURE_FILE), backend: 'html5', safe: :secure) page = Nokogiri::HTML(html) elements = page.css('pre.plantuml-error') assert_equal elements.size, 1 assert_includes html, 'is outside of jail' end def test_plantuml_subs_attributes html = ::Asciidoctor.convert(StringIO.new(DOC_SUBS_ATTRIBUTES), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal GENURL_ENCODING, element['src'] end def test_plantuml_config_include html = ::Asciidoctor.convert(StringIO.new(DOC_CONFIG_INCLUDE), backend: 'html5', safe: :secure) page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal GENURL_CONFIG, element['src'] end def test_plantuml_config_include_missing_file html = ::Asciidoctor.convert(StringIO.new(DOC_CONFIG_INCLUDE_MISSING_FILE), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('pre.plantuml-error') assert_equal elements.size, 1 assert_includes html, 'No such file or directory' end def test_plantuml_config_include_insecure_file html = ::Asciidoctor.convert(StringIO.new(DOC_CONFIG_INCLUDE_INSECURE_FILE), backend: 'html5', safe: :secure) page = Nokogiri::HTML(html) elements = page.css('pre.plantuml-error') assert_equal elements.size, 1 assert_includes html, 'is outside of jail' end def test_plantuml_config_include_macro_block html = ::Asciidoctor.convert(StringIO.new(DOC_CONFIG_INCLUDE_MACRO_BLOCK), backend: 'html5', safe: :secure) page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal GENURL_CONFIG, element['src'] end def test_plantuml_id_attribute html = ::Asciidoctor.convert(StringIO.new(DOC_ID), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal 'myId', element['id'] end def test_plantuml_dimension_attribute html = ::Asciidoctor.convert(StringIO.new(DOC_DIM), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal '100px', element['width'] assert_equal '50px', element['height'] end def test_plantuml_alt_attribute html = ::Asciidoctor.convert(StringIO.new(DOC_ALT), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal elements.size, 1 element = elements.first assert_equal 'alt', element['alt'] end def test_should_show_bad_format html = ::Asciidoctor.convert(StringIO.new(DOC_BAD_FORMAT), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('pre.plantuml-error') assert_equal elements.size, 1 end def test_plantuml_multiple_listing html = ::Asciidoctor.convert(StringIO.new(DOC_MULTI), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert elements.size >= 2 elements = page.css('.plantuml-error') assert_equal elements.size, 0 end def test_plantuml_multiple_literal html = ::Asciidoctor.convert( StringIO.new(DOC_MULTI_LITERAL), backend: 'html5' ) page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert elements.size >= 2 elements = page.css('.plantuml-error') assert_equal elements.size, 0 end def test_plantuml_bad_server Asciidoctor::PlantUml.configure do |c| c.url = 'http://nonexistent.com/plantuml' end html = ::Asciidoctor.convert(StringIO.new(DOC_MULTI), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('img.plantuml') assert_equal 3, elements.size elements = page.css('.plantuml-error') assert_equal 0, elements.size end def test_plantuml_invalid_uri Asciidoctor::PlantUml.configure do |c| c.url = 'ftp://test.com' end html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('pre.plantuml-error') assert_equal elements.size, 1 end def test_plantuml_nil_uri Asciidoctor::PlantUml.configure do |c| c.url = nil end html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('pre.plantuml-error') assert_equal elements.size, 1 end def test_plantuml_empty_uri Asciidoctor::PlantUml.configure do |c| c.url = '' end html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('pre.plantuml-error') assert_equal elements.size, 1 end def test_disable_txt Asciidoctor::PlantUml.configure do |c| c.url = 'http://localhost:8080/plantuml' c.txt_enable = false end html = ::Asciidoctor.convert(StringIO.new(DOC_TXT), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('pre.plantuml-error') assert_equal elements.size, 1 end def test_svg Asciidoctor::PlantUml.configure do |c| c.url = 'http://localhost:8080/plantuml' c.svg_enable = true end html = ::Asciidoctor.convert(StringIO.new(DOC_SVG), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css("object[type='image/svg+xml']") assert_equal elements.size, 1 element = elements.first assert_equal SVGGENURL, element['data'] end def test_disable_svg Asciidoctor::PlantUml.configure do |c| c.url = 'http://localhost:8080/plantuml' c.svg_enable = false end html = ::Asciidoctor.convert(StringIO.new(DOC_SVG), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('pre.plantuml-error') assert_equal elements.size, 1 end def test_disable_png Asciidoctor::PlantUml.configure do |c| c.url = 'http://localhost:8080/plantuml' c.png_enable = false end html = ::Asciidoctor.convert(StringIO.new(DOC_BASIC_LITERAL), backend: 'html5') page = Nokogiri::HTML(html) elements = page.css('pre.plantuml-error') assert_equal elements.size, 1 end end asciidoctor-plantuml-0.1.1/lib/ 0000755 0000041 0000041 00000000000 14235144530 016445 5 ustar www-data www-data asciidoctor-plantuml-0.1.1/lib/asciidoctor-plantuml.rb 0000644 0000041 0000041 00000000455 14235144530 023133 0 ustar www-data www-data # frozen_string_literal: true require 'asciidoctor' require 'asciidoctor/extensions' require_relative 'asciidoctor_plantuml/plantuml' Asciidoctor::Extensions.register do block Asciidoctor::PlantUml::BlockProcessor, :plantuml block_macro Asciidoctor::PlantUml::BlockMacroProcessor, :plantuml end asciidoctor-plantuml-0.1.1/lib/asciidoctor_plantuml/ 0000755 0000041 0000041 00000000000 14235144530 022664 5 ustar www-data www-data asciidoctor-plantuml-0.1.1/lib/asciidoctor_plantuml/version.rb 0000644 0000041 0000041 00000000144 14235144530 024675 0 ustar www-data www-data # frozen_string_literal: true module Asciidoctor module PlantUML VERSION = '0.1.1' end end asciidoctor-plantuml-0.1.1/lib/asciidoctor_plantuml/plantuml.rb 0000644 0000041 0000041 00000025723 14235144530 025056 0 ustar www-data www-data # frozen_string_literal: true require 'uri' require 'open-uri' require 'zlib' require 'net/http' module Asciidoctor # PlantUML Module module PlantUml # PlantUML Configuration class Configuration DEFAULT_URL = ENV.fetch('PLANTUML_URL', '') DEFAULT_ENCODING = ENV.fetch('PLANTUML_ENCODING', 'legacy') attr_accessor :url, :txt_enable, :svg_enable, :png_enable, :encoding def initialize @url = DEFAULT_URL @txt_enable = true @svg_enable = true @png_enable = true @encoding = DEFAULT_ENCODING end end class << self attr_writer :configuration end def self.configuration @configuration ||= Configuration.new end def self.configure yield(configuration) end # PlantUML Processor class Processor FORMATS = %w[png svg txt].freeze DEFAULT_FORMAT = FORMATS[0] ENCODINGS = %w[legacy deflate].freeze DEFAULT_ENCODING = ENCODINGS[0] ENCODINGS_MAGIC_STRINGS_MAP = Hash.new('') ENCODINGS_MAGIC_STRINGS_MAP['deflate'] = '~1' URI_SCHEMES_REGEXP = ::URI::DEFAULT_PARSER.make_regexp(%w[http https]) class << self def valid_format?(format) FORMATS.include?(format) end def valid_encoding?(encoding) ENCODINGS.include?(encoding) end def server_url PlantUml.configuration.url end def txt_enabled? PlantUml.configuration.txt_enable end def png_enabled? PlantUml.configuration.png_enable end def svg_enabled? PlantUml.configuration.svg_enable end def enabled? txt_enabled? || png_enabled? || svg_enabled? end def plantuml_content_format(parent, code, format, attrs = {}) content = code.read # honor subs attributes # e.g. replace asciidoc variables subs = attrs['subs'] content = parent.apply_subs(content, parent.resolve_subs(subs)) if subs # add @start... and @end... if missing content = "@startuml\n#{content}\n@enduml" unless content =~ /^@start.*@end[a-z]*$/m # insert global plantuml config after first line config_path = parent.attr('plantuml-include', '', true) unless config_path.empty? begin source_file = parent.document.normalize_system_path(config_path, nil, nil, recover: false) content = insert_config_to_content(parent, source_file, content, attrs) rescue StandardError => e return plantuml_invalid_file(source_file, e.message, attrs) rescue SecurityError => e return plantuml_insecure_file(source_file, e.message, attrs) end end if %w[png svg txt].include?(format) && method("#{format}_enabled?").call method("plantuml_#{format}_content").call(content, format, attrs) else plantuml_invalid_content(format, attrs) end end def plantuml_content(parent, code, attrs = {}) format = attrs['format'] || DEFAULT_FORMAT return plantuml_disabled_content(code, attrs) unless enabled? return plantuml_server_unavailable_content(server_url, attrs) unless valid_uri?(server_url) plantuml_content_format(parent, code, format, attrs) end def plantuml_content_from_file(parent, target, attrs = {}) source_file = parent.document.normalize_system_path(target, nil, nil, recover: false) content = ::File.open(source_file, mode: FILE_READ_MODE) plantuml_content(parent, content, attrs) rescue StandardError => e plantuml_invalid_file(source_file, e.message, attrs) rescue SecurityError => e plantuml_insecure_file(source_file, e.message, attrs) end # Compression code used to generate PlantUML URLs. Taken directly from # the transcoder class in the PlantUML java code. def gen_url(text, format) result = '' result += encoding_magic_prefix compressed_data = Zlib::Deflate.deflate(text) compressed_data.chars.each_slice(3) do |bytes| # print bytes[0], ' ' , bytes[1] , ' ' , bytes[2] b1 = bytes[0].nil? ? 0 : (bytes[0].ord & 0xFF) b2 = bytes[1].nil? ? 0 : (bytes[1].ord & 0xFF) b3 = bytes[2].nil? ? 0 : (bytes[2].ord & 0xFF) result += append3bytes(b1, b2, b3) end join_paths(server_url, "#{format}/", result).to_s end def create_plantuml_block(parent, content, attrs) Asciidoctor::Block.new parent, :pass, { content_model: :raw, source: content, subs: :default }.merge(attrs) end private def insert_config_to_content(parent, config_path, content, attrs) config = File.read(config_path, mode: FILE_READ_MODE) subs = attrs['subs'] config = parent.apply_subs(config, parent.resolve_subs(subs)) if subs return content.dup.insert(content.index("\n"), "\n#{config}") unless config.empty? end def plantuml_txt_content(code, format, attrs = {}) url = gen_url(code, format) URI(url).open do |f| plantuml_ascii_content(f.read, attrs) end rescue OpenURI::HTTPError, Errno::ECONNREFUSED, SocketError plantuml_png_content(code, format, attrs) end def plantuml_ascii_content(code, attrs = {}) content = '
\n'
content += code
content += ''
content += '\n'
content += error
content += ''
content += '