├── src ├── test │ ├── resources │ │ ├── conversions │ │ │ ├── markdown │ │ │ │ ├── tables-remove.md │ │ │ │ ├── abbr.md │ │ │ │ ├── inlinestyle-emptybreak.md │ │ │ │ ├── unknownHTML.md │ │ │ │ ├── horizontalrule.md │ │ │ │ ├── abbr-enabled.md │ │ │ │ ├── unknownHTML-allowed.md │ │ │ │ ├── broken.md │ │ │ │ ├── paragraph.md │ │ │ │ ├── inlinecode.md │ │ │ │ ├── blockquote.md │ │ │ │ ├── header.md │ │ │ │ ├── break-hardwrap.md │ │ │ │ ├── break.md │ │ │ │ ├── header-headerids.md │ │ │ │ ├── inlinestyle-inword-removed.md │ │ │ │ ├── inlinestyle.md │ │ │ │ ├── inlinestyle-inword-spaced.md │ │ │ │ ├── codeblock.md │ │ │ │ ├── definitions-enabled.md │ │ │ │ ├── tables-multimarkdown.md │ │ │ │ ├── definitions.md │ │ │ │ ├── tables-markdownextra.md │ │ │ │ ├── anchor-simpleids.md │ │ │ │ ├── anchor-inline.md │ │ │ │ ├── anchor-relative.md │ │ │ │ ├── tables-codeblock.md │ │ │ │ ├── anchor-autolink.md │ │ │ │ ├── codeblock-fenced-backtick.md │ │ │ │ ├── codeblock-fenced-tilde.md │ │ │ │ ├── listordered.md │ │ │ │ ├── listunordered.md │ │ │ │ ├── anchor.md │ │ │ │ ├── image.md │ │ │ │ └── tables.md │ │ │ └── html │ │ │ │ ├── horizontalrule.html │ │ │ │ ├── inlinestyle-emptybreak.html │ │ │ │ ├── unknownHTML.html │ │ │ │ ├── abbr.html │ │ │ │ ├── break.html │ │ │ │ ├── paragraph.html │ │ │ │ ├── inlinecode.html │ │ │ │ ├── blockquote.html │ │ │ │ ├── header.html │ │ │ │ ├── broken.html │ │ │ │ ├── image.html │ │ │ │ ├── codeblock.html │ │ │ │ ├── anchor.html │ │ │ │ ├── definitions.html │ │ │ │ ├── listordered.html │ │ │ │ ├── listunordered.html │ │ │ │ ├── inlinestyle.html │ │ │ │ └── tables.html │ │ ├── textcleaner │ │ │ ├── cleanCodeBasic.out.txt │ │ │ ├── cleanBasic.out.txt │ │ │ ├── basic.in.txt │ │ │ ├── cleanCodeFull.out.txt │ │ │ ├── cleanFull.out.txt │ │ │ └── full.in.txt │ │ └── util │ │ │ ├── MarkdownTableTest.md │ │ │ ├── MarkdownTableColspanTest.md │ │ │ ├── MarkdownTableAsCodeTest.md │ │ │ ├── MarkdownTableAlignmentTest.md │ │ │ └── MarkdownTableWideColspanTest.md │ └── java │ │ └── com │ │ └── overzealous │ │ └── remark │ │ ├── convert │ │ ├── BrokenHTMLTest.java │ │ ├── AnchorInlineTest.java │ │ ├── AnchorSimpleIdTest.java │ │ ├── TableRemoveTest.java │ │ ├── AnchorRelativeLinkTest.java │ │ ├── InwordEmphasisSpacedTest.java │ │ ├── IgnoredHTMLTest.java │ │ ├── PlainMarkdownTest.java │ │ ├── MultiMarkdownTest.java │ │ ├── GithubTest.java │ │ ├── MarkdownExtraTest.java │ │ ├── PegdownTest.java │ │ ├── RemarkTester.java │ │ ├── DocumentConverterTest.java │ │ └── TextCleanerTest.java │ │ └── util │ │ ├── TestUtils.java │ │ ├── StringUtilsTest.java │ │ ├── BlockWriterTest.java │ │ └── MarkdownTableTest.java ├── manual │ ├── favicon.ico │ ├── images │ │ ├── icon128.png │ │ ├── icon16.png │ │ ├── icon32.png │ │ ├── top-header.png │ │ ├── header-marker.png │ │ └── selected-tab.png │ ├── apple-touch-icon.png │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon-57x57-precomposed.png │ ├── apple-touch-icon-72x72-precomposed.png │ ├── apple-touch-icon-114x114-precomposed.png │ ├── .htaccess │ ├── js │ │ └── libs │ │ │ └── prettify │ │ │ └── prettify.css │ ├── license.md │ ├── _template.html │ ├── index.md │ ├── usage.md │ ├── examples.md │ └── css │ │ ├── style.css │ │ └── normalize.css └── main │ └── java │ └── com │ └── overzealous │ └── remark │ ├── util │ ├── package-info.java │ ├── MarkdownTableCell.java │ ├── StringUtils.java │ └── MarkdownTable.java │ ├── convert │ ├── package-info.java │ ├── HorizontalRule.java │ ├── InlineCode.java │ ├── BlockQuote.java │ ├── Paragraph.java │ ├── Abbr.java │ ├── Break.java │ ├── NodeRemover.java │ ├── DefaultNodeHandler.java │ ├── Header.java │ ├── NodeHandler.java │ ├── Image.java │ ├── List.java │ ├── Codeblock.java │ ├── Anchor.java │ ├── AbstractNodeHandler.java │ ├── Definitions.java │ └── Table.java │ ├── package-info.java │ ├── IgnoredHtmlElement.java │ └── Main.java ├── .travis.yml ├── LICENSE ├── .gitignore ├── pom.xml └── README.markdown /src/test/resources/conversions/markdown/tables-remove.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/abbr.md: -------------------------------------------------------------------------------- 1 | before HTML after 2 | 3 | before HTML after -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/inlinestyle-emptybreak.md: -------------------------------------------------------------------------------- 1 | *Empty Break Test* 2 | -------------------------------------------------------------------------------- /src/manual/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giflw/remark-java/HEAD/src/manual/favicon.ico -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/unknownHTML.md: -------------------------------------------------------------------------------- 1 | Division 2 | 3 | Foo Bar 4 | 5 | *Variable* -------------------------------------------------------------------------------- /src/test/resources/conversions/html/horizontalrule.html: -------------------------------------------------------------------------------- 1 |

Some text

2 |
3 |

Some more text

-------------------------------------------------------------------------------- /src/test/resources/conversions/html/inlinestyle-emptybreak.html: -------------------------------------------------------------------------------- 1 |

Empty Break Test

-------------------------------------------------------------------------------- /src/manual/images/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giflw/remark-java/HEAD/src/manual/images/icon128.png -------------------------------------------------------------------------------- /src/manual/images/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giflw/remark-java/HEAD/src/manual/images/icon16.png -------------------------------------------------------------------------------- /src/manual/images/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giflw/remark-java/HEAD/src/manual/images/icon32.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk8 5 | - oraclejdk7 6 | - openjdk7 7 | - openjdk6 8 | -------------------------------------------------------------------------------- /src/manual/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giflw/remark-java/HEAD/src/manual/apple-touch-icon.png -------------------------------------------------------------------------------- /src/manual/images/top-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giflw/remark-java/HEAD/src/manual/images/top-header.png -------------------------------------------------------------------------------- /src/manual/images/header-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giflw/remark-java/HEAD/src/manual/images/header-marker.png -------------------------------------------------------------------------------- /src/manual/images/selected-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giflw/remark-java/HEAD/src/manual/images/selected-tab.png -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/horizontalrule.md: -------------------------------------------------------------------------------- 1 | Some text 2 | 3 | -------------------- 4 | 5 | Some more text -------------------------------------------------------------------------------- /src/test/resources/textcleaner/cleanCodeBasic.out.txt: -------------------------------------------------------------------------------- 1 | + leading plus 2 | *em* **strong** 3 | < > " & < '©' 4 | ` * _ { } [ ] # -------------------------------------------------------------------------------- /src/test/resources/textcleaner/cleanBasic.out.txt: -------------------------------------------------------------------------------- 1 | \+ leading plus \*em\* \*\*strong\*\* < > " & \< '©' \` \* \_ \{ \} \[ \] \# -------------------------------------------------------------------------------- /src/manual/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giflw/remark-java/HEAD/src/manual/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /src/test/resources/textcleaner/basic.in.txt: -------------------------------------------------------------------------------- 1 | + leading plus 2 | *em* **strong** 3 | < > " & &lt; '©' 4 | ` * _ { } [ ] # -------------------------------------------------------------------------------- /src/manual/apple-touch-icon-57x57-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giflw/remark-java/HEAD/src/manual/apple-touch-icon-57x57-precomposed.png -------------------------------------------------------------------------------- /src/manual/apple-touch-icon-72x72-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giflw/remark-java/HEAD/src/manual/apple-touch-icon-72x72-precomposed.png -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/abbr-enabled.md: -------------------------------------------------------------------------------- 1 | before HTML after 2 | 3 | before HTML after 4 | 5 | 6 | *[HTML]: Hyper-Text Markup Language -------------------------------------------------------------------------------- /src/manual/apple-touch-icon-114x114-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giflw/remark-java/HEAD/src/manual/apple-touch-icon-114x114-precomposed.png -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/unknownHTML-allowed.md: -------------------------------------------------------------------------------- 1 | Division 2 | 3 | Foo Bar 4 | 5 | Variable -------------------------------------------------------------------------------- /src/manual/.htaccess: -------------------------------------------------------------------------------- 1 | RedirectMatch ^/manual$ http://remark.overzealous.com/manual/index.html 2 | RedirectMatch ^/$ http://remark.overzealous.com/manual/index.html -------------------------------------------------------------------------------- /src/test/resources/util/MarkdownTableTest.md: -------------------------------------------------------------------------------- 1 | 2 | | header1 | header2 | header3 | 3 | | -------- | -------- | -------- | 4 | | column 1 | column 2 | column 3 | -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/broken.md: -------------------------------------------------------------------------------- 1 | ***This is really bad HTML*** 2 | 3 | I'm deeply nested 4 | 5 | I'm not in a paragraph tag at all! I'm a useless tag!! -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/paragraph.md: -------------------------------------------------------------------------------- 1 | This is a paragraph 2 | 3 | This is an auto-paragraph 4 | 5 | Whitespace test 6 | 7 | line two 8 | 9 | line three -------------------------------------------------------------------------------- /src/test/resources/textcleaner/cleanCodeFull.out.txt: -------------------------------------------------------------------------------- 1 | + leading plus 2 | *em* **strong** 3 | < > " & < '©' 4 | “ ” ‘ ’ ' « » “ ” ‘ ’ « » 5 | – — … – — … 6 | ` * _ { } [ ] # | -------------------------------------------------------------------------------- /src/test/resources/util/MarkdownTableColspanTest.md: -------------------------------------------------------------------------------- 1 | 2 | | h1 | h2 | h3 | 3 | |:-------- |:----:| ----:| 4 | | col1 | col2 | col3 | 5 | | column 1 | column 2 || -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/inlinecode.md: -------------------------------------------------------------------------------- 1 | Code: ` ![]` 2 | 3 | Special Code: ``` `` ``` 4 | 5 | More Special Code: `` ` `` 6 | 7 | Code with a BR: `Two Lines` -------------------------------------------------------------------------------- /src/test/resources/util/MarkdownTableAsCodeTest.md: -------------------------------------------------------------------------------- 1 | 2 | | header1 | header2 | header3 | 3 | | -------- | -------- | -------- | 4 | | column 1 | column 2 | column 3 | -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/blockquote.md: -------------------------------------------------------------------------------- 1 | > This is quoted *text*. 2 | > 3 | > > This is also quoted *text*. 4 | 5 | * Block Quote in List 6 | 7 | > Quoted Text -------------------------------------------------------------------------------- /src/test/resources/textcleaner/cleanFull.out.txt: -------------------------------------------------------------------------------- 1 | \+ leading plus \*em\* \*\*strong\*\* < > " & \< '©' " " ' ' ' << >> " " ' ' << >> -- --- ... -- --- ... \` \* \_ \{ \} \[ \] \# \| -------------------------------------------------------------------------------- /src/test/resources/conversions/html/unknownHTML.html: -------------------------------------------------------------------------------- 1 |
2 | Division 3 |
4 | 5 |

Foo Bar

6 | 7 |

Variable

-------------------------------------------------------------------------------- /src/test/resources/conversions/html/abbr.html: -------------------------------------------------------------------------------- 1 |

before HTML after

2 |

before HTML after

-------------------------------------------------------------------------------- /src/test/resources/util/MarkdownTableAlignmentTest.md: -------------------------------------------------------------------------------- 1 | 2 | | h1 | h2 | h3 | 3 | |:-------- |:--------:| --------:| 4 | | col1 | col2 | col3 | 5 | | column 1 | column 2 | column 3 | -------------------------------------------------------------------------------- /src/test/resources/conversions/html/break.html: -------------------------------------------------------------------------------- 1 | Testing non-para breaks
broken!
2 |

Testing paragraph break
broken!

3 |

Testing anchor breaks
broken!

-------------------------------------------------------------------------------- /src/test/resources/conversions/html/paragraph.html: -------------------------------------------------------------------------------- 1 |

This is a paragraph

2 | 3 | This is an auto-paragraph 4 | 5 |

Whitespace test

line two

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

line three

-------------------------------------------------------------------------------- /src/test/resources/textcleaner/full.in.txt: -------------------------------------------------------------------------------- 1 | + leading plus 2 | *em* **strong** 3 | < > " & &lt; '©' 4 | “ ” ‘ ’ ' « » “ ” ‘ ’ « » 5 | – — … – — … 6 | ` * _ { } [ ] # | -------------------------------------------------------------------------------- /src/test/resources/conversions/html/inlinecode.html: -------------------------------------------------------------------------------- 1 |

Code: <tag> ![]

2 | 3 |

Special Code: <tag> ``

4 | 5 |

