asciidoctor-plantuml-0.0.10/0000755000175000017500000000000013561231236015271 5ustar josephjosephasciidoctor-plantuml-0.0.10/lib/0000755000175000017500000000000013561231236016037 5ustar josephjosephasciidoctor-plantuml-0.0.10/lib/asciidoctor-plantuml.rb0000644000175000017500000000035113561231236022520 0ustar josephjoseph# frozen_string_literal: true require 'asciidoctor' require 'asciidoctor/extensions' require_relative 'asciidoctor_plantuml/plantuml' Asciidoctor::Extensions.register do block Asciidoctor::PlantUml::BlockProcessor, :plantuml end asciidoctor-plantuml-0.0.10/lib/asciidoctor_plantuml/0000755000175000017500000000000013561231236022256 5ustar josephjosephasciidoctor-plantuml-0.0.10/lib/asciidoctor_plantuml/version.rb0000644000175000017500000000014513561231236024270 0ustar josephjoseph# frozen_string_literal: true module Asciidoctor module PlantUML VERSION = '0.0.10' end end asciidoctor-plantuml-0.0.10/lib/asciidoctor_plantuml/plantuml.rb0000644000175000017500000001752613561231236024452 0ustar josephjoseph# frozen_string_literal: true require 'uri' require 'open-uri' require 'zlib' require 'open-uri' require 'net/http' module Asciidoctor # PlantUML Module module PlantUml # PlantUML Configuration class Configuration DEFAULT_URL = ENV['PLANTUML_URL'] || '' attr_accessor :url, :txt_enable, :svg_enable, :png_enable def initialize @url = DEFAULT_URL @txt_enable = true @svg_enable = true @png_enable = true 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] class << self def valid_format?(format) FORMATS.include?(format) 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(code, format, attrs = {}) if %w[png svg].include?(format) plantuml_img_content(code, format, attrs) elsif format == 'txt' && txt_enabled? plantuml_txt_content(code, format, attrs) else plantuml_invalid_content(format, attrs) end end def plantuml_content(code, attrs = {}) format = attrs['format'] || DEFAULT_FORMAT return plantuml_disabled_content(code, attrs) unless enabled? unless valid_uri?(server_url) return plantuml_server_unavailable_content(server_url, attrs) end plantuml_content_format(code, format, 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 = '' 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 private 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_img_content(code, format, attrs) end def plantuml_ascii_content(code, attrs = {}) content = '
' content += '
' content += '
'
        end

        def plantuml_img_content(code, format, attrs = {})
          content = '
' content += '
' content += '" content += '
' content + '
' end def plantuml_invalid_content(format, attrs = {}) content = '
' content += '
' content += '
'
        end

        def plantuml_server_unavailable_content(url, attrs = {})
          content = '
' content += '
' content += '
'
        end

        def plantuml_disabled_content(code, attrs = {})
          content = '
