├── .gitattributes ├── .github └── workflows │ └── gh-pages.yml ├── .gitignore ├── Gemfile ├── LICENSE ├── README.adoc ├── build ├── asciidoc-coalescer.rb ├── asciidoctor-od-image │ └── Dockerfile ├── build-debug.sh ├── build.sh ├── build_image.sh └── build_typography.sh ├── docs └── a-od-basic-doc.adoc ├── lib ├── a-od ├── a-od-my │ └── my-cp-example.rb ├── a-od-out ├── a-od-pre ├── a-od-producer │ ├── asciidoctor-od.rb │ ├── basic-template.fodt │ └── misc.rb ├── build-lo-kts.sh ├── lo-kts-converter.main.jar ├── lo-kts-converter.main.kts └── slim │ ├── _index.fodt.slim │ ├── _toc.fodt.slim │ ├── admonition.fodt.slim │ ├── colist.fodt.slim │ ├── document.fodt.slim │ ├── embedded.fodt.slim │ ├── example.fodt.slim │ ├── floating_title.fodt.slim │ ├── helpers.rb │ ├── image.fodt.slim │ ├── image_frame.fodt.slim │ ├── inline_anchor.html.slim │ ├── inline_break.fodt.slim │ ├── inline_callout.fodt.slim │ ├── inline_footnote.slim │ ├── inline_image.fodt.slim │ ├── inline_indexterm.fodt.slim │ ├── inline_quoted.fodt.slim │ ├── listing.fodt.slim │ ├── literal.fodt.slim │ ├── olist.fodt.slim │ ├── open.fodt.slim │ ├── outline.fodt.slim │ ├── page_break.fodt.slim │ ├── paragraph.fodt.slim │ ├── preamble.fodt.slim │ ├── quote.fodt.slim │ ├── section.fodt.slim │ ├── stem.fodt.slim │ ├── table.fodt.slim │ ├── table_section.fodt.slim │ └── ulist.fodt.slim ├── resources └── OpenDocument-v1.2-os-schema.rng └── test ├── test.rb ├── test_cases ├── asciidoctor-pdf │ ├── basic-example.adoc │ ├── chronicles-dark-theme.yml │ ├── chronicles-example.adoc │ ├── chronicles-template.fodt │ ├── chronicles-theme.yml │ ├── edge-cases.adoc │ ├── sample-banner.svg │ ├── sample-logo.jpg │ └── wolpertinger.jpg ├── stew │ ├── 60x20-label.svg │ ├── asciidoc_icon.png │ ├── ehjnem.png │ ├── long_line.png │ ├── rasputicza.jpg │ ├── ruler.png │ ├── short_line.png │ ├── svg_example.svg │ └── test.adoc └── typography │ └── ttest.adoc └── test_fodt.rb /.gitattributes: -------------------------------------------------------------------------------- 1 | *.fodt filter=lfs diff=lfs merge=lfs -text 2 | *.jar filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | push: 4 | branches: [main] 5 | pull_request: 6 | branches: [main] 7 | schedule: 8 | - cron: "05 5 * * *" 9 | jobs: 10 | build-and-deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | with: 16 | lfs: true 17 | - name: build 18 | run: | 19 | build/build.sh 20 | - name: GitHub Pages 21 | if: success() && github.ref == 'refs/heads/main' 22 | uses: crazy-max/ghaction-github-pages@v2.3.0 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | with: 26 | build_dir: target/out 27 | - name: Log in to Docker Hub 28 | if: success() && github.ref == 'refs/heads/main' 29 | uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 30 | with: 31 | username: ${{ secrets.DOCKER_USERNAME }} 32 | password: ${{ secrets.DOCKER_PASSWORD }} 33 | - name: Push 34 | if: success() && github.ref == 'refs/heads/main' 35 | run: | 36 | docker push curs/asciidoctor-od:latest 37 | 38 | 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .*.sw* 3 | .idea 4 | Gemfile.lock 5 | todo* 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'tilt' 3 | gem 'slim' 4 | gem 'asciidoctor' 5 | gem 'nokogiri' 6 | gem 'minitest' 7 | gem 'fastimage' 8 | gem 'pry' 9 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Course Orchestra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | :toc: macro 2 | :toclevels: 4 3 | 4 | image::https://github.com/CourseOrchestra/asciidoctor-open-document/workflows/build/badge.svg[link=https://github.com/CourseOrchestra/asciidoctor-open-document/actions?query=workflow%3A"build"] 5 | 6 | = Open Document converter for Asciidoctor 7 | 8 | toc::[] 9 | 10 | == About 11 | 12 | //tag::about[] 13 | Open Document converter for Asciidoctor allows to convert asciidoc documentation into fodt (plain Open Document) format. It relies on https://docs.asciidoctor.org/home/[Asciidoctor project]. 14 | 15 | Fodt file may be converted with https://www.libreoffice.org/[LibreOffice] SDK or CLI into pdf, odt, docx, doc etc. 16 | 17 | Examples (built automatically as a part of CI routine): 18 | 19 | * https://github.com/CourseOrchestra/asciidoctor-open-document/blob/main/test/test_cases/stew/test.adoc[source asciidoc file]; 20 | * https://courseorchestra.github.io/asciidoctor-open-document/test.fodt[fodt file] (indexes are not updated); 21 | * https://courseorchestra.github.io/asciidoctor-open-document/test.odt[odt file]; 22 | * https://courseorchestra.github.io/asciidoctor-open-document/test.pdf[pdf file]; 23 | * https://courseorchestra.github.io/asciidoctor-open-document/test.docx[docx file] -- yes, sometimes messy, but generally not so bad and there is always a possibility to simplify conversion rules and tailor them to the needs of the exact project. 24 | 25 | The asciidoctor-open-document converter can be extended in the following ways: 26 | 27 | * custom fodt template. Allows to customize paragraph properties, text properties, page properties (orientation, header, footer and like), list properties, table properties. Custom fodt template can contain title page or any before/after content; 28 | * custom slim templates; 29 | * custom xml-content preprocessor; 30 | * custom Open Document style setters. 31 | //end::about[] 32 | 33 | == Usage 34 | 35 | //tag::usage[] 36 | 37 | === With docker 38 | 39 | ==== With `a-od` simplified utility 40 | 41 | Assuming you need to convert `test.adoc` from the current directory to odt format. 42 | 43 | ---- 44 | docker run --rm -v $(pwd):/documents/ curs/asciidoctor-od a-od test.adoc odt 45 | ---- 46 | 47 | General syntax of `a-od` is: 48 | 49 | ---- 50 | a-od [file] [output format] [custom fodt temlate] [custom conversion library] 51 | ---- 52 | 53 | To use asciidoctor cli options add `--asciidoctor` at the end and any asciidoctor cli options. For example, to convert `foo.adoc` to pdf with revision number `v5` use: 54 | 55 | ---- 56 | a-od foo.adoc pdf --asciidoctor -a revnumber=v5 57 | ---- 58 | 59 | ==== Or step by step without `a-od` 60 | 61 | * Convert adoc to preliminary Open Document content file 62 | + 63 | The following steps assume that asciidoc file `test.adoc` is in the `/my-adoc` directory. As a result we want to get `test.fodt` and `test.pdf`. 64 | + 65 | ---- 66 | docker run --rm -v $(pwd):/documents/ curs/asciidoctor-od a-od-pre -r asciidoctor-mathematical -r asciidoctor-diagram test.adoc -o pre.xml 67 | ---- 68 | + 69 | Here `asciidoctor-mathematical` and `asciidoctor-diagram` extensions are used. 70 | 71 | * Convert preliminary Open Document content file to fodt 72 | + 73 | ---- 74 | docker run --rm -v $(pwd):/documents/ curs/asciidoctor-od a-od-out -c /usr/local/a-od/a-od-my/my-cp-example.rb -i pre.xml -o test.fodt 75 | ---- 76 | + 77 | Here the custom library is used. It contains examples of custom features: positioning equation number, paragraph alignment role, code highlighting, positioning admonitions inside of the list element. 78 | 79 | * Convert fodt to pdf, odt, docx 80 | + 81 | ---- 82 | docker run --rm -v $(pwd):/documents/ curs/asciidoctor-od java -jar /usr/local/a-od/lo-kts-converter.main.jar -f pdf,odt,docx -i test.fodt 83 | ---- 84 | 85 | === As a ruby script 86 | 87 | Conversion consists of 2 steps. 88 | 89 | . Convert adoc to preliminary content file 90 | . Convert preliminary content file to fodt 91 | 92 | ==== Convert adoc to preliminary content file 93 | 94 | Conversion to preliminary content file is done by the standard `asciidoctor` command with a slim template (slim folder of a source code). 95 | 96 | ---- 97 | asciidoctor -T [path to slim] -b fodt asciidoc_file.adoc 98 | ---- 99 | 100 | There is a wrapper `a-od-pre` in docker image. Use it like a normal `asciidoctor` command. It just sets slim template and backend. 101 | 102 | 103 | ==== Convert preliminary content file to fodt 104 | 105 | Conversion to final fodt file is done with the `asciidoctor-od.rb` script. 106 | 107 | ---- 108 | ruby [path to script]/a-od-producer.rb -i [prelimenary content file] -o [final file] -c [custom process library] -f [fodt template] 109 | ---- 110 | 111 | There is a wrapper `a-od-out` in docker image. It just runs ruby with `a-od-producer.rb`. 112 | //end::usage[] 113 | 114 | *More details* can be found in https://courseorchestra.github.io/asciidoctor-open-document/[Open Document converter for Asciidoctor documentation]. 115 | -------------------------------------------------------------------------------- /build/asciidoc-coalescer.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This script coalesces the AsciiDoc content from a document master into a 4 | # single output file. It does so by resolving all preprocessor directives in 5 | # the document, and in any files which are included. The resolving of include 6 | # directives is likely of most interest to users of this script. 7 | # 8 | # This script works by using Asciidoctor's PreprocessorReader to read and 9 | # resolve all the lines in the specified input file. The script then writes the 10 | # result to the output. 11 | # 12 | # The script only recognizes attributes passed in as options or those defined 13 | # in the document header. It does not currently process attributes defined in 14 | # other, arbitrary locations within the document. 15 | # 16 | # You can find a similar extension written against AsciidoctorJ here: 17 | # https://github.com/hibernate/hibernate-asciidoctor-extensions/blob/master/src/main/java/org/hibernate/infra/asciidoctor/extensions/savepreprocessed/SavePreprocessedOutputPreprocessor.java 18 | 19 | # TODO 20 | # - add cli option to write attributes passed to cli to header of document 21 | # - escape all preprocessor directives after lines are processed (these are preprocessor directives that were escaped in the input) 22 | # - wrap in a custom converter so it can be used as an extension 23 | 24 | require 'asciidoctor' 25 | require 'optparse' 26 | 27 | options = { attributes: [], output: '-' } 28 | OptionParser.new do |opts| 29 | opts.banner = 'Usage: ruby asciidoc-coalescer.rb [OPTIONS] FILE' 30 | opts.on('-a', '--attribute key[=value]', 'A document attribute to set in the form of key[=value]') do |a| 31 | options[:attributes] << a 32 | end 33 | opts.on('-o', '--output FILE', 'Write output to FILE instead of stdout.') do |o| 34 | options[:output] = o 35 | end 36 | end.parse! 37 | 38 | unless (source_file = ARGV.shift) 39 | warn 'Please specify an AsciiDoc source file to coalesce.' 40 | exit 1 41 | end 42 | 43 | unless (output_file = options[:output]) == '-' 44 | if (output_file = File.expand_path output_file) == (File.expand_path source_file) 45 | warn 'Source and output cannot be the same file.' 46 | exit 1 47 | end 48 | end 49 | 50 | # NOTE first, resolve attributes defined at the end of the document header 51 | # QUESTION can we do this in a single load? 52 | doc = Asciidoctor.load_file source_file, safe: :unsafe, header_only: true, attributes: options[:attributes] 53 | # NOTE quick and dirty way to get the attributes set or unset by the document header 54 | header_attr_names = (doc.instance_variable_get :@attributes_modified).to_a 55 | header_attr_names.each {|k| doc.attributes[%(#{k}!)] = '' unless doc.attr? k } 56 | 57 | doc = Asciidoctor.load_file source_file, safe: :unsafe, parse: false, attributes: doc.attributes 58 | # FIXME also escape ifdef, ifndef, ifeval and endif directives 59 | # FIXME do this more carefully by reading line by line; if input differs by output by leading backslash, restore original line 60 | lines = doc.reader.read.gsub(/^include::(?=.*\[\]$)/m, '\\include::') 61 | 62 | if output_file == '-' 63 | puts lines 64 | else 65 | File.open(output_file, 'w') {|f| f.write lines } 66 | end 67 | -------------------------------------------------------------------------------- /build/asciidoctor-od-image/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM asciidoctor/docker-asciidoctor:latest 2 | 3 | RUN apk add --no-cache \ 4 | build-base \ 5 | libxml2-dev \ 6 | ruby-dev \ 7 | openjdk11 \ 8 | && rm -rf /var/cache/apk/* \ 9 | && gem install --no-document \ 10 | fastimage \ 11 | nokogiri \ 12 | minitest \ 13 | pry 14 | 15 | RUN set -xe \ 16 | && apk add --no-cache --purge -uU \ 17 | curl icu-libs unzip zlib-dev musl \ 18 | libreoffice libreoffice-base libreoffice-lang-uk \ 19 | ttf-freefont ttf-opensans ttf-inconsolata \ 20 | ttf-liberation ttf-dejavu \ 21 | libstdc++ dbus-x11 \ 22 | && echo "http://dl-cdn.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \ 23 | && echo "http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories \ 24 | && echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \ 25 | && apk add --no-cache -U \ 26 | ttf-font-awesome ttf-mononoki ttf-hack \ 27 | && rm -rf /var/cache/apk/* /tmp/* 28 | 29 | COPY . /usr/local/a-od/ 30 | ENV PATH="/usr/local/a-od:${PATH}" 31 | RUN chmod +x /usr/local/a-od/a-od-pre && chmod +x /usr/local/a-od/a-od-out && chmod +x /usr/local/a-od/a-od 32 | -------------------------------------------------------------------------------- /build/build-debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")" 3 | cd .. 4 | 5 | rm target -rf 6 | mkdir target 7 | mkdir target/out 8 | cp test/test_cases/stew target -r 9 | cp test/test_cases/asciidoctor-pdf target -r 10 | 11 | cd target/stew 12 | #ruby ../../test/test.rb 13 | asciidoctor -b fodt -T ../../lib/slim -r asciidoctor-mathematical -r asciidoctor-diagram test.adoc -o pre.xml 14 | #ruby ../../lib/a-od-producer/asciidoctor-od.rb -c ../../lib/a-od-my/my-cp-example.rb -i pre.xml -o test.fodt 15 | ruby ../../lib/a-od-producer/asciidoctor-od.rb -i pre.xml -o test.fodt 16 | docker run --rm -v $(pwd):/documents/ curs/asciidoctor-od unoconv -f pdf test.fodt 17 | cd ../.. 18 | 19 | #cd target/asciidoctor-pdf 20 | #asciidoctor -b fodt -T ../../lib/slim -r asciidoctor-mathematical -r asciidoctor-diagram chronicles-example.adoc -o pre.xml 21 | #ruby ../../lib/a-od-producer/asciidoctor-od.rb -i pre.xml -f chronicles-template.fodt -o chronicles-example.fodt 22 | #docker run --rm -v $(pwd):/documents/ curs/asciidoctor-od unoconv -f pdf chronicles-example.fodt 23 | #cd ../.. 24 | 25 | -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")" 3 | cd .. 4 | 5 | rm target -rf 6 | mkdir target 7 | mkdir target/out 8 | cp test/test_cases/stew target -r 9 | 10 | echo build image 11 | build/build_image.sh 12 | 13 | echo build README.adoc 14 | docker run --rm -v $(pwd):/documents/ -w /documents/ curs/asciidoctor-od asciidoctor docs/a-od-basic-doc.adoc -o target/out/index.html 15 | 16 | echo make fodt 17 | docker run --rm -v $(pwd):/documents/ -w /documents/target/stew curs/asciidoctor-od a-od-pre -r asciidoctor-mathematical -r asciidoctor-diagram test.adoc -o pre.xml 18 | docker run --rm -v $(pwd):/documents/ -w /documents/target/stew curs/asciidoctor-od a-od-out -c /usr/local/a-od/a-od-my/my-cp-example.rb -i pre.xml -o test.fodt_ 19 | 20 | #tag::pdf_convert[] 21 | docker run --rm -v $(pwd):/documents/ -w /documents/target/stew curs/asciidoctor-od java -jar /usr/local/a-od/lo-kts-converter.main.jar -f fodt,pdf,odt,docx -i test.fodt_ 22 | #end::pdf_convert[] 23 | 24 | cp target/stew/test.* target/out 25 | 26 | echo test a-od producer 27 | docker run --rm -v $(pwd):/documents/ curs/asciidoctor-od ruby test/test.rb | tee target/unit_test.log 28 | 29 | echo test final fodt 30 | docker run --rm -v $(pwd):/documents/ curs/asciidoctor-od ruby test/test_fodt.rb | tee target/result_test.log 31 | 32 | if grep -q "[1-9][0-9]* failures" target/unit_test.log; then 33 | echo test.rb failed 34 | exit 1 35 | fi 36 | 37 | if test -f target/out/test.odt; then 38 | echo odt outputed 39 | else 40 | echo no output odt 41 | exit 1 42 | fi 43 | 44 | if grep -q "[1-9][0-9]* fodt errors" target/result_test.log; then 45 | echo output failed 46 | exit 1 47 | fi 48 | -------------------------------------------------------------------------------- /build/build_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")" 3 | cd .. 4 | 5 | docker build -t curs/asciidoctor-od lib -f build/asciidoctor-od-image/Dockerfile #--no-cache 6 | -------------------------------------------------------------------------------- /build/build_typography.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")" 3 | cd .. 4 | 5 | mkdir target 6 | mkdir target/out 7 | cp test/test_cases/typography target -r 8 | 9 | echo build image 10 | build/build_image.sh 11 | 12 | echo make pdf 13 | docker run --rm -v $(pwd):/documents/ -w /documents/target/typography curs/asciidoctor-od a-od ttest.adoc pdf 14 | 15 | echo make odt 16 | docker run --rm -v $(pwd):/documents/ -w /documents/target/typography curs/asciidoctor-od a-od ttest.adoc odt 17 | 18 | cp target/typography/ttest.* target/out 19 | -------------------------------------------------------------------------------- /docs/a-od-basic-doc.adoc: -------------------------------------------------------------------------------- 1 | = Open Document converter for Asciidoctor 2 | :toc: left 3 | 4 | == About 5 | 6 | include::../README.adoc[tag=about] 7 | 8 | == Usage 9 | 10 | include::../README.adoc[tag=usage] 11 | 12 | == How does it work 13 | 14 | include::../lib/a-od-producer/asciidoctor-od.rb[tag = algorithm_description, leveloffset =+ 1] 15 | 16 | === Customizing fodt template 17 | 18 | * Only part of template that is situated between paragraphs, containing text ``, is replaced with the content of the asciidoctor file. This allows to make, for example, title pages. 19 | * Asciidoc document attributes like title, subtitle, author etc. can be used via Open Document variable-set fields. The content of these fields is taken from asciidoc document attributes. For now only text variable-set fields are supported. 20 | * Out of the box converter supports only two page styles (for portrait and landscape). This can be easily extended by custom conversion library. 21 | 22 | == Not implemented 23 | 24 | === Not implemented elements 25 | 26 | ---- 27 | audio 28 | dlist 29 | inline_button 30 | inline_kbd 31 | inline_menu 32 | open 33 | page_break 34 | pass 35 | quote 36 | section (no special processing for special sections) 37 | sidebar 38 | thematic_break 39 | verse 40 | video 41 | ---- 42 | 43 | === Converter variables 44 | 45 | Converter variables are set as global variables. They start with `def` prefix. 46 | 47 | They can now be overriden only in custom library, but the ability to set them as document attributes is in the TODO-list. 48 | 49 | include::../lib/a-od-producer/asciidoctor-od.rb[tag = notimplemented, leveloffset =+ 1] 50 | 51 | == Additional features 52 | 53 | include::../lib/a-od-producer/asciidoctor-od.rb[tag = plusfeatures, leveloffset =+ 1] 54 | -------------------------------------------------------------------------------- /lib/a-od: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | file_name=$1 4 | output_format="pdf" 5 | template_key="" 6 | 7 | i=0 8 | for var in "$@" 9 | do 10 | ((i=i+1)) 11 | if [ "$var" = "--asciidoctor" ]; then 12 | break 13 | fi 14 | aod_arg_count=$i 15 | done 16 | 17 | if [ "$aod_arg_count" -gt 1 ]; then 18 | output_format=$2 19 | fi 20 | 21 | if [ "$aod_arg_count" -gt 2 ]; then 22 | template_key=" -f $3" 23 | fi 24 | 25 | if [ "$aod_arg_count" -gt 3 ]; then 26 | custom_lib=" -c $4" 27 | fi 28 | 29 | # get only asciidoctor params 30 | shift $aod_arg_count 31 | shift 1 32 | 33 | a-od-pre -r asciidoctor-mathematical -r asciidoctor-diagram $file_name -b fodt -o pre.xml "$@" 34 | a-od-out -i pre.xml -o ${file_name%.*}.fodt_ $template_key $custom_lib 35 | java -jar /usr/local/a-od/lo-kts-converter.main.jar -f $output_format -i ${file_name%.*}.fodt_ 36 | 37 | rm pre.xml 38 | rm ${file_name%.*}.fodt_ 39 | -------------------------------------------------------------------------------- /lib/a-od-my/my-cp-example.rb: -------------------------------------------------------------------------------- 1 | require 'rouge' 2 | 3 | class MyBasicPropSetSorter < BasicPropSetSorter 4 | # Rouge syntax highlighter 5 | def h_my_list_chunk 6 | # puts @sn 7 | MyListChunk.new(@sn, @sd) if !!(@sn =~ /^adoc_lich[ ]/) 8 | end 9 | end 10 | 11 | # You may define paragraph alignment with the role 12 | # text-align-[left|center|justified|right] 13 | class MyParagraph < BasicParagraph 14 | def h_fo_text_align 15 | re = / text-align-([a-z]+) / 16 | @sd["style:paragraph-properties"]["fo:text-align"] = 17 | " #{@sn} ".match(re)[1] if !!(" #{@sn} " =~ re) 18 | end 19 | end 20 | 21 | # an example of list-level1-admonition 22 | class MyBasicContentFrame < BasicContentFrame 23 | # this method is redefined 24 | def h_horizontal_pos 25 | @sd["style:graphic-properties"]["style:horizontal-pos"] = "right" 26 | @sd["style:graphic-properties"]["style:horizontal-rel"] = "paragraph" 27 | if !!(@snr =~ / list-level1-admonition /) 28 | @sd["style:graphic-properties"]["style:rel-width"] = 29 | "#{($aodp_100_percent_mm.to_f - 13.3) / $aodp_100_percent_mm * 100}%)" 30 | else 31 | @sd["style:graphic-properties"]["style:rel-width"] = "100%" 32 | end 33 | end 34 | end 35 | 36 | # an example of custom handling equation number 37 | 38 | class MyPreprocessor < StyleSubstitutor 39 | def h_eq_caption_preprocess 40 | eq_caption_nodes = 41 | @pre.xpath("//text:p[starts-with(@text:style-name, 'adoc_ica ')]", 42 | 'text' => 'urn:oasis:names:tc:opendocument:xmlns:text:1.0') 43 | eq_caption_nodes.each do |eq_caption_node| 44 | if !!(eq_caption_node['text:style-name'] + ' ' =~ / ip_s_amp /) 45 | # puts eq_caption_node.text, eq_caption_node['text:style-name'] #, eq_node 46 | tn = Nokogiri::XML::Text.new("#{eq_caption_node.text}", @pre) 47 | 48 | eq_node = eq_caption_node.xpath('preceding-sibling::*[1]')[0] 49 | # Looks like some Libre Office bug without the following line 50 | # vertical centering works wrong 51 | eq_node.first_element_child.before('') 52 | eq_node.last_element_child.after('') 53 | eq_node.add_child(tn) 54 | eq_caption_node.remove 55 | end 56 | end 57 | end 58 | 59 | 60 | def h_rouge_syntax_highlighting 61 | color_scheme = { 62 | "w": "adoc_lich hl_r_clr_303030", 63 | "err": "adoc_lich hl_r_clr_151515 hl_r_bcgclr_ac4142", 64 | "c": "adoc_lich hl_r_clr_505050", 65 | "cd": "adoc_lich hl_r_clr_505050", 66 | "cm": "adoc_lich hl_r_clr_505050", 67 | "c1": "adoc_lich hl_r_clr_505050", 68 | "cs": "adoc_lich hl_r_clr_505050", 69 | "cp": "adoc_lich hl_r_clr_f4bf75", 70 | "nt": "adoc_lich hl_r_clr_f4bf75", 71 | "o": "adoc_lich hl_r_clr_d0d0d0", 72 | "ow": "adoc_lich hl_r_clr_d0d0d0", 73 | "p": "adoc_lich hl_r_clr_d0d0d0", 74 | "pi": "adoc_lich hl_r_clr_d0d0d0", 75 | "gi": "adoc_lich hl_r_clr_90a959", 76 | "gd": "adoc_lich hl_r_clr_ac4142", 77 | "gh": "adoc_lich hl_r_clr_6a9fb5 hl_r_bcgclr_151515 hl_r_fw_bold", 78 | "k": "adoc_lich hl_r_clr_aa759f", 79 | "kn": "adoc_lich hl_r_clr_aa759f", 80 | "kp": "adoc_lich hl_r_clr_aa759f", 81 | "kr": "adoc_lich hl_r_clr_aa759f", 82 | "kv": "adoc_lich hl_r_clr_aa759f", 83 | "kc": "adoc_lich hl_r_clr_d28445", 84 | "kt": "adoc_lich hl_r_clr_d28445", 85 | "kd": "adoc_lich hl_r_clr_d28445", 86 | "s": "adoc_lich hl_r_clr_90a959", 87 | "sb": "adoc_lich hl_r_clr_90a959", 88 | "sc": "adoc_lich hl_r_clr_90a959", 89 | "sd": "adoc_lich hl_r_clr_90a959", 90 | "s2": "adoc_lich hl_r_clr_90a959", 91 | "sh": "adoc_lich hl_r_clr_90a959", 92 | "sx": "adoc_lich hl_r_clr_90a959", 93 | "s1": "adoc_lich hl_r_clr_90a959", 94 | "sr": "adoc_lich hl_r_clr_75b5aa", 95 | "si": "adoc_lich hl_r_clr_8f5536", 96 | "se": "adoc_lich hl_r_clr_8f5536", 97 | "nn": "adoc_lich hl_r_clr_f4bf75", 98 | "nc": "adoc_lich hl_r_clr_f4bf75", 99 | "no": "adoc_lich hl_r_clr_f4bf75", 100 | "na": "adoc_lich hl_r_clr_6a9fb5", 101 | "nf": "adoc_lich hl_r_clr_6a9fb5", 102 | "n": "adoc_lich hl_r_clr_6a9fb5", 103 | "m": "adoc_lich hl_r_clr_90a959", 104 | "mf": "adoc_lich hl_r_clr_90a959", 105 | "mh": "adoc_lich hl_r_clr_90a959", 106 | "mi": "adoc_lich hl_r_clr_90a959", 107 | "il": "adoc_lich hl_r_clr_90a959", 108 | "mo": "adoc_lich hl_r_clr_90a959", 109 | "mb": "adoc_lich hl_r_clr_90a959", 110 | "mx": "adoc_lich hl_r_clr_90a959", 111 | "vi": "adoc_lich hl_r_clr_6a9fb5", 112 | "vg": "adoc_lich hl_r_clr_6a9fb5", 113 | "ss": "adoc_lich hl_r_clr_90a959"} 114 | listing_caption_nodes = 115 | @pre.xpath("//text:p[starts-with(@text:style-name, 'adoc_lip ')]", 116 | 'text' => 'urn:oasis:names:tc:opendocument:xmlns:text:1.0') 117 | 118 | listing_caption_nodes.each do |listing_caption_node| 119 | snr = " " + listing_caption_node["text:style-name"] + " " 120 | re = / lip_l_([a-zA-Z0-9-]+) / 121 | 122 | if !!(snr =~ re) 123 | language = snr.match(re)[1] 124 | else 125 | next 126 | end 127 | begin 128 | Rouge.highlight('abc', language, 'html') 129 | rescue Exception => e 130 | unknown_lexer = true if e.message.strip == 'unknown lexer' 131 | end 132 | if unknown_lexer 133 | puts "INFO: #{e.message} #{language}" 134 | next 135 | end 136 | # replace callout with a number of `~` for them to be interpreted as a chunk 137 | callouts = listing_caption_node.xpath(".//text:span[starts-with(@text:style-name, 'adoc_ic ')]", 138 | 'text' => 'urn:oasis:names:tc:opendocument:xmlns:text:1.0') 139 | callouts.each do |callout| 140 | call_out_r = "~" * callout.text.to_i 141 | callout.after "~~~~" + call_out_r 142 | callout.remove 143 | end 144 | # replace line-breaks 145 | eols = listing_caption_node.xpath(".//text:line-break", 146 | 'text' => 'urn:oasis:names:tc:opendocument:xmlns:text:1.0') 147 | eols.each do |eol| 148 | eol.after("\n") 149 | eol.remove 150 | end 151 | # replace spaces 152 | spaces = listing_caption_node.xpath(".//text:s", 153 | 'text' => 'urn:oasis:names:tc:opendocument:xmlns:text:1.0') 154 | spaces.each do |space| 155 | space.after(' ') 156 | space.remove 157 | end 158 | 159 | 160 | listing_content = listing_caption_node.text 161 | listing_caption_node.children.remove 162 | 163 | # rouge html back to open document 164 | Rouge.highlight(listing_content, language, 'html') do |chunk| 165 | content_node = listing_caption_node 166 | chunk_text = chunk 167 | 168 | if !!("#{chunk}" =~ /span/) 169 | chunk_xml = Nokogiri::XML(chunk) 170 | chunk_text = chunk_xml.xpath("/span")[0].text 171 | chunk_class = chunk_xml.xpath("/span/@class")[0].text 172 | chunk_style = color_scheme[chunk_class.to_sym] 173 | Nokogiri::XML::Builder.with(listing_caption_node) do |xml| 174 | xml['text'].span('text:style-name' => chunk_style) 175 | end 176 | content_node = listing_caption_node.last_element_child 177 | end 178 | 179 | loop_s = chunk_text 180 | while loop_s.length > 0 181 | if loop_s[0] == " " 182 | Nokogiri::XML::Builder.with(content_node) do |xml| 183 | xml['text'].s('text:c' => '1') 184 | end 185 | loop_s = loop_s[1..-1] 186 | elsif loop_s[0] == "\n" 187 | Nokogiri::XML::Builder.with(content_node) do |xml| 188 | xml['text'].send(:"line-break") 189 | end 190 | loop_s = loop_s[1..-1] 191 | elsif !!(loop_s =~ /~~~~[~]+/) 192 | callout_num = chunk.match(/~~~~([~]+)/)[1].length.to_s 193 | Nokogiri::XML::Builder.with(content_node) do |xml| 194 | xml['text'].span(callout_num, 'text:style-name' => 'adoc_ic ') 195 | end 196 | next_char_start = callout_num.to_i + 4 197 | loop_s = loop_s[next_char_start..-1] 198 | else 199 | Nokogiri::XML::Builder.with(content_node) do |xml| 200 | xml.text loop_s[0] 201 | end 202 | loop_s = loop_s[1..-1] 203 | end 204 | 205 | end 206 | end 207 | # puts listing_caption_node 208 | end 209 | end 210 | end 211 | 212 | class MyListChunk < BasicHelper 213 | def h_fo_color 214 | re = / hl_r_clr_([0-9a-f]{6}) / 215 | @sd["style:text-properties"]["fo:color"] = "#" + @snr.match(re)[1] if !!(@snr =~ re) 216 | end 217 | def h_fo_background_color 218 | re = / hl_r_bcgclr_([0-9a-f]{6}) / 219 | @sd["style:text-properties"]["fo:background-color"] = "#" + @snr.match(re)[1] if !!(@snr =~ re) 220 | end 221 | def h_fo_font_weight 222 | re = / hl_r_fw_bold / 223 | @sd["style:text-properties"]["fo:font-weight"] = "bold" if !!(@snr =~ re) 224 | end 225 | end 226 | 227 | -------------------------------------------------------------------------------- /lib/a-od-out: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ruby /usr/local/a-od/a-od-producer/asciidoctor-od.rb "$@" 4 | -------------------------------------------------------------------------------- /lib/a-od-pre: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | asciidoctor -b fodt -T /usr/local/a-od/slim "$@" 4 | -------------------------------------------------------------------------------- /lib/a-od-producer/asciidoctor-od.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby -w 2 | require 'nokogiri' 3 | require 'optparse' 4 | require_relative 'misc' 5 | 6 | $aodp_sn_p = "Text_20_body" 7 | $aodp_sn_tp = "Table_20_Contents" 8 | $aodp_sn_tp_head = "Table_20_Heading" 9 | $aodp_sn_tca = "Table" 10 | $aodp_sn_a = "Internet_20_link" 11 | $aodp_sn_a_visited = "Visited_20_Internet_20_Link" 12 | $aodp_sn_h1 = "Heading_20_1" 13 | $aodp_sn_h2 = "Heading_20_2" 14 | $aodp_sn_h3 = "Heading_20_3" 15 | $aodp_sn_h4 = "Heading_20_4" 16 | $aodp_sn_h5 = "Heading_20_5" 17 | $aodp_sn_h6 = "Heading_20_6" 18 | $aodp_sn_h7 = "Heading_20_7" 19 | $aodp_sn_h8 = "Heading_20_8" 20 | $aodp_sn_h1_discrete = "Heading_20_1_20_Discrete" 21 | $aodp_sn_h2_discrete = "Heading_20_2_20_Discrete" 22 | $aodp_sn_h3_discrete = "Heading_20_3_20_Discrete" 23 | $aodp_sn_h4_discrete = "Heading_20_4_20_Discrete" 24 | $aodp_sn_h5_discrete = "Heading_20_5_20_Discrete" 25 | $aodp_sn_h6_discrete = "Heading_20_6_20_Discrete" 26 | $aodp_sn_h7_discrete = "Heading_20_7_20_Discrete" 27 | $aodp_sn_h8_discrete = "Heading_20_8_20_Discrete" 28 | $aodp_sn_appendix = "Appendix" 29 | $aodp_sn_span_emphasis = "Emphasis" 30 | $aodp_sn_span_strong = "Strong_20_Emphasis" 31 | $aodp_sn_span_subscript = "Subscript" 32 | $aodp_sn_span_superscript = "Superscript" 33 | $aodp_sn_span_mark = "Mark" 34 | $aodp_sn_span_monospaced = "Source_20_Text" 35 | $aodp_sn_span_unquoted_small = "Small" 36 | $aodp_sn_span_unquoted_table_small = "Table_20_Small" 37 | $aodp_sn_span_unquoted_big = "Big" 38 | $aodp_sn_span_unquoted_table_big = "Table_20_Big" 39 | $aodp_sn_span_unquoted_underline = "Underline" 40 | $aodp_sn_list_arabic = "Numbering_20_123" 41 | $aodp_sn_list_decimal = "Numbering_20_dec" 42 | $aodp_sn_list_loweralpha = "Numbering_20_abc" 43 | $aodp_sn_list_upperalpha = "Numbering_20_ABC" 44 | $aodp_sn_list_lowerroman = "Numbering_20_ivx" 45 | $aodp_sn_list_upperroman = "Numbering_20_IVX" 46 | $aodp_sn_list_lowergreek = "Numbering_20_lg" 47 | $aodp_sn_list_callouts = "Callouts_20_Numbering" 48 | $aodp_sn_lca = "List_20_Caption" 49 | $aodp_sn_tlca = "Table_20_List_20_Caption" 50 | $aodp_sn_lp = "List" 51 | $aodp_sn_list_disk = "Bullet_20_Disk" 52 | $aodp_sn_list_circle = "Bullet_20_Circle" 53 | $aodp_sn_list_square = "Bullet_20_Square" 54 | $aodp_sn_list_none = "Bullet_20_None" 55 | $aodp_sn_list_no_bullet = "Bullet_20_None" 56 | $aodp_sn_list_default = "Bullet_20_Default" 57 | $aodp_sn_tlp = "Table_20_List" 58 | $aodp_sn_if = "Graphic" 59 | $aodp_sn_bip = "Block_20_Image" 60 | $aodp_100_percent_mm = 170 61 | $aodp_sn_ica = "Figure" 62 | $aodp_inline_height_mm = 5 63 | $aodp_sn_orientation_portrait = "Standard" 64 | $aodp_sn_orientation_landscape = "Landscape" 65 | $aodp_sn_footnote_anchor = "Footnote_20_anchor" 66 | $aodp_stem_dpi = 80 67 | $aodp_sn_sp = "Equation" 68 | $aodp_sn_cfp = "Content_20_Frame_20_Paragraph" 69 | $aodp_sn_tcfp = "Table_20_Content_20_Frame_20_Paragraph" 70 | $aodp_sn_af = "Admonition" 71 | $aodp_sn_ahp = "Admonition_20_Heading" 72 | $aodp_sn_aca = "Admonition_20_Caption" 73 | $aodp_sn_taca = "Table_20_Admonition_20_Caption" 74 | $aodp_sn_tahp = "Table_20_Admonition_20_Heading" 75 | $aodp_sn_ef = "Example" 76 | $aodp_sn_eca = "Example_20_Caption" 77 | $aodp_sn_teca = "Table_20_Example_20_Caption" 78 | $aodp_sn_lica = "Listing_20_Caption" 79 | $aodp_sn_tlica = "Table_20_Listing_20_Caption" 80 | $aodp_sn_lip = "Listing" 81 | $aodp_sn_tlip = "Table_20_Listing" 82 | $aodp_sn_span_callout = "Callout" 83 | $aodp_sn_colp = "Callout_20_List" 84 | $aodp_sn_tcolp = "Table_20_Callout_20_List" 85 | $aodp_sn_span_callout_list_callout_number = "Callout_20_List_20_Callout_20_Number" 86 | $aodp_table_margin_top = "0cm" 87 | $aodp_table_margin_bottom = "0.25cm" 88 | $aodp_ntable_margin_top = "0.1cm" 89 | $aodp_ntable_margin_bottom = "0.1cm" 90 | $aodp_toc_pn_column = "15mm" 91 | $aodp_toc_title_column = "155mm" 92 | $aodp_sn_toc_h1 = "TOC_20_Level_20_1" 93 | $aodp_sn_toc_h2 = "TOC_20_Level_20_2" 94 | $aodp_sn_toc_h3 = "TOC_20_Level_20_3" 95 | $aodp_sn_toc_h4 = "TOC_20_Level_20_4" 96 | $aodp_sn_toc_h5 = "TOC_20_Level_20_5" 97 | $aodp_sn_toc_pn_h1 = "TOC_20_Page_20_Number" 98 | $aodp_sn_toc_pn_h2 = "TOC_20_Page_20_Number" 99 | $aodp_sn_toc_pn_h3 = "TOC_20_Page_20_Number" 100 | $aodp_sn_toc_pn_h4 = "TOC_20_Page_20_Number" 101 | $aodp_sn_toc_pn_h5 = "TOC_20_Page_20_Number" 102 | $aodp_sn_toc_header = "Contents_20_Heading" 103 | $aodp_sn_toc_dots_title = "TOC_20_Dots_20_Title" 104 | $aodp_sn_toc_dots_page_number = "TOC_20_Dots_20_Page_20_Number" 105 | $aodp_sn_toc_dots_space_page_number = "TOC_20_Dots_20_Space_20_Page_20_Number" 106 | $aodp_sn_index_separator = "Index_20_Separator" 107 | $aodp_sn_index_heading = "Zero_20_Height" 108 | $aodp_sn_index_1 = "Index_20_1" 109 | $aodp_sn_index_2 = "Index_20_2" 110 | $aodp_sn_index_3 = "Index_20_3" 111 | $aodp_sn_alphabetical_index_pagenum = "Alphabetical_20_Index_20_Pagenum" 112 | $aodp_assume_src_dpi = 130 113 | $aodp_list_first_level_indent = "12.7mm" 114 | $aodp_list_other_levels_indent = "6.4mm" 115 | $aodp_sn_preamble_before_paragraph = "Preamble_20_Before_20_Paragraph" 116 | 117 | =begin 118 | tag::algorithm_description[] 119 | == Setting predefined styles 120 | 121 | "`You should create documentation in Word, RIGHT WAY`". 122 | 123 | MS Word as well as Open Office suggest that each element should have only one style. That greatly contradicts to the concept of cascaded styles (CSS), but makes things easier. If we can define such a unique style for text or paragraph, then we need nothing more. 124 | 125 | It is always the case with list styles, but sometimes with text or less often paragraph styles. 126 | 127 | StyleSubstitor just sets the predefined style for certain elements. 128 | 129 | Here we may also do any xml preprocessing. Change the order of elements, insert custom elements. As we do it with xml, such preprocessing is extremely fast. 130 | 131 | To extend setting predefined styles routine just make a descendant of `StyleSubstitutor` in your custom library. 132 | 133 | end::algorithm_description[] 134 | =end 135 | 136 | class Initiator 137 | def initialize template 138 | @template = template 139 | h_init_globals_from_template 140 | end 141 | def h_init_globals_from_template 142 | meta_user_defineds = @template.xpath("//meta:user-defined", 143 | 'meta' => 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0' ) 144 | meta_user_defineds.each do |meta_user_defined| 145 | meta_name = meta_user_defined.xpath("@meta:name", 146 | 'meta' => 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0').to_s 147 | text = meta_user_defined.text.to_s 148 | aod_param = "$aodp_#{meta_name.gsub! '-', '_'}" 149 | if eval("defined?(#{aod_param})") == 'global-variable' 150 | eval("#{aod_param} = '#{text}'") 151 | end 152 | end 153 | end 154 | end 155 | 156 | class StyleSubstitutor 157 | def initialize pre, template 158 | @pre = pre 159 | @template = template 160 | if descendants.count == 0 161 | methods.each do |method| 162 | method(method).call if !!(method =~ /^h_/) 163 | end 164 | # If there are subclasses, we instantiate them. Can't make it clever, be it so 165 | else 166 | descendants.each do |descendant| 167 | descendant.new @pre, @template 168 | end 169 | end 170 | end 171 | def descendants 172 | ObjectSpace.each_object(::Class).select {|c| c < self.class} 173 | end 174 | def h_subs_index 175 | index_style_list = @pre.xpath("//text:alphabetical-index-source//*/@text:style-name", 176 | 'text' => 'urn:oasis:names:tc:opendocument:xmlns:text:1.0') 177 | index_style_list.each do |index_style| 178 | snr = " #{index_style} " 179 | index_style.value = $aodp_sn_index_separator if !!(snr =~ / index\_separator /) 180 | index_style.value = $aodp_sn_index_heading if !!(snr =~ / index\_heading /) 181 | index_style.value = $aodp_sn_index_1 if !!(snr =~ / index\_1 /) 182 | index_style.value = $aodp_sn_index_2 if !!(snr =~ / index\_2 /) 183 | index_style.value = $aodp_sn_index_3 if !!(snr =~ / index\_3 /) 184 | index_style.value = $aodp_sn_alphabetical_index_pagenum if !!(snr =~ / alphabetical\_index\_pagenum /) 185 | end 186 | end 187 | def h_subs_lists 188 | list_style_list = @pre.xpath("//text:list/@text:style-name", 189 | 'text' => 'urn:oasis:names:tc:opendocument:xmlns:text:1.0') 190 | list_style_list.each do |list_style| 191 | re = / [ou]lp_s_([a-z-]+) / 192 | substitued_olist_style = " #{list_style} ".match(re)[1].gsub "-", "_" 193 | list_style.value = eval("$aodp_sn_list_#{substitued_olist_style}") 194 | end 195 | end 196 | def h_subs_links 197 | anchor_list = @pre.xpath("//text:a", 198 | 'text' => 'urn:oasis:names:tc:opendocument:xmlns:text:1.0') 199 | anchor_list.each do |anchor| 200 | snr = ' ' + anchor['text:style-name'] + ' ' 201 | if !!(snr =~ / adoc_a /) 202 | if !!(snr =~ / toc\_entry /) 203 | anchor['text:style-name'] = "Index_20_Link" 204 | anchor['text:visited-style-name'] = "Index_20_Link" 205 | else 206 | anchor['text:style-name'] = $aodp_sn_a 207 | anchor['text:visited-style-name'] = $aodp_sn_a_visited 208 | end 209 | end 210 | end 211 | end 212 | def h_subs_footnotes 213 | footnotes_ref_style_list = 214 | @pre.xpath("//text:span/@text:style-name[starts-with(., 'adoc_fra ')]", 215 | 'text' => 'urn:oasis:names:tc:opendocument:xmlns:text:1.0') 216 | footnotes_ref_style_list.each do |footnote_ref_style| 217 | footnote_ref_style.value = $aodp_sn_footnote_anchor 218 | end 219 | end 220 | def h_subs_variable_set_fields 221 | variable_fields = 222 | @template.xpath("//text:variable-set|//text:variable-get", 223 | 'text' => 'urn:oasis:names:tc:opendocument:xmlns:text:1.0') 224 | variable_fields.each do |variable_field| 225 | attr_name = variable_field["text:name"] 226 | if attr_name == 'title' 227 | node_to_insert = @pre.xpath("//a-od-params/title/node()", 228 | 'office' => 'urn:oasis:names:tc:opendocument:xmlns:office:1.0', 229 | 'xlink' => 'http://www.w3.org/1999/xlink') 230 | variable_field.after(node_to_insert) 231 | variable_field.remove 232 | end 233 | if attr_name == 'subtitle' 234 | node_to_insert = @pre.xpath("//a-od-params/subtitle/node()", 235 | 'office' => 'urn:oasis:names:tc:opendocument:xmlns:office:1.0', 236 | 'xlink' => 'http://www.w3.org/1999/xlink') 237 | variable_field.after(node_to_insert) 238 | variable_field.remove 239 | end 240 | pre_attribute = @pre.xpath("//a-od-params/attribute[@name='#{attr_name}']") 241 | unless pre_attribute.count == 0 242 | value = pre_attribute[0]["value"] 243 | unless value.nil? 244 | text_node = Nokogiri::XML::Text.new(value, @template) 245 | variable_field.after(text_node) 246 | variable_field.remove 247 | end 248 | #variable_set_field.content = value unless value.nil? 249 | end 250 | end 251 | end 252 | end 253 | 254 | =begin 255 | tag::algorithm_description[] 256 | == `Automatic styles` calculation 257 | 258 | If you add you custom formatting to any paragraph Open Office simply generates `automatic style` that references parent (predefined style) and adds properties. Thee properties add or replace predefined style formatting. 259 | 260 | Templating technology can't generate styles, but this converter adds formatting hints to the name of the style. `AutoStyleSetter` transforms this hint into `automatic styles`. 261 | 262 | `BasicPropSetSorter` chooses the right property setter class and property setter class sets parent (predefined) class and additional properties. 263 | 264 | To extend style substitution just inherit from any property setter class, for example `BasicBlockImageParagraph`. Or from the basic sorter class `BasicPropSetSorter`. 265 | 266 | All methods should start with `h_`. 267 | 268 | end::algorithm_description[] 269 | =end 270 | 271 | class AutoStyleSetter 272 | def initialize(style_rep, contents) 273 | @style_rep = style_rep 274 | @contents = contents 275 | init_style_types 276 | end 277 | def init_style_types 278 | @style_types = 279 | { 280 | "graphic": { 281 | "applies": ["draw:frame"], 282 | "name_attribute_prefix": "draw", 283 | "property_types": [ 284 | "style:graphic-properties" 285 | ] 286 | }, 287 | "text": { 288 | "applies": ["text:a", "text:span"], 289 | "name_attribute_prefix": "text", 290 | "property_types": [ 291 | "style:text-properties" 292 | ] 293 | }, 294 | "paragraph": { 295 | "applies": ["text:p", "text:h"], 296 | "name_attribute_prefix": "text", 297 | "property_types": [ 298 | "style:paragraph-properties", 299 | "style:text-properties" 300 | ] 301 | }, 302 | "table": { 303 | "applies": ["table:table"], 304 | "name_attribute_prefix": "table", 305 | "property_types": [ 306 | "style:table-properties" 307 | ] 308 | }, 309 | "table-column": { 310 | "applies": ["table:table-column"], 311 | "name_attribute_prefix": "table", 312 | "property_types": [ 313 | "style:table-column-properties" 314 | ] 315 | }, 316 | "table-row": { 317 | "applies": ["table:table-row"], 318 | "name_attribute_prefix": "table", 319 | "property_types": [ 320 | "style:table-row-properties" 321 | ] 322 | }, 323 | "table-cell": { 324 | "applies": ["table:table-cell"], 325 | "name_attribute_prefix": "table", 326 | "property_types": [ 327 | "style:table-cell-properties" 328 | ] 329 | } 330 | } 331 | end 332 | def setAutoStyles 333 | @style_types.each do |type, definition| 334 | definition[:applies].each do |applied_tag| 335 | nodes = @contents.xpath("//#{applied_tag}/@#{definition[:name_attribute_prefix]}:style-name|//#{applied_tag}/@#{definition[:name_attribute_prefix]}:visited-style-name", 336 | 'table' => 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', 337 | 'text' => 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', 338 | 'draw' => 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0' 339 | ) 340 | nodes.each do |node| 341 | if !!("#{node}" =~ /^adoc_/) and not @style_rep.key?("#{node}") 342 | @style_rep["#{node}"] = {applies: applied_tag, type: "#{type}"} 343 | definition[:property_types].each do |property_type| 344 | @style_rep["#{node}"][property_type] = {} 345 | end 346 | BasicPropSetSorter.new "#{node}", @style_rep["#{node}"] 347 | end 348 | node.value = MiscMethods.normilize_style_name node.value 349 | end 350 | end 351 | end 352 | end 353 | end 354 | 355 | class BasicHelper 356 | def initialize(sn, sd) 357 | @sn = sn 358 | @snr = " #{sn} " 359 | @sd = sd 360 | # If no subclasses, we just run its methods 361 | if descendants.count == 0 362 | methods.each do |method| 363 | method(method).call if !!(method =~ /^h_/) 364 | end 365 | # If there are subclasses, we instantiate them. Can't make it clever, be it so 366 | else 367 | descendants.each do |descendant| 368 | descendant.new @sn, @sd 369 | end 370 | end 371 | end 372 | def descendants 373 | ObjectSpace.each_object(::Class).select {|c| c < self.class} 374 | end 375 | end 376 | 377 | class BasicPropSetSorter < BasicHelper 378 | def h_basic_table; BasicTable.new(@sn, @sd) if 379 | !!(@sn =~ /^adoc_t[ ]/) end 380 | def h_basic_table_column; BasicTableColumn.new(@sn, @sd) if 381 | !!(@sn =~ /^adoc_tc[ ]/) end 382 | def h_basic_table_row; BasicTableRow.new(@sn, @sd) if 383 | !!(@sn =~ /^adoc_tr[ ]/) end 384 | def h_basic_table_cell; BasicTableCell.new(@sn, @sd) if 385 | !!(@sn =~ /^adoc_tce[ ]/) end 386 | def h_basic_paragraph 387 | if !!(@sn =~ /^adoc_p[ ]/) 388 | if !!(@snr =~ / in_cell_[0-9] /) and !!(@snr =~ / in_list_item_[0-9] /) 389 | BasicTableListParagraph.new(@sn, @sd) 390 | elsif !!(@snr =~ / in_cell_[0-9] /) 391 | BasicTableParagraph.new(@sn, @sd) 392 | elsif !!(@snr =~ / in_list_item_[0-9] /) 393 | BasicListParagraph.new(@sn, @sd) 394 | else 395 | BasicParagraph.new(@sn, @sd) 396 | end 397 | end 398 | end 399 | def h_basic_blockimage_paragraph; BasicBlockImageParagraph.new(@sn, @sd) if 400 | !!(@sn =~ /^adoc_bip[ ]/) end 401 | def h_basic_table_caption; BasicTableCaption.new(@sn, @sd) if 402 | !!(@sn =~ /^adoc_tca[ ]/) end 403 | def h_basic_header; BasicSectionHeader.new(@sn, @sd) if 404 | !!(@sn =~ /^adoc_s[ ]/) end 405 | def h_basic_inline_quoted; BasicInlineQuoted.new(@sn, @sd) if 406 | !!(@sn =~ /^adoc_iq[ ]/) end 407 | def h_basic_list_caption; BasicListCaption.new(@sn, @sd) if 408 | !!(@sn =~ /^adoc_lca[ ]/) end 409 | def h_basic_image_frame; BasicImageFrame.new(@sn, @sd) if 410 | !!(@sn =~ /^adoc_if[ ]/) end 411 | def h_basic_image_caption; BasicImageCaption.new(@sn, @sd) if 412 | !!(@sn =~ /^adoc_ica[ ]/) end 413 | def h_basic_orientation; BasicOrientation.new(@sn, @sd) if 414 | !!(@sn =~ /^adoc_(p|s|tca|ica|lca|eca)[ ]/) end 415 | def h_basic_content_frame; BasicContentFrame.new(@sn, @sd) if 416 | !!(@sn =~ /^adoc_cf[ ]/) end 417 | def h_basic_content_frame_paragraph; BasicContentFrameParagraph.new(@sn, @sd) if 418 | !!(@sn =~ /^adoc_cfp[ ]/) end 419 | def h_basic_admonition_heading_paragraph; BasicAdmonitionHeadingParagraph.new(@sn, @sd) if 420 | !!(@sn =~ /^adoc_ahp[ ]/) end 421 | def h_basic_admonition_caption; BasicAdmonitionCaption.new(@sn, @sd) if 422 | !!(@sn =~ /^adoc_aca[ ]/) end 423 | def h_basic_example_caption; BasicExampleCaption.new(@sn, @sd) if 424 | !!(@sn =~ /^adoc_eca[ ]/) end 425 | def h_basic_listing_caption; BasicListingCaption.new(@sn, @sd) if 426 | !!(@sn =~ /^adoc_lica[ ]/) end 427 | def h_basic_listing_paragraph; BasicListingParagraph.new(@sn, @sd) if 428 | !!(@sn =~ /^adoc_lip[ ]/) end 429 | def h_basic_inline_callout; BasicInlineCallout.new(@sn, @sd) if 430 | !!(@sn =~ /^adoc_ic[ ]/) end 431 | def h_basic_callout_list_item_paragraph; BasicCalloutListItemParagraph.new(@sn, @sd) if 432 | !!(@sn =~ /^adoc_colp[ ]/) end 433 | def h_basic_callout_list_callout_number; BasicCalloutListCalloutNumber.new(@sn, @sd) if 434 | !!(@sn =~ /^adoc_colcn[ ]/) end 435 | def h_basic_toc_table_column; BasicTocTableColumn.new(@sn, @sd) if 436 | !!(@sn =~ /^adoc_tocc[ ]/) end 437 | def h_basic_toc_cell; BasicTocCell.new(@sn, @sd) if 438 | !!(@sn =~ /^adoc_tocce[ ]/) end 439 | def h_basic_toc_paragraph; BasicTocParagraph.new(@sn, @sd) if 440 | !!(@sn =~ /^adoc_tocp[ ]/) end 441 | def h_basic_toc_header; BasicTocHeader.new(@sn, @sd) if 442 | !!(@sn =~ /^adoc_toch[ ]/) end 443 | def h_basic_toc_inline_quoted; BasicTocInlineQuoted.new(@sn, @sd) if 444 | !!(@sn =~ /^adoc_tociq[ ]/) end 445 | def h_basic_preamble_first_paragraph; BasicPreambleFirstParagraph.new(@sn, @sd) if 446 | !!(@sn =~ /^adoc_pr[ ]/) end 447 | end 448 | 449 | 450 | =begin 451 | tag::notimplemented[] 452 | == Table properties 453 | 454 | * `frame` and `grid` are implemented only if (1) both are none, (2) `frame` is `topbot` and `grid` is `rows`, (3) `frame` is `sides` and `grid` is `cols` 455 | * `float` 456 | * `width` (always `100%`) 457 | * `options = autowidth` 458 | 459 | end::notimplemented[] 460 | =end 461 | 462 | class BasicTable < BasicHelper 463 | def h_general 464 | @sd["style:table-properties"]["style:rel-width"] = "100%" 465 | end 466 | def h_style_may_break_between_rows 467 | @sd["style:table-properties"]["style:may-break-between-rows"] = 468 | "false" if !!(@snr =~ / tp_o_unbreakable /) 469 | end 470 | def h_style_keep_with_next 471 | @sd["style:table-properties"]["fo:keep-with-next"] = 472 | "always" if !!(@snr =~ / keep_with_next /) 473 | end 474 | def h_fo_margins 475 | @sd["style:table-properties"]["fo:margin-top"] = $aodp_table_margin_top 476 | @sd["style:table-properties"]["fo:margin-bottom"] = $aodp_table_margin_bottom unless 477 | (@snr =~ / no_margin_bottom /) 478 | if !!(@snr =~ / in_cell_[0-9] /) 479 | @sd["style:table-properties"]["fo:margin-top"] = $aodp_ntable_margin_top 480 | @sd["style:table-properties"]["fo:margin-bottom"] = $aodp_ntable_margin_bottom unless 481 | (@snr =~ / no_margin_bottom /) 482 | end 483 | end 484 | end 485 | 486 | 487 | class BasicTableColumn < BasicHelper 488 | def h_style_rel_column_width 489 | re = / tcp_rcw_([\d]+) / 490 | @sd["style:table-column-properties"]["style:rel-column-width"] = 491 | @snr.match(re)[1] + '00*' if !!(@snr =~ re) 492 | end 493 | end 494 | 495 | class BasicTableRow < BasicHelper 496 | def h_set_row_keep_together 497 | @sd["style:table-row-properties"]["fo:keep-together"] = 498 | "always" if !!(@snr =~ / row_keep_together /) 499 | end 500 | end 501 | 502 | class BasicTableCell < BasicHelper 503 | def h_general 504 | @sd["style:table-cell-properties"]["fo:padding"] = "0.1cm" 505 | end 506 | def h_fo_border 507 | if !!(@snr =~ / tp_f_none /) and !!(@snr =~ / tp_g_none /) 508 | elsif !!(@snr =~ / tp_f_topbot /) and !!(@snr =~ / tp_g_rows /) 509 | @sd["style:table-cell-properties"]["fo:border-top"] = "0.5pt solid #000000" 510 | @sd["style:table-cell-properties"]["fo:border-bottom"] = "0.5pt solid #000000" 511 | elsif !!(@snr =~ / tp_f_sides /) and !!(@snr =~ / tp_g_cols /) 512 | @sd["style:table-cell-properties"]["fo:border-right"] = "0.5pt solid #000000" 513 | @sd["style:table-cell-properties"]["fo:border-left"] = "0.5pt solid #000000" 514 | else 515 | @sd["style:table-cell-properties"]["fo:border"] = "0.5pt solid #000000" 516 | end 517 | end 518 | def h_style_vertical_align 519 | re = / tcep_va_([a-z]+) / 520 | @sd["style:table-cell-properties"]["style:vertical-align"] = 521 | @snr.match(re)[1] if !!(@snr =~ re) 522 | end 523 | def h_fo_background_color 524 | @sd["style:table-cell-properties"]["fo:background-color"] = 525 | "#f7f7f8" if !!(@snr =~ /( head )|( foot)/) 526 | @sd["style:table-cell-properties"]["fo:background-color"] = 527 | "#f7f7f8" if !!(@snr =~ / tcep_s_header /) 528 | end 529 | end 530 | 531 | class BasicParagraph < BasicHelper 532 | def h_parent_style_name 533 | @sd[:parent_style_name] = $aodp_sn_p 534 | end 535 | end 536 | 537 | class BasicTableParagraph < BasicHelper 538 | def h_parent_style_name 539 | re = /( head )|( tcep_s_header )/ 540 | if !!(@snr =~ re) 541 | @sd[:parent_style_name] = $aodp_sn_tp_head 542 | else 543 | @sd[:parent_style_name] = $aodp_sn_tp 544 | end 545 | end 546 | def h_fo_text_align 547 | # First cell properties 548 | re = / tcep_ha_([a-z]+) / 549 | @sd["style:paragraph-properties"]["fo:text-align"] = 550 | @snr.match(re)[1] if !!(@snr =~ re) 551 | # Then custom properties 552 | re = / text-align-([a-z]+) / 553 | @sd["style:paragraph-properties"]["fo:text-align"] = 554 | @snr.match(re)[1] if !!(@snr =~ re) 555 | end 556 | end 557 | 558 | class BasicTableCaption < BasicHelper 559 | def h_parent_style_name 560 | @sd[:parent_style_name] = $aodp_sn_tca 561 | end 562 | end 563 | 564 | class BasicSectionHeader < BasicHelper 565 | def h_parent_style_name 566 | re = / sp_sl_([1-8]) / 567 | section_level = @snr.match(re)[1] 568 | if !!(@snr =~ / sp_sn_appendix /) 569 | @sd[:parent_style_name] = $aodp_sn_appendix 570 | else 571 | if !!(@snr =~ / sp_s_discrete /) 572 | @sd[:parent_style_name] = eval("$aodp_sn_h#{section_level}_discrete") 573 | else 574 | @sd[:parent_style_name] = eval("$aodp_sn_h#{section_level}") 575 | end 576 | end 577 | end 578 | end 579 | 580 | class BasicInlineQuoted < BasicHelper 581 | def h_parent_style_name 582 | re = / t_([a-z]+) / 583 | type = @snr.match(re)[1] 584 | if type != "unquoted" 585 | @sd[:parent_style_name] = eval("$aodp_sn_span_#{type}") 586 | else 587 | ['small', 'big', 'underline'].each do |unquoted_type| 588 | if !!(@snr =~ / #{unquoted_type} /) 589 | @sd[:parent_style_name] = eval("$aodp_sn_span_unquoted_#{unquoted_type}") 590 | @sd[:parent_style_name] = eval("$aodp_sn_span_unquoted_table_#{unquoted_type}") if 591 | !!(@snr =~ / in_cell_[0-9] /) and ['small', 'big'].include? unquoted_type 592 | end 593 | end 594 | end 595 | end 596 | end 597 | 598 | 599 | class BasicListCaption < BasicHelper 600 | def h_parent_style_name 601 | @sd[:parent_style_name] = $aodp_sn_lca 602 | @sd[:parent_style_name] = $aodp_sn_tlca if !!(@snr =~ / in_cell_[0-9] /) 603 | end 604 | end 605 | 606 | class BasicListParagraph < BasicHelper 607 | def h_parent_style_name 608 | @sd[:parent_style_name] = $aodp_sn_lp 609 | end 610 | end 611 | 612 | class BasicTableListParagraph < BasicHelper 613 | def h_parent_style_name 614 | @sd[:parent_style_name] = $aodp_sn_tlp 615 | end 616 | end 617 | 618 | class BasicBlockImageParagraph < BasicHelper 619 | def h_parent_style_name 620 | @sd[:parent_style_name] = $aodp_sn_bip 621 | @sd[:parent_style_name] = $aodp_sn_sp if !!(@snr =~ / ip_s_amp /) 622 | end 623 | end 624 | 625 | =begin 626 | tag::plusfeatures[] 627 | == Image attributes 628 | 629 | * fitrect -- dimensions to fit image in like `100x50mm`. Only mm unit is supported 630 | * srcdpi -- resolution of the source image. Usually when we add something to our diagram (like new process to the process diagram), the dimensions of the image change, but resolution doesn't change. Default -- 100 dpi. 631 | * svgunit -- units, in which svg dimensions are defined 632 | 633 | This attribute doesn't eliminate the need to have a set of image for each resolution, but for simple situation it is quite enough. 634 | 635 | If `fitrect` is not defined in inline images it is assumed from the following attributes: 636 | 637 | * def_100_percent_mm -- width 638 | * def_inline_height_mm -- height 639 | end::plusfeatures[] 640 | =end 641 | 642 | class BasicImageFrame < BasicHelper 643 | def h_parent_style_name 644 | @sd[:parent_style_name] = $aodp_sn_if 645 | end 646 | def h_svg_width_height 647 | if !!(@snr =~ / ip_ibt_inline /) and !!(@snr !~ / ip_w_/) and !!(@snr !~ / ip_fr_/) 648 | @snr += "ip_fr_#{$aodp_100_percent_mm}x#{$aodp_inline_height_mm}mm " 649 | end 650 | 651 | if !!(@snr =~ / ip_s_amp /) 652 | # maximum as a square with max percent for formulae looks quite logical 653 | # at least when writing this comment 654 | @snr += "ip_fr_#{$aodp_100_percent_mm}x#{$aodp_100_percent_mm}mm ip_sd_#{$aodp_stem_dpi} " 655 | end 656 | 657 | re = / ip_w_([0-9\.]+[a-z_]+) / 658 | unless !!(@snr =~ re) 659 | img_ew = "70_prc" 660 | else 661 | img_ew = @snr.match(re)[1] 662 | end 663 | 664 | 665 | 666 | re = / ip_t_([a-z]+) / 667 | type = @snr.match(re)[1] 668 | 669 | re = / ip_x2y_([0-9]+)x([0-9]+) / 670 | img_rw = @snr.match(re)[1].to_f 671 | img_rh = @snr.match(re)[2].to_f 672 | 673 | re = / ip_fr_([0-9\.]+x[0-9\.]+[a-z]+) / 674 | if !!(@snr =~ re) 675 | fitrect = MiscMethods.get_normalized_fitrect_attribute @snr.match(re)[1] 676 | re = /([0-9\.]+)x([0-9\.]+)/ 677 | rect_w = fitrect.match(re)[1].to_f 678 | rect_h = fitrect.match(re)[2].to_f 679 | end 680 | 681 | re = / ip_sd_([0-9]+) / 682 | srcdpi = $aodp_assume_src_dpi 683 | srcdpi = @snr.match(re)[1].to_f if !!(@snr =~ re) 684 | 685 | re = / ip_su_([a-z]+) / 686 | svgunit = 'px' 687 | svgunit = @snr.match(re)[1] if !!(@snr =~ re) 688 | 689 | # if guessing then use fictrect strategy with a 100% square minus list indent 690 | # todo should count 100% from parent inner width 691 | guess_width = false 692 | if !!(@snr !~ / ip_fr_/) && !!(@snr !~ / ip_w_[0-9]/) 693 | list_level = 0 694 | re = / in_list_item_([0-9]) / 695 | list_level = @snr.match(re)[1].to_f if !!(@snr =~ re) 696 | list_first_level_indent_mm = MiscMethods.text_measurement_to_mm $aodp_list_first_level_indent 697 | list_other_levels_indent_mm = MiscMethods.text_measurement_to_mm $aodp_list_other_levels_indent 698 | rect_w = $aodp_100_percent_mm 699 | rect_h = $aodp_100_percent_mm 700 | if list_level > 0 701 | rect_w = rect_w - list_first_level_indent_mm - (list_level - 1) * list_other_levels_indent_mm 702 | end 703 | guess_width = true 704 | end 705 | 706 | 707 | if !!(@snr !~ / ip_fr_/) && !guess_width 708 | re = /([0-9\.]+)([a-z_]+)/ 709 | img_cw = img_ew.match(re)[1].to_f 710 | img_cunit = img_ew.match(re)[2] 711 | if img_cunit == "_px" 712 | img_cw = img_cw / srcdpi * 25.4 713 | img_cunit = "mm" 714 | elsif img_cunit == "_prc" 715 | img_cw = ($aodp_100_percent_mm * img_cw/100).round(1) 716 | img_cunit = "mm" 717 | end 718 | y2x_ratio = 100.0 719 | y2x_ratio = img_rh / img_rw * 100.0 720 | img_ch = (img_cw * y2x_ratio.to_f / 100).round(1) 721 | @sd["style:graphic-properties"]["svg:width"] = "#{img_cw}#{img_cunit}" 722 | @sd["style:graphic-properties"]["svg:height"] = "#{img_ch}#{img_cunit}" 723 | else 724 | if type == "svg" and svgunit != 'px' 725 | img_rw = MiscMethods.measurement_to_mm(img_rw, svgunit) 726 | img_rh = MiscMethods.measurement_to_mm(img_rh, svgunit) 727 | else 728 | img_rw = img_rw / srcdpi * 25.4 729 | img_rh = img_rh / srcdpi * 25.4 730 | end 731 | optimal_dimensions = MiscMethods.get_optimal_dimensions(img_rw, img_rh, rect_w, rect_h) 732 | @sd["style:graphic-properties"]["svg:width"] = "#{optimal_dimensions[:img_cw]}mm" 733 | @sd["style:graphic-properties"]["svg:height"] = "#{optimal_dimensions[:img_ch]}mm" 734 | end 735 | end 736 | def h_style_vertical_pos 737 | @sd["style:graphic-properties"]["style:vertical-pos"] = 738 | "middle" 739 | end 740 | def h_style_vertical_rel 741 | @sd["style:graphic-properties"]["style:vertical-rel"] = 742 | "text" 743 | end 744 | end 745 | 746 | class BasicImageCaption < BasicHelper 747 | def h_parent_style_name 748 | @sd[:parent_style_name] = $aodp_sn_ica 749 | end 750 | end 751 | 752 | 753 | =begin 754 | tag::plusfeatures[] 755 | 756 | == Page orientation 757 | 758 | Page orientation is regulated by special roles: portrait, landscape. 759 | 760 | The role can be applied to paragraph, section and caption (table, figure, list, example) elements. 761 | 762 | NOTE: Page orientation roles switch orientation for all elements to the end of the document. It can be switched back starting at any supported element 763 | 764 | end::plusfeatures[] 765 | =end 766 | 767 | class BasicOrientation < BasicHelper 768 | def h_master_page_name 769 | re = / (portrait|landscape) / 770 | @sd[:master_page_name] = eval("$aodp_sn_orientation_#{@snr.match(re)[1]}") if !!(@snr =~ re) 771 | end 772 | end 773 | 774 | =begin 775 | tag::notimplemented[] 776 | 777 | == Frame usage 778 | 779 | Admonitions and examples are created with the help of a frame. 780 | 781 | Frame width in Open Document format can't be defined in relation to paragraph, only paragraph area. So in lists frames will start from the left page margin. 782 | 783 | As frames are aligned to right, it is possible to introduce some attribute that would decrease frame width. The example is in the `a-od-my` custom library of this project: list-level1-admonition. 784 | 785 | IMPORTANT: Frames don't flow across pages 786 | 787 | end::notimplemented[] 788 | =end 789 | 790 | class BasicContentFrame < BasicHelper 791 | def h_parent_style_name 792 | @sd[:parent_style_name] = $aodp_sn_af if !!(@snr =~ / cft_admonition /) 793 | @sd[:parent_style_name] = $aodp_sn_ef if !!(@snr =~ / cft_example /) 794 | end 795 | def h_style_vertical_pos 796 | @sd["style:graphic-properties"]["style:vertical-pos"] = "from-top" 797 | @sd["style:graphic-properties"]["style:vertical-rel"] = "paragraph-content" 798 | @sd["style:graphic-properties"]["text:anchor-type"] = "paragraph" 799 | @sd["style:graphic-properties"]["style:flow-with-text"] = "true" 800 | @sd["style:graphic-properties"]["style:wrap"] = "none" 801 | end 802 | def h_horizontal_pos 803 | @sd["style:graphic-properties"]["style:horizontal-pos"] = "right" 804 | @sd["style:graphic-properties"]["style:horizontal-rel"] = "paragraph" 805 | @sd["style:graphic-properties"]["style:rel-width"] = "100%" 806 | #@sd["style:graphic-properties"]["svg:width"] = $aodp_100_percent_mm 807 | end 808 | end 809 | 810 | 811 | class BasicContentFrameParagraph < BasicHelper 812 | def h_parent_style_name 813 | @sd[:parent_style_name] = $aodp_sn_cfp 814 | @sd[:parent_style_name] = $aodp_sn_tcfp if !!(@snr =~ / in_cell_[0-9] /) 815 | end 816 | end 817 | 818 | class BasicAdmonitionHeadingParagraph < BasicHelper 819 | def h_parent_style_name 820 | @sd[:parent_style_name] = $aodp_sn_ahp 821 | @sd[:parent_style_name] = $aodp_sn_tahp if !!(@snr =~ / in_cell_[0-9] /) 822 | end 823 | end 824 | 825 | class BasicAdmonitionCaption < BasicHelper 826 | def h_parent_style_name 827 | @sd[:parent_style_name] = $aodp_sn_aca 828 | @sd[:parent_style_name] = $aodp_sn_taca if !!(@snr =~ / in_cell_[0-9] /) 829 | end 830 | end 831 | 832 | class BasicExampleCaption < BasicHelper 833 | def h_parent_style_name 834 | @sd[:parent_style_name] = $aodp_sn_eca 835 | @sd[:parent_style_name] = $aodp_sn_teca if !!(@snr =~ / in_cell_[0-9] /) 836 | end 837 | end 838 | 839 | class BasicListingCaption < BasicHelper 840 | def h_parent_style_name 841 | @sd[:parent_style_name] = $aodp_sn_lica 842 | @sd[:parent_style_name] = $aodp_sn_tlica if !!(@snr =~ / in_cell_[0-9] /) 843 | end 844 | end 845 | 846 | class BasicListingParagraph < BasicHelper 847 | def h_parent_style_name 848 | @sd[:parent_style_name] = $aodp_sn_lip 849 | @sd[:parent_style_name] = $aodp_sn_tlip if !!(@snr =~ / in_cell_[0-9] /) 850 | end 851 | end 852 | 853 | class BasicInlineCallout < BasicHelper 854 | def h_parent_style_name 855 | @sd[:parent_style_name] = eval("$aodp_sn_span_callout") 856 | end 857 | end 858 | 859 | class BasicCalloutListItemParagraph < BasicHelper 860 | def h_parent_style_name 861 | @sd[:parent_style_name] = $aodp_sn_colp 862 | @sd[:parent_style_name] = $aodp_sn_tcolp if !!(@snr =~ / in_cell_[0-9] /) 863 | end 864 | end 865 | 866 | class BasicCalloutListCalloutNumber < BasicHelper 867 | def h_parent_style_name 868 | @sd[:parent_style_name] = $aodp_sn_span_callout_list_callout_number 869 | end 870 | end 871 | 872 | 873 | class BasicTocTableColumn < BasicHelper 874 | def h_style_column_width 875 | re = / sec_pn / 876 | @sd["style:table-column-properties"]["style:column-width"] = 877 | "#{$aodp_toc_pn_column}" if !!(@snr =~ re) 878 | re = / sec_title / 879 | @sd["style:table-column-properties"]["style:column-width"] = 880 | "#{$aodp_toc_title_column}" if !!(@snr =~ re) 881 | end 882 | end 883 | 884 | class BasicTocCell < BasicHelper 885 | def h_style_vertical_align 886 | re = / sec_pn / 887 | @sd["style:table-cell-properties"]["style:vertical-align"] = "bottom" if !!(@snr =~ re) 888 | end 889 | end 890 | 891 | class BasicTocParagraph < BasicHelper 892 | def h_parent_style_name 893 | re = / slevel\_([0-9]) / 894 | slevel = @snr.match(re)[1] 895 | @sd[:parent_style_name] = eval("$aodp_sn_toc_h#{slevel}") 896 | re = / sec_pn / 897 | @sd[:parent_style_name] = eval("$aodp_sn_toc_pn_h#{slevel}") if !!(@snr =~ re) 898 | end 899 | def h_style_margin_left 900 | re = / sec_pn / 901 | @sd["style:paragraph-properties"]["fo:margin-left"] = "0mm" if !!(@snr =~ re) 902 | end 903 | end 904 | 905 | class BasicTocHeader < BasicHelper 906 | def h_parent_style_name 907 | @sd[:parent_style_name] = eval("$aodp_sn_toc_header") 908 | end 909 | end 910 | 911 | class BasicTocInlineQuoted < BasicHelper 912 | def h_parent_style_name 913 | @sd[:parent_style_name] = $aodp_sn_toc_dots_title if !!(@snr =~ / dots_title /) 914 | @sd[:parent_style_name] = $aodp_sn_toc_dots_page_number if !!(@snr =~ / dots_page_number /) 915 | @sd[:parent_style_name] = $aodp_sn_toc_dots_space_page_number if !!(@snr =~ / dots_space_page_number /) 916 | end 917 | end 918 | 919 | class BasicPreambleFirstParagraph < BasicHelper 920 | def h_parent_style_name 921 | @sd[:parent_style_name] = $aodp_sn_preamble_before_paragraph if !!(@snr =~ / before-paragraph /) 922 | end 923 | end 924 | 925 | class StyleToXml 926 | def self.to_doc style_rep, xml_node 927 | style_rep.each do |sn, sd| 928 | #puts sn, sd if !!(sn =~ /^adoc_tce /) # main debug 929 | style_node = Nokogiri::XML::Node.new "style:style", xml_node.document 930 | style_node["style:name"] = MiscMethods.normilize_style_name sn 931 | style_node["style:family"] = sd[:type] 932 | style_node["style:parent-style-name"] = 933 | sd[:"parent_style_name"] if sd.key?(:"parent_style_name") 934 | style_node["style:master-page-name"] = 935 | sd[:"master_page_name"] if sd.key?(:"master_page_name") 936 | sd.each do |sd_attribute, sd_attribute_value| 937 | if not [:applies, :type, :parent_style_name, :master_page_name].include?(sd_attribute) 938 | style_family = sd_attribute.match(/:(.*)/)[1] 939 | property_node = Nokogiri::XML::Node.new "style:#{style_family}", xml_node.document 940 | style_node.add_child property_node 941 | sd_attribute_value.each do |property_attribute, value| 942 | property_node[property_attribute] = value 943 | end 944 | end 945 | end 946 | xml_node.add_child style_node 947 | end 948 | end 949 | end 950 | 951 | custom_processors = [] 952 | input_file = "" 953 | output_file = "" 954 | template_file = "#{__dir__}/basic-template.fodt" 955 | 956 | OptionParser.new do |parser| 957 | parser.on("-c", "--custom LIBRARY", 958 | "Require the custom processor (several processors are allowed)") do |p_custom_processor| 959 | custom_processors << p_custom_processor 960 | end 961 | parser.on("-i", "--input FILE", 962 | "Input file") do |p_input_file| 963 | input_file = p_input_file 964 | end 965 | parser.on("-o", "--output FILE", 966 | "Output file") do |p_output_file| 967 | output_file = p_output_file 968 | end 969 | parser.on("-f", "--template FILE", 970 | "Template .fodt file") do |p_template_file| 971 | template_file = p_template_file 972 | end 973 | parser.on("-h", "--help", "Prints this help") do 974 | puts opts 975 | exit 976 | end 977 | end.parse! 978 | 979 | custom_processors.each do |cp| 980 | cp = "./#{cp}" if cp[0] != '/' 981 | require cp 982 | end 983 | 984 | template = File.open(template_file) { |f| Nokogiri::XML(f) } 985 | pre = File.open(input_file) { |f| Nokogiri::XML(f) } 986 | 987 | Initiator.new(template) 988 | StyleSubstitutor.new(pre, template) 989 | 990 | tagged_nodes = 991 | template.xpath("/*/*/office:text/*[descendant-or-self::*[contains(text(),'asciidoc-od')]" + 992 | " and local-name() != 'table-of-content']", 993 | "office" => "urn:oasis:names:tc:opendocument:xmlns:office:1.0") 994 | body_to_insert = 995 | pre.xpath("/office:body/office:text/*", 996 | 'office' => 'urn:oasis:names:tc:opendocument:xmlns:office:1.0', 997 | 'xlink' => 'http://www.w3.org/1999/xlink') 998 | tagged_nodes[0].add_previous_sibling(body_to_insert) if tagged_nodes.count > 0 999 | done = false 1000 | tagged_nodes[0].xpath("following-sibling::*").each do |node| 1001 | done = 1002 | true if node.xpath("descendant-or-self::*[contains(text(),'asciidoc-od')]").count > 0 1003 | node.remove 1004 | break if done 1005 | end 1006 | 1007 | tagged_nodes[0].remove 1008 | 1009 | AutoStyleSetter.new(style_rep = Hash.new, template).setAutoStyles 1010 | StyleToXml.to_doc style_rep, 1011 | template.xpath("//office:automatic-styles", 1012 | "office" => "urn:oasis:names:tc:opendocument:xmlns:office:1.0").first 1013 | 1014 | File.write(output_file, template.to_xml) 1015 | 1016 | 1017 | 1018 | 1019 | -------------------------------------------------------------------------------- /lib/a-od-producer/basic-template.fodt: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:536a38b93fedfab5fda10feffcdb635368f2eda9b328c59b942ff42f1afd877b 3 | size 342750 4 | -------------------------------------------------------------------------------- /lib/a-od-producer/misc.rb: -------------------------------------------------------------------------------- 1 | class MiscMethods 2 | def self.text_measurement_to_mm text_measurement 3 | re = /^([0-9]+[\.]?[0-9]*)([a-z]+)$/ 4 | if !!(text_measurement =~ re) 5 | measurement = text_measurement.match(re)[1].to_f 6 | unit = text_measurement.match(re)[2] 7 | else 8 | raise "\"#{text_measurement}\" is not a text measurement value" 9 | end 10 | measurement_to_mm measurement, unit 11 | end 12 | def self.measurement_to_mm measurement, unit 13 | coef = {mm: 1.0, 14 | cm: 10.0, 15 | pt: 0.352778, 16 | pc: 4.23333, 17 | in: 25.4} 18 | raise "Unit #{unit} is unsupported" unless coef.key?(unit.to_sym) 19 | measurement.to_f * coef[unit.to_sym] 20 | end 21 | def self.get_optimal_dimensions img_rw, img_rh, rect_w, rect_h 22 | if img_rw <= 0 or img_rh <= 0 or rect_w <= 0 or rect_h <= 0 23 | {img_cw: 0, img_ch: 0} 24 | elsif img_rw.to_f <= rect_w and img_rh <= rect_h 25 | {img_cw: img_rw, img_ch: img_rh} 26 | else 27 | ratio = rect_w.to_f / img_rw.to_f 28 | ratio = rect_h.to_f / img_rh.to_f if ratio > rect_h.to_f / img_rh.to_f 29 | {img_cw: img_rw.to_f * ratio, img_ch: img_rh.to_f * ratio} 30 | end 31 | end 32 | def self.get_normalized_fitrect_attribute fitrect_attribute 33 | re = /[0-9]+x[0-9]+mm/ 34 | raise "Rectfit is accepted only in millimeters, like 20x10mm" if !!(fitrect_attribute !~ re) 35 | fitrect_attribute 36 | end 37 | def self.normilize_style_name style_name 38 | style_name.gsub(/ /,"_20_") 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/build-lo-kts.sh: -------------------------------------------------------------------------------- 1 | pushd "$(dirname "$0")" 2 | 3 | cp $(kscript -p lo-kts-converter.main.kts 2> >(grep -o "/.*")).jar . 4 | 5 | popd 6 | -------------------------------------------------------------------------------- /lib/lo-kts-converter.main.jar: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:16abd5689dd75c603a55fa00a662b110ec52bbeb60ce2319a849926575419547 3 | size 4088861 4 | -------------------------------------------------------------------------------- /lib/lo-kts-converter.main.kts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env kotlin 2 | 3 | @file:DependsOn("org.libreoffice:juh:7.4.7") 4 | @file:DependsOn("org.libreoffice:unoil:7.4.7") 5 | @file:DependsOn("org.libreoffice:ridl:7.4.7") 6 | @file:DependsOn("org.libreoffice:libreoffice:7.4.7") 7 | @file:DependsOn("org.libreoffice:unoloader:7.4.7") 8 | @file:DependsOn("org.libreoffice:jurt:7.4.7") 9 | @file:DependsOn("com.xenomachina:kotlin-argparser:2.0.7") 10 | 11 | import com.sun.star.beans.PropertyValue 12 | import com.sun.star.beans.XPropertySet 13 | import com.sun.star.bridge.XBridgeFactory 14 | import com.sun.star.comp.helper.Bootstrap 15 | import com.sun.star.connection.XConnection 16 | import com.sun.star.connection.XConnector 17 | import com.sun.star.frame.XComponentLoader 18 | import com.sun.star.frame.XDesktop 19 | import com.sun.star.frame.XStorable 20 | import com.sun.star.lang.XComponent 21 | import com.sun.star.lang.XMultiComponentFactory 22 | import com.sun.star.text.XDocumentIndex 23 | import com.sun.star.text.XDocumentIndexesSupplier 24 | import com.sun.star.text.XTextDocument 25 | import com.sun.star.uno.UnoRuntime 26 | import com.sun.star.uno.XComponentContext 27 | import com.xenomachina.argparser.ArgParser 28 | import com.xenomachina.argparser.default 29 | import com.xenomachina.argparser.mainBody 30 | import java.io.File 31 | import java.io.IOException 32 | import kotlin.system.exitProcess 33 | 34 | class MyArgs(parser: ArgParser) { 35 | val pInputDoc by parser.storing( 36 | "-i", "--input-doc", help = "Input document") 37 | val pOutputFormats by parser.storing( 38 | "-f", "--output-formats", help = "Comma separated result format list like docx,pdf,odt. Default result format is PDF").default("pdf") 39 | val pLoCliCommand by parser.storing( 40 | "-c", "--lo-cli-command", help = "CLI command to run Libre Office. Default command is 'soffice'.").default("soffice") 41 | } 42 | lateinit var inputDoc: String 43 | lateinit var outputFormats: String 44 | lateinit var loCliCommand: String 45 | 46 | mainBody { 47 | ArgParser(args).parseInto(::MyArgs).run { 48 | inputDoc = pInputDoc 49 | outputFormats = pOutputFormats 50 | loCliCommand = pLoCliCommand 51 | } 52 | } 53 | 54 | val matchedDocBasePath = """.*(?=[\.][a-zA-Z_]+$)""".toRegex().find(inputDoc) 55 | val outputDocBasePath = matchedDocBasePath?.value ?: inputDoc 56 | val xContext = socketContext() 57 | val xMCF: XMultiComponentFactory? = xContext.serviceManager 58 | //val available = if (xMCF != null) "available" else "not available" 59 | val desktop: Any = xMCF!!.createInstanceWithContext("com.sun.star.frame.Desktop", xContext) 60 | val xDeskop = qi(XDesktop::class.java, desktop) 61 | val xComponentLoader = qi(XComponentLoader::class.java, desktop) 62 | val loadProps = arrayOf() 63 | lateinit var component : XComponent 64 | try { 65 | component = xComponentLoader.loadComponentFromURL(fnmToURL(inputDoc), "_blank", 0, loadProps) 66 | } catch (e: Exception) { 67 | println(e) 68 | println("ERROR: Unable to open $inputDoc. If file exists and not corrupted try to delete LibreOffice lock files") 69 | exitProcess(-1) 70 | } 71 | val xTextDocument = qi(XTextDocument::class.java, component) 72 | 73 | // Update indexes 74 | val indexes = qi(XDocumentIndexesSupplier::class.java, xTextDocument) 75 | for (i in 0..indexes.documentIndexes.count - 1) { 76 | val index = qi(XDocumentIndex::class.java, indexes.documentIndexes.getByIndex(i)) 77 | index.update() 78 | } 79 | println("INFO: Indexes updated") 80 | 81 | val xStorable = qi(XStorable::class.java, component) 82 | val saveProps = Array(2) { PropertyValue() } 83 | saveProps[0].Name = "Overwrite" 84 | saveProps[0].Value = true 85 | outputFormats.split(",").forEach { 86 | saveProps[1].Name = "FilterName" 87 | saveProps[1].Value = ext2format(it) 88 | try { 89 | xStorable.storeToURL(fnmToURL("$outputDocBasePath.$it"), saveProps) 90 | println("INFO: $outputDocBasePath.$it stored") 91 | } catch (e: Exception) { 92 | println(e) 93 | println("ERROR: Unable to save $outputDocBasePath.$it. Probably file is locked") 94 | } 95 | } 96 | 97 | xDeskop.terminate() 98 | 99 | fun socketContext(): XComponentContext // use socket connection to Office 100 | // https://forum.openoffice.org/en/forum/viewtopic.php?f=44&t=1014 101 | { 102 | val xcc: XComponentContext? // the remote office component context 103 | try { 104 | val cmdArray = arrayOfNulls(3) 105 | cmdArray[0] = loCliCommand 106 | // requires soffice to be in Windows/Linux PATH env var. 107 | cmdArray[1] = "--headless" 108 | cmdArray[2] = "--accept=socket,host=localhost,port=" + 109 | "8100" + ";urp;" 110 | val p = Runtime.getRuntime().exec(cmdArray) 111 | if (p != null) println("INFO: Office process created") 112 | val localContext = Bootstrap.createInitialComponentContext(null) 113 | // Get the local service manager 114 | val localFactory = localContext.serviceManager 115 | // connect to Office via its socket 116 | val connector: XConnector = qi(XConnector::class.java, 117 | localFactory.createInstanceWithContext( 118 | "com.sun.star.connection.Connector", localContext)) 119 | lateinit var connection: XConnection 120 | var connected = false 121 | var attempts = 10 122 | while (attempts > 0 && !connected) { 123 | try { 124 | connection = connector.connect( 125 | "socket,host=localhost,port=" + "8100") 126 | connected = true 127 | } catch (_: Exception) { 128 | } 129 | delay(500) 130 | attempts -= 1 131 | } 132 | 133 | // create a bridge to Office via the socket 134 | val bridgeFactory: XBridgeFactory = qi(XBridgeFactory::class.java, 135 | localFactory.createInstanceWithContext( 136 | "com.sun.star.bridge.BridgeFactory", localContext)) 137 | 138 | // create a nameless bridge with no instance provider 139 | val bridge = bridgeFactory.createBridge("socketBridgeAD", "urp", connection, null) 140 | 141 | // get the remote service manager 142 | val serviceManager: XMultiComponentFactory = qi(XMultiComponentFactory::class.java, 143 | bridge.getInstance("StarOffice.ServiceManager")) 144 | 145 | // retrieve Office's remote component context as a property 146 | val props: XPropertySet = qi(XPropertySet::class.java, serviceManager) 147 | val defaultContext = props.getPropertyValue("DefaultContext") 148 | 149 | // get the remote interface XComponentContext 150 | xcc = qi(XComponentContext::class.java, defaultContext) 151 | } catch (e: Exception) { 152 | println("ERROR: Unable to socket connect to Office") 153 | exitProcess(-1) 154 | } 155 | return xcc 156 | } // end of socketContext() 157 | 158 | fun delay(ms: Int) { 159 | try { 160 | Thread.sleep(ms.toLong()) 161 | } catch (_: InterruptedException) { 162 | } 163 | } 164 | 165 | // ====== interface object creation wrapper (uses generics) =========== 166 | fun qi(aType: Class?, o: Any?): T // the "Loki" function -- reduces typing 167 | { 168 | return UnoRuntime.queryInterface(aType, o) 169 | } 170 | 171 | fun fnmToURL(fnm: String): String? // convert a file path to URL format 172 | { 173 | return try { 174 | val sb: StringBuffer? 175 | val path = File(fnm).canonicalPath 176 | sb = StringBuffer("file:///") 177 | sb.append(path.replace('\\', '/')) 178 | sb.toString() 179 | } catch (e: IOException) { 180 | println("Could not access $fnm") 181 | null 182 | } 183 | } // end of fnmToURL() 184 | 185 | fun ext2format(ext: String): String { 186 | val format = when (ext) { 187 | "docx" -> "Office Open XML Text" 188 | "pdf" -> "writer_pdf_Export" 189 | "odt" -> "writer8" 190 | "fodt" -> "OpenDocument Text Flat XML" 191 | else -> null 192 | } 193 | if (format == null) { 194 | throw Exception("Output format [$ext] not supported") 195 | } else { 196 | return format 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /lib/slim/_index.fodt.slim: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | , 10 | 11 | 12 | 13 | 14 | , 15 | 16 | 17 | 18 | 19 | , 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /lib/slim/_toc.fodt.slim: -------------------------------------------------------------------------------- 1 | text:p text:style-name="adoc_toch " 2 | =(attr 'toc-title') 3 | = converter.convert document, 'outline' 4 | -------------------------------------------------------------------------------- /lib/slim/admonition.fodt.slim: -------------------------------------------------------------------------------- 1 | =content_frame ("admonition") do 2 | draw:text-box 3 | text:p text:style-name="adoc_ahp #{get_basic_style}" 4 | =(attr :textlabel) 5 | - if title? 6 | text:p text:style-name="adoc_aca #{get_basic_style}" 7 | =captioned_title 8 | - if content_model == :simple 9 | text:p text:style-name="adoc_p #{get_basic_style}" 10 | =content 11 | - else 12 | =content -------------------------------------------------------------------------------- /lib/slim/colist.fodt.slim: -------------------------------------------------------------------------------- 1 | - blocks.each_with_index do |item, index| 2 | text:p text:style-name="adoc_colp #{get_basic_style}" 3 | text:span text:style-name = "adoc_colcn " 4 | =(index + 1) 5 | text:s text:c = "1" 6 | =item.text 7 | 8 | /- list_style = "#{get_basic_style} olp_s_callouts" 9 | /text:list text:style-name="adoc_ol #{list_style}" 10 | / - blocks.each_with_index do |item, index| 11 | / text:list-item 12 | / - set_current_list_item_style 1, "" 13 | / text:p text:style-name="adoc_p olp_s_callouts #{get_basic_style}" 14 | / =item.text 15 | / - if item.blocks? 16 | / =item.content 17 | / - set_current_list_item_style -1 18 | / 19 | / 20 | -------------------------------------------------------------------------------- /lib/slim/document.fodt.slim: -------------------------------------------------------------------------------- 1 | / - require 'pry' 2 | / - binding.pry 3 | 4 | - $aod_tl = 0 5 | - $aod_current_cell_style = {$aod_tl => ""} 6 | - $aod_ll = 0 7 | - $aod_current_list_item_style = {$aod_ll => ""} 8 | 9 | | 10 | office:body( 11 | xmlns:text='urn:oasis:names:tc:opendocument:xmlns:text:1.0' 12 | xmlns:office='urn:oasis:names:tc:opendocument:xmlns:office:1.0' 13 | xmlns:table='urn:oasis:names:tc:opendocument:xmlns:table:1.0' 14 | xmlns:draw='urn:oasis:names:tc:opendocument:xmlns:drawing:1.0' 15 | xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 16 | xmlns:xlink="http://www.w3.org/1999/xlink" 17 | ) 18 | a-od-params 19 | - @attributes.each do |attr, value| 20 | attribute name = attr value = escape_html(value.to_s) 21 | - title_parts = document.doctitle partition: true 22 | title 23 | =title_parts.title 24 | subtitle 25 | =title_parts.subtitle 26 | 27 | office:text text:use-soft-page-breaks='true' 28 | - if (attr? :toc) && (attr? 'toc-placement', 'auto') 29 | include _toc.fodt 30 | =content 31 | -------------------------------------------------------------------------------- /lib/slim/embedded.fodt.slim: -------------------------------------------------------------------------------- 1 | =content 2 | -------------------------------------------------------------------------------- /lib/slim/example.fodt.slim: -------------------------------------------------------------------------------- 1 | - if title? 2 | text:p text:style-name="adoc_eca #{get_basic_style}" 3 | =captioned_title 4 | =content_frame ("example") 5 | draw:text-box 6 | - if content_model == :simple 7 | text:p text:style-name="adoc_p #{get_basic_style}" 8 | =content 9 | - else 10 | =content 11 | -------------------------------------------------------------------------------- /lib/slim/floating_title.fodt.slim: -------------------------------------------------------------------------------- 1 | - section_style = "adoc_s sp_sl_#{level} #{get_basic_style}" 2 | - section_style += "sp_s_#{(attr :style)}" if @attributes.key?("style") 3 | text:p text:style-name="#{section_style}" 4 | text:bookmark-start text:name="#{@id}" 5 | |#{title} 6 | text:bookmark-end text:name="#{@id}" 7 | =content 8 | -------------------------------------------------------------------------------- /lib/slim/helpers.rb: -------------------------------------------------------------------------------- 1 | require 'cgi' 2 | require 'asciidoctor' 3 | 4 | module Slim::Helpers 5 | 6 | # manages cell styles subelements 7 | def set_current_cell_style op, cell_style = "" 8 | $aod_tl += op 9 | $aod_current_cell_style[$aod_tl] = "in_cell_#{$aod_tl} #{cell_style}" if $aod_tl > 0 10 | end 11 | 12 | def set_current_list_item_style op, list_item_style = "" 13 | $aod_ll += op 14 | $aod_current_list_item_style[$aod_ll] = "in_list_item_#{$aod_ll} #{list_item_style}" if $aod_ll > 0 15 | end 16 | 17 | # Calculates style based on role and position in some elements 18 | def get_basic_style 19 | ("#{role} #{$aod_current_cell_style[$aod_tl]} #{$aod_current_list_item_style[$aod_ll]}").strip 20 | end 21 | 22 | def content_frame cftype 23 | pstyle = "adoc_cfp cft_#{cftype} #{get_basic_style}" 24 | fstyle = "adoc_cf cft_#{cftype} #{get_basic_style}" 25 | id = "#{id.nil? ? '' : id}" 26 | "#{yield}" 27 | end 28 | 29 | def escape_html str 30 | CGI.escape_html(CGI.unescape_html(str)) 31 | end 32 | 33 | def section_level(sec = self) 34 | @_section_level ||= (sec.level == 0 && sec.special) ? 1 : sec.level 35 | end 36 | 37 | def section_title(sec = self) 38 | sectnumlevels = document.attr(:sectnumlevels, 3).to_i 39 | 40 | if sec.numbered && !sec.caption && sec.level <= sectnumlevels 41 | [sec.sectnum, sec.captioned_title].join(' ') 42 | else 43 | sec.captioned_title 44 | end 45 | end 46 | 47 | end 48 | 49 | 50 | -------------------------------------------------------------------------------- /lib/slim/image.fodt.slim: -------------------------------------------------------------------------------- 1 | - image_block_type = 'block' 2 | - utarget = (attr :target) 3 | - image_style = "#{get_basic_style} ip_ibt_#{image_block_type}" 4 | - image_style += " ip_s_amp" if !!(utarget =~ /^stem-/) 5 | - image_style += " ip_s_amp" if !id.nil? and @document.catalog[:refs][id].node_name == 'stem' 6 | text:p text:style-name="adoc_bip #{image_style}" 7 | - unless id.nil? 8 | text:bookmark text:name="#{id}" 9 | include image_frame.fodt.slim 10 | - if title? 11 | text:p text:style-name="adoc_ica #{image_style}" 12 | =captioned_title 13 | -------------------------------------------------------------------------------- /lib/slim/image_frame.fodt.slim: -------------------------------------------------------------------------------- 1 | - require 'open-uri' 2 | - require 'base64' 3 | - require 'fastimage' 4 | 5 | - images_dir = "#{@document.attributes['imagesdir'] || '.'}/" 6 | - re = /https?:\/\/.*/ 7 | - if !!(utarget =~ re) 8 | - images_dir = "" 9 | - re = /^data:image\/([a-z]+);base64,(.*)$/ 10 | - if !!(utarget =~ re) 11 | - enc = utarget.match(re)[2] 12 | - type = utarget.match(re)[1] 13 | - else 14 | - if !!(utarget =~ /https?:\/\//) 15 | - enc = Base64.encode64(URI.open("#{images_dir}#{utarget}") { |io| io.read }) 16 | - else 17 | - enc = Base64.encode64(open("#{images_dir}#{utarget}") { |io| io.read }) 18 | - type = FastImage.type("#{images_dir}#{utarget}") 19 | - x_y_array = FastImage.size("data:image/#{type};base64,#{enc}") 20 | - if x_y_array.nil? 21 | - puts "ERROR: File #{utarget} is broken" 22 | - x_y_array = [1,1] 23 | - image_style += " ip_x2y_#{x_y_array[0]}x#{x_y_array[1]}" 24 | - if @attributes.key?("width") 25 | - image_width = (attr :width).dup.gsub "%", "_prc" unless (attr :width).nil? 26 | - image_style += " ip_w_#{image_width}" 27 | - image_style += " ip_fr_#{attr :fitrect}" if @attributes.key?("fitrect") 28 | - image_style += " ip_sd_#{attr :srcdpi}" if @attributes.key?("srcdpi") 29 | - image_style += " ip_su_#{attr :svgunit}" if @attributes.key?("svgunit") 30 | - image_style += " ip_t_#{type}" 31 | draw:frame (draw:style-name="adoc_if #{image_style}" 32 | draw:name="#{id.nil? ? 'image' : id}") 33 | draw:image 34 | office:binary-data 35 | =enc 36 | -------------------------------------------------------------------------------- /lib/slim/inline_anchor.html.slim: -------------------------------------------------------------------------------- 1 | - case @type 2 | - when :xref 3 | - if (path = attributes['path']) 4 | - refid = "#{attr :refid}" 5 | - reftext = text || path 6 | - else 7 | - refid = "##{attr :refid}" 8 | - if @text.nil? 9 | - unless @document.catalog[:refs][attr :refid].nil? 10 | - reftext = @document.catalog[:refs][attr :refid].xreftext @document.attr :xrefstyle, nil, true 11 | - reftext = %([#{attr :refid}]) if reftext.nil? 12 | - else 13 | - reftext = %([#{attr :refid}]) 14 | - else 15 | - reftext = @text 16 | text:a (xlink:type="simple" xlink:href="#{refid}" 17 | text:style-name="adoc_a xref" 18 | text:visited-style-name="adoc_a xref visited") 19 | =reftext 20 | - when :link 21 | text:a (xlink:type="simple" 22 | xlink:href="#{@target}" 23 | text:style-name="adoc_a link" 24 | text:visited-style-name="adoc_a link visited") 25 | ="#{@text}" 26 | - else 27 | - puts "Not implemented inline anchor: #{@type}" 28 | -------------------------------------------------------------------------------- /lib/slim/inline_break.fodt.slim: -------------------------------------------------------------------------------- 1 | =text 2 | text:line-break 3 | -------------------------------------------------------------------------------- /lib/slim/inline_callout.fodt.slim: -------------------------------------------------------------------------------- 1 | ="~~~~#{text}~~~~" 2 | 3 | /text:span text:style-name="adoc_ic #{get_basic_style}" 4 | / ="#{text}" 5 | -------------------------------------------------------------------------------- /lib/slim/inline_footnote.slim: -------------------------------------------------------------------------------- 1 | - index = (attr :index) 2 | - if index.nil? 3 | text:span 4 | ="[Unresolved footnote reference]" 5 | - else 6 | - if type == :xref 7 | text:span text:style-name = "adoc_fra " 8 | text:note-ref text:note-class="footnote" text:ref-name="ftn#{index}" text:reference-format="text" 9 | ="#{index}" 10 | 11 | - else 12 | text:note text:id="ftn#{index}" text:note-class="footnote" 13 | text:note-citation 14 | ="#{index}" 15 | text:note-body 16 | text:p 17 | =@text 18 | -------------------------------------------------------------------------------- /lib/slim/inline_image.fodt.slim: -------------------------------------------------------------------------------- 1 | - image_block_type = 'inline' 2 | - utarget = target 3 | - image_style = "#{get_basic_style} ip_ibt_#{image_block_type}" 4 | - image_style += " ip_s_amp" if !!(utarget =~ /^stem-/) 5 | - image_style += " ip_s_amp" if !id.nil? and @document.catalog[:refs][id].node_name == 'stem' 6 | include image_frame.fodt.slim 7 | -------------------------------------------------------------------------------- /lib/slim/inline_indexterm.fodt.slim: -------------------------------------------------------------------------------- 1 | / - require "pry" 2 | / - binding.pry 3 | 4 | - require 'securerandom' 5 | - uid = SecureRandom.uuid 6 | - if type == :visible 7 | text:alphabetical-index-mark-start text:id = "#{uid}" 8 | =text 9 | text:alphabetical-index-mark-end text:id = "#{uid}" 10 | - else 11 | - if (attr :terms).length() == 1 12 | text:alphabetical-index-mark text:string-value = (attr :terms)[0] 13 | - if (attr :terms).length() == 2 14 | text:alphabetical-index-mark (text:string-value = (attr :terms)[1] 15 | text:key1 = (attr :terms)[0]) 16 | - if (attr :terms).length() == 3 17 | text:alphabetical-index-mark (text:string-value = (attr :terms)[2] 18 | text:key1 = (attr :terms)[0] 19 | text:key2 = (attr :terms)[1]) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /lib/slim/inline_quoted.fodt.slim: -------------------------------------------------------------------------------- 1 | text:span text:style-name="adoc_iq #{get_basic_style} t_#{@type}" 2 | =text 3 | 4 | -------------------------------------------------------------------------------- /lib/slim/listing.fodt.slim: -------------------------------------------------------------------------------- 1 | - if title? 2 | text:p text:style-name="adoc_lica #{get_basic_style}" 3 | =captioned_title 4 | - source_lang = (attr :language) if @style == 'source' 5 | - listing_text = content 6 | - listing_text = listing_text.gsub(/\n/, '') 7 | - listing_text = listing_text.gsub(/\s{16}/, '' * 16) 8 | - listing_text = listing_text.gsub(/\s{8}/, '' * 8) 9 | - listing_text = listing_text.gsub(/\s{4}/, '' * 4) 10 | - listing_text = listing_text.gsub(/\s{2}/, '' * 2) 11 | - listing_text = listing_text.gsub(/~~~~([0-9]+)~~~~/, '\1') 12 | text:p text:style-name="adoc_lip lip_l_#{source_lang} #{get_basic_style}" 13 | =listing_text 14 | -------------------------------------------------------------------------------- /lib/slim/literal.fodt.slim: -------------------------------------------------------------------------------- 1 | =content -------------------------------------------------------------------------------- /lib/slim/olist.fodt.slim: -------------------------------------------------------------------------------- 1 | - list_style = "#{get_basic_style} olp_s_#{style}" 2 | - if title? 3 | text:p text:style-name="adoc_lca #{list_style}" 4 | text:bookmark text:name="#{id}" 5 | =captioned_title 6 | text:list text:style-name="adoc_ol #{list_style}" 7 | - items.each_with_index do |item, index| 8 | text:list-item 9 | - set_current_list_item_style 1, "" 10 | text:p text:style-name="adoc_p #{get_basic_style}" 11 | =item.text 12 | - if item.blocks? 13 | =item.content 14 | - set_current_list_item_style -1 15 | -------------------------------------------------------------------------------- /lib/slim/open.fodt.slim: -------------------------------------------------------------------------------- 1 | =content 2 | -------------------------------------------------------------------------------- /lib/slim/outline.fodt.slim: -------------------------------------------------------------------------------- 1 | - sectnumlevels = (@document.attributes["sectnumlevels"] || 3).to_i 2 | - unless sections.empty? 3 | - toclevels ||= (document.attr 'toclevels', 3).to_i 4 | - slevel = section_level sections.first 5 | - sections.each do |sec| 6 | - keep_with_next = (sec.sections.empty? || sec.level >= toclevels) ? "" : "keep_with_next" 7 | table:table table:style-name="adoc_t no_margin_bottom #{keep_with_next}" 8 | table:table-column table:style-name="adoc_tocc sec_title" 9 | table:table-column table:style-name="adoc_tocc sec_pn" 10 | table:table-row table:style-name="adoc_tr row_keep_together" 11 | table:table-cell table:style-name="adoc_tocce sec_title" 12 | text:p text:style-name="adoc_tocp sec_title slevel_#{slevel}" 13 | text:a xlink:type="simple" xlink:href="##{sec.id}" text:style-name="adoc_a toc_entry" 14 | |#{(sec.numbered && slevel <= sectnumlevels) ? sec.sectnum : ""} #{sec.title} 15 | text:span text:style-name = "adoc_tociq dots_space_dots_title" 16 | =" " 17 | text:span text:style-name = "adoc_tociq dots_title" 18 | text:tab 19 | table:table-cell table:style-name="adoc_tocce sec_pn" 20 | text:p text:style-name="adoc_tocp sec_pn slevel_#{slevel}" 21 | text:a xlink:type="simple" xlink:href="##{sec.id}" text:style-name="adoc_a toc_entry" 22 | text:span text:style-name = "adoc_tociq dots_page_number" 23 | text:tab 24 | text:span text:style-name = "adoc_tociq dots_space_page_number" 25 | =" " 26 | text:bookmark-ref text:reference-format="page" text:ref-name="#{sec.id}" 27 | ="0" 28 | - if (sec.level < toclevels) && (child_toc = converter.convert sec, 'outline') 29 | =child_toc 30 | -------------------------------------------------------------------------------- /lib/slim/page_break.fodt.slim: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CourseOrchestra/asciidoctor-open-document/4314eab50daeb1d8cbb4d7a44b6a9d122e2f5403/lib/slim/page_break.fodt.slim -------------------------------------------------------------------------------- /lib/slim/paragraph.fodt.slim: -------------------------------------------------------------------------------- 1 | text:p text:style-name="adoc_p #{get_basic_style}" 2 | =content 3 | -------------------------------------------------------------------------------- /lib/slim/preamble.fodt.slim: -------------------------------------------------------------------------------- 1 | text:p text:style-name="adoc_pr before-paragraph " 2 | =content 3 | -------------------------------------------------------------------------------- /lib/slim/quote.fodt.slim: -------------------------------------------------------------------------------- 1 | - puts 'WARNING: quote blocks are not yet supported:' 2 | - puts content 3 | -------------------------------------------------------------------------------- /lib/slim/section.fodt.slim: -------------------------------------------------------------------------------- 1 | - sectnumlevels = (@document.attributes["sectnumlevels"] || 3).to_i 2 | - section_style = "adoc_s sp_sl_#{level} #{get_basic_style}" 3 | - section_style += "sp_sn_#{(attr :sectname)}" if @attributes.key?("sectname") 4 | - section_style += "sp_s_#{(attr :style)}" if @attributes.key?("style") 5 | text:h text:style-name="#{section_style}" text:outline-level="#{level}" 6 | text:bookmark-start text:name="#{@id}" 7 | |#{(numbered && level <= sectnumlevels) ? sectnum : ""} #{title} 8 | text:bookmark-end text:name="#{@id}" 9 | =content 10 | - if (attr :style) == "index" 11 | - index_sect_name = "#{numbered ? sectnum : ''} #{title}" 12 | include _index.fodt 13 | -------------------------------------------------------------------------------- /lib/slim/stem.fodt.slim: -------------------------------------------------------------------------------- 1 | =content -------------------------------------------------------------------------------- /lib/slim/table.fodt.slim: -------------------------------------------------------------------------------- 1 | - table_style = "#{get_basic_style}" 2 | - table_style = "#{table_style} tp_o_unbreakable" if @attributes.key?("unbreakable-option") 3 | - table_style = "#{table_style} tp_f_#{attr :frame}" if @attributes.key?("frame") 4 | - table_style = "#{table_style} tp_g_#{attr :grid}" if @attributes.key?("grid") 5 | - if title? 6 | text:p text:style-name="adoc_tca #{table_style}" 7 | text:bookmark text:name="#{id}" 8 | =captioned_title 9 | table:table table:name = "table" table:style-name = "adoc_t #{table_style}" 10 | - @columns.each do |col| 11 | table:table-column table:style-name = 'adoc_tc tcp_rcw_#{(col.attr :width)}' 12 | - [:head].select {|tblsec| !@rows[tblsec].empty? }.each do |tblsec| 13 | table:table-header-rows 14 | include table_section.fodt.slim 15 | - [:foot, :body].select {|tblsec| !@rows[tblsec].empty? }.each do |tblsec| 16 | include table_section.fodt.slim 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /lib/slim/table_section.fodt.slim: -------------------------------------------------------------------------------- 1 | - @rows[tblsec].each do |row| 2 | table:table-row table:style-name="adoc_tr #{table_style}" 3 | - row.each do |cell| 4 | - cell_style = "#{table_style} #{tblsec} tcep_va_#{cell.attr :valign} tcep_ha_#{cell.attr :halign} tcep_s_#{cell.attr :style} #{cell.attr :role}" 5 | table:table-cell (table:style-name="adoc_tce #{cell_style}" 6 | table:number-columns-spanned = cell.colspan 7 | table:number-rows-spanned= cell.rowspan ) 8 | - set_current_cell_style 1, cell_style 9 | - if cell.style == :asciidoc 10 | = cell.content 11 | - else 12 | text:p text:style-name="adoc_p pp_cs_non_asciidoc #{get_basic_style}" 13 | = cell.text 14 | - set_current_cell_style -1 15 | -------------------------------------------------------------------------------- /lib/slim/ulist.fodt.slim: -------------------------------------------------------------------------------- 1 | - list_style = "#{get_basic_style} ulp_s_#{style.nil? ? 'default' : style}" 2 | - if title? 3 | text:p text:style-name="adoc_lca #{list_style}" 4 | text:bookmark text:name="#{id}" 5 | =captioned_title 6 | text:list text:style-name="adoc_ul #{list_style}" 7 | - items.each_with_index do |item, index| 8 | text:list-item 9 | - set_current_list_item_style 1, "" 10 | text:p text:style-name="adoc_p #{get_basic_style}" 11 | =item.text 12 | - if item.blocks? 13 | =item.content 14 | - set_current_list_item_style -1 15 | -------------------------------------------------------------------------------- /test/test.rb: -------------------------------------------------------------------------------- 1 | require "minitest/autorun" 2 | require_relative "../lib/a-od-producer/misc.rb" 3 | 4 | class TestMiscMethods < Minitest::Test 5 | describe "Method text_measurement_to_mm " do 6 | it "should correctly calculate measurement in mm" do 7 | assert_equal MiscMethods.text_measurement_to_mm("1pt").round(3), 0.353 8 | assert_equal MiscMethods.text_measurement_to_mm("1.5pt").round(3), 0.529 9 | assert_raises RuntimeError do 10 | MiscMethods.measurement_to_mm(1, "inches") 11 | end 12 | end 13 | end 14 | describe "Method measurement_to_mm " do 15 | it "should correctly calculate measurement in mm" do 16 | assert_equal MiscMethods.measurement_to_mm(1, "mm").round(3), 1.000 17 | assert_equal MiscMethods.measurement_to_mm(1, "cm").round(3), 10.000 18 | assert_equal MiscMethods.measurement_to_mm(1, "pt").round(3), 0.353 19 | assert_equal MiscMethods.measurement_to_mm(1, "pc").round(3), 4.233 20 | assert_equal MiscMethods.measurement_to_mm(1, "in").round(3), 25.400 21 | assert_raises RuntimeError do 22 | MiscMethods.measurement_to_mm(1, "inches") 23 | end 24 | end 25 | end 26 | describe "Method get_normalized_fitrect_attribute" do 27 | it "should return normalized fitrect, for now, only mm are accepted" do 28 | assert_equal MiscMethods.get_normalized_fitrect_attribute("20x10mm"), "20x10mm" 29 | end 30 | it "should raise error on anything not mm like" do 31 | assert_raises RuntimeError do 32 | MiscMethods.get_normalized_fitrect_attribute("20x10") 33 | end 34 | assert_raises RuntimeError do 35 | MiscMethods.get_normalized_fitrect_attribute("20x10in") 36 | end 37 | end 38 | end 39 | describe "Method get_optimal_dimensions" do 40 | it "should return optimal dimensions" do 41 | assert_equal MiscMethods.get_optimal_dimensions(20,10, 40, 20), {img_cw: 20, img_ch: 10} 42 | assert_equal MiscMethods.get_optimal_dimensions(20, 10, 30, 5), {img_cw: 10, img_ch: 5} 43 | assert_equal MiscMethods.get_optimal_dimensions(20, 10, 5, 15), {img_cw: 5, img_ch: 2.5} 44 | assert_equal MiscMethods.get_optimal_dimensions(20, 10, 10, 10), {img_cw: 10, img_ch: 5} 45 | assert_equal MiscMethods.get_optimal_dimensions(2, 1, 0.5, 10), {img_cw: 0.5, img_ch: 0.25} 46 | end 47 | end 48 | describe "Method normilize_style_name" do 49 | it "should substitute incorrect chars" do 50 | assert_equal MiscMethods.normilize_style_name("adoc_p smth"), "adoc_p_20_smth" 51 | end 52 | end 53 | end 54 | 55 | require 'nokogiri' 56 | 57 | class TestNokogiriCornerCase 58 | describe "Nokogiri" do 59 | before do 60 | @xml_text_src = <<-TESTXML 61 | 62 | 63 | 64 | TESTXML 65 | @xml_text_dest = <<-TESTXML 66 | 67 | 68 | TESTXML 69 | @xml_doc_src = Nokogiri::XML @xml_text_src 70 | @xml_doc_dest = Nokogiri::XML @xml_text_dest 71 | end 72 | it "should not lose attribute namespace" do 73 | ns = {'p1': "ns1", 'p2': "ns2"} 74 | node_dest = @xml_doc_dest.xpath("//p1:a", ns)[0] 75 | node_to_insert = @xml_doc_src.xpath("//p1:b", ns)[0] 76 | node_dest.add_child node_to_insert 77 | # https://github.com/sparklemotion/nokogiri/issues/2228 now in prod 78 | # assert_nil @xml_doc_dest.xpath("/p1:a/p1:b", 79 | # ns)[0].attribute_with_ns('c', 'ns2'), 80 | # "check attribute with ns" 81 | # node_dest.xpath("//*/@c").each do |node| 82 | # node.parent["p2:c"] = node.to_s 83 | # node.remove 84 | # end 85 | assert_equal @xml_doc_dest.xpath("/p1:a/p1:b", 86 | ns)[0].attribute_with_ns('c', 'ns2').to_s, 87 | "d", "check namespace restored" 88 | end 89 | end 90 | end 91 | 92 | #class TestAttributeNsCopied 93 | # describe "Nokogiri" do 94 | # before do 95 | # @xml_text_src = <<-TESTXML 96 | # 97 | # 98 | # 99 | # TESTXML 100 | # @xml_text_dest = <<-TESTXML 101 | # 102 | # 103 | # TESTXML 104 | # @xml_doc_src = Nokogiri::XML @xml_text_src 105 | # @xml_doc_dest = Nokogiri::XML @xml_text_dest 106 | # end 107 | # it "should not lose attribute namespace" do 108 | # ns = {'p1': "ns1", 'p2': "ns2"} 109 | # node_dest = @xml_doc_dest.xpath("//p1:a", ns)[0] 110 | # node_to_insert = @xml_doc_src.xpath("//p1:b", ns)[0] 111 | # node_dest.add_child node_to_insert 112 | # assert_equal @xml_doc_dest.xpath("/p1:a/p1:b", 113 | # ns)[0].attribute_with_ns('c', 'ns2').to_s, 114 | # "d", "check namespace remained" 115 | # end 116 | # end 117 | #end 118 | -------------------------------------------------------------------------------- /test/test_cases/asciidoctor-pdf/basic-example.adoc: -------------------------------------------------------------------------------- 1 | = Document Title 2 | Doc Writer 3 | :reproducible: 4 | :listing-caption: Listing 5 | :source-highlighter: rouge 6 | :toc: 7 | // Uncomment next line to add a title page (or set doctype to book) 8 | //:title-page: 9 | // Uncomment next line to set page size (default is A4) 10 | //:pdf-page-size: Letter 11 | 12 | An example of a basic http://asciidoc.org[AsciiDoc] document prepared by {author}. 13 | 14 | == Introduction 15 | 16 | A paragraph followed by an unordered list{empty}footnote:[AsciiDoc supports unordered, ordered, and description lists.] with square bullets.footnote:[You may choose from square, disc, and circle for the bullet style.] 17 | 18 | [square] 19 | * item 1 20 | * item 2 21 | * item 3 22 | 23 | == Main 24 | 25 | Here's how you say "`Hello, World!`" in Prawn: 26 | 27 | .Create a basic PDF document using Prawn 28 | [source,ruby] 29 | ---- 30 | require 'prawn' 31 | 32 | Prawn::Document.generate 'example.pdf' do 33 | text 'Hello, World!' 34 | end 35 | ---- 36 | 37 | == Conclusion 38 | 39 | That's all, folks! 40 | -------------------------------------------------------------------------------- /test/test_cases/asciidoctor-pdf/chronicles-dark-theme.yml: -------------------------------------------------------------------------------- 1 | # invoke with -a pdf-theme=chronicles-dark -a rouge-style=molokai 2 | extends: chronicles 3 | page: 4 | background-color: 000000 5 | base: 6 | font-color: CCCCCC 7 | border-color: 111111 8 | link: 9 | font-color: BD7435 10 | literal: 11 | font-color: 4EDEB9 12 | mark: 13 | background-color: 0000FF 14 | key: 15 | background-color: 0A0A0A 16 | border-color: 333333 17 | heading: 18 | font-color: $base-font-color 19 | title_page: 20 | title: 21 | font-color: 666666 22 | authors: 23 | font-color: E7E7E7 24 | abstract: 25 | font-color: A39D99 26 | admonition: 27 | column-rule-color: $base-border-color 28 | icon: 29 | caution: 30 | stroke-color: 40CBFF 31 | note: 32 | stroke-color: E6BF83 33 | tip: 34 | stroke-color: EEEEEE 35 | warning: 36 | stroke-color: 4096FF 37 | blockquote: 38 | border-color: $base-border-color 39 | cite: 40 | font-color: 666666 41 | verse: 42 | border-color: $blockquote-border-color 43 | cite: 44 | font-color: $blockquote-cite-font-color 45 | code: 46 | font-color: $base-font-color 47 | background-color: 0A0A0A 48 | border-color: 333333 49 | conum: 50 | font-color: $literal-font-color 51 | example: 52 | background-color: $page-background-color 53 | border-color: $base-border-color 54 | sidebar: 55 | background-color: 111111 56 | border-color: 1A1A1A 57 | title: 58 | font-color: $heading-font-color 59 | thematic-break: 60 | border-color: $base-border-color 61 | table: 62 | background-color: $page-background-color 63 | border-color: 222222 64 | foot: 65 | background-color: $table-background-color 66 | header: 67 | font-color: 666666 68 | footer: 69 | border-color: 222222 70 | -------------------------------------------------------------------------------- /test/test_cases/asciidoctor-pdf/chronicles-example.adoc: -------------------------------------------------------------------------------- 1 | = The Dangerous & _Thrilling_ Documentation Chronicles: Based on True Events 2 | Kismet Caméléon; Lazarus het_Draeke 3 | v1.0, 2014-01-01 4 | :description: This story chronicles the inexplicable hazards and vicious beasts a \ 5 | team must conquer and vanquish on their journey to discovering the true power of \ 6 | Open Source. 7 | :organization: Company Name 8 | :doctype: book 9 | // Settings: 10 | :experimental: 11 | :reproducible: 12 | :icons: font 13 | :listing-caption: Listing 14 | :sectnums: 15 | :toc: 16 | :toclevels: 3 17 | :xrefstyle: short 18 | ifdef::backend-pdf[] 19 | :pdf-theme: chronicles 20 | :pdf-themesdir: {docdir} 21 | :title-logo-image: image:sample-banner.svg[pdfwidth=4.25in,align=center] 22 | :source-highlighter: rouge 23 | //:rouge-style: github 24 | endif::[] 25 | // URIs: 26 | :uri-devoxx: https://devoxx.be 27 | :uri-devoxx-top-talks: https://www.youtube.com/watch?v=1OpAgZvYXLQ&list=PLRsbF2sD7JVq7fv1GZGORShSUIae1ZAPy&index=1 28 | :uri-stbernardusabt12: http://www.sintbernardus.be/stbernardusabt12.php?l=en 29 | :uri-wolpertinger: http://en.wikipedia.org/wiki/Wolpertinger 30 | 31 | //[abstract] 32 | //{description} 33 | 34 | == It's a City Under Siege 35 | 36 | This journey begins one late Monday afternoon at {uri-devoxx}[Devoxx]. 37 | This journey begins one late Monday afternoon at {uri-devoxx}[((Devoxx))]. 38 | Our team needs coffee, _desperately_, but none of us dare open the theater doors... 39 | 40 | During the {uri-devoxx-top-talks}[opening university session], a script-happy warlock inadvertently released a legion of Wolpertingers! 41 | To leave now would mean *code dismemberment and certain death*. 42 | 43 | Behold -> the horror! 44 | 45 | .Wolpertinger, stuffed 46 | [.left.thumb] 47 | image::wolpertinger.jpg[Wolpertinger,pdfwidth=50%,link={uri-wolpertinger}] 48 | 49 | (((Wolpertinger))) 50 | (((Ravenous Beast,Wolpertinger))) 51 | You may not be familiar with these {uri-wolpertinger}[ravenous beasts]. 52 | Trust us, they'll eat your shorts and suck loops from your code. 53 | In light of this danger, we've searched high and wide for the security crew's defensive operations manual. 54 | We can't find it and the DefOps{empty}footnote:defops[DefOps is a portmanteau of "`defensive`" and "`operations`".] werewolves haven't returned from their rendezvous at Bier Central. 55 | They've either eaten each other or fallen victim to the Wolpertingers roaming the streets of ((Antwerp)).Quick, hit 56 | 57 | // todo kbd:[Ctrl,Alt,Backspace] or select menu:File[Quit] and let's bail out of here! 58 | // 59 | WARNING: Working with DefOps{empty}footnote:defops[] werewolves leads to howling and trying to train aggressive regular expressions with Pavlovian reinforcement. 60 | 61 | _Weak light from the hallway trickled across the theater, chased by a distant scream._ 62 | 63 | === Rendezvous Point 64 | 65 | Come on, [[bier-central,Bier Central]]_Bier Central_, of course! 66 | Did you have to ask? 67 | If you beat me there, order me a {uri-stbernardusabt12}[St. Bernardus Abt 12]. 68 | Here's some €. 69 | 70 | [#ravages] 71 | == The Ravages of Writing 72 | 73 | Crystalline XML tags relentlessly bombarded the theater. 74 | 75 | .XML tags 76 | [source,xml] 77 | ---- 78 | 79 | 80 | Lazarus 81 | het Draeke 82 | 83 | 84 | ---- 85 | 86 | Despite the assault, we continued our pursuit to draft a DefOps{empty}footnote:defops[] plan. 87 | 88 | .DefOps Plan 89 | ==== 90 | // todo Click btn:[Download Zip] to download the defensive operation plan bundle. 91 | 92 | OMG! 93 | Somebody please save us now! 94 | I want my mum -- and an extra-large double macchiato, please. 95 | ==== 96 | 97 | //Unfortunaly, Lazarus and I had both come to the conclusion that we weren't going to get out of this without corrupted hardrives if we didn't locate caffeine within the next few hours. 98 | // 99 | //=== A Recipe for Potion That Will Ensure You Win the Hearts of Developers 100 | // 101 | //This potion for a sample document contains the following ingredients, which are listed in a very random, chaotically nested order. 102 | // 103 | //.Ingredients for Potion that Demystifies Documents 104 | //* all the headings 105 | //** syntax highlighted source code 106 | //*** non-syntax highlighted source code or just a listing block 107 | //* quote block 108 | //** verse block 109 | //*** table with some cell formatting 110 | //**** sequential paragraphs 111 | //***** admonition blocks, but use them sparingly 112 | //*** bullet list with nesting 113 | //** numbered list with nesting 114 | //** definition list 115 | //*** sidebar 116 | //* example block 117 | //** block image (no inline images) 118 | //*** inline formatting in a paragraph 119 | //**** two fresh Burdockian leaves 120 | //***** They must be harvested by the light of the teal moons. 121 | // 122 | //Are you square? 123 | // 124 | //[square] 125 | //* one 126 | //* two 127 | //* three 128 | // 129 | //What is there to do? 130 | // 131 | //* [x] Done 132 | //* [ ] Next 133 | //* Who's counting? 134 | // 135 | //==== Searching for Burdockian 136 | // 137 | //.Steps for finding and preparing Burdockian leaves 138 | //. Locate dusty botany 139 | //.. Sneeze 140 | //... Sneeze some more 141 | //. Find section on Burdockian 142 | //.. Review its characteristics 143 | //... Take a picture of the diagram of its leaves 144 | //.... Don't rip out the picture like a troglodyte 145 | //..... Don't do it, I'm watching you 146 | //. Put on your hiking boots 147 | //. Freeze your butt off on the side of a mountain at midnight 148 | // 149 | //Let's skip a few steps and start counting from 10. 150 | // 151 | //[start=10] 152 | //. arabic (10) 153 | //.. loweralpha (a) 154 | //... lowerroman (i) 155 | //... lowerroman (ii) 156 | //... lowerroman (iii) 157 | //... lowerroman (iv) 158 | //.... upperalpha (A) 159 | //. arabic (11) 160 | // 161 | //It's time for a top 5 list, made using the `reversed` option on //an ordered list! 162 | // 163 | //[%reversed] 164 | //. Stone Imperial Russian Stout 165 | //. Pliny the Elder 166 | //. Chimay Grande Réserve (Blue) 167 | //. St. Bernardus Abt 12 168 | //. Westvleteren 12 (XII) 169 | // 170 | //How about a list with some terms? 171 | // 172 | //* Fruits 173 | // 174 | //Apple:: 175 | //The round fruit of a tree of the rose family, which typically has thin red or green skin and crisp flesh. 176 | //Yes, I said _flesh_. 177 | // 178 | //Pear:: 179 | //A yellowish- or brownish-green edible fruit that is typically narrow at the stalk and wider toward the base, with sweet, slightly gritty flesh. 180 | //More flesh. 181 | //Mmmmm. 182 | // 183 | //* Vegetables 184 | // 185 | //Carrot:: 186 | //An orange-colored root eaten as a vegetable. 187 | //Beware, it's a favorite of the Wolpertinger. 188 | // 189 | //===== Are You Still Here? 190 | // 191 | //.Move, move, move! 192 | //[CAUTION] 193 | //==== 194 | //The Wolpertingers can smell your procrastination. 195 | //It's not their fault you can't find your boots. 196 | //==== 197 | // 198 | //====== Sigh... 199 | // 200 | //TIP: Your boots are in your closet. 201 | // 202 | //== Dawn on the Plateau 203 | // 204 | //Lazarus was hanging from the bottom limb of a Burdockian tree, licking the bark. 205 | // 206 | //[quote,Mark Tobey] 207 | //On pavements and the bark of trees I have found whole worlds. 208 | // 209 | //"`If there are whole worlds on that bark, he just swallowed them,`" Kizmet replied. 210 | // 211 | //[verse,The documentation attorneys] 212 | //____ 213 | //No bark was harmed in the making of this potion. 214 | // We're not so sure about a couple ants though. 215 | // 216 | // Nor those worlds... 217 | // 218 | // Crap, I smell an injunction. 219 | //____ 220 | // 221 | //We'd retrieved the leaves, but we'd obviously lost our minds in the process. 222 | // 223 | //[verse] 224 | //Roses are +++red+++. 225 | //Violets are +++blue+++__-ish__. 226 | // 227 | //== Words Seasoned with Power 228 | // 229 | //To _tame_ the [.wild]#wild# wolpertingers, we needed to build a *charm*. 230 | //But **ul**timate victory could only be won if we divined the *_true name_* of the __war__lock. 231 | // 232 | //"`What kind of charm?`" Lazarus asked. "`An odoriferous one or a mineral one?`" 233 | //Kizmet shrugged. "`The note from Olaf's desk says '`wormwood and licorice,`' but these could be normal groceries for werewolves.`" 234 | // 235 | //"`Well the H~2~O written on the security whiteboard could be part of a shopping list, but I don't think the local bodega also sells e = mc^2^,`" Lazarus replied. 236 | // 237 | //"`Wait!`" Indigo plucked a small vial from her desk's top drawer and held it toward us. 238 | //The vial's label read '```e = mc^2^ *_the scent of science_* _smells like a genius_```'. 239 | // 240 | //=== Can I Get Some `Code`? 241 | // 242 | //[%hardbreaks] 243 | //Sure. 244 | //Have a listing block. 245 | // 246 | //---- 247 | //This is an example of a listing block. 248 | //The content inside is rendered as
 text.