More Special Code: `

6 | 7 |

Code with a BR: Two
Lines

-------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/header.md: -------------------------------------------------------------------------------- 1 | # Header 1 # 2 | 3 | content 4 | 5 | ## Header 2 ## 6 | 7 | content 8 | 9 | ### Header 3 ### 10 | 11 | content 12 | 13 | #### Header 4 #### 14 | 15 | content 16 | 17 | ##### Header 5 ##### 18 | 19 | ###### Header 6 ###### -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/break-hardwrap.md: -------------------------------------------------------------------------------- 1 | *Testing non-para breaks 2 | broken!* 3 | 4 | Testing paragraph break 5 | broken! 6 | 7 | [Testing anchor breaks 8 | broken!][Testing anchor breaks broken] 9 | 10 | 11 | [Testing anchor breaks broken]: http://www.google.com -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/break.md: -------------------------------------------------------------------------------- 1 | *Testing non-para breaks 2 | broken!* 3 | 4 | Testing paragraph break 5 | broken! 6 | 7 | [Testing anchor breaks 8 | broken!][Testing anchor breaks broken] 9 | 10 | 11 | [Testing anchor breaks broken]: http://www.google.com -------------------------------------------------------------------------------- /src/test/resources/conversions/html/blockquote.html: -------------------------------------------------------------------------------- 1 |
2 | This is quoted text. 3 |
4 | This is also quoted text. 5 |
6 |
7 | 8 | -------------------------------------------------------------------------------- /src/test/resources/conversions/html/header.html: -------------------------------------------------------------------------------- 1 |

Header 1

2 |

content

3 |

Header 2

4 |

content

5 |

Header 3

6 |

content

7 |

Header 4

8 |

content

9 |
Header 5
10 |
Header 6
-------------------------------------------------------------------------------- /src/test/resources/conversions/html/broken.html: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | This is really bad HTML 5 | 6 |

7 |
8 |

I'm deeply nested

9 |
10 |
I'm not in a paragraph tag at all! I'm a useless tag!! 11 |
-------------------------------------------------------------------------------- /src/test/resources/util/MarkdownTableWideColspanTest.md: -------------------------------------------------------------------------------- 1 | 2 | | h1 | h2 | h3 | h4 | 3 | |:-------- |:-----------------:| ----------------:| ----------------:| 4 | | col1 | col2 | col3 | col4 | 5 | | column 1 | this is a really, really, really, really wide colspan ||| -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/header-headerids.md: -------------------------------------------------------------------------------- 1 | # Header 1 # {#head1} 2 | 3 | content 4 | 5 | ## Header 2 ## {#head2} 6 | 7 | content 8 | 9 | ### Header 3 ### {#head3} 10 | 11 | content 12 | 13 | #### Header 4 #### {#head4} 14 | 15 | content 16 | 17 | ##### Header 5 ##### {#head5} 18 | 19 | ###### Header 6 ###### {#head6} -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/inlinestyle-inword-removed.md: -------------------------------------------------------------------------------- 1 | *italics* 2 | 3 | **bold** 4 | 5 | *italics old* 6 | 7 | **bold old** 8 | 9 | *spanned italics* 10 | 11 | **spanned bold** 12 | 13 | ***spanned both*** 14 | 15 | **nested** 16 | 17 | Empty, *leading*, inline and trailing spaces. 18 | 19 | InWordStyle - InWordStyle - InWordStyle 20 | 21 | Contains ~~strike through~~ text -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/inlinestyle.md: -------------------------------------------------------------------------------- 1 | *italics* 2 | 3 | **bold** 4 | 5 | *italics old* 6 | 7 | **bold old** 8 | 9 | *spanned italics* 10 | 11 | **spanned bold** 12 | 13 | ***spanned both*** 14 | 15 | **nested** 16 | 17 | Empty, *leading*, ***inline*** and *trailing* spaces. 18 | 19 | *In*WordStyle - In***Word***Style - InWord*Style* 20 | 21 | Contains ~~strike through~~ text -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/inlinestyle-inword-spaced.md: -------------------------------------------------------------------------------- 1 | *italics* 2 | 3 | **bold** 4 | 5 | *italics old* 6 | 7 | **bold old** 8 | 9 | *spanned italics* 10 | 11 | **spanned bold** 12 | 13 | ***spanned both*** 14 | 15 | **nested** 16 | 17 | Empty, *leading*, ***inline*** and *trailing* spaces. 18 | 19 | *In* WordStyle - In ***Word*** Style - InWord *Style* 20 | 21 | Contains ~~strike through~~ text -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/codeblock.md: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | Code 4 | Block 5 | 6 | Unsafe 7 | thing[0]; 8 | -> ^_^ <- 9 | 10 | after 11 | 12 | This is a paragraph introducing: 13 | 14 | ~~~~ 15 | a one-line code block 16 | ~~~~ 17 | 18 | after 2 19 | 20 | This pre has 21 | line break tags 22 | another one 23 | 24 | end -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/definitions-enabled.md: -------------------------------------------------------------------------------- 1 | Term First: 2 | 3 | Whazoo 4 | : This is a thing-a-majig 5 | 6 | Thing-a-majig 7 | : A whazoo 8 | 9 | Definition first: 10 | 11 | : What am I defining???? 12 | 13 | IDK 14 | : I don't know... 15 | 16 | Multiple definitions: 17 | 18 | Whooziwhat 19 | : A thing-a-majig 20 | : A whazoo 21 | 22 | Thing-a-majig 23 | : A whazoo 24 | : A whooziwhat -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/tables-multimarkdown.md: -------------------------------------------------------------------------------- 1 | | --- | 2 | | One | 3 | 4 | | --- | --- | 5 | | One | Two | 6 | 7 | | ----- | 8 | | One | 9 | | -Two- | 10 | 11 | | head 1 | head 2 | 12 | | ------ | ------ | 13 | | one | two | 14 | 15 | | head 1 | head 2 | 16 | |:------:| ------:| 17 | | one | two | 18 | 19 | | head 1 | head 2 || 20 | |:------:| ---:|:----- | 21 | | one | two | three | -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/definitions.md: -------------------------------------------------------------------------------- 1 | Term First: 2 | 3 | Whazoo 4 | 5 | This is a thing-a-majig 6 | 7 | Thing-a-majig 8 | 9 | A whazoo 10 | 11 | Definition first: 12 | 13 | What am I defining???? 14 | 15 | IDK 16 | 17 | I don't know... 18 | 19 | Multiple definitions: 20 | 21 | Whooziwhat 22 | 23 | A thing-a-majig 24 | 25 | A whazoo 26 | 27 | Thing-a-majig 28 | 29 | A whazoo 30 | 31 | A whooziwhat -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/tables-markdownextra.md: -------------------------------------------------------------------------------- 1 | | --- | 2 | | One | 3 | 4 | | --- | --- | 5 | | One | Two | 6 | 7 | | ----- | 8 | | One | 9 | | -Two- | 10 | 11 | | head 1 | head 2 | 12 | | ------ | ------ | 13 | | one | two | 14 | 15 | | head 1 | head 2 | 16 | |:------:| ------:| 17 | | one | two | 18 | 19 | | head 1 | head 2 | | 20 | |:------:| ------:|:----- | 21 | | one | two | three | -------------------------------------------------------------------------------- /src/test/resources/conversions/html/image.html: -------------------------------------------------------------------------------- 1 |

Basic: Example Image

2 | 3 |

Title not alt:

4 | 5 |

Duplicate:

6 | 7 |

No alternate:

8 | 9 |

No valid alternate: %$^$

10 | 11 |

No alternate, no name:

-------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/anchor-simpleids.md: -------------------------------------------------------------------------------- 1 | Empty Link: 2 | 3 | Ignored Link 4 | 5 | [Hash Link][1] 6 | 7 | [Relative Path Link][2] 8 | 9 | [Full Path Link][3] 10 | 11 | Test duplicate URLs: [Full Path Link 2][3] 12 | 13 | Autolink: [http://www.google.com/][3] 14 | 15 | [***Over-Styled Link***][3] 16 | 17 | 18 | [1]: http://www.example.com/#hash 19 | [2]: http://www.example.com/rel/path 20 | [3]: http://www.google.com/ -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/anchor-inline.md: -------------------------------------------------------------------------------- 1 | Empty Link: 2 | 3 | Ignored Link 4 | 5 | [Hash Link](http://www.example.com/#hash) 6 | 7 | [Relative Path Link](http://www.example.com/rel/path) 8 | 9 | [Full Path Link](http://www.google.com/) 10 | 11 | Test duplicate URLs: [Full Path Link 2](http://www.google.com/) 12 | 13 | Autolink: [http://www.google.com/](http://www.google.com/) 14 | 15 | [***Over-Styled Link***](http://www.google.com/) -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/anchor-relative.md: -------------------------------------------------------------------------------- 1 | Empty Link: 2 | 3 | Ignored Link 4 | 5 | [Hash Link][] 6 | 7 | [Relative Path Link][] 8 | 9 | [Full Path Link][] 10 | 11 | Test duplicate URLs: [Full Path Link 2][Full Path Link] 12 | 13 | Autolink: [http://www.google.com/][Full Path Link] 14 | 15 | [***Over-Styled Link***][Full Path Link] 16 | 17 | 18 | [Hash Link]: #hash 19 | [Relative Path Link]: rel/path 20 | [Full Path Link]: http://www.google.com/ -------------------------------------------------------------------------------- /src/test/resources/conversions/html/codeblock.html: -------------------------------------------------------------------------------- 1 |

Before

2 |
Code
 3 |     Block
4 | 5 |
 6 | Unsafe <Characters>
 7 |     thing[0];
 8 |     -> ^_^ <-
 9 | 
10 | 11 |

after

12 | 13 |
This is a paragraph introducing:
14 | 
15 | ~~~~
16 | a one-line code block
17 | ~~~~
18 | 
19 | 20 |

after 2

21 | 22 |
This pre has 
line break tags 23 | another one

end
-------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/tables-codeblock.md: -------------------------------------------------------------------------------- 1 | | --- | 2 | | One | 3 | 4 | | --- | --- | 5 | | One | Two | 6 | 7 | | ----- | 8 | | One | 9 | | -Two- | 10 | 11 | | head 1 | head 2 | 12 | | ------ | ------ | 13 | | one | two | 14 | 15 | | head 1 | head 2 | 16 | |:------:| ------:| 17 | | one | two | 18 | 19 | | head 1 | head 2 || 20 | |:------:| ---:|:----- | 21 | | one | two | three | -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/anchor-autolink.md: -------------------------------------------------------------------------------- 1 | Empty Link: 2 | 3 | Ignored Link 4 | 5 | [Hash Link][] 6 | 7 | [Relative Path Link][] 8 | 9 | [Full Path Link][] 10 | 11 | Test duplicate URLs: [Full Path Link 2][Full Path Link] 12 | 13 | Autolink: http://www.google.com/ 14 | 15 | [***Over-Styled Link***][Full Path Link] 16 | 17 | 18 | [Hash Link]: http://www.example.com/#hash 19 | [Relative Path Link]: http://www.example.com/rel/path 20 | [Full Path Link]: http://www.google.com/ -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/codeblock-fenced-backtick.md: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | `````````` 4 | Code 5 | Block 6 | `````````` 7 | 8 | `````````` 9 | Unsafe 10 | thing[0]; 11 | -> ^_^ <- 12 | `````````` 13 | 14 | after 15 | 16 | `````````` 17 | This is a paragraph introducing: 18 | 19 | ~~~~ 20 | a one-line code block 21 | ~~~~ 22 | `````````` 23 | 24 | after 2 25 | 26 | `````````` 27 | This pre has 28 | line break tags 29 | another one 30 | 31 | end 32 | `````````` -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/codeblock-fenced-tilde.md: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | ~~~~~~~~~~ 4 | Code 5 | Block 6 | ~~~~~~~~~~ 7 | 8 | ~~~~~~~~~~ 9 | Unsafe 10 | thing[0]; 11 | -> ^_^ <- 12 | ~~~~~~~~~~ 13 | 14 | after 15 | 16 | ~~~~~~~~~~ 17 | This is a paragraph introducing: 18 | 19 | ~~~~ 20 | a one-line code block 21 | ~~~~ 22 | ~~~~~~~~~~ 23 | 24 | after 2 25 | 26 | ~~~~~~~~~~ 27 | This pre has 28 | line break tags 29 | another one 30 | 31 | end 32 | ~~~~~~~~~~ -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/listordered.md: -------------------------------------------------------------------------------- 1 | Simple 2 | 3 | 1. Item 1 4 | 2. Item 2 5 | 3. Item 3 6 | 7 | Nested 8 | 9 | 1. Item 1 10 | 11 | 1. Item 1.1 12 | 2. Item 1.2 13 | 3. Item 1.3 14 | 2. Item 2 15 | 3. Item 3 16 | 17 | 1. Item 3.1 18 | 2. Item 3.2 19 | 20 | 1. Item 3.2.1 21 | 2. Item 3.2.2 22 | 3. Item 3.3 23 | 24 | Inner Block Content 25 | 26 | 1. Paragraph 1 27 | 28 | Paragraph 2 29 | 2. > Quoted -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/listunordered.md: -------------------------------------------------------------------------------- 1 | Simple 2 | 3 | * Item 1 4 | * Item 2 5 | * Item 3 6 | 7 | Nested 8 | 9 | * Item 1 10 | 11 | * Item 1.1 12 | * Item 1.2 13 | * Item 1.3 14 | * Item 2 15 | * Item 3 16 | 17 | * Item 3.1 18 | * Item 3.2 19 | 20 | * Item 3.2.1 21 | * Item 3.2.2 22 | * Item 3.3 23 | 24 | Inner Block Content 25 | 26 | * Paragraph 1 27 | 28 | Paragraph 2 29 | * > Quoted -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/anchor.md: -------------------------------------------------------------------------------- 1 | Empty Link: 2 | 3 | Ignored Link 4 | 5 | [Hash Link][] 6 | 7 | [Relative Path Link][] 8 | 9 | [Full Path Link][] 10 | 11 | Test duplicate URLs: [Full Path Link 2][Full Path Link] 12 | 13 | Autolink: [http://www.google.com/][Full Path Link] 14 | 15 | [***Over-Styled Link***][Full Path Link] 16 | 17 | 18 | [Hash Link]: http://www.example.com/#hash 19 | [Relative Path Link]: http://www.example.com/rel/path 20 | [Full Path Link]: http://www.google.com/ -------------------------------------------------------------------------------- /src/test/resources/conversions/html/anchor.html: -------------------------------------------------------------------------------- 1 |

Empty Link:

2 |

Ignored Link

3 |

Hash Link

4 |

Relative Path Link

5 |

Full Path Link

6 |

Test duplicate URLs: Full Path Link 2

7 |

Autolink: http://www.google.com/

8 |

Over-Styled Link

-------------------------------------------------------------------------------- /src/test/resources/conversions/html/definitions.html: -------------------------------------------------------------------------------- 1 |

Term First:

2 |
3 |
Whazoo
4 |
This is a thing-a-majig
5 |
Thing-a-majig
6 |
A whazoo
7 |
8 | 9 |

Definition first:

10 |
11 |
What am I defining????
12 |
IDK
13 |
I don't know...
14 |
15 | 16 |

Multiple definitions:

17 |
18 |
Whooziwhat
19 |
A thing-a-majig
20 |
A whazoo
21 |
Thing-a-majig
22 |
A whazoo
23 |
A whooziwhat
24 |
-------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/image.md: -------------------------------------------------------------------------------- 1 | Basic: ![Example Image][] 2 | 3 | Title not alt: ![Example Image 2][] 4 | 5 | Duplicate: ![Example Image 3][Example Image] 6 | 7 | No alternate: ![example3.jpg][] 8 | 9 | No valid alternate: ![%$^$][Image 1] 10 | 11 | No alternate, no name: ![Image 2][] 12 | 13 | 14 | [Example Image]: http://www.example.com/example.jpg 15 | [Example Image 2]: http://www.example.com/example2.jpg 16 | [example3.jpg]: http://www.example.com/example3.jpg 17 | [Image 1]: http://www.example.com/foo/ 18 | [Image 2]: http://www.example.com/blah/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011 OverZealous Creations, LLC 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /src/manual/js/libs/prettify/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} -------------------------------------------------------------------------------- /src/test/resources/conversions/html/listordered.html: -------------------------------------------------------------------------------- 1 |

Simple

2 |
    3 |
  1. Item 1
  2. 4 |
  3. Item 2
  4. 5 |
  5. Item 3
  6. 6 |
7 |

Nested

8 |
    9 |
  1. 10 | Item 1 11 |
      12 |
    1. Item 1.1
    2. 13 |
    3. Item 1.2
    4. 14 |
    5. Item 1.3
    6. 15 |
    16 |
  2. 17 |
  3. Item 2
  4. 18 |
  5. 19 | Item 3 20 |
      21 |
    1. Item 3.1
    2. 22 |
    3. 23 | Item 3.2 24 |
        25 |
      1. Item 3.2.1
      2. 26 |
      3. Item 3.2.2
      4. 27 |
      28 |
    4. 29 |
    5. Item 3.3
    6. 30 |
    31 |
  6. 32 |
33 |

Inner Block Content

34 |
    35 |
  1. 36 |

    Paragraph 1

    37 |

    Paragraph 2

    38 |
  2. 39 |
  3. 40 |
    Quoted
    41 |
  4. 42 |
-------------------------------------------------------------------------------- /src/test/resources/conversions/html/listunordered.html: -------------------------------------------------------------------------------- 1 |

Simple

2 |
    3 |
  • Item 1
  • 4 |
  • Item 2
  • 5 |
  • Item 3
  • 6 |
7 |

Nested

8 |
    9 |
  • 10 | Item 1 11 |
      12 |
    • Item 1.1
    • 13 |
    • Item 1.2
    • 14 |
    • Item 1.3
    • 15 |
    16 |
  • 17 |
  • Item 2
  • 18 |
  • 19 | Item 3 20 |
      21 |
    • Item 3.1
    • 22 |
    • 23 | Item 3.2 24 |
        25 |
      • Item 3.2.1
      • 26 |
      • Item 3.2.2
      • 27 |
      28 |
    • 29 |
    • Item 3.3
    • 30 |
    31 |
  • 32 |
33 |

Inner Block Content

34 |
    35 |
  • 36 |

    Paragraph 1

    37 |

    Paragraph 2

    38 |
  • 39 |
  • 40 |
    Quoted
    41 |
  • 42 |
-------------------------------------------------------------------------------- /src/test/resources/conversions/html/inlinestyle.html: -------------------------------------------------------------------------------- 1 |

italics

2 | 3 |

bold

4 | 5 |

italics old

6 | 7 |

bold old

8 | 9 |

spanned italics

10 | 11 |

spanned bold

12 | 13 |

spanned both

14 | 15 |

nested

16 | 17 |

Empty, leading, inline and trailing spaces.

18 | 19 |

InWordStyle - InWordStyle - InWordStyle

20 | 21 |

Contains strike through text

-------------------------------------------------------------------------------- /src/test/resources/conversions/html/tables.html: -------------------------------------------------------------------------------- 1 |
One
2 | 3 |
OneTwo
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
One
-Two-
13 | 14 | 15 | 16 | 17 |
head 1head 2
onetwo
18 | 19 | 20 | 21 | 22 |
head 1head 2
onetwo
23 | 24 | 25 | 26 | 27 |
head 1head 2
onetwothree
-------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/util/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * This package contains utility classes that assist the main parser in rendering Markdown output. 19 | * 20 | *

For more information, see the BitBucket Repository.

21 | * 22 | * @since 0.7 23 | */ 24 | package com.overzealous.remark.util; -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * This package contains the classes that walk the Jsoup document and processConvert it into Markdown code. 19 | * 20 | *

For more information, see the BitBucket Repository.

21 | * 22 | * @since 0.7 23 | */ 24 | package com.overzealous.remark.convert; -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/BrokenHTMLTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.IgnoredHtmlElement; 20 | import com.overzealous.remark.Options; 21 | import com.overzealous.remark.Remark; 22 | import org.junit.Test; 23 | 24 | /** 25 | * @author Phil DeJarnett 26 | */ 27 | public class BrokenHTMLTest extends RemarkTester { 28 | 29 | @Test public void testBrokenHTML() throws Exception { test("broken"); } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * The root package for the OverZealous Creations, LLC, Remark library. 19 | * 20 | *

This library is used to processConvert HTML files back into valid Markdown code.

21 | * 22 | *

To get started, check out the {@link com.overzealous.remark.Remark} class.

23 | * 24 | *

For more information, see the BitBucket Repository.

25 | * 26 | * @since 0.7 27 | */ 28 | package com.overzealous.remark; -------------------------------------------------------------------------------- /src/test/resources/conversions/markdown/tables.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
One
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
OneTwo
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
One
-Two-
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
head 1head 2
onetwo
43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
head 1head 2
onetwo
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
head 1head 2
onetwothree
-------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/HorizontalRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.util.StringUtils; 20 | import org.jsoup.nodes.Element; 21 | 22 | /** 23 | * Handles a simple hr tag 24 | * 25 | * @author Phil DeJarnett 26 | */ 27 | public class HorizontalRule extends AbstractNodeHandler { 28 | 29 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 30 | converter.output.startBlock(); 31 | StringUtils.multiply(converter.output, '-', 20); 32 | converter.output.endBlock(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/AnchorInlineTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import com.overzealous.remark.Remark; 21 | import org.junit.Test; 22 | 23 | /** 24 | * @author Phil DeJarnett 25 | */ 26 | public class AnchorInlineTest extends RemarkTester { 27 | 28 | @Override 29 | public Remark setupRemark() { 30 | Options opts = Options.markdown(); 31 | opts.inlineLinks = true; 32 | return new Remark(opts); 33 | } 34 | 35 | @Test public void testAnchors() throws Exception { test("anchor", "inline"); } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/AnchorSimpleIdTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import com.overzealous.remark.Remark; 21 | import org.junit.Test; 22 | 23 | /** 24 | * @author Phil DeJarnett 25 | */ 26 | public class AnchorSimpleIdTest extends RemarkTester { 27 | 28 | @Override 29 | public Remark setupRemark() { 30 | Options opts = Options.markdown(); 31 | opts.simpleLinkIds = true; 32 | return new Remark(opts); 33 | } 34 | 35 | @Test public void testAnchors() throws Exception { test("anchor", "simpleids"); } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/TableRemoveTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import com.overzealous.remark.Remark; 21 | import org.junit.Test; 22 | 23 | /** 24 | * @author Phil DeJarnett 25 | */ 26 | public class TableRemoveTest extends RemarkTester { 27 | 28 | @Override 29 | public Remark setupRemark() { 30 | Options opts = Options.markdown(); 31 | opts.tables = Options.Tables.REMOVE; 32 | return new Remark(opts); 33 | } 34 | 35 | @Test public void testTables() throws Exception { test("tables", "remove"); } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/AnchorRelativeLinkTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import com.overzealous.remark.Remark; 21 | import org.junit.Test; 22 | 23 | /** 24 | * @author Phil DeJarnett 25 | */ 26 | public class AnchorRelativeLinkTest extends RemarkTester { 27 | 28 | @Override 29 | public Remark setupRemark() { 30 | Options opts = Options.markdown(); 31 | opts.preserveRelativeLinks = true; 32 | return new Remark(opts); 33 | } 34 | 35 | @Test public void testAnchors() throws Exception { test("anchor", "relative"); } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/InwordEmphasisSpacedTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import com.overzealous.remark.Remark; 21 | import org.junit.Test; 22 | 23 | /** 24 | * @author Phil DeJarnett 25 | */ 26 | public class InwordEmphasisSpacedTest extends RemarkTester { 27 | 28 | @Override 29 | public Remark setupRemark() { 30 | Options opts = Options.markdown(); 31 | opts.inWordEmphasis = Options.InWordEmphasis.ADD_SPACES; 32 | return new Remark(opts); 33 | } 34 | 35 | @Test public void testInlineStyle() throws Exception { test("inlinestyle", "inword-spaced"); } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/InlineCode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import org.jsoup.nodes.Element; 20 | 21 | /** 22 | * Handles inline fixed-width code (code, tt) tags. 23 | * @author Phil DeJarnett 24 | */ 25 | public class InlineCode extends AbstractNodeHandler { 26 | 27 | /** 28 | * Renders inline-styled code. 29 | * 30 | * @param parent The previous node walker, in case we just want to remove an element. 31 | * @param node Node to handle 32 | * @param converter Parent converter for this object. 33 | */ 34 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 35 | converter.output.write(converter.cleaner.cleanInlineCode(node)); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/manual/license.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | **Remark** is released under the [Apache 2.0 license][]. 4 | 5 | Copyright 2011 OverZealous Creations, LLC 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | Feel free to copy and share this library in your personal and commercial projects. 20 | 21 | ## Dependent Licenses 22 | 23 | **Remark** depends on [jsoup][] and [Apache Commons Lang 3][]. If you want to use it from the command line, it also depends on [Apache Commons CLI][]. 24 | 25 | jsoup uses the [MIT License][jsoup license], which is roughly comparable to the [Apache 2.0 License][] used by Remark and the Apache dependencies. 26 | 27 | 28 | [jsoup]: http://jsoup.org/ 29 | [jsoup license]: http://jsoup.org/license 30 | [Apache Commons Lang 3]: http://commons.apache.org/lang/ 31 | [Apache Commons CLI]: http://commons.apache.org/cli/ 32 | [Apache 2.0 License]: http://www.apache.org/licenses/LICENSE-2.0 -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/IgnoredHTMLTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.IgnoredHtmlElement; 20 | import com.overzealous.remark.Options; 21 | import com.overzealous.remark.Remark; 22 | import org.junit.Test; 23 | 24 | /** 25 | * @author Phil DeJarnett 26 | */ 27 | public class IgnoredHTMLTest extends RemarkTester { 28 | 29 | @Override 30 | public Remark setupRemark() { 31 | Options opts = Options.markdown(); 32 | opts.getIgnoredHtmlElements().add(IgnoredHtmlElement.create("var")); 33 | opts.getIgnoredHtmlElements().add(IgnoredHtmlElement.create("foo-bar", "class")); 34 | return new Remark(opts); 35 | } 36 | 37 | @Test public void testUnknownHTML() throws Exception { test("unknownHTML", "allowed"); } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/BlockQuote.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import org.jsoup.nodes.Element; 20 | 21 | /** 22 | * Handles blockquote tags 23 | * @author Phil DeJarnett 24 | */ 25 | public class BlockQuote extends AbstractNodeHandler { 26 | 27 | /** 28 | * Processes a quoted section. 29 | * 30 | * @param parent The previous node walker, in case we just want to remove an element. 31 | * @param node Node to handle 32 | * @param converter Parent converter for this object. 33 | */ 34 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 35 | // handle block quotes 36 | converter.output.startBlock(); 37 | prependAndRecurse("> ", node, converter, converter.blockNodes); 38 | converter.output.endBlock(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/Paragraph.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import org.jsoup.nodes.Element; 20 | 21 | /** 22 | * Handles paragraph (p) tags. 23 | * @author Phil DeJarnett 24 | */ 25 | public class Paragraph extends AbstractNodeHandler { 26 | 27 | /** 28 | * Creates a standard text block, then walks down over inline nodes. 29 | * 30 | * @param parent The previous node walker, in case we just want to remove an element. 31 | * @param node Node to handle 32 | * @param converter Parent converter for this object. 33 | */ 34 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 35 | converter.output.startBlock(); 36 | converter.walkNodes(this, node, converter.inlineNodes); 37 | converter.output.endBlock(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/Abbr.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import org.jsoup.nodes.Element; 20 | 21 | /** 22 | * Handles abbr and acronym tags 23 | * @author Phil DeJarnett 24 | */ 25 | public class Abbr extends AbstractNodeHandler { 26 | 27 | /** 28 | * Handles abbreviations. 29 | * 30 | * @param parent The previous node walker, in case we just want to remove an element. 31 | * @param node Node to handle 32 | * @param converter Parent converter for this object. 33 | */ 34 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 35 | String abbr = converter.cleaner.clean(node); 36 | if(abbr.length() > 0) { 37 | converter.output.print(abbr); 38 | String desc = node.attr("title"); 39 | if(desc.length() > 0) { 40 | converter.addAbbreviation(abbr, desc); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/Break.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import org.jsoup.nodes.Element; 20 | 21 | /** 22 | * Handles manual breaks (br) 23 | * @author Phil DeJarnett 24 | */ 25 | public class Break extends AbstractNodeHandler { 26 | 27 | /** 28 | * Renders out forced linebreaks. If hardwraps are enabled, then this 29 | * simply prints a newline. Otherwise, it prints two spaces and a newline. 30 | * 31 | * @param parent The previous node walker, in case we just want to remove an element. 32 | * @param node Node to handle 33 | * @param converter Parent converter for this object. 34 | */ 35 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 36 | if(!converter.options.hardwraps) { 37 | converter.output.println(" "); 38 | } else { 39 | converter.output.println(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/NodeRemover.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import org.jsoup.nodes.Element; 20 | import org.jsoup.nodes.TextNode; 21 | 22 | /** 23 | * @author Phil DeJarnett 24 | */ 25 | public class NodeRemover implements NodeHandler { 26 | 27 | private static NodeRemover instance; 28 | 29 | private NodeRemover() { 30 | // singleton 31 | } 32 | 33 | public static NodeRemover getInstance() { 34 | if(instance == null) { 35 | instance = new NodeRemover(); 36 | } 37 | return instance; 38 | } 39 | 40 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 41 | // do nothing, node is removed. 42 | } 43 | 44 | public void handleTextNode(TextNode node, DocumentConverter converter) { 45 | // do nothing, node is removed. 46 | } 47 | 48 | public void handleIgnoredHTMLElement(Element node, DocumentConverter converter) { 49 | // do nothing, node is removed. 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/DefaultNodeHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import org.jsoup.nodes.Element; 20 | 21 | /** 22 | * Default handler for unknown top-level nodes. 23 | * @author Phil DeJarnett 24 | */ 25 | public class DefaultNodeHandler extends AbstractNodeHandler { 26 | 27 | private static DefaultNodeHandler instance; 28 | 29 | public static DefaultNodeHandler getInstance() { 30 | if(instance == null) { 31 | instance = new DefaultNodeHandler(); 32 | } 33 | return instance; 34 | } 35 | 36 | protected DefaultNodeHandler() { 37 | // exists for Singleton pattern 38 | } 39 | 40 | /** 41 | * For the default state element, the nodes are simply ignored, recursing as necessary. 42 | * 43 | * @param parent The previous node walker, in case we just want to remove an element. 44 | * @param node Node to handle 45 | * @param converter Parent converter for this object. 46 | */ 47 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 48 | converter.walkNodes(this, node, converter.blockNodes); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/util/TestUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.util; 18 | 19 | import org.apache.commons.io.FileUtils; 20 | 21 | import java.io.File; 22 | import java.net.URL; 23 | 24 | /** 25 | * @author Phil DeJarnett 26 | */ 27 | public class TestUtils { 28 | 29 | /** 30 | * Reads a resource into a string. 31 | * @param path Path to resource 32 | * @return String contents of resource 33 | */ 34 | public static String readResourceToString(String path) { 35 | String result; 36 | try { 37 | URL u = StringUtils.class.getResource(path); 38 | if(u == null) { 39 | throw new Exception("Resource not found"); 40 | } 41 | File f = FileUtils.toFile(u); 42 | if(!f.isFile()) { 43 | throw new Exception("Resource file does not exist or is not a file."); 44 | } 45 | result = FileUtils.readFileToString(f, "UTF-8"); 46 | if(result == null) { 47 | throw new Exception("Error reading resource file."); 48 | } 49 | } catch(Exception e) { 50 | e.printStackTrace(); 51 | result = "UNABLE TO LOAD RESOURCE "+path+": "+e.getMessage(); 52 | } 53 | return result; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Gradle ### 4 | .gradle 5 | build/ 6 | 7 | # Ignore Gradle GUI config 8 | gradle-app.setting 9 | 10 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 11 | !gradle-wrapper.jar 12 | 13 | # Created by https://www.gitignore.io 14 | 15 | ### Maven ### 16 | target/ 17 | pom.xml.tag 18 | pom.xml.releaseBackup 19 | pom.xml.versionsBackup 20 | pom.xml.next 21 | release.properties 22 | dependency-reduced-pom.xml 23 | buildNumber.properties 24 | settings.xml 25 | 26 | # Created by https://www.gitignore.io/api/intellij 27 | 28 | ### Intellij ### 29 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 30 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 31 | 32 | # User-specific stuff: 33 | .idea/workspace.xml 34 | .idea/tasks.xml 35 | 36 | # Sensitive or high-churn files: 37 | .idea/dataSources/ 38 | .idea/dataSources.ids 39 | .idea/dataSources.xml 40 | .idea/dataSources.local.xml 41 | .idea/sqlDataSources.xml 42 | .idea/dynamic.xml 43 | .idea/uiDesigner.xml 44 | 45 | # Gradle: 46 | .idea/gradle.xml 47 | .idea/libraries 48 | 49 | # Mongo Explorer plugin: 50 | .idea/mongoSettings.xml 51 | 52 | ## File-based project format: 53 | *.iws 54 | 55 | ## Plugin-specific files: 56 | 57 | # IntelliJ 58 | /out/ 59 | 60 | # mpeltonen/sbt-idea plugin 61 | .idea_modules/ 62 | 63 | # JIRA plugin 64 | atlassian-ide-plugin.xml 65 | 66 | # Crashlytics plugin (for Android Studio and IntelliJ) 67 | com_crashlytics_export_strings.xml 68 | crashlytics.properties 69 | crashlytics-build.properties 70 | fabric.properties 71 | 72 | ### Intellij Patch ### 73 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 74 | 75 | # *.iml 76 | # modules.xml 77 | # .idea/misc.xml 78 | # *.ipr 79 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/Header.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.util.BlockWriter; 20 | import com.overzealous.remark.util.StringUtils; 21 | import org.jsoup.nodes.Element; 22 | 23 | /** 24 | * Handles header nodes (h1 through h6) 25 | * 26 | * @author Phil DeJarnett 27 | */ 28 | public class Header extends AbstractNodeHandler { 29 | 30 | /** 31 | * Renders a header node (h1..h6). If enabled, also handles the headerID attribute. 32 | * 33 | * @param parent The previous node walker, in case we just want to remove an element. 34 | * @param node Node to handle 35 | * @param converter Parent converter for this object. 36 | */ 37 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 38 | int depth = Integer.parseInt(node.tagName().substring(1, 2)); 39 | BlockWriter out = converter.output; 40 | out.startBlock(); 41 | StringUtils.multiply(out, '#', depth); 42 | out.print(' '); 43 | out.print(converter.getInlineContent(this, node).replace("\n", " ")); 44 | out.print(' '); 45 | StringUtils.multiply(out, '#', depth); 46 | if(converter.options.headerIds && node.hasAttr("id")) { 47 | out.printf(" {#%s}", node.attr("id")); 48 | } 49 | out.endBlock(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/NodeHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import org.jsoup.nodes.Element; 20 | import org.jsoup.nodes.TextNode; 21 | 22 | /** 23 | * Interface for classes that handle processing HTML Elements. 24 | * @author Phil DeJarnett 25 | */ 26 | public interface NodeHandler { 27 | 28 | /** 29 | * Handles an HTML Element node. This is where most of the work is done. 30 | * 31 | * Which NodeHandler is used is based on the tagName of the element. 32 | * 33 | * @param parent The previous node walker, in case we just want to remove an element. 34 | * @param node Node to handle 35 | * @param converter Parent converter for this object. 36 | */ 37 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter); 38 | 39 | /** 40 | * Handle a child text node. 41 | * 42 | * @param node Node to handle 43 | * @param converter Parent converter for this object. 44 | */ 45 | public void handleTextNode(TextNode node, DocumentConverter converter); 46 | 47 | /** 48 | * Handle an ignored HTMLElement. 49 | * @param node Node to handle 50 | * @param converter Parent converter for this object. 51 | */ 52 | public void handleIgnoredHTMLElement(Element node, DocumentConverter converter); 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/Image.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.util.BlockWriter; 20 | import org.jsoup.nodes.Element; 21 | 22 | /** 23 | * Handles img tags. 24 | * @author Phil DeJarnett 25 | */ 26 | public class Image extends AbstractNodeHandler { 27 | 28 | /** 29 | * Creates a link reference to an image, and renders the correct output. 30 | * 31 | * @param parent The previous node walker, in case we just want to remove an element. 32 | * @param node Node to handle 33 | * @param converter Parent converter for this object. 34 | */ 35 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 36 | String url = converter.cleaner.cleanUrl(node.attr("src")); 37 | String alt = node.attr("alt"); 38 | if(alt == null || alt.trim().length() == 0) { 39 | alt = node.attr("title"); 40 | if(alt == null) { 41 | alt = ""; 42 | } 43 | } 44 | alt = converter.cleaner.clean(alt.trim()); 45 | if(converter.options.inlineLinks) { 46 | if(alt.length() == 0) { 47 | alt = "Image"; 48 | } 49 | converter.output.printf("![%s](%s)", alt, url); 50 | } else { 51 | String linkId = converter.addLink(url, alt, true); 52 | // give a usable description based on filename whenever possible 53 | if(alt.length() == 0) { 54 | alt = linkId; 55 | } 56 | BlockWriter out = converter.output; 57 | if(alt.equals(linkId)) { 58 | out.printf("![%s][]", linkId); 59 | } else { 60 | out.printf("![%s][%s]", alt, linkId); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/PlainMarkdownTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import org.junit.Test; 20 | 21 | /** 22 | * @author Phil DeJarnett 23 | */ 24 | public class PlainMarkdownTest extends RemarkTester { 25 | 26 | @Test public void testAbbr() throws Exception { test("abbr"); } 27 | @Test public void testAnchor() throws Exception { test("anchor"); } 28 | @Test public void testBlockQuote() throws Exception { test("blockquote"); } 29 | @Test public void testHeader() throws Exception { test("header"); } 30 | @Test public void testBreak() throws Exception { test("break"); } 31 | @Test public void testCodeblock() throws Exception { test("codeblock"); } 32 | @Test public void testDefinitions() throws Exception { test("definitions"); } 33 | @Test public void testHorizontalRule() throws Exception { test("horizontalrule"); } 34 | @Test public void testImage() throws Exception { test("image"); } 35 | @Test public void testInlineCode() throws Exception { test("inlinecode"); } 36 | @Test public void testInlineStyle() throws Exception { test("inlinestyle"); } 37 | @Test public void testInlineStyleEmptyBreak() throws Exception { test("inlinestyle-emptybreak"); } 38 | @Test public void testListOrdered() throws Exception { test("listordered"); } 39 | @Test public void testListUnordered() throws Exception { test("listunordered"); } 40 | @Test public void testParagraph() throws Exception { test("paragraph"); } 41 | @Test public void testTables() throws Exception { test("tables"); } 42 | @Test public void testUnknownHTML() throws Exception { test("unknownHTML"); } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/List.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.util.BlockWriter; 20 | import org.jsoup.nodes.Element; 21 | 22 | /** 23 | * Handles ol and ul lists. 24 | * @author Phil DeJarnett 25 | */ 26 | public class List extends AbstractNodeHandler { 27 | 28 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 29 | // the first node doesn't get a linebreak 30 | boolean first = true; 31 | // if this is an ol, it's numbered. 32 | boolean numericList = node.tagName().equals("ol"); 33 | // keep track of where we are in the list. 34 | int listCounter = 1; 35 | 36 | // we need to store this, because we're going to replace it for each li below (for padding). 37 | BlockWriter parentWriter = converter.output; 38 | parentWriter.startBlock(); 39 | for(final Element child : node.children()) { 40 | // handle linebreaks between li's 41 | if(first) { 42 | first = false; 43 | } else { 44 | parentWriter.println(); 45 | } 46 | // handle starting character 47 | if(numericList) { 48 | parentWriter.print(listCounter); 49 | parentWriter.print(". "); 50 | if(listCounter < 10) { 51 | parentWriter.print(' '); 52 | } 53 | } else { 54 | parentWriter.print(" * "); 55 | } 56 | 57 | // now, recurse downward, padding the beginning of each line so it looks nice. 58 | converter.output = new BlockWriter(parentWriter).setPrependNewlineString(" ", true); 59 | converter.walkNodes(this, child, converter.blockNodes); 60 | listCounter++; 61 | } 62 | // cleanup 63 | parentWriter.endBlock(); 64 | converter.output = parentWriter; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/Codeblock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import com.overzealous.remark.util.BlockWriter; 21 | import com.overzealous.remark.util.StringUtils; 22 | import org.jsoup.nodes.Element; 23 | 24 | /** 25 | * Handles preformatted sections (pre), renders them as code blocks. 26 | * 27 | * @author Phil DeJarnett 28 | */ 29 | public class Codeblock extends AbstractNodeHandler { 30 | 31 | /** 32 | * Converts a pre-formatted block of code. 33 | * Depending on the options, this may render as a block with four spaces added to the beginning, 34 | * or as a fenced code block. 35 | * 36 | * @param parent The previous node walker, in case we just want to remove an element. 37 | * @param node Node to handle 38 | * @param converter Parent converter for this object. 39 | */ 40 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 41 | BlockWriter out; 42 | Options.FencedCodeBlocks fenced = converter.options.getFencedCodeBlocks(); 43 | if(fenced.isEnabled()) { 44 | String fence = StringUtils.multiply(fenced.getSeparatorCharacter(), 45 | converter.options.fencedCodeBlocksWidth); 46 | out = converter.output; 47 | converter.output.startBlock(); 48 | out.println(fence); 49 | out.write(converter.cleaner.cleanCode(node)); 50 | out.println(); 51 | out.print(fence); 52 | converter.output.endBlock(); 53 | } else { 54 | converter.output.startBlock(); 55 | out = new BlockWriter(converter.output).setPrependNewlineString(" "); 56 | out.write(converter.cleaner.cleanCode(node)); 57 | converter.output.endBlock(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/MultiMarkdownTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import com.overzealous.remark.Remark; 21 | import org.junit.Test; 22 | 23 | /** 24 | * @author Phil DeJarnett 25 | */ 26 | public class MultiMarkdownTest extends RemarkTester { 27 | 28 | @Override 29 | public Remark setupRemark() { 30 | return new Remark(Options.multiMarkdown()); 31 | } 32 | 33 | @Test public void testAbbr() throws Exception { test("abbr"); } 34 | @Test public void testAnchor() throws Exception { test("anchor"); } 35 | @Test public void testBlockQuote() throws Exception { test("blockquote"); } 36 | @Test public void testHeader() throws Exception { test("header"); } 37 | @Test public void testBreak() throws Exception { test("break"); } 38 | @Test public void testCodeblock() throws Exception { test("codeblock"); } 39 | @Test public void testDefinitions() throws Exception { test("definitions", "enabled"); } 40 | @Test public void testHorizontalRule() throws Exception { test("horizontalrule"); } 41 | @Test public void testImage() throws Exception { test("image"); } 42 | @Test public void testInlineCode() throws Exception { test("inlinecode"); } 43 | @Test public void testInlineStyle() throws Exception { test("inlinestyle"); } 44 | @Test public void testListOrdered() throws Exception { test("listordered"); } 45 | @Test public void testListUnordered() throws Exception { test("listunordered"); } 46 | @Test public void testParagraph() throws Exception { test("paragraph"); } 47 | @Test public void testTables() throws Exception { test("tables", "multimarkdown"); } 48 | @Test public void testUnknownHTML() throws Exception { test("unknownHTML"); } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/GithubTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import com.overzealous.remark.Remark; 21 | import org.junit.Test; 22 | 23 | /** 24 | * @author Phil DeJarnett 25 | */ 26 | public class GithubTest extends RemarkTester { 27 | 28 | @Override 29 | public Remark setupRemark() { 30 | return new Remark(Options.github()); 31 | } 32 | 33 | @Test public void testAbbr() throws Exception { test("abbr"); } 34 | @Test public void testAnchor() throws Exception { test("anchor", "autolink"); } 35 | @Test public void testBlockQuote() throws Exception { test("blockquote"); } 36 | @Test public void testHeader() throws Exception { test("header"); } 37 | @Test public void testBreak() throws Exception { test("break", "hardwrap"); } 38 | @Test public void testCodeblock() throws Exception { test("codeblock", "fenced-backtick"); } 39 | @Test public void testDefinitions() throws Exception { test("definitions"); } 40 | @Test public void testHorizontalRule() throws Exception { test("horizontalrule"); } 41 | @Test public void testImage() throws Exception { test("image"); } 42 | @Test public void testInlineCode() throws Exception { test("inlinecode"); } 43 | @Test public void testInlineStyle() throws Exception { test("inlinestyle"); } 44 | @Test public void testListOrdered() throws Exception { test("listordered"); } 45 | @Test public void testListUnordered() throws Exception { test("listunordered"); } 46 | @Test public void testParagraph() throws Exception { test("paragraph"); } 47 | @Test public void testTables() throws Exception { test("tables", "codeblock"); } 48 | @Test public void testUnknownHTML() throws Exception { test("unknownHTML"); } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/MarkdownExtraTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import com.overzealous.remark.Remark; 21 | import org.junit.Test; 22 | 23 | /** 24 | * @author Phil DeJarnett 25 | */ 26 | public class MarkdownExtraTest extends RemarkTester { 27 | 28 | @Override 29 | public Remark setupRemark() { 30 | return new Remark(Options.markdownExtra()); 31 | } 32 | 33 | @Test public void testAbbr() throws Exception { test("abbr", "enabled"); } 34 | @Test public void testAnchor() throws Exception { test("anchor"); } 35 | @Test public void testBlockQuote() throws Exception { test("blockquote"); } 36 | @Test public void testHeader() throws Exception { test("header", "headerids"); } 37 | @Test public void testBreak() throws Exception { test("break"); } 38 | @Test public void testCodeblock() throws Exception { test("codeblock", "fenced-tilde"); } 39 | @Test public void testDefinitions() throws Exception { test("definitions", "enabled"); } 40 | @Test public void testHorizontalRule() throws Exception { test("horizontalrule"); } 41 | @Test public void testImage() throws Exception { test("image"); } 42 | @Test public void testInlineCode() throws Exception { test("inlinecode"); } 43 | @Test public void testInlineStyle() throws Exception { test("inlinestyle"); } 44 | @Test public void testListOrdered() throws Exception { test("listordered"); } 45 | @Test public void testListUnordered() throws Exception { test("listunordered"); } 46 | @Test public void testParagraph() throws Exception { test("paragraph"); } 47 | @Test public void testTables() throws Exception { test("tables", "markdownextra"); } 48 | @Test public void testUnknownHTML() throws Exception { test("unknownHTML"); } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/PegdownTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import com.overzealous.remark.Remark; 21 | import org.junit.Test; 22 | 23 | /** 24 | * @author Phil DeJarnett 25 | */ 26 | public class PegdownTest extends RemarkTester { 27 | 28 | @Override 29 | public Remark setupRemark() { 30 | return new Remark(Options.pegdownAllExtensions()); 31 | } 32 | 33 | @Test public void testAbbr() throws Exception { test("abbr", "enabled"); } 34 | @Test public void testAnchor() throws Exception { test("anchor", "autolink"); } 35 | @Test public void testBlockQuote() throws Exception { test("blockquote"); } 36 | @Test public void testHeader() throws Exception { test("header"); } 37 | @Test public void testBreak() throws Exception { test("break", "hardwrap"); } 38 | @Test public void testCodeblock() throws Exception { test("codeblock", "fenced-tilde"); } 39 | @Test public void testDefinitions() throws Exception { test("definitions", "enabled"); } 40 | @Test public void testHorizontalRule() throws Exception { test("horizontalrule"); } 41 | @Test public void testImage() throws Exception { test("image"); } 42 | @Test public void testInlineCode() throws Exception { test("inlinecode"); } 43 | @Test public void testInlineStyle() throws Exception { test("inlinestyle", "inword-removed"); } 44 | @Test public void testListOrdered() throws Exception { test("listordered"); } 45 | @Test public void testListUnordered() throws Exception { test("listunordered"); } 46 | @Test public void testParagraph() throws Exception { test("paragraph"); } 47 | @Test public void testTables() throws Exception { test("tables", "multimarkdown"); } 48 | @Test public void testUnknownHTML() throws Exception { test("unknownHTML"); } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/RemarkTester.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Remark; 20 | import com.overzealous.remark.util.TestUtils; 21 | import org.junit.Assert; 22 | import org.junit.Before; 23 | 24 | /** 25 | * @author Phil DeJarnett 26 | */ 27 | public abstract class RemarkTester { 28 | 29 | private static final String BASE_PATH = "/conversions/"; 30 | private static final String INPUT_PATH = BASE_PATH+"html/"; 31 | private static final String MD_PATH = BASE_PATH+"markdown/"; 32 | private static final String HTML_EXT = ".html"; 33 | private static final String MD_EXT = ".md"; 34 | 35 | @SuppressWarnings({"WeakerAccess"}) 36 | Remark remark; 37 | @SuppressWarnings({"WeakerAccess", "CanBeFinal"}) 38 | String baseURI = "http://www.example.com/"; 39 | 40 | /** 41 | * Override to change the remark method 42 | * @return A Remark instance 43 | */ 44 | Remark setupRemark() { 45 | return new Remark(); 46 | } 47 | 48 | @Before 49 | public void setUp() throws Exception { 50 | this.remark = setupRemark(); 51 | Assert.assertNotNull(this.remark); 52 | } 53 | 54 | 55 | void test(String testName) throws Exception { 56 | test(testName, null); 57 | } 58 | 59 | @SuppressWarnings({"RedundantThrows"}) 60 | void test(String testName, String option) throws Exception { 61 | String input = TestUtils.readResourceToString(INPUT_PATH + testName + HTML_EXT); 62 | if(option == null) { 63 | option = ""; 64 | } else { 65 | option = '-' + option; 66 | } 67 | String expected = TestUtils.readResourceToString(MD_PATH + testName + option + MD_EXT); 68 | String converted = remark.convertFragment(input, baseURI); 69 | if(!converted.equals(expected)) { 70 | System.out.println("=============================="); 71 | System.out.println(expected); 72 | System.out.println("------------------------------"); 73 | System.out.println(converted); 74 | System.out.println("=============================="); 75 | } 76 | Assert.assertEquals(expected, converted); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/DocumentConverterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import org.junit.Assert; 21 | import org.junit.Before; 22 | import org.junit.Test; 23 | 24 | /** 25 | * @author Phil DeJarnett 26 | */ 27 | public class DocumentConverterTest { 28 | 29 | private DocumentConverter dc; 30 | 31 | private static final String LINK_URL = "http://www.example.com/file.html"; 32 | private static final String IMAGE_URL = "http://www.example.com/image.jpg"; 33 | private static final String IMAGE_NOFILE_URL = "http://www.example.com/blah/"; 34 | private static final String IMAGE_INVALID_URL = "http://www.example.com/_!!_"; 35 | 36 | @Before 37 | public void setUp() throws Exception { 38 | dc = new DocumentConverter(Options.markdown()); 39 | } 40 | 41 | @Test 42 | public void testSimpleLink() throws Exception { 43 | Assert.assertEquals("test", dc.cleanLinkId(LINK_URL, "test", false)); 44 | Assert.assertEquals("test-test", dc.cleanLinkId(LINK_URL, "test-test", false)); 45 | Assert.assertEquals("test test", dc.cleanLinkId(LINK_URL, "test\ntest", false)); 46 | } 47 | 48 | @Test 49 | public void testReplacementLinks() throws Exception { 50 | Assert.assertEquals("test_test", dc.cleanLinkId(LINK_URL, "test*&^$#˜∂test", false)); 51 | Assert.assertEquals("test", dc.cleanLinkId(LINK_URL, "&(^test", false)); 52 | Assert.assertEquals("test", dc.cleanLinkId(LINK_URL, "test^%$", false)); 53 | } 54 | 55 | @Test 56 | public void testGenericLinks() throws Exception { 57 | Assert.assertEquals("Link 1", dc.cleanLinkId(LINK_URL, "*(&(*&(", false)); 58 | Assert.assertEquals("Link 2", dc.cleanLinkId(LINK_URL, "_!!\n!!_", false)); 59 | } 60 | 61 | @Test 62 | public void testGenericImages() throws Exception { 63 | Assert.assertEquals("Image 1", dc.cleanLinkId(IMAGE_NOFILE_URL, "*(&(*&(", true)); 64 | Assert.assertEquals("Image 2", dc.cleanLinkId(IMAGE_NOFILE_URL, "_!!\n!!_", true)); 65 | Assert.assertEquals("image.jpg", dc.cleanLinkId(IMAGE_URL, "!", true)); 66 | Assert.assertEquals("Image 3", dc.cleanLinkId(IMAGE_INVALID_URL, "!", true)); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/Anchor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import org.jsoup.nodes.Element; 20 | 21 | import java.util.regex.Pattern; 22 | 23 | /** 24 | * Handles anchor (a) tags, both links and named anchors. 25 | * @author Phil DeJarnett 26 | */ 27 | public class Anchor extends AbstractNodeHandler { 28 | 29 | private static final Pattern INLINE_LINK_ESCAPE = Pattern.compile("([\\(\\)])"); 30 | private static final String INLINE_LINK_REPLACEMENT = "\\\\$1"; 31 | 32 | /** 33 | * Creates a link reference, and renders the correct output. 34 | * 35 | * If this happens to be a named anchor, then it is simply removed from output. 36 | * 37 | * @param parent The previous node walker, in case we just want to remove an element. 38 | * @param node Node to handle 39 | * @param converter Parent converter for this object. 40 | */ 41 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 42 | if(node.hasAttr("href") && node.attr("href").trim().length() > 0) { 43 | // Must be a real link. 44 | String url = converter.cleaner.cleanUrl(node.attr("href")); 45 | String label = converter.getInlineContent(this, node); 46 | 47 | if(label.length() > 0) { 48 | if(converter.options.autoLinks && url.equals(label)) { 49 | // embed autolink 50 | converter.output.write(label); 51 | } else if(converter.options.inlineLinks) { 52 | // standard link 53 | if(converter.options.fixPegdownStrongEmphasisInLinks) { 54 | label = label.replace("***", "**"); 55 | } 56 | converter.output.printf("[%s](%s)", label, url); 57 | } else { 58 | // standard link 59 | if(converter.options.fixPegdownStrongEmphasisInLinks) { 60 | label = label.replace("***", "**"); 61 | } 62 | String linkId = converter.addLink(url, label, false); 63 | if(label.equals(linkId)) { 64 | converter.output.printf("[%s][]", label); 65 | } else { 66 | converter.output.printf("[%s][%s]", label, linkId); 67 | } 68 | } 69 | } // else, ignore links with no label 70 | } else { 71 | // named anchor, not a link 72 | // simply remove it from the flow. 73 | converter.walkNodes(parent, node); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/manual/_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ${title} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

Remark

22 | 34 |
35 |
36 | ${body} 37 |
38 | 41 | 42 | 43 | 51 | 52 | 53 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/AbstractNodeHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.util.BlockWriter; 20 | import org.jsoup.nodes.Document; 21 | import org.jsoup.nodes.Element; 22 | import org.jsoup.nodes.TextNode; 23 | 24 | import java.util.Map; 25 | 26 | /** 27 | * Contains basic implementations for handling text nodes and ignored HTML elements. 28 | * 29 | * @author Phil DeJarnett 30 | */ 31 | public abstract class AbstractNodeHandler implements NodeHandler { 32 | 33 | /** 34 | * Handle a child text node. 35 | * The default method, implemented here, is to simply write the cleaned 36 | * text directly. 37 | * 38 | * @param node Node to handle 39 | * @param converter Parent converter for this object. 40 | */ 41 | public void handleTextNode(TextNode node, DocumentConverter converter) { 42 | converter.output.write(converter.cleaner.clean(node)); 43 | } 44 | 45 | /** 46 | * Handle an ignored HTMLElement. 47 | * The default method here is to either write the HTMLElement as a block if it is a block element, 48 | * or write it directly if it is not. 49 | * 50 | * @param node Node to handle 51 | * @param converter Parent converter for this object. 52 | */ 53 | public void handleIgnoredHTMLElement(Element node, DocumentConverter converter) { 54 | if(node.isBlock()) { 55 | converter.output.writeBlock(node.toString()); 56 | } else { 57 | // Note: because this is an inline element, we want to make sure it stays that way! 58 | // this means turning off prettyPrinting, so that JSoup doesn't add unecessary spacing around 59 | // the child nodes. 60 | Document doc = node.ownerDocument(); 61 | boolean oldPrettyPrint = doc.outputSettings().prettyPrint(); 62 | doc.outputSettings().prettyPrint(false); 63 | converter.output.write(node.toString()); 64 | doc.outputSettings().prettyPrint(oldPrettyPrint); 65 | } 66 | } 67 | 68 | /** 69 | * Recursively processes child nodes, and prepends the given string to the output. 70 | * @param prepend String to prepend 71 | * @param node Starting Node 72 | * @param converter Parent document converter 73 | * @param nodes Map of valid nodes 74 | */ 75 | @SuppressWarnings({"WeakerAccess", "SameParameterValue"}) 76 | protected void prependAndRecurse(String prepend, Element node, DocumentConverter converter, Map nodes) { 77 | BlockWriter oldOutput = converter.output; 78 | converter.output = new BlockWriter(oldOutput).setPrependNewlineString(prepend); 79 | converter.walkNodes(this, node, nodes); 80 | converter.output = oldOutput; 81 | } 82 | } -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/Definitions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.util.BlockWriter; 20 | import org.jsoup.nodes.Element; 21 | 22 | /** 23 | * Handles dl, dt, and dd elements 24 | * @author Phil DeJarnett 25 | */ 26 | public class Definitions extends AbstractNodeHandler { 27 | 28 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 29 | // the first child node doesn't get a linebreak 30 | boolean first = true; 31 | boolean lastNodeWasDD = false; 32 | 33 | // we need to store this, because we're going to replace it for each dd below (for padding). 34 | BlockWriter parentWriter = converter.output; 35 | 36 | /* Note on block handling: 37 | * We need a gap between each dd and the following dt, like so: 38 | * term 39 | * : definition 40 | * 41 | * term 42 | * : definition 43 | * 44 | * To do this, we wrap the whole thing in a start/end block. Then, 45 | * every time we come across a dt, we end a block and start a new one. 46 | * The only exception to this rule is if the first node we come to is 47 | * a dt - then we don't do anything. 48 | */ 49 | parentWriter.startBlock(); 50 | for(final Element child : node.children()) { 51 | 52 | if(child.tagName().equals("dt")) { 53 | // print term 54 | if(first) { 55 | // the first node is a term, so we already started a block. 56 | first = false; 57 | } else { 58 | // add block separation between defs and next term. 59 | parentWriter.endBlock(); 60 | parentWriter.startBlock(); 61 | } 62 | converter.walkNodes(this, child, converter.inlineNodes); 63 | parentWriter.println(); 64 | lastNodeWasDD = false; 65 | 66 | } else if(child.tagName().equals("dd")) { 67 | // print definition 68 | if(first) { 69 | // the first node is a def, so we'll need a new block next time. 70 | first = false; 71 | } 72 | if(lastNodeWasDD) { 73 | parentWriter.println(); 74 | } 75 | parentWriter.print(": "); 76 | // Is this necessary? We only allow inline, and inline is always one line, so padding is redundant. 77 | // Of course, we may want to offer wrapped blocks later, when hardwraps are turned off. 78 | converter.output = new BlockWriter(parentWriter).setPrependNewlineString(" ", true); 79 | converter.walkNodes(this, child, converter.blockNodes); 80 | converter.output = parentWriter; 81 | 82 | lastNodeWasDD = true; 83 | 84 | } // else, ignore, bad node 85 | } 86 | // cleanup 87 | parentWriter.endBlock(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/IgnoredHtmlElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark; 18 | 19 | import java.util.HashSet; 20 | import java.util.Set; 21 | 22 | /** 23 | * Provides a standard class to note which HTML elements should be left in the final output. 24 | * @author Phil DeJarnett 25 | */ 26 | public class IgnoredHtmlElement { 27 | 28 | private String tagName; 29 | 30 | private Set attributes; 31 | 32 | /** 33 | * Create a new IgnoredHtmlElement. The tagname may also be referred to as the NodeName. 34 | * 35 | * @param tagName The tag name, such as {@code DIV}, case-insensitive. 36 | */ 37 | public IgnoredHtmlElement(String tagName) { 38 | this.tagName = tagName; 39 | this.attributes = new HashSet(); 40 | } 41 | 42 | /** 43 | * Utility method to quickly create a new element. 44 | * 45 | * @param tagName The elements tag name. 46 | * @param attributes Zero or more attributes that should be enabled on this tag. 47 | * @return The newly created element. 48 | */ 49 | public static IgnoredHtmlElement create(String tagName, String... attributes) { 50 | IgnoredHtmlElement el = new IgnoredHtmlElement(tagName); 51 | for(final String attr : attributes) { 52 | el.addAttribute(attr); 53 | } 54 | return el; 55 | } 56 | 57 | /** 58 | * Returns the tagname for this object. 59 | * @return The name of this element. 60 | */ 61 | public String getTagName() { 62 | return tagName; 63 | } 64 | 65 | /** 66 | * Gets all the attributes that should be left on this tag. 67 | * @return A set of attributes that are left on the tag. 68 | */ 69 | public Set getAttributes() { 70 | return attributes; 71 | } 72 | 73 | /** 74 | * Adds a single to the list of allowed attributes. 75 | * @param attributeName The name of the attribute to allow. 76 | * @return true if the attribute was not already set. 77 | */ 78 | public boolean addAttribute(String attributeName) { 79 | return this.attributes.add(attributeName); 80 | } 81 | 82 | /** 83 | * Adds one or more attributes to the list of allowed attributes. 84 | * @param attributes The attribute names that are to be allowed. 85 | */ 86 | public void addAttributes(String... attributes) { 87 | for(final String attr : attributes) { 88 | this.addAttribute(attr); 89 | } 90 | } 91 | 92 | @Override 93 | public boolean equals(Object o) { 94 | if(this == o) { return true; } 95 | if(o == null || getClass() != o.getClass()) { return false; } 96 | 97 | IgnoredHtmlElement that = (IgnoredHtmlElement) o; 98 | 99 | return tagName.equals(that.tagName); 100 | } 101 | 102 | @Override 103 | public int hashCode() { 104 | return tagName.hashCode(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.overzealous 7 | remark 8 | 1.1.0 9 | 2011 10 | Remark 11 | Remark is a library for taking (X)HTML input and outputting clean Markdown, Markdown Extra, or 12 | MultiMarkdown compatible text. 13 | 14 | https://bitbucket.org/OverZealous/remark 15 | 16 | 17 | The Apache Software License, Version 2.0 18 | http://www.apache.org/licenses/LICENSE-2.0 19 | repo 20 | 21 | 22 | 23 | 24 | Phil DeJarnett 25 | OverZealous Creations, LLC 26 | http://www.overzealous.com 27 | 28 | Original Developer 29 | 30 | 31 | 32 | Guilherme I F L Weizenmann 33 | g.weizenmann@gmail.com 34 | ITQuasar 35 | http://itquasar.com 36 | 37 | Developer 38 | 39 | 40 | 41 | 42 | 43 | Alex Boyko 44 | 45 | https://github.com/BoykoAlex 46 | 47 | 48 | 49 | 50 | scm:git:git@github.com:giflw/remark-java.git 51 | scm:git:git@github.com:giflw/remark-java.git 52 | git@github.com:giflw/remark-java.git 53 | 54 | 55 | 56 | bintray-giflw-maven 57 | giflw-maven 58 | https://api.bintray.com/maven/giflw/maven/remark-java/;publish=1 59 | 60 | 61 | 62 | 63 | junit 64 | junit 65 | 4.12 66 | test 67 | 68 | 69 | commons-io 70 | commons-io 71 | 2.4 72 | test 73 | 74 | 75 | org.jsoup 76 | jsoup 77 | 1.6.3 78 | compile 79 | 80 | 81 | org.apache.commons 82 | commons-lang3 83 | 3.4 84 | compile 85 | 86 | 87 | 88 | commons-cli 89 | commons-cli 90 | 1.2 91 | compile 92 | true 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/convert/TextCleanerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import com.overzealous.remark.util.TestUtils; 21 | import org.junit.Assert; 22 | import org.junit.Test; 23 | 24 | /** 25 | * @author Phil DeJarnett 26 | */ 27 | public class TextCleanerTest { 28 | 29 | private static final TextCleaner BASIC = new TextCleaner(Options.markdown()); 30 | 31 | private static final TextCleaner FULL = new TextCleaner(Options.pegdownAllExtensions()); 32 | 33 | private String loadOut(String name) { 34 | return load(name, "out"); 35 | } 36 | 37 | private String loadBasicIn() { 38 | return load("basic", "in"); 39 | } 40 | 41 | private String loadFullIn() { 42 | return load("full", "in"); 43 | } 44 | 45 | private String load(String name, String type) { 46 | return TestUtils.readResourceToString("/textcleaner/" + name + '.' + type + ".txt"); 47 | } 48 | 49 | private void assertEqualsAndPrint(String expected, String result) { 50 | System.out.println(expected); 51 | System.out.println(result); 52 | System.out.println(); 53 | Assert.assertEquals(expected, result); 54 | } 55 | 56 | @Test 57 | public void testCleanBasic() throws Exception { 58 | assertEqualsAndPrint(loadOut("cleanBasic"), BASIC.clean(loadBasicIn())); 59 | } 60 | 61 | @Test 62 | public void testCleanFull() throws Exception { 63 | assertEqualsAndPrint(loadOut("cleanFull"), FULL.clean(loadFullIn())); 64 | } 65 | 66 | @Test 67 | public void testCleanCodeBasic() throws Exception { 68 | assertEqualsAndPrint(loadOut("cleanCodeBasic"), BASIC.cleanCode(loadBasicIn())); 69 | } 70 | 71 | @Test 72 | public void testCleanCodeFull() throws Exception { 73 | assertEqualsAndPrint(loadOut("cleanCodeFull"), FULL.cleanCode(loadFullIn())); 74 | } 75 | 76 | @Test 77 | public void testCleanInlineCodeSimple() throws Exception { 78 | Assert.assertEquals("`hello & > world`", BASIC.cleanInlineCode("hello & \n> world")); 79 | } 80 | 81 | @Test 82 | public void testCleanInlineCodeLeadingTick() throws Exception { 83 | Assert.assertEquals("`` `tick``", BASIC.cleanInlineCode("`tick")); 84 | } 85 | 86 | @Test 87 | public void testCleanInlineCodeTrailingTick() throws Exception { 88 | Assert.assertEquals("``tick` ``", BASIC.cleanInlineCode("tick`")); 89 | } 90 | 91 | @Test 92 | public void testCleanInlineCodeInlineTick() throws Exception { 93 | Assert.assertEquals("``ti`ck``", BASIC.cleanInlineCode("ti`ck")); 94 | } 95 | 96 | @Test 97 | public void testCleanInlineCodeLotsOfTicks() throws Exception { 98 | Assert.assertEquals("```` ``t```i`ck` ````", BASIC.cleanInlineCode("``t```i`ck`")); 99 | } 100 | 101 | /** Disabled because this is too dependent on the computer 102 | @Test 103 | public void testReplacementPerformance() throws Exception { 104 | String basic = loadBasicIn(); 105 | String full = loadFullIn(); 106 | int numberOfTrials = 500; 107 | int numberOfTests = 4; // based on tests inside loop below 108 | long start = System.currentTimeMillis(); 109 | for(int i=0; i (end-start)); 122 | } 123 | */ 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/convert/Table.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.convert; 18 | 19 | import com.overzealous.remark.Options; 20 | import com.overzealous.remark.util.MarkdownTable; 21 | import com.overzealous.remark.util.MarkdownTableCell; 22 | import org.jsoup.nodes.Element; 23 | 24 | import java.util.List; 25 | import java.util.regex.Matcher; 26 | import java.util.regex.Pattern; 27 | 28 | /** 29 | * @author Phil DeJarnett 30 | */ 31 | public class Table extends AbstractNodeHandler { 32 | 33 | private static final Pattern STYLE_ALIGNMENT_PATTERN = 34 | Pattern.compile("text-align:\\s*([a-z]+)", Pattern.CASE_INSENSITIVE); 35 | 36 | public void handleNode(NodeHandler parent, Element node, DocumentConverter converter) { 37 | MarkdownTable table = new MarkdownTable(); 38 | 39 | // loop over every direct child of the table node. 40 | for(final Element child : node.children()) { 41 | 42 | if(child.tagName().equals("thead")) { 43 | // handle explicitly declared header sections 44 | for(final Element headerRow : child.children()) { 45 | processRow(table.addHeaderRow(), headerRow, converter); 46 | } 47 | 48 | } else if(child.tagName().equals("tbody") || child.tagName().equals("tfoot")) { 49 | // handle body or foot sections - note: there's no special handling for tfoot 50 | for(final Element bodyRow : child.children()) { 51 | processRow(table.addBodyRow(), bodyRow, converter); 52 | } 53 | 54 | } else if(child.tagName().equals("tr")) { 55 | // Hrm, a row was added outside a valid table body or header... 56 | if(!child.children().isEmpty()) { 57 | if(child.children().get(0).tagName().equals("th")) { 58 | // handle manual TH cells 59 | processRow(table.addHeaderRow(), child, converter); 60 | 61 | } else { 62 | // OK, must be a table row. 63 | processRow(table.addBodyRow(), child, converter); 64 | 65 | } 66 | } 67 | } 68 | } 69 | 70 | // OK, now render this sucker 71 | Options.Tables opts = converter.options.getTables(); 72 | converter.output.startBlock(); 73 | table.renderTable(converter.output, opts.isColspanEnabled(), opts.isRenderedAsCode()); 74 | converter.output.endBlock(); 75 | } 76 | 77 | private void processRow(List row, Element tableRow, DocumentConverter converter) { 78 | for(final Element cell : tableRow.children()) { 79 | String contents = converter.getInlineContent(this, cell, true); 80 | row.add(new MarkdownTableCell(contents, getAlignment(cell), getColspan(cell))); 81 | } 82 | } 83 | 84 | private MarkdownTable.Alignment getAlignment(Element cell) { 85 | MarkdownTable.Alignment alignment = MarkdownTable.Alignment.LEFT; 86 | String alignmentString = null; 87 | if(cell.hasAttr("align")) { 88 | alignmentString = cell.attr("align").toLowerCase(); 89 | } else if(cell.hasAttr("style")) { 90 | Matcher m = STYLE_ALIGNMENT_PATTERN.matcher(cell.attr("style")); 91 | if(m.find()) { 92 | alignmentString = m.group(1).toLowerCase(); 93 | } 94 | } 95 | if(alignmentString != null) { 96 | if(alignmentString.equals("center")) { 97 | alignment = MarkdownTable.Alignment.CENTER; 98 | } else if(alignmentString.equals("right")) { 99 | alignment = MarkdownTable.Alignment.RIGHT; 100 | } 101 | } 102 | return alignment; 103 | } 104 | 105 | private int getColspan(Element cell) { 106 | int colspan = 1; 107 | if(cell.hasAttr("colspan")) { 108 | try { 109 | colspan = Integer.parseInt(cell.attr("colspan")); 110 | } catch(NumberFormatException ex) { 111 | // ignore invalid numbers 112 | } 113 | } 114 | return colspan; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/util/StringUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.util; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | 22 | import java.io.PrintWriter; 23 | import java.io.StringWriter; 24 | 25 | /** 26 | * @author Phil DeJarnett 27 | */ 28 | public class StringUtilsTest { 29 | 30 | private final StringWriter testWriter = new StringWriter(); 31 | private final PrintWriter testPrintWriter = new PrintWriter(testWriter); 32 | 33 | private PrintWriter getWriter() { 34 | testWriter.getBuffer().setLength(0); 35 | return testPrintWriter; 36 | } 37 | 38 | @SuppressWarnings({"RedundantThrows"}) 39 | private void assertWriter(String expected) throws Exception { 40 | Assert.assertEquals(expected, testWriter.toString()); 41 | } 42 | 43 | @Test 44 | public void testAlignLeft() throws Exception { 45 | Assert.assertEquals("foo ", StringUtils.align("foo", 9, StringUtils.ALIGN_LEFT)); 46 | Assert.assertEquals("fu+++++++", StringUtils.align("fu", 9, '+', StringUtils.ALIGN_LEFT)); 47 | } 48 | 49 | @Test 50 | public void testAlignCenter() throws Exception { 51 | Assert.assertEquals(" foo ", StringUtils.align("foo", 9, StringUtils.ALIGN_CENTER)); 52 | Assert.assertEquals("+++fu++++", StringUtils.align("fu", 9, '+', StringUtils.ALIGN_CENTER)); 53 | } 54 | 55 | @Test 56 | public void testAlignRight() throws Exception { 57 | Assert.assertEquals(" foo", StringUtils.align("foo", 9, StringUtils.ALIGN_RIGHT)); 58 | Assert.assertEquals("+++++++fu", StringUtils.align("fu", 9, '+', StringUtils.ALIGN_RIGHT)); 59 | } 60 | 61 | @Test 62 | public void testAlignWithWriterLeft() throws Exception { 63 | StringUtils.align(getWriter(), "foo", 9, StringUtils.ALIGN_LEFT); 64 | assertWriter("foo "); 65 | 66 | StringUtils.align(getWriter(), "fu", 9, '+', StringUtils.ALIGN_LEFT); 67 | assertWriter("fu+++++++"); 68 | } 69 | 70 | @Test 71 | public void testAlignWithWriterCenter() throws Exception { 72 | StringUtils.align(getWriter(), "foo", 9, StringUtils.ALIGN_CENTER); 73 | assertWriter(" foo "); 74 | 75 | StringUtils.align(getWriter(), "fu", 9, '+', StringUtils.ALIGN_CENTER); 76 | assertWriter("+++fu++++"); 77 | } 78 | 79 | @Test 80 | public void testAlignWithWriterRight() throws Exception { 81 | StringUtils.align(getWriter(), "foo", 9, StringUtils.ALIGN_RIGHT); 82 | assertWriter(" foo"); 83 | StringUtils.align(getWriter(), "fu", 9, '+', StringUtils.ALIGN_RIGHT); 84 | assertWriter("+++++++fu"); 85 | } 86 | 87 | @Test 88 | public void testMultiplyCharacter() throws Exception { 89 | Assert.assertEquals("", StringUtils.multiply('c', 0)); 90 | Assert.assertEquals("ccc", StringUtils.multiply('c', 3)); 91 | } 92 | @Test 93 | public void testMultiplyString() throws Exception { 94 | Assert.assertEquals("", StringUtils.multiply("str", 0)); 95 | Assert.assertEquals("strstrstrstr", StringUtils.multiply("str", 4)); 96 | } 97 | 98 | @Test 99 | public void testMultiplyCharacterWithWriter() throws Exception { 100 | StringUtils.multiply(getWriter(), 'c', 0); 101 | assertWriter(""); 102 | StringUtils.multiply(getWriter(), 'c', 3); 103 | assertWriter("ccc"); 104 | } 105 | @Test 106 | public void testMultiplyStringWithWriter() throws Exception { 107 | StringUtils.multiply(getWriter(), "str", 0); 108 | assertWriter(""); 109 | StringUtils.multiply(getWriter(), "str", 4); 110 | assertWriter("strstrstrstr"); 111 | } 112 | 113 | @Test 114 | public void testPrependEachLine() throws Exception { 115 | Assert.assertEquals(" 1\n 2\n 3", StringUtils.prependEachLine("1\n2\n3", " ")); 116 | } 117 | 118 | @Test 119 | public void testPrependEachLineWithWriter() throws Exception { 120 | StringUtils.prependEachLine(getWriter(), "1\n2\n3", " "); 121 | assertWriter(" 1\n 2\n 3"); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/manual/index.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | **Remark** is a library for taking (X)HTML input and outputting clean [Markdown][], [Markdown Extra][], or [MultiMarkdown][] compatible text. The purpose of this conversion is mainly to allow for the use of client-side HTML GUI editors while retaining safe, mobile-device editable markdown text behind the scenes. It is recommended that the markdown text is stored, to reduce XSS attacks by code injection. 4 | 5 | ## Example Usage Scenario 6 | 7 | * The user logs in from their desktop. 8 | * Adding some text, the user inputs into a full-featured GUI, such as [Dojo's rich text editor][dojo_rte], or [any of these editors][other_rtes]. 9 | * The webserver takes the generated HTML, which may contain a lot of bad HTML, depending on the browser, and passes it to **Remark**. 10 | * **Remark** passes the HTML to [jsoup][], to clean up the input text, which strips unsupported HTML tags (the text will remain). 11 | * **Remark** walks the generated DOM tree, and outputs clean, structured markdown text. 12 | * The markdown text is returned. 13 | * The webserver stores this markdown text for future display. 14 | * The user chooses to re-edit the HTML text from their desktop. 15 | * The webserver converts the Markdown back to HTML, and sends it to the client. 16 | * Repeat the steps above to save it. 17 | * The user later logs in from their mobile device. 18 | * Mobile devices often not support rich text editing through the web browser. 19 | * So, instead, render a plain text field with the raw markdown text. 20 | * Because markdown is relatively easy to read and edit, the user can make simple changes without struggling with hundreds of messy HTML tags. 21 | 22 | ## Advanced Features 23 | 24 | **Remark** can be configured to output extra functionality beyond straight markdown. 25 | 26 | * [Markdown Extra tables][] or [Multimarkdown tables][] (which add column spanning support), including a best-guess attempt at alignment (based on style or align attributes) 27 | * Reversal of various smart HTML entities or unicode characters: 28 | * `“` (“) and `”` (”) become `"` 29 | * `‘` (‘), `’` (’), and `'` become `'` 30 | * `«` («) becomes `<<` 31 | * `»` (») becomes `>>` 32 | * `…` (…) becomes `...` 33 | * `&endash;` (–) becomes `--` 34 | * `&emdash;` (—) becomes `---` 35 | * Simplified hardwraps — A `
` is converted to just a single linebreak, instead of `(space)(space)(newline)`, common in most third-party markdown renderers 36 | * Autolinks — a link that has the same content as it's label (and starts with http or https) is simply rendered as is, like `http://www.overzealous.com` 37 | * [Markdown Extra definition lists][] 38 | * [Markdown Extra abbreviations][] 39 | * [Markdown Extra header IDs][] 40 | * Fenced code blocks, using either [Markdown Extra's format][Markdown Extra fenced code block] using `~~~`, or [Github's format][Github fenced code block] using ` ``` ` 41 | * Customization of allowed HTML tags - not really recommended. 42 | 43 | The basic theory is that you match the extensions to your Markdown conversion library. 44 | 45 | ### Dependencies 46 | 47 | **Remark** depends on [jsoup][] and [Apache Commons Lang 3][]. If you want to use it from the command line, it also depends on [Apache Commons CLI][]. Alternatively, you can download the standalone version of the Jar, which contains all the dependencies. 48 | 49 | During testing, **Remark** also depends on some additional libraries, which are automatically downloaded by the gradle build script. 50 | 51 | [Markdown]: http://daringfireball.net/projects/markdown/ 52 | [Markdown Extra]: http://michelf.com/projects/php-markdown/extra/ 53 | [Markdown Extra tables]: http://michelf.com/projects/php-markdown/extra/#table 54 | [Markdown Extra definition lists]: http://michelf.com/projects/php-markdown/extra/#def-list 55 | [Markdown Extra fenced code block]: http://michelf.com/projects/php-markdown/extra/#fenced-code-blocks 56 | [Markdown Extra abbreviations]: http://michelf.com/projects/php-markdown/extra/#abbr 57 | [Markdown Extra header IDs]: http://michelf.com/projects/php-markdown/extra/#header-id 58 | [MultiMarkdown]: http://fletcherpenney.net/multimarkdown/ 59 | [MultiMarkdown tables]: http://fletcher.github.com/peg-multimarkdown/#tables 60 | [Github fenced code block]: http://github.github.com/github-flavored-markdown/ 61 | [dojo_rte]: http://dojotoolkit.org/reference-guide/dijit/Editor.html 62 | [other_rtes]: http://www.queness.com/post/212/10-jquery-and-non-jquery-javascript-rich-text-editors 63 | [jsoup]: http://jsoup.org/ 64 | [jsoup license]: http://jsoup.org/license 65 | [Apache Commons Lang 3]: http://commons.apache.org/lang/ 66 | [Apache Commons CLI]: http://commons.apache.org/cli/ 67 | [Apache 2.0 License]: http://www.apache.org/licenses/LICENSE-2.0 -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/util/BlockWriterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.util; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | 22 | /** 23 | * @author Phil DeJarnett 24 | */ 25 | public class BlockWriterTest { 26 | 27 | @Test 28 | public void testWriteTwoBlocks() throws Exception { 29 | BlockWriter bw = BlockWriter.create(); 30 | bw.writeBlock("block1"); 31 | bw.writeBlock("block2"); 32 | 33 | Assert.assertEquals("block1\n\nblock2", bw.toString()); 34 | } 35 | 36 | @Test 37 | public void testWriteInlineThenBlock() throws Exception { 38 | BlockWriter bw = BlockWriter.create(); 39 | bw.write("inline1"); 40 | bw.writeBlock("block1"); 41 | 42 | Assert.assertEquals("inline1\n\nblock1", bw.toString()); 43 | } 44 | 45 | @Test 46 | public void testWriteBlockThenInlineThenBlock() throws Exception { 47 | BlockWriter bw = BlockWriter.create(); 48 | bw.writeBlock("block1"); 49 | bw.write("inline1"); 50 | bw.writeBlock("block2"); 51 | 52 | Assert.assertEquals("block1\n\ninline1\n\nblock2", bw.toString()); 53 | } 54 | 55 | @Test 56 | public void testWriteManualBlock() throws Exception { 57 | BlockWriter bw = BlockWriter.create(); 58 | bw.startBlock(); 59 | bw.print("hello"); 60 | bw.println(); 61 | bw.print("world"); 62 | bw.endBlock(); 63 | bw.write("inline"); 64 | bw.write("1"); 65 | bw.startBlock(); 66 | bw.write("block1"); 67 | bw.endBlock(); 68 | 69 | Assert.assertEquals("hello\nworld\n\ninline1\n\nblock1", bw.toString()); 70 | } 71 | 72 | @Test 73 | public void testWriteNestedBlocks() throws Exception { 74 | BlockWriter bw = BlockWriter.create(); 75 | bw.startBlock(); 76 | bw.write("hello"); 77 | bw.startBlock(); 78 | bw.write("world"); 79 | bw.endBlock(); 80 | bw.endBlock(); 81 | 82 | Assert.assertEquals(0, bw.getBlockDepth()); 83 | 84 | Assert.assertEquals("hello\n\nworld", bw.toString()); 85 | } 86 | 87 | @Test 88 | public void testPrintf() throws Exception { 89 | BlockWriter bw = BlockWriter.create(); 90 | bw.printf("%s", "hello"); 91 | bw.printf("%d", 42); 92 | bw.writeBlock("block1"); 93 | 94 | Assert.assertEquals("hello42\n\nblock1", bw.toString()); 95 | } 96 | 97 | @Test 98 | public void testPrependChar() throws Exception { 99 | BlockWriter bw = BlockWriter.create(); 100 | bw.setPrependNewlineString("XXXX"); 101 | bw.write('a'); 102 | 103 | Assert.assertEquals("XXXXa", bw.toString()); 104 | } 105 | 106 | @Test 107 | public void testPrependCharBuffer() throws Exception { 108 | BlockWriter bw = BlockWriter.create(); 109 | bw.setPrependNewlineString("XXXX"); 110 | bw.write("abc\ndef\nghi".toCharArray()); 111 | 112 | Assert.assertEquals("XXXXabc\nXXXXdef\nXXXXghi", bw.toString()); 113 | } 114 | 115 | @Test 116 | public void testPrependString() throws Exception { 117 | BlockWriter bw = BlockWriter.create(); 118 | bw.setPrependNewlineString("XXXX"); 119 | bw.write("abc\n\ndef\nghi"); 120 | bw.write("\njkl"); 121 | 122 | Assert.assertEquals("XXXXabc\nXXXX\nXXXXdef\nXXXXghi\nXXXXjkl", bw.toString()); 123 | } 124 | 125 | @Test 126 | public void testPrependNotFirst() throws Exception { 127 | BlockWriter bw = BlockWriter.create(); 128 | bw.setPrependNewlineString("XXXX", true); 129 | bw.write("- "); 130 | bw.write("abc\ndef\nghi"); 131 | 132 | Assert.assertEquals("- abc\nXXXXdef\nXXXXghi", bw.toString()); 133 | } 134 | 135 | @Test 136 | public void testPrependBlocks() throws Exception { 137 | BlockWriter bw = BlockWriter.create(); 138 | bw.setPrependNewlineString("XXXX"); 139 | bw.printBlock("abc\ndef"); 140 | bw.printBlock("ghi\njkl"); 141 | 142 | Assert.assertEquals("XXXXabc\nXXXXdef\nXXXX\nXXXXghi\nXXXXjkl", bw.toString()); 143 | } 144 | 145 | @Test 146 | public void testEmptyBlocks() throws Exception { 147 | BlockWriter bw = BlockWriter.create(); 148 | bw.writeBlock("block1"); 149 | bw.startBlock(); 150 | bw.startBlock(); 151 | bw.writeBlock("block2"); 152 | bw.endBlock(); 153 | bw.endBlock(); 154 | 155 | Assert.assertEquals("block1\n\nblock2", bw.toString()); 156 | } 157 | 158 | @Test 159 | public void testEmptyAutoBlocks() throws Exception { 160 | BlockWriter bw = BlockWriter.create(); 161 | bw.writeBlock("block1"); 162 | bw.startBlock(); 163 | bw.write("inline1"); 164 | bw.startBlock(); 165 | bw.writeBlock("block2"); 166 | bw.endBlock(); 167 | bw.write("inline2"); 168 | bw.endBlock(); 169 | 170 | Assert.assertEquals("block1\n\ninline1\n\nblock2\n\ninline2", bw.toString()); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/util/MarkdownTableCell.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.util; 18 | 19 | /** 20 | * This class contains the contents of a table cell. It's used to help keep track of 21 | * information about the table so the final table can be built with clean formatting. 22 | * 23 | * @author Phil DeJarnett 24 | */ 25 | public class MarkdownTableCell { 26 | 27 | private MarkdownTable.Alignment alignment = MarkdownTable.Alignment.LEFT; 28 | 29 | private String contents = ""; 30 | 31 | private int colspan = 1; 32 | 33 | /** 34 | * Creates a new, empty MarkdownTableCell 35 | */ 36 | @SuppressWarnings({"UnusedDeclaration"}) 37 | public MarkdownTableCell() { 38 | this("", MarkdownTable.Alignment.LEFT, 1); 39 | } 40 | 41 | /** 42 | * Creates a new MarkdownTableCell with only contents 43 | * @param contents The contents of this cell 44 | */ 45 | public MarkdownTableCell(String contents) { 46 | this(contents, MarkdownTable.Alignment.LEFT, 1); 47 | } 48 | 49 | /** 50 | * Creates a new MarkdownTableCell with contents and alignment 51 | * @param contents The contents of this cell 52 | * @param alignment The alignment of this cell (if specified) 53 | */ 54 | public MarkdownTableCell(String contents, MarkdownTable.Alignment alignment) { 55 | this(contents, alignment, 1); 56 | } 57 | 58 | /** 59 | * Creates a new MarkdownTableCell with contents and a colspan 60 | * @param contents The contents of this cell 61 | * @param colspan The number of columns this cell spans 62 | */ 63 | public MarkdownTableCell(String contents, int colspan) { 64 | this(contents, MarkdownTable.Alignment.LEFT, colspan); 65 | } 66 | 67 | /** 68 | * Creates a new MarkdownTableCell with contents and a colspan 69 | * @param contents The contents of this cell 70 | * @param alignment The alignment of this cell (if specified) 71 | * @param colspan The number of columns this cell spans 72 | */ 73 | public MarkdownTableCell(String contents, MarkdownTable.Alignment alignment, int colspan) { 74 | this.setContents(contents); 75 | this.setAlignment(alignment); 76 | this.setColspan(colspan); 77 | } 78 | 79 | /** 80 | * Gets the text-alignment of this cell 81 | * @return The alignment of this cell 82 | */ 83 | public MarkdownTable.Alignment getAlignment() { 84 | return alignment; 85 | } 86 | 87 | /** 88 | * Sets the text-alignment. Note: the alignment cannot be null. 89 | * @param alignment The new alignment 90 | */ 91 | public void setAlignment(MarkdownTable.Alignment alignment) { 92 | if(alignment == null) { 93 | throw new IllegalArgumentException("Alignment cannot be null"); 94 | } 95 | this.alignment = alignment; 96 | } 97 | 98 | public String getContents() { 99 | return contents; 100 | } 101 | 102 | /** 103 | * Sets the contents of this cell. 104 | * If the contents contain any linebreaks, they will be replaced with spaces. 105 | * 106 | * @param contents The new cell contents 107 | */ 108 | public void setContents(String contents) { 109 | if(contents == null) { 110 | contents = ""; 111 | } 112 | // We don't allow linebreaks in a table cell 113 | this.contents = contents.replace("\n", " "); 114 | } 115 | 116 | public int getColspan() { 117 | return colspan; 118 | } 119 | 120 | /** 121 | * Sets the number of columns this cell spans. If the colspan is less than 1, it is set to 1. 122 | * @param colspan The new colspan 123 | */ 124 | public void setColspan(int colspan) { 125 | if(colspan < 1) { 126 | colspan = 1; 127 | } 128 | this.colspan = colspan; 129 | } 130 | 131 | /** 132 | * Returns the number of characters needed to show this column. 133 | * It adds two to the content width, so there is padding around the content. 134 | * @return The width of this column in characters, plus 2 chars for spacing. 135 | */ 136 | public int getWidth() { 137 | return this.contents.length() + 2; 138 | } 139 | 140 | @Override 141 | public String toString() { 142 | return "MarkdownTableCell{" + 143 | "colspan=" + colspan + 144 | ", alignment=" + alignment + 145 | ", contents='" + contents + '\'' + 146 | '}'; 147 | } 148 | 149 | @Override 150 | public boolean equals(Object o) { 151 | if(this == o) { return true; } 152 | if(o == null || getClass() != o.getClass()) { return false; } 153 | 154 | MarkdownTableCell that = (MarkdownTableCell) o; 155 | 156 | return !(colspan != that.colspan || 157 | alignment != that.alignment || 158 | !contents.equals(that.contents)); 159 | } 160 | 161 | @Override 162 | public int hashCode() { 163 | int result = alignment.hashCode(); 164 | result = 31 * result + contents.hashCode(); 165 | result = 31 * result + colspan; 166 | return result; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/test/java/com/overzealous/remark/util/MarkdownTableTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.util; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | 22 | import java.io.PrintWriter; 23 | import java.io.StringWriter; 24 | import java.util.List; 25 | 26 | /** 27 | * @author Phil DeJarnett 28 | */ 29 | public class MarkdownTableTest { 30 | 31 | @Test 32 | public void testTable() throws Exception { 33 | MarkdownTable mt = new MarkdownTable(); 34 | List header = mt.addHeaderRow(); 35 | List body = mt.addBodyRow(); 36 | for(int i=1; i<4; i++) { 37 | header.add(new MarkdownTableCell("header"+i)); 38 | body.add(new MarkdownTableCell("column "+i)); 39 | } 40 | 41 | Assert.assertEquals(TestUtils.readResourceToString("/util/MarkdownTableTest.md"), getTableString(mt, false, false)); 42 | } 43 | 44 | @Test 45 | public void testTableWithAlignment() throws Exception { 46 | MarkdownTable mt = new MarkdownTable(); 47 | List header = mt.addHeaderRow(); 48 | List body1 = mt.addBodyRow(); 49 | List body2 = mt.addBodyRow(); 50 | for(int i=1; i<4; i++) { 51 | header.add(new MarkdownTableCell("h"+i, MarkdownTable.Alignment.find(i-2))); 52 | body1.add(new MarkdownTableCell("col"+i)); 53 | body2.add(new MarkdownTableCell("column "+i)); 54 | } 55 | 56 | Assert.assertEquals(TestUtils.readResourceToString("/util/MarkdownTableAlignmentTest.md"), getTableString(mt, false, false)); 57 | } 58 | 59 | @Test 60 | public void testTableWithColspan() throws Exception { 61 | MarkdownTable mt = new MarkdownTable(); 62 | List header = mt.addHeaderRow(); 63 | List body1 = mt.addBodyRow(); 64 | List body2 = mt.addBodyRow(); 65 | for(int i=1; i<4; i++) { 66 | header.add(new MarkdownTableCell("h"+i, MarkdownTable.Alignment.find(i-2))); 67 | body1.add(new MarkdownTableCell("col"+i)); 68 | } 69 | body2.add(new MarkdownTableCell("column 1")); 70 | body2.add(new MarkdownTableCell("column 2", 2)); 71 | 72 | Assert.assertEquals(TestUtils.readResourceToString("/util/MarkdownTableColspanTest.md"), getTableString(mt, true, false)); 73 | } 74 | 75 | @Test 76 | public void testTableWithWideColspan() throws Exception { 77 | MarkdownTable mt = new MarkdownTable(); 78 | List header = mt.addHeaderRow(); 79 | List body1 = mt.addBodyRow(); 80 | List body2 = mt.addBodyRow(); 81 | for(int i=1; i<5; i++) { 82 | header.add(new MarkdownTableCell("h"+i, MarkdownTable.Alignment.find(i-2))); 83 | body1.add(new MarkdownTableCell("col"+i)); 84 | } 85 | body2.add(new MarkdownTableCell("column 1")); 86 | body2.add(new MarkdownTableCell("this is a really, really, really, really wide colspan", 3)); 87 | 88 | Assert.assertEquals(TestUtils.readResourceToString("/util/MarkdownTableWideColspanTest.md"), getTableString(mt, true, false)); 89 | } 90 | 91 | @Test 92 | public void testTableAsCode() throws Exception { 93 | MarkdownTable mt = new MarkdownTable(); 94 | List header = mt.addHeaderRow(); 95 | List body = mt.addBodyRow(); 96 | for(int i=1; i<4; i++) { 97 | header.add(new MarkdownTableCell("header"+i)); 98 | body.add(new MarkdownTableCell("column "+i)); 99 | } 100 | 101 | Assert.assertEquals(TestUtils.readResourceToString("/util/MarkdownTableAsCodeTest.md"), getTableString(mt, false, true)); 102 | } 103 | 104 | private String getTableString(MarkdownTable mt, boolean allowColSpan, boolean renderAsCode) { 105 | StringWriter sw = new StringWriter(); 106 | //noinspection IOResourceOpenedButNotSafelyClosed 107 | mt.renderTable(new PrintWriter(sw), allowColSpan, renderAsCode); 108 | return '\n' + sw.toString(); 109 | } 110 | 111 | @Test 112 | public void testGetNumberOfColumns() throws Exception { 113 | MarkdownTable mt = new MarkdownTable(); 114 | for(int i=0; i<10; i++) { 115 | List row = mt.addBodyRow(); 116 | for(int j=0; j <= i%5; j++) { 117 | row.add(new MarkdownTableCell("cell "+j)); 118 | } 119 | } 120 | 121 | Assert.assertEquals(5, mt.getNumberOfColumns()); 122 | } 123 | 124 | @Test 125 | public void testGetNumberOfColumnsWithUnevenColumns() throws Exception { 126 | MarkdownTable mt = new MarkdownTable(); 127 | for(int i=0; i<3; i++) { 128 | List row = mt.addBodyRow(); 129 | for(int j=0; j <= 2; j++) { 130 | if(i==1 && j==1) { 131 | // add a three-column cell 132 | row.add(new MarkdownTableCell("cell "+j, 3)); 133 | } else { 134 | row.add(new MarkdownTableCell("cell "+j)); 135 | } 136 | } 137 | } 138 | 139 | Assert.assertEquals(5, mt.getNumberOfColumns()); 140 | 141 | List row = mt.addHeaderRow(); 142 | for(int i=1; i<=7; i++) { 143 | row.add(new MarkdownTableCell("head "+i)); 144 | } 145 | 146 | Assert.assertEquals(7, mt.getNumberOfColumns()); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/manual/usage.md: -------------------------------------------------------------------------------- 1 | # Usage Examples 2 | 3 | The following are some basic usage examples. 4 | 5 | ## Simplest Usage 6 | 7 | For straight-up Markdown, simply use the `Remark` class directly, like so: 8 | 9 | import com.overzealous.remark.Remark; 10 | 11 | Remark remark = new Remark(); 12 | String htmlInput = ...; 13 | String markdown = remark.convertFragment(htmlImput); 14 | 15 | ## Alternate Usage 16 | 17 | Because Remark uses [jsoup][] under the surface, it can parse HTML from a variety of sources, including: 18 | 19 | * Whole HTML Files 20 | 21 | remark.convert(new File("path/to/file.html")); 22 | 23 | * Download HTML files from a URL 24 | 25 | remark.convert(new URL("http://www.overzealous.com/"), 15000); 26 | 27 | * Convert whole HTML files already loaded in-memory 28 | 29 | remark.convert(htmlInput); 30 | 31 | * Convert fragments with a base URI to allow for relative links 32 | 33 | remark.convertFragment(htmlInput, "http://www.example.com"); 34 | 35 | **Note:** Relative links will be *removed* from the final document if you do not provide a base URI on fragments! 36 | 37 | 38 | ## Compatibility With Markdown Extensions 39 | 40 | For some of the more common Markdown extensions, you can use the `Options` class to create a `Remark` converter with additional functionality. 41 | 42 | import com.overzealous.remark.Remark; 43 | import com.overzealous.remark.Options; 44 | 45 | // PHP Markdown Extra 46 | Remark markdownExtraRemark = new Remark(Options.markdownExtra()); 47 | 48 | // MultiMarkdown 49 | Remark multiMarkdownRemark = new Remark(Options.multiMarkdown()); 50 | 51 | // Github Flavored Markdown 52 | Remark githubMarkdown = new Remark(Options.github()); 53 | 54 | // Pegdown with all extensions enabled 55 | Remark pegdownMarkdown = new Remark(Options.pegdownAllExtensions()); 56 | 57 | For more information on what these static option sets provide, please check out [the JavaDoc API][javadoc Options]. 58 | 59 | ## Custom Options 60 | 61 | You can also set up your own options, to change the behavior for certain features. The following is just a highlight. The [JavaDoc API for `Options`][javadoc Options] has the complete list of settings. 62 | 63 | > Note: all options must be configured **before** creating the Remark object. However, the Options object is cloned when creating the Remark instance, so you can reuse the Options object to create different Remark instances. 64 | 65 | ### Replace Tables with plain text 66 | 67 | Even if your markdown parser doesn't support tables, **Remark** can be used to convert tables into a simplified plain-text representation. 68 | 69 | Options opts = Options.markdown(); 70 | opts.tables = Options.Tables.CONVERT_TO_CODEBLOCK; 71 | 72 | If you want them removed completely, for (security reasons, for example), you can change the option like this: 73 | 74 | opts.tables = Options.Tables.REMOVE; 75 | 76 | ### Use Simple Link IDs 77 | 78 | By default, Remark attempts to use the description of a link to generate link ids. (If you want links inline, see the next section.) 79 | 80 | This means that this input: 81 | 82 |