' content += '
' content += '
'
        end

        def encode6bit(bit)
          return ('0'.ord + bit).chr if bit < 10

          bit -= 10
          return ('A'.ord + bit).chr if bit < 26

          bit -= 26
          return ('a'.ord + bit).chr if bit < 26

          bit -= 26
          return '-' if bit.zero?

          return '_' if bit == 1

          '?'
        end

        def append3bytes(bit1, bit2, bit3)
          c1 = bit1 >> 2
          c2 = ((bit1 & 0x3) << 4) | (bit2 >> 4)
          c3 = ((bit2 & 0xF) << 2) | (bit3 >> 6)
          c4 = bit3 & 0x3F
          encode6bit(c1 & 0x3F).chr +
            encode6bit(c2 & 0x3F).chr +
            encode6bit(c3 & 0x3F).chr +
            encode6bit(c4 & 0x3F).chr
        end

        # Make a call to the PlantUML server with the simplest diagram possible
        # to check if the server is available or not.
        def check_server(check_url)
          response = Net::HTTP.get_response(URI(check_url))
          response.is_a?(Net::HTTPSuccess)
        rescue OpenURI::HTTPError, Errno::ECONNREFUSED, SocketError
          false
        end

        def valid_uri?(uri)
          !(uri =~ /\A#{URI.regexp(%w[http https])}\z/).nil?
        end

        def join_paths(*paths, separator: '/')
          paths = paths.compact.reject(&:empty?)
          last = paths.length - 1
          paths.each_with_index.map do |path, index|
            expand_path(path, index, last, separator)
          end.join
        end

        def expand_path(path, current, last, separator)
          path = path[1..-1] if path.start_with?(separator) && current.zero?

          unless path.end_with?(separator) || current == last
            path = [path, separator]
          end

          path
        end
      end
    end

    # PlantUML BlockProcessor
    class BlockProcessor < Asciidoctor::Extensions::BlockProcessor
      use_dsl
      named :plantuml
      on_context :listing
      content_model :simple

      def process(parent, target, attrs)
        lines = target.lines

        unless target.lines[0] =~ /@startuml/
          lines = ['@startuml'] + target.lines
        end

        lines += ['@enduml'] unless target.lines[-1] =~ /@enduml/

        content = Processor.plantuml_content(lines.join("\n"), attrs)

        create_plantuml_block(parent, content, attrs)
      end

      private

      def create_plantuml_block(parent, content, attrs)
        Asciidoctor::Block.new parent, :pass,  {
          content_model: :raw,
          source: content,
          subs: :default
        }.merge(attrs)
      end
    end
  end
end
asciidoctor-plantuml-0.0.10/test/0000755000175000017500000000000013561231236016250 5ustar  josephjosephasciidoctor-plantuml-0.0.10/test/test_plantuml.rb0000644000175000017500000001434113561231236021473 0ustar  josephjoseph# 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_BASIC2 = <<~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_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_TXT = <<~ENDOFSTRING
  = Hello PlantUML!

  [plantuml, format="txt"]
  ----
  User -> (Start)
  User --> (Use the application) : Label
  ----
ENDOFSTRING

class PlantUmlTest < Test::Unit::TestCase
  GENURL = 'http://localhost:8080/plantuml/png/U9npA2v9B2efpStX2YrEBLBGjLFG20Q9Q4Bv804WIw4a8rKXiQ0W9pCviIGpFqzJmKh19p4fDOVB8JKl1QWT05kd5wq0'
  GENURL2 = 'http://localhost:8080/plantuml/png/U9npA2v9B2efpStXYdRszmqmZ8NGHh4mleAkdGAAa15G22Pc7Clba9gN0jGE00W75Cm0'

  def setup
    Asciidoctor::PlantUml.configure do |c|
      c.url = 'http://localhost:8080/plantuml'
      c.txt_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_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_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_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
    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_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
end
asciidoctor-plantuml-0.0.10/asciidoctor-plantuml.gemspec0000644000175000017500000000461213561231236022776 0ustar  josephjoseph#########################################################
# This file has been automatically generated by gem2tgz #
#########################################################
# -*- encoding: utf-8 -*-
# stub: asciidoctor-plantuml 0.0.10 ruby lib

Gem::Specification.new do |s|
  s.name = "asciidoctor-plantuml".freeze
  s.version = "0.0.10"

  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
  s.require_paths = ["lib".freeze]
  s.authors = ["Horacio Sanson".freeze]
  s.date = "2019-11-03"
  s.description = "Asciidoctor PlantUML extension".freeze
  s.email = ["hsanson@gmail.com".freeze]
  s.files = ["lib/asciidoctor-plantuml.rb".freeze, "lib/asciidoctor_plantuml/plantuml.rb".freeze, "lib/asciidoctor_plantuml/version.rb".freeze, "test/test_plantuml.rb".freeze]
  s.homepage = "https://github.com/hsanson/asciidoctor-plantuml".freeze
  s.licenses = ["MIT".freeze]
  s.required_ruby_version = Gem::Requirement.new(">= 2.3".freeze)
  s.rubygems_version = "2.5.2.1".freeze
  s.summary = "Asciidoctor support for PlantUML diagrams.".freeze
  s.test_files = ["test/test_plantuml.rb".freeze]

  if s.respond_to? :specification_version then
    s.specification_version = 4

    if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
      s.add_runtime_dependency(%q.freeze, ["< 3.0.0", ">= 1.5.6"])
      s.add_development_dependency(%q.freeze, ["~> 1.3"])
      s.add_development_dependency(%q.freeze, ["~> 1.6"])
      s.add_development_dependency(%q.freeze, ["~> 10.5"])
      s.add_development_dependency(%q.freeze, ["~> 0.66"])
      s.add_development_dependency(%q.freeze, ["~> 3.3"])
    else
      s.add_dependency(%q.freeze, ["< 3.0.0", ">= 1.5.6"])
      s.add_dependency(%q.freeze, ["~> 1.3"])
      s.add_dependency(%q.freeze, ["~> 1.6"])
      s.add_dependency(%q.freeze, ["~> 10.5"])
      s.add_dependency(%q.freeze, ["~> 0.66"])
      s.add_dependency(%q.freeze, ["~> 3.3"])
    end
  else
    s.add_dependency(%q.freeze, ["< 3.0.0", ">= 1.5.6"])
    s.add_dependency(%q.freeze, ["~> 1.3"])
    s.add_dependency(%q.freeze, ["~> 1.6"])
    s.add_dependency(%q.freeze, ["~> 10.5"])
    s.add_dependency(%q.freeze, ["~> 0.66"])
    s.add_dependency(%q.freeze, ["~> 3.3"])
  end
end