249 | //----
250 | //
251 | //But I'm not giving you any highlighting shazam just yet.
252 | //
253 | //.What is a listing block?
254 | //****
255 | //Like literal blocks, the content in listing blocks is displayed exactly as you entered it.
256 | //Listing block content is rendered as `
` text.
257 | //
258 | //The `listing` style is applied to an element, such as a paragraph, by setting the `listing` attribute on that element.
259 | //****
260 | //
261 | //Let's get our #((highlighting))# on!
262 | //
263 | //<<<
264 | //
265 | //Install Prawn:
266 | //
267 | // $ gem install prawn
268 | //
269 | //Then create your first PDF document in Ruby!
270 | //
271 | //.Generates a basic PDF document using Prawn
272 | //[source,ruby]
273 | //----
274 | //require 'prawn' # <1>
275 | //
276 | //Prawn::Document.generate 'output.pdf' do # <3>
277 | //  text 'Hello, World!' # <2>
278 | //end
279 | //----
280 | //<1> Imports Prawn library
281 | //<2> Adds text “Hello, World!” to first page
282 | //<3> Writes PDF to [file]_output.pdf_ after executing all statements
283 | //
284 | //How about some source code that styles code? So meta!
285 | //
286 | //[source,css]
287 | //----
288 | //code {
289 | //  padding: 2px 4px;
290 | //  font-size: 90%;
291 | //  font-weight: normal;
292 | //  color: #c7254e;
293 | //  white-space: nowrap !important;
294 | //  background-color: #f9f2f4;
295 | //  border-radius: 4px;
296 | //}
297 | //----
298 | //
299 | //Where could we go without some Java(TM)?
300 | //Naturally, some autosizing is necessary.
301 | //
302 | //[source%autofit,java]
303 | //----
304 | //package org.javaee7.cdi.events;
305 | //
306 | //import javax.annotation.PostConstruct;
307 | //import javax.enterprise.context.SessionScoped;
308 | //import javax.enterprise.event.Observes;
309 | //import java.io.Serializable;
310 | //import java.util.ArrayList;
311 | //import java.util.Collections;
312 | //import java.util.List;
313 | //import javax.ws.rs.*;
314 | //
315 | // /**
316 | // * This session-scoped bean receives greeting strings from the event bus
317 | // * and provides access to the collection of these greetings via a REST API.
318 | // *
319 | // * @author The Duke
320 | // * @since 1.0
321 | // */
322 | //@SessionScoped
323 | //public class GreetingReceiver implements EventReceiver, Serializable {
324 | //
325 | //    private List greetings;
326 | //
327 | //    @PostConstruct
328 | //    void init() {
329 | //        this.greetings = new ArrayList();
330 | //    }
331 | //
332 | //    void receive(@Observes String greet) {
333 | //        this.greetings.add(greet);
334 | //    }
335 | //
336 | //    @GET
337 | //    @Produces("application/json")
338 | //    public List listAll(@QueryParam("start") Integer start, @QueryParam("max") Integer max) {
339 | //        int numGreetings = this.greetings.size();
340 | //
341 | //        if (numGreetings == 0 || max == 0) {
342 | //            return Collections.emptyList();
343 | //        }
344 | //
345 | //        if (start == null) {
346 | //            start = 0;
347 | //        }
348 | //
349 | //        if (max == null) {
350 | //            max = numGreetings;
351 | //        }
352 | //
353 | //        return this.greetings.subList(start, Math.min(max + start, numGreetings));
354 | //    }
355 | //
356 | //}
357 | //----
358 | //
359 | //We already showed you an XML example in <>, a language we often rant about over beers at <>.
360 | //
361 | //I'll trade you a little table for some of that bark.
362 | //
363 | //[cols=4,frame=ends,grid=rows]
364 | //|===
365 | //|Column 1 |Column 2 |Column 3 |Column 4
366 | //
367 | //^m|Prefix `{vbar}` with `{caret}` to center content horizontally within the cell.
368 | //.>|Prefix `{vbar}` with a `.` and `>` to align content to the bottom of the cell.
369 | //^.^|Prefix `{vbar}` with a `^`, `.`, and `^` to place content in the middle of the cell.
370 | //>|Prefix `{vbar}` with `>` to align content to the right horizontally within the cell.
371 | //
372 | //4+^e|This content spans all four columns (`4{plus}`) and is centered horizontally (`{caret}`) within the cell.
373 | //|===
374 | //
375 | //Wait.
376 | //What?
377 | //Where is this story going?
378 | //
379 | //``:: an html tag that makes me crazy
380 | //
381 | //align:: something I never get going in the right direction.
382 | //Also has to do with my poor verbal communication skills
383 | //
384 | //float::
385 | //style::
386 | //don't make me laugh
387 | //
388 | //Does anyone have the time?
389 | //
390 | //Tg lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
391 | //Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
392 | //Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
393 | //Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborumj.
394 | //
395 | //== Keeping It Together
396 | //
397 | //On this page we have nested "`keep together`" logic.
398 | //The combined block will be shifted to the next page if there isn't room available on this one.
399 | //
400 | //[verse]
401 | //First,
402 | //we
403 | //need
404 | //to
405 | //waste
406 | //several
407 | //lines
408 | //using
409 | //a
410 | //verse
411 | //to
412 | //push
413 | //the
414 | //next
415 | //block
416 | //to
417 | //its
418 | //breaking
419 | //point.
420 | //
421 | //[NOTE]
422 | //.What happens if there is both a field and a method with the same name?
423 | //====
424 | //Back to the previous example, suppose that we have both a field and a method with the same name, as in:
425 | //
426 | //.Java class with a field and method that share the same name
427 | //[source,java]
428 | //----
429 | //public class Foo {
430 | //  public String bar;
431 | //
432 | //  public String bar() {
433 | //    return bar;
434 | //  }
435 | //}
436 | //----
437 | //
438 | //*Golo resolves methods first, fields last.*
439 | //Hence, the following Golo code will resolve the `bar()` method, not the `bar` field:
440 | //
441 | //.Golo picks the method over the field with the same name
442 | //[source,golo]
443 | //----
444 | //let foo = Foo()
445 | //
446 | //foo: bar("baz") # <1>
447 | //
448 | //println(foo: bar()) # <2>
449 | //----
450 | //<1> Writes the field
451 | //<2> Calls the `bar()` method
452 | //====
453 | //
454 | //<<<
455 | //
456 | //Here's a preview of how each heading level is rendered.
457 | //
458 | //[discrete]
459 | //= Heading 1 (Level 0)
460 | //
461 | //filler content
462 | //
463 | //[discrete]
464 | //== Heading 2 (Level 1)
465 | //
466 | //filler content
467 | //
468 | //[discrete]
469 | //=== Heading 3 (Level 2)
470 | //
471 | //filler content
472 | //
473 | //[discrete]
474 | //==== Heading 4 (Level 3)
475 | //
476 | //filler content
477 | //
478 | //[discrete]
479 | //===== Heading 5 (Level 4)
480 | //
481 | //filler content
482 | //
483 | //[discrete]
484 | //====== Heading 6 (Level 5)
485 | //
486 | //filler content
487 | //
488 | //---
489 | //
490 | //--
491 | //Here's some content inside an open block.
492 | //--
493 | //
494 | //[appendix]
495 | //== Credits
496 | //
497 | //.Brought to you with icon:heart[set=fas,role=love] by OpenDevise
498 | //[%header%footer,cols="2,2s,^4",grid=rows,frame=ends,width=75%,caption=]
499 | //|===
500 | //|Name |Title |Alias
501 | //
502 | //|Sarah White
503 | //|President
504 | //|http://twitter.com/carbonfray[@carbonfray]
505 | //
506 | //|Dan Allen
507 | //|Vice President
508 | //|http://twitter.com/mojavelinux[@mojavelinux]
509 | //
510 | //3+^.e|Powered by Open Source
511 | //|===
512 | //
513 | [index]
514 | == Index
515 | 


--------------------------------------------------------------------------------
/test/test_cases/asciidoctor-pdf/chronicles-template.fodt:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:79d3cfdcaa47ab80a49f41ba72e6051e2c40081fe151016716c5c7fa8c078c29
3 | size 539649
4 | 


--------------------------------------------------------------------------------
/test/test_cases/asciidoctor-pdf/chronicles-theme.yml:
--------------------------------------------------------------------------------
 1 | extends: default
 2 | role:
 3 |   love:
 4 |     font-color: E0245E
 5 |   wild:
 6 |     font-color: 222222
 7 |     text-transform: uppercase
 8 |     font-style: italic
 9 |     background-color: BCD637
10 |     border-offset: 1.5
11 | header:
12 |   font_color: 999999
13 |   height: 0.5in
14 |   recto:
15 |     columns: "<40% =20% >40%"
16 |     right:
17 |       content: '_{organization}_'
18 |     center:
19 |       content: image:sample-logo.jpg[pdfwidth=0.25in]
20 |   verso:
21 |     columns: $header_recto_columns
22 |     left:
23 |       content: $header_recto_right_content
24 |     center:
25 |       content: $header_recto_center_content
26 | footer:
27 |   height: 0.45in
28 |   recto:
29 |     right:
30 |       content: '{section-or-chapter-title} | {page-number}'
31 |   verso:
32 |     left:
33 |       content: '{page-number} | {chapter-title}'
34 | 


--------------------------------------------------------------------------------
/test/test_cases/asciidoctor-pdf/edge-cases.adoc:
--------------------------------------------------------------------------------
 1 | = Edge Cases
 2 | :doctype: book
 3 | 
 4 | == Long code listing
 5 | 
 6 | Here's a long listing block that spans multiple pages.
 7 | 
 8 | .Long listing
 9 | ----
10 | Line 1
11 | Line 2
12 | Line 3
13 | Line 4
14 | Line 5
15 | Line 6
16 | Line 7
17 | Line 8
18 | Line 9
19 | Line 10
20 | Line 11
21 | Line 12
22 | Line 13
23 | Line 14
24 | Line 15
25 | Line 16
26 | Line 17
27 | Line 18
28 | Line 19
29 | Line 20
30 | Line 21
31 | Line 22
32 | Line 23
33 | Line 24
34 | Line 25
35 | Line 26
36 | Line 27
37 | Line 28
38 | Line 29
39 | Line 30
40 | Line 31
41 | Line 32
42 | Line 33
43 | Line 34
44 | Line 35
45 | Line 36
46 | Line 37
47 | Line 38
48 | Line 39
49 | Line 40
50 | Line 41
51 | Line 42
52 | Line 43
53 | Line 44
54 | Line 45
55 | Line 46
56 | Line 47
57 | Line 48
58 | Line 49
59 | Line 50
60 | ----
61 | 
62 | <<<
63 | 
64 | . Step X
65 | +
66 | ----
67 | puts "Hello, World!" <1>
68 | ----
69 | <1> Prints "Hello, World!" to the console
70 | 
71 | //^
72 | 
73 | Step X::
74 | +
75 | ----
76 | puts "Hello, World!" <1>
77 | ----
78 | <1> Prints "Hello, World!" to the console
79 | 


--------------------------------------------------------------------------------
/test/test_cases/asciidoctor-pdf/sample-banner.svg:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 | 
 5 | 
 6 | 
 7 | 
 8 | 
 9 | 
10 | 
11 | 
12 | 
13 | 
14 | 
15 | 
16 | 
17 | 
18 | 
19 | 
20 | 
21 | 
22 | 
23 | 
24 | 
25 | 
26 | 
27 | 
28 | 
29 | 
30 | 
31 | 
32 | 
33 | 
34 | 
35 | 
36 | 
37 | 
38 | 
39 | 
40 | 
41 | 
42 | 
43 | 
44 | 
45 | 
46 | 
47 | 
48 | 
49 | 
50 | 


--------------------------------------------------------------------------------
/test/test_cases/asciidoctor-pdf/sample-logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CourseOrchestra/asciidoctor-open-document/4314eab50daeb1d8cbb4d7a44b6a9d122e2f5403/test/test_cases/asciidoctor-pdf/sample-logo.jpg


--------------------------------------------------------------------------------
/test/test_cases/asciidoctor-pdf/wolpertinger.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CourseOrchestra/asciidoctor-open-document/4314eab50daeb1d8cbb4d7a44b6a9d122e2f5403/test/test_cases/asciidoctor-pdf/wolpertinger.jpg


--------------------------------------------------------------------------------
/test/test_cases/stew/60x20-label.svg:
--------------------------------------------------------------------------------
  1 | 
  2 | 
 17 |   
 19 |   
 38 |   
 40 |     
 41 |       
 43 |         image/svg+xml
 44 |         
 46 |         
 47 |       
 48 |     
 49 |   
 50 |   
 54 |     
 63 |     
 68 |       
 72 |       
 76 |       
 80 |       
 84 |       
 88 |       
 92 |       
 96 |       
100 |       
104 |       
108 |       
112 |       
116 |       
120 |       
124 |       
128 |       
132 |       
136 |       
140 |       
144 |       
148 |     
149 |   
150 | 
151 | 


--------------------------------------------------------------------------------
/test/test_cases/stew/asciidoc_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CourseOrchestra/asciidoctor-open-document/4314eab50daeb1d8cbb4d7a44b6a9d122e2f5403/test/test_cases/stew/asciidoc_icon.png


--------------------------------------------------------------------------------
/test/test_cases/stew/ehjnem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CourseOrchestra/asciidoctor-open-document/4314eab50daeb1d8cbb4d7a44b6a9d122e2f5403/test/test_cases/stew/ehjnem.png


--------------------------------------------------------------------------------
/test/test_cases/stew/long_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CourseOrchestra/asciidoctor-open-document/4314eab50daeb1d8cbb4d7a44b6a9d122e2f5403/test/test_cases/stew/long_line.png


--------------------------------------------------------------------------------
/test/test_cases/stew/rasputicza.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CourseOrchestra/asciidoctor-open-document/4314eab50daeb1d8cbb4d7a44b6a9d122e2f5403/test/test_cases/stew/rasputicza.jpg


--------------------------------------------------------------------------------
/test/test_cases/stew/ruler.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CourseOrchestra/asciidoctor-open-document/4314eab50daeb1d8cbb4d7a44b6a9d122e2f5403/test/test_cases/stew/ruler.png


--------------------------------------------------------------------------------
/test/test_cases/stew/short_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CourseOrchestra/asciidoctor-open-document/4314eab50daeb1d8cbb4d7a44b6a9d122e2f5403/test/test_cases/stew/short_line.png


--------------------------------------------------------------------------------
/test/test_cases/stew/svg_example.svg:
--------------------------------------------------------------------------------
 1 | First componentSecond componentThird component


--------------------------------------------------------------------------------
/test/test_cases/stew/test.adoc:
--------------------------------------------------------------------------------
  1 | = Test document{nbsp}title (test escape{nbsp}characters in{nbsp}title: ',{nbsp}",{nbsp}<,{nbsp}>,{nbsp}&)
  2 | :xrefstyle: short
  3 | :sectnums:
  4 | :stem:
  5 | :mathematical-format: svg
  6 | :doctype: book
  7 | :source-highlighter: rouge
  8 | :toc: center
  9 | :toclevels: 5
 10 | :toc-title: Table of contents (Asciidoctor)
 11 | 
 12 | 
 13 | :base64-image-example: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
 14 | 
 15 | 
 16 | Some sort of [((preamble))]
 17 | multiline paragraph.
 18 | 
 19 | Another preamble paragraph.
 20 | 
 21 | |===
 22 | |preamble table
 23 | |===
 24 | 
 25 | [dedication]
 26 | == Dedication
 27 | 
 28 | Some dedication (((preamble, good preamble)))
 29 | 
 30 | 中文文字
 31 | 
 32 | Texte français
 33 | 
 34 | ein Löwe
 35 | 
 36 | Русский текст
 37 | 
 38 | == Links
 39 | 
 40 | https://asciidoctor.org/[Asciidoctor]
 41 | 
 42 | == Diagram extension
 43 | 
 44 | .A diagram in adoc file
 45 | [plantuml, diagram-classes, png, fitrect="170x225mm", srcdpi=300, width = "70%"]
 46 | ....
 47 | skinparam dpi 300
 48 | 
 49 | class BlockProcessor
 50 | class DiagramBlock
 51 | class DitaaBlock
 52 | class PlantUmlBlock
 53 | 
 54 | BlockProcessor <|-- DiagramBlock
 55 | DiagramBlock <|-- DitaaBlock
 56 | DiagramBlock <|-- PlantUmlBlock
 57 | ....
 58 | 
 59 | 
 60 | 
 61 | == Listings
 62 | 
 63 | 
 64 | .Asciidoc listing
 65 | [source, asciidoc]
 66 | ----
 67 | Rouge doesn't support asciidoc
 68 | ----
 69 | 
 70 | .Some ruby listing
 71 | [source, ruby]
 72 | ----
 73 | class BasicParagraph < BasicHelper <1> <2>
 74 |   def h_parent_style_name <2>
 75 |     @sd[:parent_style_name] = $defsn_p
 76 |     a = b + <3>
 77 |         abc[1]
 78 |   end
 79 | end
 80 | ----
 81 | <1> Property setter class should inherit BasicHelper
 82 | <2> Another callout and methods should start with `h_`
 83 | <3> And something else
 84 | 
 85 | .In lists
 86 | * List item 1
 87 | +
 88 | [source, ruby]
 89 | ----
 90 | class OneMore <1>
 91 | end
 92 | ----
 93 | <1> One more class
 94 | * List item 2
 95 | 
 96 | .In table
 97 | |===
 98 | a|
 99 | [source, xml]
100 | ----
101 | 
102 |    <1>
103 |     e
104 |   
105 | 
106 | ----
107 | <1> Tag with an attribute
108 | 
109 | |===
110 | 
111 | 
112 | == Examples
113 | 
114 | .Optional title
115 | [example]
116 | This is an example of an example block.
117 | 
118 | ====
119 | The book hit the floor with a *thud*.
120 | 
121 | He could hear doves *cooing* in the pine trees`' branches.
122 | ====
123 | 
124 | |===
125 | a|
126 | .Example in table
127 | ====
128 | .Table list example
129 | * List item 1
130 | * List item 1
131 | 
132 | [cols="1,2"]
133 | !===
134 | !Just for the fun of it
135 | a!
136 | .Some header
137 | [example]
138 | A deeply digged example. Interesting to see the number
139 | !===
140 | ====
141 | |===
142 | 
143 | == Admonitions
144 | 
145 | Some text.
146 | 
147 | [IMPORTANT]
148 | .Feeding the Werewolves
149 | ====
150 | While werewolves are hardy community members, keep in mind the following dietary concerns:
151 | 
152 | . They are allergic to cinnamon.
153 | . More than two glasses of orange juice in 24 hours makes them howl in harmony with alarms and sirens.
154 | . Celery makes them sad.
155 | ====
156 | 
157 | Some text.
158 | 
159 | NOTE: Inline admonition
160 | 
161 | == Test equations
162 | 
163 | Reference to equation <>.
164 | 
165 | [latexmath]
166 | ++++
167 | C = \alpha + \beta Y^{\gamma} + \epsilon
168 | ++++
169 | 
170 | 
171 | [[eq-1]]
172 | [latexmath]
173 | .({counter: eq})
174 | ++++
175 | \begin{bmatrix}
176 | a & b \\
177 | c & d
178 | \end{bmatrix}\binom{n}{k}
179 | ++++
180 | 
181 | And here is an inline equation stem:[sqrt(4) = 2].
182 | 
183 | == Test discrete
184 | 
185 | [discrete]
186 | === Discrete heading
187 | 
188 | Some text
189 | 
190 | [discrete]
191 | ===== One more discrete heading
192 | 
193 | Some more text
194 | 
195 | == Test footnotes
196 | 
197 | The example of footnotefootnote:f1[A referenced footnote] and its referencefootnote:f1[]. Just a simple footnotefootnote:[Just a footnote].
198 | 
199 | == Test images 1
200 | 
201 | .Base64 image example
202 | image::{base64-image-example}[width=10%]
203 | 
204 | image:asciidoc_icon.png[width=20px] Asciidoc inline icon, defined in pixels (20px), here is a long text to make this icon fall at least to the second line: image:asciidoc_icon.png[width=20px]. And some text after to have at least third line after this icon, just to see how does it influence line spacing.
205 | 
206 | The example of Asciidoc inline Base64 icon without width dimensions, it is 5px. Assuming 100 dpi resolution it should have smaller height than its line: image:{base64-image-example}[]. And some text after to have at least third line after this icon, just to see how does it influence line spacing.
207 | 
208 | This label height should be exactly 5mm in height: image:ehjnem.png[fitrect="170x5mm"]
209 | 
210 | .SVG example
211 | image::svg_example.svg[srcdpi=300, fitrect="170x250mm"]
212 | 
213 | .SVG example 60x20
214 | image::60x20-label.svg[fitrect="170x250mm", svgunit="mm"]
215 | 
216 | .Spring slush
217 | image::rasputicza.jpg[width=100%]
218 | 
219 | .Image form URI
220 | image::https://habrastorage.org/webt/zh/nm/-y/zhnm-ybyd4wrl7i6wnxdxnm_ki8.png[width=100%]
221 | 
222 | == Vertical image test
223 | 
224 | Both images should look the same way in browser (10%), but differently in odt.
225 | 
226 | .Ruler, made to fit page vertically (if condidered 500dpi)
227 | image::ruler.png[fitrect="170x225mm", srcdpi=500, width = "10%"]
228 | 
229 | .Ruler, made to fit page vertically (if considered 100dpi)
230 | image::ruler.png[fitrect="170x225mm", srcdpi=100, width = "10%"]
231 | 
232 | 
233 | :!sectnums:
234 | 
235 | == Test Lists (this section heading is without a number)
236 | 
237 | :sectnums:
238 | 
239 | [square]
240 | .Marked list heading
241 | * First point (long line with fitted width)
242 | +
243 | image::long_line.png[]
244 | ** Subpoint 1 (long line with fitted width)
245 | +
246 | image::long_line.png[]
247 | . And a little mix-in (short line that shouldn't be fitted due to size)
248 | +
249 | image::short_line.png[]
250 | ** Subpoint 2
251 | * Second point with no bullet
252 | * Third point
253 | [no-bullet]
254 | ** Non-bulleted subpoint 1
255 | ** Non-bulleted subpoint 2
256 | 
257 | .Numbered list heading
258 | . First point
259 | +
260 | .Admonition in list heading
261 | [WARNING.list-level1-admonition] 
262 | ==== 
263 | Here the custom role `list-level1-admonition` is applied
264 | ====
265 | .. Subpoint 1
266 | ... Subsubpoint 1
267 | ... Subsubpoint 2
268 | .. Subpoint 2
269 | . Second point
270 | +
271 | Second point continuation. For example some large paragraph, that describes the jist by all necessary means.
272 | . Third point
273 | .. Third sub point
274 | 
275 | + 
276 | Third point (not sub) continuation. For example some large paragraph, that describes the jist by all necessary means.
277 | . Forth point
278 | 
279 | [.landscape]
280 | == A Test Section 1 (landscape)
281 | 
282 | Example of _italic_, *bold*, ~subscript~, ^superscript^, #simply marked#, [small]#small text#, [big]#big text#, [underline]#underlined text#, and `monospaced`.
283 | 
284 | Example of inline break +
285 | next string
286 | 
287 | Example of informal table with no vertical lines.
288 | 
289 | [frame=topbot, grid=rows]
290 | |===
291 | |a1 a|b1 [small]#small in table# [underline]#underlined in table#
292 | |a2 |b2
293 | |a3 |b3
294 | |a4 |b4
295 | |===
296 | 
297 | 
298 | [.portrait]
299 | === A Test Section 1.1 (portrait)
300 | 
301 | A Table (<>) Test paragraph text. A Test paragraph text. A Test paragraph text. A Test paragraph text. A Test paragraph text. A Test paragraph text. A Test paragraph text.
302 | 
303 | [.text-align-center]
304 | A Test centered paragraph text. A Test centered paragraph text. A Test centered paragraph text. A Test centered paragraph text. A Test centered paragraph text. A Test centered paragraph text. A Test centered paragraph text. A Test centered paragraph text. A Test centered paragraph text.
305 | 
306 | [[t1]]
307 | [cols="4,1,3", options="header, unbreakable"]
308 | .Table title
309 | |===
310 | ^|a1 centered
311 | 
312 | .^|b1 centered vertically
313 | |c1 Here a very long multiline text is situated in order to show that vertical alignment works
314 | 2+a|a2 spanned
315 | 
316 | Asciidoc contents
317 | 
318 | * a
319 | . a1
320 | . a2
321 | +
322 | And some text as a paragraph to check paragraph in list alignment
323 | * b
324 | 
325 | |c2
326 | h|a3 (cell header)
327 | .2+|b3 spanned vertically
328 | ^|c3 just centered
329 | |a4
330 | 
331 | |c5
332 | 2+a|
333 | a5
334 | [cols="1,1"]
335 | !===
336 | !sa1 !sa2
337 | !sb1 !sb2
338 | !===
339 | |b5
340 | a|
341 | a6
342 | 
343 | CAUTION: Inline in table
344 | 
345 | 2+a|
346 | b6
347 | 
348 | .Table TIP caption
349 | [TIP]
350 | ====
351 | And some TIP in a table
352 | ====
353 | 3+|a7
354 | 3+|a8
355 | 3+|a9
356 | 3+|a10
357 | 
358 | |===
359 | 
360 | [.text-align-right]
361 | A Test right-aligned paragraph text.
362 | 
363 | == Some long, very long, very very long, very very very long, very very very very long h e a d i n g of level 1 (doesn't hang over page number)
364 | === Some long, very long, very very long, very very very long, very very very very long h e a d i n g of level 2
365 | === Some another with possible break above long, very long, very very long, very very very long, very very very very long h e a d i n g of level 2
366 | ==== Some long, very long, very very long, very very very long, very very very very long h e a d i n g of level 3
367 | ===== Some long, very long, very very long, very very very long, very very very very long h e a d i n g of level 4
368 | ====== Some long, very long, very very long, very very very long, very very very very long h e a d i n g of level 5
369 | 
370 | [appendix]
371 | == Appendix example
372 | 
373 | An appendix
374 | 


--------------------------------------------------------------------------------
/test/test_cases/typography/ttest.adoc:
--------------------------------------------------------------------------------
   1 | = Typography tests
   2 | :xrefstyle: short
   3 | :sectnums:
   4 | :stem:
   5 | :mathematical-format: svg
   6 | :doctype: book
   7 | :source-highlighter: rouge
   8 | 
   9 | == Some section with rather a long ong ong long long long long long long long long long long name
  10 | 
  11 | para
  12 | 
  13 | para
  14 | 
  15 | para
  16 | 
  17 | para
  18 | 
  19 | para
  20 | 
  21 | para
  22 | 
  23 | para
  24 | 
  25 | para
  26 | 
  27 | para
  28 | 
  29 | para
  30 | 
  31 | para
  32 | 
  33 | para
  34 | 
  35 | para
  36 | 
  37 | para
  38 | 
  39 | para
  40 | 
  41 | [cols="1,7"]
  42 | |===
  43 | |Some paragraph text, that shouldn't split between pages
  44 | |
  45 | |===
  46 | 
  47 | para
  48 | 
  49 | para
  50 | 
  51 | para
  52 | 
  53 | para
  54 | 
  55 | para
  56 | 
  57 | para
  58 | 
  59 | para
  60 | 
  61 | para
  62 | 
  63 | para
  64 | 
  65 | para
  66 | 
  67 | para
  68 | 
  69 | para
  70 | 
  71 | para
  72 | 
  73 | para
  74 | 
  75 | para
  76 | 
  77 | para
  78 | 
  79 | para
  80 | 
  81 | para
  82 | 
  83 | para
  84 | 
  85 | para
  86 | 
  87 | para
  88 | 
  89 | para
  90 | 
  91 | para
  92 | 
  93 | para
  94 | 
  95 | para
  96 | 
  97 | para
  98 | 
  99 | para
 100 | 
 101 | para
 102 | 
 103 | para
 104 | 
 105 | para
 106 | 
 107 | para
 108 | 
 109 | para
 110 | 
 111 | para
 112 | 
 113 | para
 114 | 
 115 | para
 116 | 
 117 | para
 118 | 
 119 | para
 120 | 
 121 | para
 122 | 
 123 | para
 124 | 
 125 | para
 126 | 
 127 | para
 128 | 
 129 | para
 130 | 
 131 | para
 132 | 
 133 | para
 134 | 
 135 | para
 136 | 
 137 | para
 138 | 
 139 | para
 140 | 
 141 | para
 142 | 
 143 | para
 144 | 
 145 | para
 146 | 
 147 | para
 148 | 
 149 | para
 150 | 
 151 | para
 152 | 
 153 | para
 154 | 
 155 | para
 156 | 
 157 | para
 158 | 
 159 | para
 160 | 
 161 | para
 162 | 
 163 | para
 164 | 
 165 | para
 166 | 
 167 | para
 168 | 
 169 | para
 170 | 
 171 | para
 172 | 
 173 | para
 174 | 
 175 | para
 176 | 
 177 | para
 178 | 
 179 | para
 180 | 
 181 | para
 182 | 
 183 | para
 184 | 
 185 | para
 186 | 
 187 | para
 188 | 
 189 | para
 190 | 
 191 | para
 192 | 
 193 | para
 194 | 
 195 | para
 196 | 
 197 | para
 198 | 
 199 | para
 200 | 
 201 | para
 202 | 
 203 | para
 204 | 
 205 | para
 206 | 
 207 | para
 208 | 
 209 | para
 210 | 
 211 | para
 212 | 
 213 | para
 214 | 
 215 | para
 216 | 
 217 | para
 218 | 
 219 | para
 220 | 
 221 | para
 222 | 
 223 | para
 224 | 
 225 | para
 226 | 
 227 | para
 228 | 
 229 | para
 230 | 
 231 | para
 232 | 
 233 | para
 234 | 
 235 | para
 236 | 
 237 | para
 238 | 
 239 | para
 240 | 
 241 | para
 242 | 
 243 | para
 244 | 
 245 | para
 246 | 
 247 | para
 248 | 
 249 | para
 250 | 
 251 | para
 252 | 
 253 | para
 254 | 
 255 | para
 256 | 
 257 | para
 258 | 
 259 | para
 260 | 
 261 | para
 262 | 
 263 | para
 264 | 
 265 | para
 266 | 
 267 | para
 268 | 
 269 | para
 270 | 
 271 | para
 272 | 
 273 | para
 274 | 
 275 | para
 276 | 
 277 | para
 278 | 
 279 | para
 280 | 
 281 | para
 282 | 
 283 | para
 284 | 
 285 | para
 286 | 
 287 | para
 288 | 
 289 | para
 290 | 
 291 | para
 292 | 
 293 | para
 294 | 
 295 | para
 296 | 
 297 | para
 298 | 
 299 | para
 300 | 
 301 | para
 302 | 
 303 | para
 304 | 
 305 | para
 306 | 
 307 | para
 308 | 
 309 | para
 310 | 
 311 | para
 312 | 
 313 | para
 314 | 
 315 | para
 316 | 
 317 | para
 318 | 
 319 | para
 320 | 
 321 | para
 322 | 
 323 | para
 324 | 
 325 | para
 326 | 
 327 | para
 328 | 
 329 | para
 330 | 
 331 | para
 332 | 
 333 | para
 334 | 
 335 | para
 336 | 
 337 | para
 338 | 
 339 | para
 340 | 
 341 | para
 342 | 
 343 | para
 344 | 
 345 | para
 346 | 
 347 | para
 348 | 
 349 | para
 350 | 
 351 | para
 352 | 
 353 | para
 354 | 
 355 | para
 356 | 
 357 | para
 358 | 
 359 | para
 360 | 
 361 | para
 362 | 
 363 | para
 364 | 
 365 | para
 366 | 
 367 | para
 368 | 
 369 | para
 370 | 
 371 | para
 372 | 
 373 | para
 374 | 
 375 | para
 376 | 
 377 | para
 378 | 
 379 | para
 380 | 
 381 | para
 382 | 
 383 | para
 384 | 
 385 | para
 386 | 
 387 | para
 388 | 
 389 | para
 390 | 
 391 | para
 392 | 
 393 | para
 394 | 
 395 | para
 396 | 
 397 | para
 398 | 
 399 | para
 400 | 
 401 | para
 402 | 
 403 | para
 404 | 
 405 | para
 406 | 
 407 | para
 408 | 
 409 | para
 410 | 
 411 | para
 412 | 
 413 | para
 414 | 
 415 | para
 416 | 
 417 | para
 418 | 
 419 | para
 420 | 
 421 | para
 422 | 
 423 | para
 424 | 
 425 | para
 426 | 
 427 | para
 428 | 
 429 | para
 430 | 
 431 | para
 432 | 
 433 | para
 434 | 
 435 | para
 436 | 
 437 | para
 438 | 
 439 | para
 440 | 
 441 | para
 442 | 
 443 | para
 444 | 
 445 | para
 446 | 
 447 | para
 448 | 
 449 | para
 450 | 
 451 | para
 452 | 
 453 | para
 454 | 
 455 | para
 456 | 
 457 | para
 458 | 
 459 | para
 460 | 
 461 | para
 462 | 
 463 | para
 464 | 
 465 | para
 466 | 
 467 | para
 468 | 
 469 | para
 470 | 
 471 | para
 472 | 
 473 | para
 474 | 
 475 | para
 476 | 
 477 | para
 478 | 
 479 | para
 480 | 
 481 | para
 482 | 
 483 | para
 484 | 
 485 | para
 486 | 
 487 | para
 488 | 
 489 | para
 490 | 
 491 | para
 492 | 
 493 | para
 494 | 
 495 | para
 496 | 
 497 | para
 498 | 
 499 | para
 500 | 
 501 | para
 502 | 
 503 | para
 504 | 
 505 | para
 506 | 
 507 | para
 508 | 
 509 | para
 510 | 
 511 | para
 512 | 
 513 | para
 514 | 
 515 | para
 516 | 
 517 | para
 518 | 
 519 | para
 520 | 
 521 | para
 522 | 
 523 | para
 524 | 
 525 | para
 526 | 
 527 | para
 528 | 
 529 | para
 530 | 
 531 | para
 532 | 
 533 | para
 534 | 
 535 | para
 536 | 
 537 | para
 538 | 
 539 | para
 540 | 
 541 | para
 542 | 
 543 | para
 544 | 
 545 | para
 546 | 
 547 | para
 548 | 
 549 | para
 550 | 
 551 | para
 552 | 
 553 | para
 554 | 
 555 | para
 556 | 
 557 | para
 558 | 
 559 | para
 560 | 
 561 | para
 562 | 
 563 | para
 564 | 
 565 | para
 566 | 
 567 | para
 568 | 
 569 | para
 570 | 
 571 | para
 572 | 
 573 | para
 574 | 
 575 | para
 576 | 
 577 | para
 578 | 
 579 | para
 580 | 
 581 | para
 582 | 
 583 | para
 584 | 
 585 | para
 586 | 
 587 | para
 588 | 
 589 | para
 590 | 
 591 | para
 592 | 
 593 | para
 594 | 
 595 | para
 596 | 
 597 | para
 598 | 
 599 | para
 600 | 
 601 | para
 602 | 
 603 | para
 604 | 
 605 | para
 606 | 
 607 | para
 608 | 
 609 | para
 610 | 
 611 | para
 612 | 
 613 | para
 614 | 
 615 | [cols="1,7"]
 616 | |===
 617 | |Some paragraph text, that shouldn't split between pages
 618 | |
 619 | |===
 620 | 
 621 | para
 622 | 
 623 | para
 624 | 
 625 | para
 626 | 
 627 | para
 628 | 
 629 | para
 630 | 
 631 | para
 632 | 
 633 | para
 634 | 
 635 | para
 636 | 
 637 | para
 638 | 
 639 | para
 640 | 
 641 | para
 642 | 
 643 | para
 644 | 
 645 | para
 646 | 
 647 | para
 648 | 
 649 | para
 650 | 
 651 | para
 652 | 
 653 | para
 654 | 
 655 | para
 656 | 
 657 | para
 658 | 
 659 | para
 660 | 
 661 | para
 662 | 
 663 | para
 664 | 
 665 | para
 666 | 
 667 | para
 668 | 
 669 | para
 670 | 
 671 | para
 672 | 
 673 | para
 674 | 
 675 | para
 676 | 
 677 | para
 678 | 
 679 | para
 680 | 
 681 | para
 682 | 
 683 | para
 684 | 
 685 | para
 686 | 
 687 | para
 688 | 
 689 | para
 690 | 
 691 | para
 692 | 
 693 | para
 694 | 
 695 | para
 696 | 
 697 | para
 698 | 
 699 | para
 700 | 
 701 | para
 702 | 
 703 | para
 704 | 
 705 | para
 706 | 
 707 | para
 708 | 
 709 | para
 710 | 
 711 | para
 712 | 
 713 | para
 714 | 
 715 | para
 716 | 
 717 | para
 718 | 
 719 | para
 720 | 
 721 | para
 722 | 
 723 | para
 724 | 
 725 | para
 726 | 
 727 | para
 728 | 
 729 | para
 730 | 
 731 | para
 732 | 
 733 | para
 734 | 
 735 | para
 736 | 
 737 | para
 738 | 
 739 | para
 740 | 
 741 | para
 742 | 
 743 | para
 744 | 
 745 | para
 746 | 
 747 | para
 748 | 
 749 | para
 750 | 
 751 | para
 752 | 
 753 | para
 754 | 
 755 | para
 756 | 
 757 | para
 758 | 
 759 | para
 760 | 
 761 | para
 762 | 
 763 | para
 764 | 
 765 | para
 766 | 
 767 | para
 768 | 
 769 | para
 770 | 
 771 | para
 772 | 
 773 | para
 774 | 
 775 | para
 776 | 
 777 | para
 778 | 
 779 | para
 780 | 
 781 | para
 782 | 
 783 | para
 784 | 
 785 | para
 786 | 
 787 | para
 788 | 
 789 | para
 790 | 
 791 | para
 792 | 
 793 | para
 794 | 
 795 | para
 796 | 
 797 | para
 798 | 
 799 | para
 800 | 
 801 | para
 802 | 
 803 | para
 804 | 
 805 | para
 806 | 
 807 | para
 808 | 
 809 | para
 810 | 
 811 | para
 812 | 
 813 | para
 814 | 
 815 | para
 816 | 
 817 | para
 818 | 
 819 | para
 820 | 
 821 | para
 822 | 
 823 | para
 824 | 
 825 | para
 826 | 
 827 | para
 828 | 
 829 | para
 830 | 
 831 | para
 832 | 
 833 | para
 834 | 
 835 | para
 836 | 
 837 | para
 838 | 
 839 | para
 840 | 
 841 | para
 842 | 
 843 | para
 844 | 
 845 | para
 846 | 
 847 | para
 848 | 
 849 | para
 850 | 
 851 | para
 852 | 
 853 | para
 854 | 
 855 | para
 856 | 
 857 | para
 858 | 
 859 | para
 860 | 
 861 | para
 862 | 
 863 | para
 864 | 
 865 | para
 866 | 
 867 | para
 868 | 
 869 | para
 870 | 
 871 | para
 872 | 
 873 | para
 874 | 
 875 | para
 876 | 
 877 | para
 878 | 
 879 | para
 880 | 
 881 | para
 882 | 
 883 | para
 884 | 
 885 | para
 886 | 
 887 | para
 888 | 
 889 | para
 890 | 
 891 | para
 892 | 
 893 | para
 894 | 
 895 | para
 896 | 
 897 | para
 898 | 
 899 | para
 900 | 
 901 | para
 902 | 
 903 | para
 904 | 
 905 | para
 906 | 
 907 | para
 908 | 
 909 | para
 910 | 
 911 | para
 912 | 
 913 | para
 914 | 
 915 | para
 916 | 
 917 | para
 918 | 
 919 | para
 920 | 
 921 | para
 922 | 
 923 | para
 924 | 
 925 | para
 926 | 
 927 | para
 928 | 
 929 | para
 930 | 
 931 | para
 932 | 
 933 | para
 934 | 
 935 | para
 936 | 
 937 | para
 938 | 
 939 | para
 940 | 
 941 | para
 942 | 
 943 | para
 944 | 
 945 | para
 946 | 
 947 | para
 948 | 
 949 | para
 950 | 
 951 | para
 952 | 
 953 | para
 954 | 
 955 | para
 956 | 
 957 | para
 958 | 
 959 | para
 960 | 
 961 | para
 962 | 
 963 | para
 964 | 
 965 | para
 966 | 
 967 | para
 968 | 
 969 | para
 970 | 
 971 | para
 972 | 
 973 | para
 974 | 
 975 | para
 976 | 
 977 | para
 978 | 
 979 | para
 980 | 
 981 | para
 982 | 
 983 | para
 984 | 
 985 | para
 986 | 
 987 | para
 988 | 
 989 | para
 990 | 
 991 | para
 992 | 
 993 | para
 994 | 
 995 | para
 996 | 
 997 | para
 998 | 
 999 | para
1000 | 
1001 | para
1002 | 
1003 | para
1004 | 
1005 | para
1006 | 
1007 | para
1008 | 
1009 | para
1010 | 
1011 | para
1012 | 
1013 | para
1014 | 
1015 | para
1016 | 
1017 | para
1018 | 
1019 | para
1020 | 
1021 | para
1022 | 
1023 | para
1024 | 
1025 | para
1026 | 
1027 | para
1028 | 
1029 | para
1030 | 
1031 | para
1032 | 
1033 | para
1034 | 
1035 | para
1036 | 
1037 | para
1038 | 
1039 | para
1040 | 
1041 | para
1042 | 
1043 | para
1044 | 
1045 | para
1046 | 
1047 | para
1048 | 
1049 | para
1050 | 
1051 | para
1052 | 
1053 | para
1054 | 
1055 | para
1056 | 
1057 | para
1058 | 
1059 | para
1060 | 
1061 | para
1062 | 
1063 | para
1064 | 
1065 | para
1066 | 
1067 | para
1068 | 
1069 | para
1070 | 
1071 | para
1072 | 
1073 | para
1074 | 
1075 | para
1076 | 
1077 | para
1078 | 
1079 | para
1080 | 
1081 | para
1082 | 
1083 | para
1084 | 
1085 | para
1086 | 
1087 | para
1088 | 
1089 | para
1090 | 
1091 | para
1092 | 
1093 | para
1094 | 
1095 | para
1096 | 
1097 | para
1098 | 
1099 | para
1100 | 
1101 | para
1102 | 
1103 | para
1104 | 
1105 | para
1106 | 
1107 | para
1108 | 
1109 | para
1110 | 
1111 | para
1112 | 
1113 | para
1114 | 
1115 | para
1116 | 
1117 | para
1118 | 
1119 | para
1120 | 
1121 | para
1122 | 
1123 | para
1124 | 
1125 | para
1126 | 
1127 | para
1128 | 
1129 | para
1130 | 
1131 | para
1132 | 
1133 | para
1134 | 
1135 | para
1136 | 
1137 | para
1138 | 
1139 | para
1140 | 
1141 | para
1142 | 
1143 | para
1144 | 
1145 | para
1146 | 
1147 | para
1148 | 
1149 | para
1150 | 
1151 | para
1152 | 
1153 | para
1154 | 
1155 | para
1156 | 
1157 | para
1158 | 
1159 | para
1160 | 
1161 | para
1162 | 
1163 | para
1164 | 
1165 | para
1166 | 
1167 | para
1168 | 
1169 | para
1170 | 
1171 | para
1172 | 
1173 | para
1174 | 
1175 | para
1176 | 
1177 | para
1178 | 
1179 | para
1180 | 
1181 | para
1182 | 
1183 | para
1184 | 
1185 | para
1186 | 
1187 | para
1188 | 
1189 | para
1190 | 
1191 | [cols="1,7"]
1192 | |===
1193 | |Some paragraph text, that shouldn't split between pages
1194 | |
1195 | |===
1196 | 
1197 | para
1198 | 
1199 | para
1200 | 
1201 | para
1202 | 
1203 | para
1204 | 
1205 | para
1206 | 
1207 | para
1208 | 
1209 | para
1210 | 
1211 | para
1212 | 
1213 | para
1214 | 
1215 | para
1216 | 
1217 | para
1218 | 
1219 | para
1220 | 
1221 | para
1222 | 
1223 | para
1224 | 
1225 | para
1226 | 
1227 | para
1228 | 
1229 | para
1230 | 
1231 | para
1232 | 
1233 | para
1234 | 
1235 | para
1236 | 
1237 | para
1238 | 
1239 | para
1240 | 
1241 | para
1242 | 
1243 | para
1244 | 
1245 | para
1246 | 
1247 | para
1248 | 
1249 | para
1250 | 
1251 | para
1252 | 
1253 | para
1254 | 
1255 | para
1256 | 
1257 | para
1258 | 
1259 | para
1260 | 
1261 | para
1262 | 
1263 | para
1264 | 
1265 | para
1266 | 
1267 | para
1268 | 
1269 | para
1270 | 
1271 | para
1272 | 
1273 | para
1274 | 
1275 | para
1276 | 
1277 | para
1278 | 
1279 | para
1280 | 
1281 | para
1282 | 
1283 | para
1284 | 
1285 | para
1286 | 
1287 | para
1288 | 
1289 | para
1290 | 
1291 | para
1292 | 
1293 | para
1294 | 
1295 | para
1296 | 
1297 | para
1298 | 
1299 | para
1300 | 
1301 | para
1302 | 
1303 | para
1304 | 
1305 | para
1306 | 
1307 | para
1308 | 
1309 | para
1310 | 
1311 | para
1312 | 
1313 | para
1314 | 
1315 | para
1316 | 
1317 | para
1318 | 
1319 | para
1320 | 
1321 | para
1322 | 
1323 | para
1324 | 
1325 | para
1326 | 
1327 | para
1328 | 
1329 | para
1330 | 
1331 | para
1332 | 
1333 | para
1334 | 
1335 | para
1336 | 
1337 | para
1338 | 
1339 | para
1340 | 
1341 | para
1342 | 
1343 | para
1344 | 
1345 | para
1346 | 
1347 | para
1348 | 
1349 | para
1350 | 
1351 | para
1352 | 
1353 | para
1354 | 
1355 | para
1356 | 
1357 | para
1358 | 
1359 | para
1360 | 
1361 | para
1362 | 
1363 | para
1364 | 
1365 | para
1366 | 
1367 | para
1368 | 
1369 | para
1370 | 
1371 | para
1372 | 
1373 | para
1374 | 
1375 | para
1376 | 
1377 | para
1378 | 
1379 | para
1380 | 
1381 | para
1382 | 
1383 | para
1384 | 
1385 | para
1386 | 
1387 | para
1388 | 
1389 | para
1390 | 
1391 | para
1392 | 
1393 | para
1394 | 
1395 | para
1396 | 
1397 | para
1398 | 
1399 | para
1400 | 
1401 | para
1402 | 
1403 | para
1404 | 
1405 | para
1406 | 
1407 | para
1408 | 
1409 | para
1410 | 
1411 | para
1412 | 
1413 | para
1414 | 
1415 | para
1416 | 
1417 | para
1418 | 
1419 | para
1420 | 
1421 | para
1422 | 
1423 | para
1424 | 
1425 | para
1426 | 
1427 | para
1428 | 
1429 | para
1430 | 
1431 | para
1432 | 
1433 | para
1434 | 
1435 | para
1436 | 
1437 | para
1438 | 
1439 | para
1440 | 
1441 | para
1442 | 
1443 | para
1444 | 
1445 | para
1446 | 
1447 | para
1448 | 
1449 | para
1450 | 
1451 | para
1452 | 
1453 | para
1454 | 
1455 | para
1456 | 
1457 | para
1458 | 
1459 | para
1460 | 
1461 | para
1462 | 
1463 | para
1464 | 
1465 | para
1466 | 
1467 | para
1468 | 
1469 | para
1470 | 
1471 | para
1472 | 
1473 | para
1474 | 
1475 | para
1476 | 
1477 | para
1478 | 
1479 | para
1480 | 
1481 | para
1482 | 
1483 | para
1484 | 
1485 | para
1486 | 
1487 | para
1488 | 
1489 | para
1490 | 
1491 | para
1492 | 
1493 | para
1494 | 
1495 | para
1496 | 
1497 | para
1498 | 
1499 | para
1500 | 
1501 | para
1502 | 
1503 | para
1504 | 
1505 | para
1506 | 
1507 | para
1508 | 
1509 | para
1510 | 
1511 | para
1512 | 
1513 | para
1514 | 
1515 | para
1516 | 
1517 | para
1518 | 
1519 | para
1520 | 
1521 | para
1522 | 
1523 | para
1524 | 
1525 | para
1526 | 
1527 | para
1528 | 
1529 | para
1530 | 
1531 | para
1532 | 
1533 | para
1534 | 
1535 | para
1536 | 
1537 | para
1538 | 
1539 | para
1540 | 
1541 | para
1542 | 
1543 | para
1544 | 
1545 | para
1546 | 
1547 | para
1548 | 
1549 | para
1550 | 
1551 | para
1552 | 
1553 | para
1554 | 
1555 | para
1556 | 
1557 | para
1558 | 
1559 | para
1560 | 
1561 | para
1562 | 
1563 | para
1564 | 
1565 | para
1566 | 
1567 | para
1568 | 
1569 | para
1570 | 
1571 | para
1572 | 
1573 | para
1574 | 
1575 | para
1576 | 
1577 | para
1578 | 
1579 | para
1580 | 
1581 | para
1582 | 
1583 | para
1584 | 
1585 | para
1586 | 
1587 | para
1588 | 
1589 | para
1590 | 
1591 | para
1592 | 
1593 | para
1594 | 
1595 | para
1596 | 
1597 | para
1598 | 
1599 | para
1600 | 
1601 | para
1602 | 
1603 | para
1604 | 
1605 | para
1606 | 
1607 | para
1608 | 
1609 | para
1610 | 
1611 | para
1612 | 
1613 | para
1614 | 
1615 | para
1616 | 
1617 | para
1618 | 
1619 | para
1620 | 
1621 | para
1622 | 
1623 | para
1624 | 
1625 | para
1626 | 
1627 | para
1628 | 
1629 | para
1630 | 
1631 | para
1632 | 
1633 | para
1634 | 
1635 | para
1636 | 
1637 | para
1638 | 
1639 | para
1640 | 
1641 | para
1642 | 
1643 | para
1644 | 
1645 | para
1646 | 
1647 | para
1648 | 
1649 | para
1650 | 
1651 | para
1652 | 
1653 | para
1654 | 
1655 | para
1656 | 
1657 | para
1658 | 
1659 | para
1660 | 
1661 | para
1662 | 
1663 | para
1664 | 
1665 | para
1666 | 
1667 | para
1668 | 
1669 | para
1670 | 
1671 | para
1672 | 
1673 | para
1674 | 
1675 | para
1676 | 
1677 | para
1678 | 
1679 | para
1680 | 
1681 | para
1682 | 
1683 | para
1684 | 
1685 | para
1686 | 
1687 | para
1688 | 
1689 | para
1690 | 
1691 | para
1692 | 
1693 | para
1694 | 
1695 | para
1696 | 
1697 | para
1698 | 
1699 | para
1700 | 
1701 | para
1702 | 
1703 | para
1704 | 
1705 | para
1706 | 
1707 | para
1708 | 
1709 | para
1710 | 
1711 | para
1712 | 
1713 | para
1714 | 
1715 | para
1716 | 
1717 | para
1718 | 
1719 | para
1720 | 
1721 | para
1722 | 
1723 | para
1724 | 
1725 | para
1726 | 
1727 | para
1728 | 
1729 | para
1730 | 
1731 | para
1732 | 
1733 | para
1734 | 
1735 | para
1736 | 
1737 | para
1738 | 
1739 | para
1740 | 
1741 | para
1742 | 
1743 | para
1744 | 
1745 | para
1746 | 
1747 | para
1748 | 
1749 | para
1750 | 
1751 | para
1752 | 
1753 | == Some another section with rather a long long long long long long long long lollg lollg lollg nall
1754 | 


--------------------------------------------------------------------------------
/test/test_fodt.rb:
--------------------------------------------------------------------------------
 1 | require 'nokogiri' 
 2 | schema_path = "resources/OpenDocument-v1.2-os-schema.rng"   
 3 | doc_path = "target/stew/test.fodt_"
 4 | template_path  = "lib/a-od-producer/basic-template.fodt" 
 5 | 
 6 | schema = Nokogiri::XML::RelaxNG(File.open(schema_path))
 7 | 
 8 | instance = Nokogiri::XML(File.open(doc_path))
 9 | template_instance = Nokogiri::XML(File.open(template_path))
10 | instance_errors = schema.validate(instance)
11 | template_errors = schema.validate(template_instance)
12 | instance_error_count = 0
13 | instance_errors.each_with_index do |error, index|
14 |   if error != template_errors[index]
15 |     puts error 
16 |     instance_error_count += 1
17 |   end
18 | end
19 | 
20 | puts "#{instance_error_count} fodt errors"
21 | 


--------------------------------------------------------------------------------