An Example

83 |

Google Me

84 | 85 | gets converted into something like: 86 | 87 | [An Example][] 88 | [Google Me][] 89 | 90 | [An Example]: http://example.com 91 | [Google Me]: http://google.com 92 | 93 | If you'd prefer the resulting link IDs to be simple numeric IDs, change the setting like so: 94 | 95 | Options opts = Options.markdown(); 96 | opts.simpleLinkIds = true; 97 | 98 | Now the output will be: 99 | 100 | [An Example][1] 101 | [Google Me][2] 102 | 103 | [1]: http://example.com 104 | [2]: http://google.com 105 | 106 | > Note: this setting affects image links as well. 107 | 108 | ### Inline Links 109 | 110 | You can also change the output so that all links are inline, which may be easier for your users. 111 | 112 | Options opts = Options.markdown(); 113 | opts.inlineLinks = true; 114 | 115 | > Note: this setting affects image links as well. 116 | 117 | 118 | ### Preserve Relative Links 119 | 120 | By default, relative links in JSoup are resolved against your base URI (whether explicitly provided or determined automatically when downloading a file). If you'd prefer to keep relative links, you can set the `preserveRelativeLinks` option to `true`. 121 | 122 | Options opts = Options.markdown(); 123 | opts.preserveRelativeLinks = true; 124 | 125 | ### Allowing custom HTML tags 126 | 127 | If you decide you want to allow custom HTML tags, these (and their attributes) can be added to the Options file before creating 128 | the Remark object. 129 | 130 | import com.overzealous.remark.Remark; 131 | import com.overzealous.remark.Options; 132 | import com.overzealous.remark.IgnoredHtmlElement; 133 | 134 | Options opts = Options.markdown(); 135 | opts.ignoredHtmlElements.add(IgnoredHtmlElement.create("quote", "class"); 136 | Remark remarkWithQuote = new Remark(opts); 137 | 138 | These elements (and the specified attributes) will be kept in the final output. It is up to you to secure these items. 139 | 140 | > Note: this will not allow you to override default bahavior. For example, there is no way to preserve `` tags, since they are analyzed for bold and italic font changes. 141 | > 142 | > This means that, currently, there is no way to support color within the Markdown output. 143 | 144 | ## Streaming the Output 145 | 146 | Usually, the Remark conversion happens completely in memory. If you are converting a large document, however, you can stream the conversion process. This helps a tiny bit. The resulting document can be streamed to any `Writer` or `OutputStream`. 147 | 148 | Writer myWriter = null; 149 | try { 150 | // look up your writer 151 | myWriter = ...; 152 | Remark remark = new Remark().withWriter(myWriter); 153 | remark.convert(new URL("http://www.google.com"), 15000); 154 | } finally { 155 | myWriter.close(); 156 | } 157 | 158 | 159 | 160 | [jsoup]: http://jsoup.org/ 161 | [javadoc Options]: ../javadoc/com/overzealous/remark/Options.html -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/giflw/remark-java.svg?branch=master)](https://travis-ci.org/giflw/remark-java) 2 | 3 | 4 | # OverZealous Creations Remark 5 | 6 | **Remark** is a library for taking (X)HTML input and outputting clean [Markdown][], [Markdown Extra][], or [MultiMarkdown][] compatible text. The purpose of this conversion is mainly to allow for the use of client-side HTML GUI editors while retaining safe, mobile-device editable markdown text behind the scenes. It is recommended that the markdown text is stored, to reduce XSS attacks by code injection. 7 | 8 | ## Example Usage Scenario 9 | 10 | * The user logs in from their desktop. 11 | * Adding some text, the user inputs into a full-featured GUI, such as [Dojo's rich text editor][dojo_rte], or [any of these editors][other_rtes]. 12 | * The webserver takes the generated HTML, which may contain a lot of bad HTML, depending on the browser, and passes it to **Remark**. 13 | * **Remark** passes the HTML to [jsoup][], to clean up the input text, which strips unsupported HTML tags (the text will remain). 14 | * **Remark** walks the generated DOM tree, and outputs clean, structured markdown text. 15 | * The markdown text is returned. 16 | * The webserver stores this markdown text for future display. 17 | * The user chooses to re-edit the HTML text from their desktop. 18 | * The webserver converts the Markdown back to HTML, and sends it to the client. 19 | * Repeat the steps above to save it. 20 | * The user later logs in from their mobile device. 21 | * Mobile devices often not support rich text editing through the web browser. 22 | * So, instead, render a plain text field with the raw markdown text. 23 | * Because markdown is relatively easy to read and edit, the user can make simple changes without struggling with hundreds of messy HTML tags. 24 | 25 | ## Advanced Features 26 | 27 | **Remark** can be configured to output extra functionality beyond straight markdown. 28 | 29 | * [Markdown Extra tables][] or [Multimarkdown tables][] (which add column spanning support), including a best-guess attempt at alignment (based on style or align attributes) 30 | * Reversal of various smart HTML entities or unicode characters: 31 | * `“` (“) and `”` (”) become `"` 32 | * `‘` (‘), `’` (’), and `'` become `'` 33 | * `«` («) becomes `<<` 34 | * `»` (») becomes `>>` 35 | * `…` (…) becomes `...` 36 | * `&endash;` (–) becomes `--` 37 | * `&emdash;` (—) becomes `---` 38 | * Simplified hardwraps — A `
` is converted to just a single linebreak, instead of `(space)(space)(newline)`, common in most third-party markdown renderers 39 | * Autolinks — a link that has the same content as it's label (and starts with http or https) is simply rendered as is, like `http://www.overzealous.com` 40 | * [Markdown Extra definition lists][] 41 | * [Markdown Extra abbreviations][] 42 | * [Markdown Extra header IDs][] 43 | * Fenced code blocks, using either [Markdown Extra's format][Markdown Extra fenced code block] using `~~~`, or [Github's format][Github fenced code block] using ` ``` ` 44 | * Customization of allowed HTML tags - not really recommended. 45 | 46 | The basic theory is that you match the extensions to your Markdown conversion library. 47 | 48 | ## A Note on Forking: 49 | 50 | Want to fork this project? *Great!* However, please note that I use [hgflow][] to manage the develop-release cycle. If you are uncomfortable with that, that's fine, too! Just switch to the **develop** branch before working, or I won't be able to easily merge the changes back in. 51 | 52 | Source code build is done via [Gradle][]. 53 | 54 | ## Dependencies 55 | 56 | **Remark** depends on [jsoup][] and [Apache Commons Lang 3][]. If you want to use it from the command line, it also depends on [Apache Commons CLI][]. Alternatively, you can download the standalone version of the Jar, which contains all the dependencies. 57 | 58 | jsoup uses the [MIT License][jsoup license], which is roughly comparable to the [Apache 2.0 License][] used by Remark and the Apache dependencies. 59 | 60 | During testing, **Remark** also depends on some additional libraries, which are automatically downloaded by the gradle build script. 61 | 62 | ## License 63 | 64 | **Remark** is released under the [Apache 2.0 license][]. 65 | 66 | Copyright 2011 OverZealous Creations, LLC 67 | 68 | Licensed under the Apache License, Version 2.0 (the "License"); 69 | you may not use this file except in compliance with the License. 70 | You may obtain a copy of the License at 71 | 72 | http://www.apache.org/licenses/LICENSE-2.0 73 | 74 | Unless required by applicable law or agreed to in writing, software 75 | distributed under the License is distributed on an "AS IS" BASIS, 76 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 77 | See the License for the specific language governing permissions and 78 | limitations under the License. 79 | 80 | [Markdown]: http://daringfireball.net/projects/markdown/ 81 | [Markdown Extra]: http://michelf.com/projects/php-markdown/extra/ 82 | [Markdown Extra tables]: http://michelf.com/projects/php-markdown/extra/#table 83 | [Markdown Extra definition lists]: http://michelf.com/projects/php-markdown/extra/#def-list 84 | [Markdown Extra fenced code block]: http://michelf.com/projects/php-markdown/extra/#fenced-code-blocks 85 | [Markdown Extra abbreviations]: http://michelf.com/projects/php-markdown/extra/#abbr 86 | [Markdown Extra header IDs]: http://michelf.com/projects/php-markdown/extra/#header-id 87 | [MultiMarkdown]: http://fletcherpenney.net/multimarkdown/ 88 | [MultiMarkdown tables]: http://fletcher.github.com/peg-multimarkdown/#tables 89 | [Github fenced code block]: http://github.github.com/github-flavored-markdown/ 90 | [dojo_rte]: http://dojotoolkit.org/reference-guide/dijit/Editor.html 91 | [other_rtes]: http://www.queness.com/post/212/10-jquery-and-non-jquery-javascript-rich-text-editors 92 | [jsoup]: http://jsoup.org/ 93 | [jsoup license]: http://jsoup.org/license 94 | [Apache Commons Lang 3]: http://commons.apache.org/lang/ 95 | [Apache Commons CLI]: http://commons.apache.org/cli/ 96 | [hgflow]: https://bitbucket.org/yinwm/hgflow/wiki/Home 97 | [Gradle]: http://gradle.org/ 98 | [Apache 2.0 License]: http://www.apache.org/licenses/LICENSE-2.0 99 | -------------------------------------------------------------------------------- /src/manual/examples.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Here are some basic input/output examples. 4 | 5 | ## Options: None (Straight Markdown) 6 | 7 | ### Hello World 8 | 9 | * HTML 10 | 11 |

Hello World

12 | 13 | * Markdown 14 | 15 | Hello World 16 | 17 | 18 | ### Lists & Styling 19 | 20 | * HTML 21 | 22 | Unordered 23 |
    24 |
  • Item 1
  • 25 |
  • Item 2
  • 26 |
  • Item 3
  • 27 |
28 | Ordered 29 |
    30 |
  1. Item 1
  2. 31 |
  3. Item 2
  4. 32 |
  5. Item 3
  6. 33 |
34 | 35 | * Markdown 36 | 37 | *Unordered* 38 | 39 | * Item 1 40 | * Item 2 41 | * Item 3 42 | 43 | **Ordered** 44 | 45 | 1. Item 1 46 | 2. Item 2 47 | 3. Item 3 48 | 49 | 50 | ### Links 51 | 52 | * HTML 53 | 54 |
    55 |
  1. Example.com
  2. 56 |
  3. Google
  4. 57 |
  5. Yahoo!
  6. 58 |
  7. Another Example.com
  8. 59 |
60 | 61 | * Markdown 62 | 63 | 1. [Example.com][] 64 | 2. [Google][] 65 | 3. [Yahoo!][Yahoo] 66 | 4. [Another Example.com][Example.com] 67 | 68 | 69 | [Example.com]: http://www.example.com 70 | [Google]: http://www.google.com 71 | [Yahoo]: http://www.yahoo.com 72 | 73 | 74 | ### Blockquotes and Code Samples 75 | 76 | * HTML 77 | 78 |
79 | Yes, Me Too 80 |
81 | I agree 82 |
83 | Top posting is confusing 84 |
85 |
86 |
87 | 88 |
// Ain't Groovy Grand?
 89 | 		// From: http://marxsoftware.blogspot.com/2011/06/ten-groovy-one-liners-to-impress-your.html
 90 | 		(1..100).each{println "${it%3?'':'Fizz'}${it%5?'':'Buzz'}" ?: it }
 91 | 		
92 | 93 | * Markdown 94 | 95 | > Yes, Me Too 96 | > 97 | > > I agree 98 | > > 99 | > > > Top posting is confusing 100 | 101 | // Ain't Groovy Grand? 102 | // From: http://marxsoftware.blogspot.com/2011/06/ten-groovy-one-liners-to-impress-your.html 103 | (1..100).each{println "${it%3?'':'Fizz'}${it%5?'':'Buzz'}" ?: it } 104 | 105 | 106 | ### Broken and Poor HTML 107 | 108 | In this example, you can see how Remark (along with JSoup) does it's best to salvage a usable result from bad input. 109 | 110 | * HTML 111 | 112 |

113 | 114 | 115 | This is really bad HTML 116 | 117 |

118 |
119 |

I'm deeply nested

120 |
121 |
I'm not in a paragraph tag at all! 122 |
123 | I'm a useless tag!! 124 | 125 | * Markdown 126 | 127 | ***This is really bad HTML*** 128 | 129 | I'm deeply nested 130 | 131 | I'm not in a paragraph tag at all! 132 | 133 | I'm a useless tag!! 134 | 135 | ## Options: Markdown Extra 136 | 137 | These are some unique results when the options are set to use PHP Markdown Extra special features. 138 | 139 | ### Header IDs & Abbreviations 140 | 141 | * HTML 142 | 143 |

Header 1

144 |

This is HTML!

145 | 146 | * Markdown 147 | 148 | # Header 1 # {#header1} 149 | 150 | This is HTML! 151 | 152 | 153 | *[HTML]: Hyper-Text Markup Language 154 | 155 | 156 | ### Definition Lists 157 | 158 | * HTML 159 | 160 |
161 |
HTML
162 |
A markup language commonly used on the web.
163 |
Markdown
164 |
A markup language specifically designed to be human-readable.
165 |
166 | 167 | * Markdown 168 | 169 | HTML 170 | : A markup language commonly used on the web. 171 | 172 | Markdown 173 | : A markup language specifically designed to be human-readable. 174 | 175 | 176 | ### Markdown Extra Tables 177 | 178 | * HTML 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 |
 Grouping
First HeaderSecond HeaderThird Header
ContentLong Cell
ContentCellCell
New SectionMoreData
And moreAnd more
215 | 216 | * Markdown 217 | 218 | |   | Grouping | | 219 | | First Header | Second Header | Third Header | 220 | |:------------ |:-------------:| ------------:| 221 | | Content | *Long Cell* | | 222 | | Content | **Cell** | Cell | 223 | | New Section | More | Data | 224 | | And more | And more | | 225 | 226 | 227 | 228 | ## Options: Pegdown (All Extensions) 229 | 230 | These are some unique results when the options are set to use Pegdown (All Extensions) special features. 231 | 232 | ### Autolinks & Entity / Smartquote Reversal 233 | 234 | * HTML 235 | 236 |

This link will render completely inline: http://www.example.com

237 |

These fancy characters will be reverted to simple UTF-8 or simple quotes:

238 |

“This — that…”

239 | 240 | * Markdown 241 | 242 | This link will render completely inline: http://www.example.com 243 | 244 | These fancy characters will be reverted to simple UTF-8 or simple quotes: 245 | 246 | "This --- that..." 247 | 248 | 249 | ### Tables (with colspan) 250 | 251 | Notice that, unlike the previous table example, the column-spanning is preserved. 252 | 253 | * HTML 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 |
 Grouping
First HeaderSecond HeaderThird Header
ContentLong Cell
ContentCellCell
New SectionMoreData
And moreAnd more
290 | 291 | * Markdown 292 | 293 | |   | Grouping || 294 | | First Header | Second Header | Third Header | 295 | |:------------ |:-------------:| ------------:| 296 | | Content | *Long Cell* || 297 | | Content | **Cell** | Cell | 298 | | New Section | More | Data | 299 | | And more | And more || 300 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/util/StringUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.util; 18 | 19 | import java.io.PrintWriter; 20 | import java.io.StringWriter; 21 | 22 | /** 23 | * A small collection of utilities for manipulating strings. 24 | * 25 | * @author Phil DeJarnett 26 | */ 27 | public class StringUtils { 28 | 29 | /** Represents left alignment. */ 30 | public static final int ALIGN_LEFT = -1; 31 | /** Represents centered alignment. */ 32 | public static final int ALIGN_CENTER = 0; 33 | /** Represents right alignment. */ 34 | public static final int ALIGN_RIGHT = 1; 35 | 36 | /** 37 | * Pads out a left-, right-, or center-aligned string using spaces up to the specified width. 38 | * @param s String to pad 39 | * @param width Minimum width of final string 40 | * @param alignment How to align the string < 0 means left, 0 means center, and > 0 means right 41 | * @return Padded string 42 | */ 43 | @SuppressWarnings({"SameParameterValue", "SameParameterValue"}) 44 | public static String align(String s, int width, int alignment) { 45 | return align(s, width, ' ', alignment); 46 | } 47 | 48 | /** 49 | * Pads out a left-, right-, or center-aligned string using the specified character up to the specified width. 50 | * @param s String to pad 51 | * @param width Minimum width of final string 52 | * @param paddingChar Character to pad with 53 | * @param alignment How to align the string < 0 means left, 0 means center, and > 0 means right 54 | * @return Padded string 55 | */ 56 | public static String align(String s, int width, char paddingChar, int alignment) { 57 | if(s.length() < width) { 58 | int diff = width - s.length(); 59 | String left = ""; 60 | String right = ""; 61 | if(alignment == 0) { 62 | int numLeftChars = diff/2; 63 | int numRightChars = numLeftChars + (diff % 2); 64 | left = multiply(paddingChar, numLeftChars); 65 | right = multiply(paddingChar, numRightChars); 66 | } else if(alignment < 0) { 67 | right = multiply(paddingChar, diff); 68 | } else { 69 | left = multiply(paddingChar, diff); 70 | } 71 | s = left + s + right; 72 | } 73 | return s; 74 | } 75 | 76 | /** 77 | * Pads out a left-, right-, or center-aligned string using spaces up to the specified width. 78 | * @param output Writer to output the centered string to 79 | * @param s String to pad 80 | * @param width Minimum width of final string 81 | * @param alignment How to align the string < 0 means left, 0 means center, and > 0 means right 82 | */ 83 | public static void align(PrintWriter output, String s, int width, int alignment) { 84 | align(output, s, width, ' ', alignment); 85 | } 86 | 87 | /** 88 | * Pads out a left-, right-, or center-aligned string using the specified character up to the specified width. 89 | * @param output Writer to output the centered string to 90 | * @param s String to pad 91 | * @param width Minimum width of final string 92 | * @param paddingChar Character to pad with 93 | * @param alignment How to align the string < 0 means left, 0 means center, and > 0 means right 94 | */ 95 | public static void align(PrintWriter output, String s, int width, char paddingChar, int alignment) { 96 | if(s.length() < width) { 97 | int diff = width - s.length(); 98 | if(alignment == 0) { 99 | int numLeftChars = diff/2; 100 | int numRightChars = numLeftChars + (diff % 2); 101 | multiply(output, paddingChar, numLeftChars); 102 | output.write(s); 103 | multiply(output, paddingChar, numRightChars); 104 | } else if(alignment < 0) { 105 | output.write(s); 106 | multiply(output, paddingChar, diff); 107 | } else { 108 | multiply(output, paddingChar, diff); 109 | output.write(s); 110 | } 111 | } else { 112 | output.write(s); 113 | } 114 | } 115 | 116 | /** 117 | * Duplicates the given character {@code count} times. 118 | * If {@code count} is less than or equal to 0, the empty string is returned. 119 | * @param c Character to duplicate 120 | * @param count Number of times to duplicate 121 | * @return Duplicated string. 122 | */ 123 | public static String multiply(char c, int count) { 124 | return multiply(String.valueOf(c), count); 125 | } 126 | 127 | /** 128 | * Duplicates the given string {@code count} times. 129 | * If {@code count} is less than or equal to 0, the empty string is returned. 130 | * @param s String to duplicate 131 | * @param count Number of times to duplicate 132 | * @return Duplicated string. 133 | */ 134 | public static String multiply(String s, int count) { 135 | if(count < 1) { 136 | return ""; 137 | } else { 138 | StringBuilder sb = new StringBuilder(); 139 | for(int i=0; i error = new ArrayList(); 109 | org.apache.commons.cli.Options opts = makeOptions(); 110 | CommandLineParser clp = new PosixParser(); 111 | 112 | try { 113 | CommandLine cl = clp.parse(opts, args, true); 114 | if(cl.hasOption('h')) { 115 | printHelp(opts); 116 | result = null; 117 | } else { 118 | checkType(cl, result, error); 119 | checkTimeout(cl, result, error); 120 | checkBaseURL(cl, result, error); 121 | checkCharset(cl, result, error); 122 | checkOutput(cl, result, error); 123 | checkInput(cl, result, error); 124 | result.html = cl.hasOption("html"); 125 | result.options.preserveRelativeLinks = result.relative = cl.hasOption("relative"); 126 | } 127 | } catch(ParseException ex) { 128 | System.err.println("Unexpected error parsing the command line."); 129 | System.err.println(); 130 | printHelp(opts); 131 | result = null; 132 | } 133 | 134 | if(!error.isEmpty()) { 135 | printErrors(error); 136 | result = null; 137 | } 138 | 139 | return result; 140 | } 141 | 142 | private void printHelp(org.apache.commons.cli.Options opts) { 143 | HelpFormatter hf = new HelpFormatter(); 144 | hf.printHelp("remark [options] [-o ] ", opts); 145 | System.err.println(); 146 | } 147 | 148 | private void printErrors(List error) { 149 | if(error.size() == 1) { 150 | System.err.print("Error: "); 151 | System.err.println(error.get(0)); 152 | } else { 153 | System.err.println("Error:"); 154 | for(final String err : error) { 155 | System.err.print(" - "); 156 | System.err.println(err); 157 | } 158 | } 159 | System.err.println("Run with the argument -h for help."); 160 | System.err.println(); 161 | } 162 | 163 | private org.apache.commons.cli.Options makeOptions() { 164 | org.apache.commons.cli.Options opts = new org.apache.commons.cli.Options(); 165 | opts.addOption("t", "type", true, "Type of markdown to target: markdown, markdownextra, multimarkdown, pegdown, pegdownall, or github"); 166 | opts.addOption("o", "output", true, "Name of file to output to; defaults to system out"); 167 | opts.addOption("timeout", true, "Timeout in seconds for downloading from URLs only; defaults to 15s"); 168 | opts.addOption("baseurl", true, "Base URL for file inputs, this helps in handling relative links."); 169 | opts.addOption("relative", false, "If set, preserve relative URLs."); 170 | opts.addOption("charset", true, "Character set for file inputs; defaults to UTF-8."); 171 | opts.addOption("html", false, "If set, the cleaned HTML document will be echoed out before conversion."); 172 | opts.addOption("h", "help", false, "Displays this help."); 173 | return opts; 174 | } 175 | 176 | private void checkType(CommandLine cl, Args result, List error) { 177 | if(cl.hasOption('t')) { 178 | String type = cl.getOptionValue('t'); 179 | if("markdown".equalsIgnoreCase(type)) { 180 | result.options = Options.markdown(); 181 | } else if("markdown".equalsIgnoreCase(type)) { 182 | result.options = Options.markdown(); 183 | } else if("multimarkdown".equalsIgnoreCase(type)) { 184 | result.options = Options.multiMarkdown(); 185 | } else if("markdownextra".equalsIgnoreCase(type)) { 186 | result.options = Options.markdownExtra(); 187 | } else if("pegdown".equalsIgnoreCase(type)) { 188 | result.options = Options.pegdownBase(); 189 | } else if("pegdownall".equalsIgnoreCase(type)) { 190 | result.options = Options.pegdownAllExtensions(); 191 | } else if("github".equalsIgnoreCase(type)) { 192 | result.options = Options.github(); 193 | } else { 194 | error.add("Invalid type specified"); 195 | } 196 | } else { 197 | result.options = Options.markdown(); 198 | } 199 | } 200 | 201 | private void checkOutput(CommandLine cl, Args result, List error) { 202 | if(cl.hasOption('o')) { 203 | File output = new File(cl.getOptionValue('o')).getAbsoluteFile(); 204 | result.output = output; 205 | if(!output.exists()) { 206 | // check for parent path 207 | File parent = output.getParentFile(); 208 | if(parent.exists() && !parent.isDirectory()) { 209 | error.add("Output does is not a valid path."); 210 | } else { 211 | if(!parent.exists() && !parent.mkdirs()) { 212 | error.add("Output path could not be created."); 213 | } else if(!parent.canWrite()) { 214 | error.add("Output directory cannot be written to."); 215 | } 216 | } 217 | } else if(!output.isFile()) { 218 | error.add("Output file exists and is not a file"); 219 | } else if(!output.canWrite()) { 220 | error.add("Output file cannot be written to."); 221 | } 222 | } 223 | } 224 | 225 | private void checkTimeout(CommandLine cl, Args result, List error) { 226 | if(cl.hasOption("timeout")) { 227 | try { 228 | Integer timeout = Integer.parseInt(cl.getOptionValue("timeout")); 229 | if(timeout < 1) { 230 | error.add("Invalid timeout specified."); 231 | } else { 232 | result.inputTimeout = timeout; 233 | } 234 | } catch(NumberFormatException ex) { 235 | error.add("Invalid timeout specified."); 236 | } 237 | } 238 | } 239 | 240 | @SuppressWarnings({"UnusedParameters"}) 241 | private void checkBaseURL(CommandLine cl, Args result, List error) { 242 | if(cl.hasOption("baseurl")) { 243 | result.baseURL = cl.getOptionValue("baseurl"); 244 | } 245 | } 246 | 247 | private void checkCharset(CommandLine cl, Args result, List error) { 248 | if(cl.hasOption("charset")) { 249 | String charset = cl.getOptionValue("charset"); 250 | if(Charset.isSupported(charset)) { 251 | result.charset = charset; 252 | } else { 253 | error.add("Unsupported charset."); 254 | } 255 | } 256 | } 257 | 258 | private void checkInput(CommandLine cl, Args result, List error) { 259 | List leftover = cl.getArgList(); 260 | if(leftover.isEmpty()) { 261 | error.add("No input file or URL specified."); 262 | } else if(leftover.size() > 1) { 263 | error.add("Too many arguments."); 264 | } else { 265 | String arg = (String)leftover.get(0); 266 | if(arg.contains("://")) { 267 | try { 268 | result.urlInput = new URL(arg); 269 | } catch(MalformedURLException ex) { 270 | error.add("Malformed URL: "+ex.getMessage()); 271 | } 272 | } else { 273 | File input = new File(arg); 274 | if(input.isFile()) { 275 | if(input.canRead()) { 276 | result.fileInput = input; 277 | } else { 278 | error.add(String.format("Unable to read input file: %s", arg)); 279 | } 280 | } else { 281 | error.add(String.format("Input file does not exist or is not a file: %s", arg)); 282 | } 283 | } 284 | } 285 | } 286 | 287 | } 288 | -------------------------------------------------------------------------------- /src/main/java/com/overzealous/remark/util/MarkdownTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 OverZealous Creations, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.overzealous.remark.util; 18 | 19 | import java.io.PrintWriter; 20 | import java.util.LinkedList; 21 | import java.util.List; 22 | 23 | import static com.overzealous.remark.util.MarkdownTable.Alignment.LEFT; 24 | import static com.overzealous.remark.util.MarkdownTable.Alignment.RIGHT; 25 | 26 | /** 27 | * Provides a class to contain the structure of an HTML table so it can be cleanly formatted 28 | * as a plain text structure (either as Markdown or within a Markdown code block). 29 | * 30 | * @author Phil DeJarnett 31 | */ 32 | public class MarkdownTable { 33 | 34 | /** 35 | * Simple enum to manage the alignment of a column. 36 | */ 37 | public enum Alignment { 38 | LEFT(-1), CENTER(0), RIGHT(1); 39 | 40 | private final int dir; 41 | 42 | Alignment(int dir) { 43 | this.dir = dir; 44 | } 45 | 46 | public int getDir() { 47 | return dir; 48 | } 49 | 50 | public static Alignment find(int alignment) { 51 | if(alignment == 0) { 52 | return CENTER; 53 | } else if(alignment < 0) { 54 | return LEFT; 55 | } else { 56 | return RIGHT; 57 | } 58 | } 59 | } 60 | 61 | private final List> header; 62 | private final List> body; 63 | 64 | private int cols; 65 | private int[] widths; 66 | private Alignment[] alignments; 67 | 68 | private boolean firstNewline = true; 69 | 70 | 71 | /** 72 | * Creates a new, empty MarkdownTable 73 | */ 74 | public MarkdownTable() { 75 | this.header = new LinkedList>(); 76 | this.body = new LinkedList>(); 77 | } 78 | 79 | /** 80 | * Creates a new header row, and returns it so it can have cells added to it. 81 | * 82 | * @return A list that can have columns added to it. 83 | */ 84 | public List addHeaderRow() { 85 | List newRow = new LinkedList(); 86 | this.header.add(newRow); 87 | return newRow; 88 | } 89 | 90 | /** 91 | * Creates a new body row, and returns it so it can have cells added to it. 92 | * 93 | * @return A list that can have columns added to it. 94 | */ 95 | public List addBodyRow() { 96 | List newRow = new LinkedList(); 97 | this.body.add(newRow); 98 | return newRow; 99 | } 100 | 101 | /** 102 | * Renders out the final table. 103 | * This process starts by calculating widths and alignment for the columns. The final output should 104 | * be nicely spaced, centered, and look very clean. 105 | * 106 | * @param output The writer to receive the final output. 107 | * @param allowColspan If true, cells that span multiple columns are preserved. If false, they are rendered in 108 | * their own column, then empty columns are placed after. 109 | * @param renderAsCode If true, the output is rendered as a code block 110 | */ 111 | public void renderTable(PrintWriter output, boolean allowColspan, boolean renderAsCode) { 112 | cols = this.getNumberOfColumns(); 113 | widths = new int[cols]; 114 | alignments = new Alignment[cols]; 115 | for(int i = 0; i> rows, boolean allowColspan) { 144 | for(List row : rows) { 145 | int col = 0; 146 | for(final MarkdownTableCell cell : row) { 147 | 148 | if(cell.getAlignment() != LEFT) { 149 | // if a non-standard alignment, set the column alignment 150 | // note: the last row gets the alignment preference. 151 | // since Markdown tables only support one shared alignment, 152 | // we can't do much about this. 153 | alignments[col] = cell.getAlignment(); 154 | } 155 | 156 | if( !allowColspan || cell.getColspan() == 1) { 157 | 158 | // single column, just get the maximum width 159 | widths[col] = Math.max(widths[col], cell.getWidth()); 160 | 161 | } else { 162 | 163 | // multiple columns. 164 | // need to adjust the width based on multiple columns 165 | int totalWidth = 0; 166 | for(int i = col; i totalWidth) { 175 | // add some to each of the columns 176 | int diff = cellWidth - totalWidth; 177 | // this distributes the extra width needed over the columns as evenly as we can 178 | int addToEveryColumn = diff/cell.getColspan(); 179 | int columnsWithMore = diff % cell.getColspan(); 180 | for(int i = 0; i> rows, boolean allowColspan, boolean renderAsCode) { 209 | for(List row : rows) { 210 | println(output); 211 | if(renderAsCode) { 212 | output.write(" "); 213 | } 214 | output.write('|'); 215 | int col = 0; 216 | for(final MarkdownTableCell cell : row) { 217 | Alignment alignment = alignments[col]; 218 | if( !allowColspan || cell.getColspan() == 1) { 219 | // write the cell 220 | // we pre-pad the string to ensure that left-and-right alignment look nice 221 | String contents = String.format(" %s ", cell.getContents()); 222 | StringUtils.align(output, contents, widths[col], alignment.getDir()); 223 | output.write('|'); 224 | 225 | if(cell.getColspan() > 1) { 226 | // clean up colspans when we have them disabled 227 | for(int emptyCol=col+1; emptyCol < col+cell.getColspan(); emptyCol++) { 228 | StringUtils.multiply(output, ' ', widths[emptyCol]); 229 | output.write('|'); 230 | } 231 | } 232 | 233 | } else { 234 | // initialize totalWidth to leave room for the extra | chars 235 | int totalWidth = 0; 236 | for(int i=col; i < col+cell.getColspan(); i++) { 237 | totalWidth += widths[i]; 238 | } 239 | // we pre-pad the string to ensure that left-and-right alignment look nice 240 | String contents = String.format(" %s ", cell.getContents()); 241 | StringUtils.align(output, contents, totalWidth, alignment.getDir()); 242 | // render out a \ for each column spanned 243 | StringUtils.multiply(output, '|', cell.getColspan()); 244 | } 245 | 246 | // increment our column counter. 247 | // due to column spanning, we can't rely on the size of the row. 248 | col += cell.getColspan(); 249 | } 250 | } 251 | } 252 | 253 | private void renderHeaderSeparator(PrintWriter output, boolean renderAsCode) { 254 | println(output); 255 | // check to see if there's any alignment at all. 256 | // if not, don't bother rendering the ':' 257 | boolean alignmentFound = false; 258 | for(int i=0; i row : this.header) { 307 | columns = Math.max(columns, getNumberOfColumnsInRow(row)); 308 | } 309 | for(List row : this.body) { 310 | columns = Math.max(columns, getNumberOfColumnsInRow(row)); 311 | } 312 | return columns; 313 | } 314 | 315 | private int getNumberOfColumnsInRow(List row) { 316 | int count = 0; 317 | for(final MarkdownTableCell cell : row) { 318 | count += cell.getColspan(); 319 | } 320 | return count; 321 | } 322 | } 323 | --------------------------------------------------------------------------------