├── .Rbuildignore ├── .github └── workflows │ └── R-CMD-check.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── Makefile ├── NAMESPACE ├── NEWS.md ├── R ├── old.R ├── package.R ├── render.R ├── rmarkdown.R ├── rpubs.R └── utils.R ├── README.md ├── inst ├── examples │ └── render-options.R └── resources │ ├── default.css │ ├── markdown.html │ ├── markdown.latex │ ├── prism-xcode.css │ ├── snap.css │ └── snap.js ├── man ├── html_format.Rd ├── mark.Rd ├── markdown-package.Rd ├── markdown_options.Rd ├── renderMarkdown.Rd ├── rpubsUpload.Rd └── smartypants.Rd ├── markdown.Rproj └── tests ├── empty.R ├── tests.R └── tests.Rout.save /.Rbuildignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | ^.*\.Rproj$ 3 | ^\.Rproj\.user$ 4 | ^\.git$ 5 | ^tools$ 6 | .lvimrc 7 | tests/.RData 8 | Makefile 9 | inst/RnwToMd.R 10 | ^\.travis\.yml$ 11 | ^\.github$ 12 | ^revdep$ 13 | ^LICENSE\.md$ 14 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [main, master] 4 | pull_request: 5 | branches: [main, master] 6 | 7 | name: R-CMD-check 8 | 9 | jobs: 10 | R-CMD-check: 11 | runs-on: ${{ matrix.config.os }} 12 | 13 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 14 | 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | config: 19 | - {os: macOS-latest, r: 'release'} 20 | - {os: windows-latest, r: 'release'} 21 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 22 | - {os: ubuntu-latest, r: 'release'} 23 | - {os: ubuntu-latest, r: 'oldrel-1'} 24 | 25 | env: 26 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 27 | R_KEEP_PKG_SOURCE: yes 28 | 29 | steps: 30 | - uses: actions/checkout@v3 31 | 32 | - uses: r-lib/actions/setup-pandoc@v2 33 | 34 | - uses: r-lib/actions/setup-r@v2 35 | with: 36 | r-version: ${{ matrix.config.r }} 37 | http-user-agent: ${{ matrix.config.http-user-agent }} 38 | use-public-rspm: true 39 | 40 | - uses: r-lib/actions/setup-r-dependencies@v2 41 | with: 42 | extra-packages: any::rcmdcheck 43 | needs: check 44 | 45 | - uses: r-lib/actions/check-r-package@v2 46 | with: 47 | upload-snapshots: true 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .lvimrc 5 | src/*.o 6 | src/*.so 7 | src/*.rds 8 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: markdown 2 | Type: Package 3 | Title: A thin wrapper of 'litedown' to render Markdown documents 4 | Version: 2.0.1 5 | Authors@R: c( 6 | person("Yihui", "Xie", role = c("aut", "cre"), email = "xie@yihui.name", comment = c(ORCID = "0000-0003-0645-5666")), 7 | person("JJ", "Allaire", role = "aut"), 8 | person("Jeffrey", "Horner", role = "aut"), 9 | person("Henrik", "Bengtsson", role = "ctb"), 10 | person("Jim", "Hester", role = "ctb"), 11 | person("Yixuan", "Qiu", role = "ctb"), 12 | person("Kohske", "Takahashi", role = "ctb"), 13 | person("Adam", "November", role = "ctb"), 14 | person("Nacho", "Caballero", role = "ctb"), 15 | person("Jeroen", "Ooms", role = "ctb"), 16 | person("Thomas", "Leeper", role = "ctb"), 17 | person("Joe", "Cheng", role = "ctb"), 18 | person("Andrzej", "Oles", role = "ctb"), 19 | person(given = "Posit Software, PBC", role = c("cph", "fnd")) 20 | ) 21 | Description: Render Markdown to HTML/LaTeX. This package has been superseded by 'litedown'. Please call 'litedown::mark()' directly. 22 | Depends: 23 | R (>= 3.2.0) 24 | Imports: 25 | utils, 26 | xfun, 27 | litedown (>= 0.6) 28 | Suggests: 29 | knitr, 30 | rmarkdown (>= 2.18), 31 | yaml, 32 | RCurl 33 | License: MIT + file LICENSE 34 | URL: https://github.com/rstudio/markdown 35 | BugReports: https://github.com/rstudio/markdown/issues 36 | RoxygenNote: 7.3.2 37 | Encoding: UTF-8 38 | Roxygen: list(markdown = TRUE) 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2023 2 | COPYRIGHT HOLDER: Posit Software, PBC 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2023 Posit Software, PBC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | tests: tests/tests.Rout.save 2 | 3 | tests/tests.Rout.save: tests/tests.R inst/examples/render-options.R 4 | Rscript -e "Rd2roxygen::rab('.', install=TRUE)" 5 | rm markdown_*.tar.gz 6 | cd tests && R CMD BATCH --no-save --no-restore --no-timing tests.R tests.Rout.save 7 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(html_format) 4 | export(latex_format) 5 | export(mark) 6 | export(mark_html) 7 | export(mark_latex) 8 | export(markdownToHTML) 9 | export(markdown_options) 10 | export(renderMarkdown) 11 | export(rpubsUpload) 12 | export(smartypants) 13 | import(utils) 14 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # CHANGES IN markdown VERSION 2.0 2 | 3 | - The core function `mark()` is a thin wrapper of `litedown::mark()` now. Users are recommended to call **litedown** directly instead of through the wrapper. 4 | 5 | # CHANGES IN markdown VERSION 1.13 6 | 7 | - Cleaned `sourcepos` records when they come from metadata (thanks, @dmurdoch, #111). 8 | 9 | - The **markdown** package is in the maintenance-only mode now. It is feature-complete, and will receive no updates except for fixing CRAN problems. New development will continue only in **litedown**: . 10 | 11 | # CHANGES IN markdown VERSION 1.12 12 | 13 | - Provided three internal functions `html_document`, `html_vignette`, and `pdf_document` as compatibility layers to functions of the same names in the **rmarkdown** package (thanks, @jangorecki, #108). 14 | 15 | - The default HTML template no longer wraps meta variables `include-before` and `include-after` inside `
`, because their values may contain incomplete HTML tags, e.g., `include-before = '
'` and `include-after = '
'`. 16 | 17 | # CHANGES IN markdown VERSION 1.11 18 | 19 | - Verbatim code blocks of the form ```` ```{lang attr1 attr2 ...} ```` were not correctly rendered. 20 | 21 | # CHANGES IN markdown VERSION 1.10 22 | 23 | - Raw blocks (```` ```{=lang} ````) were broken in the previous version when the support for code block attributes was added. 24 | 25 | # CHANGES IN markdown VERSION 1.9 26 | 27 | - Added support for attributes on fenced code blocks, e.g., ```` ```{.lang .class2 #id attr="value"}```` (thanks, @thothal, #106). 28 | 29 | - Fixed the bug that the option `number_sections: true` doesn't work for HTML output when then input contains certain Unicode characters (thanks, @fyuniv, #104). 30 | 31 | - Added support for rendering HTML Widgets such as **ggplotly** (thanks, @fyuniv, #105). 32 | 33 | # CHANGES IN markdown VERSION 1.8 34 | 35 | - Fixed the superfluous warning about path lengths in `mark_html()` (thanks, @kenjisato, #103). 36 | 37 | # CHANGES IN markdown VERSION 1.7 38 | 39 | - The `file` argument of `mark()` will be treated as a file path only if the file exists and the value is not wrapped in `I()`. Previously, it would be treated as a file path when it has a file extension, which could lead to confusing errors like #100 (thanks, @LukasWallrich). 40 | 41 | - When there are emojis in the text, `mark()` may fail to identify and embed web resources (thanks, @tdhock, yihui/knitr#2254). 42 | 43 | # CHANGES IN markdown VERSION 1.6 44 | 45 | - Added support for footnotes, fenced `Div`s, section numbers, `{}` attributes for images/headings/fenced `Div`s, and appendices. See `vignette('intro', package = 'markdown')` for details. 46 | 47 | - A lot of enhancements to the HTML slides format. See `vignette('slides', package = 'markdown')` for details. 48 | 49 | - Added `vignette('article', package = 'markdown')` to demonstrate how to write an HTML article. 50 | 51 | - If the input to `mark()` is a file, the output will also be a file by default. Previously the output would be text. If you want `mark()` to return text output when the input is a file, you may specify the argument `output = NULL`. 52 | 53 | - The Markdown option `base64_images` has been renamed to `embed_resources`. This option can take two possible values, `"local"` and `"https"`, meaning whether to embed local and/or web (https) resources. You can specify none, either, or both of them. See `vignette('intro', package = 'markdown')` for details. 54 | 55 | - Removed the option `standalone` from the list of Markdown options. Please use the argument `template = TRUE/FALSE` of `mark()` instead. The option `standalone = TRUE` was equivalent to `template = TRUE`. 56 | 57 | - Added the option `auto_identifiers` (enabled by default) to automatically add IDs to headings, e.g., `# Hello world!` will be converted to `

Hello world!

`. You can certainly override the automatic ID by providing an ID manually via the `{#id}` attribute, e.g., `# Hello world! {#hello}`. 58 | 59 | - Renamed the `mathjax` option to `js_math` to allow for other JS math libraries. The default library was changed from MathJax to KaTeX. To continue using MathJax, you may set `js_math: mathjax`. 60 | 61 | - Removed the option `mathjax_embed` from the list of Markdown options. To embed the MathJax library, enable `"https"` in the `embed_resources` option instead. Note that only MathJax v3 can be partially embedded, and lower versions cannot. 62 | 63 | - Renamed the option `highlight_code` to `js_highlight`, and added support for an alternative syntax highlighting JS library Prism.js, which became the default. To continue using the old default `highlight.js`, you may set the `js_highlight` option to `highlight`. 64 | 65 | - The default version of MathJax has been changed from v2 to v3. 66 | 67 | - The default version of highlight.js has been changed from 11.6.0 to 11.7.0, and the default style has been switched from `github` to `xcode`. 68 | 69 | # CHANGES IN markdown VERSION 1.5 70 | 71 | - Values of meta variables `title`, `author`, and `date` (if provided) will be transformed to the target output format before they are passed into templates. 72 | 73 | - Fixed the bug that the default CSS was not added to HTML output. 74 | 75 | - Removed dependency on the **mime** package. 76 | 77 | - Added experimental support for HTML slides: `markdown::mark_html(..., meta = list(css = c('default', 'slides'), js = 'slides'))`. If you prefer knitting `Rmd` documents in RStudio, you may use the output format: 78 | 79 | ```yaml 80 | output: 81 | markdown::html_format: 82 | meta: 83 | css: [default, slides] 84 | js: [slides] 85 | ``` 86 | 87 | See https://yihui.org/en/2023/01/minimal-r-markdown/ for a demo. 88 | 89 | # CHANGES IN markdown VERSION 1.4 90 | 91 | - Empty `\title{}` in LaTeX output will be removed (along with `\maketitle`). 92 | 93 | - highlight.js is loaded from https://www.jsdelivr.com/package/gh/highlightjs/cdn-release by default now. This means more languages are supported (not only R), but also means syntax-highlighting will not work offline at the moment (it will be improved in future). 94 | 95 | - MathJax failed to load in the previous version. The bug has been fixed now. 96 | 97 | - Removed the function `markdownExtensions()`. 98 | 99 | # CHANGES IN markdown VERSION 1.3 100 | 101 | - Switched the underlying Markdown rendering engine from the C library **sundown** (which has been deprecated for a decade) to the R package **commonmark** (thanks, @jeroen, yihui/knitr#1329). 102 | 103 | - The functions `renderMarkdown()` and `markdownToHTML()` have been renamed to `mark()` and `mark_html()`, respectively. The old names are still kept in this package for backward-compatibility. 104 | 105 | - Removed the arguments `stylesheet` and `fragment.only` in `mark_html()`. For `stylesheet`, please use the argument `meta = list(css = ...)` to provide the CSS stylesheet. For `fragment.only`, please use `mark_html(template = FALSE)` or `mark_html(options = '-standalone')` instead of `fragment.only = TRUE`. Currently these old arguments are still accepted internally, but may be deprecated and dropped in the long run. 106 | 107 | - The `file` argument of `mark()` and `mark_html()` can also take a character vector of Markdown text now. 108 | 109 | - Removed functions `rendererExists()`, `rendererOutputType()`, and `registeredRenderer()`. They were primarily for internal use. 110 | 111 | - Deprecated the function `markdownExtensions()`. All extensions should be specified via the `options` argument of functions like `mark()`, e.g., `mark(options = '+table+tasklist')`. See all options on the help page `?markdown::markdown_options`. 112 | 113 | - Renamed `markdownHTMLOptions()` to `markdown_options()`. 114 | 115 | # CHANGES IN markdown VERSION 1.2 116 | 117 | - Fixed the warnings "a function declaration without a prototype is deprecated in all versions of C" (#94). 118 | 119 | # CHANGES IN markdown VERSION 1.1 120 | 121 | ## MAJOR CHANGES 122 | 123 | - renderMarkdown() and markdownToHTML() will signal an error if the input file is not encoded in "UTF-8". 124 | 125 | # CHANGES IN markdown VERSION 1.0 126 | 127 | ## MAJOR CHANGES 128 | 129 | - The default value of the encoding argument of renderMarkdown() and markdownToHTML() has been changed from getOption("encoding") to "UTF-8". The encoding of the input file will always be assumed to be UTF-8. 130 | 131 | - markdownToHTML() will return a character vector encoded in UTF-8 (instead of the system's native encoding) when not writing to an output file. 132 | 133 | # CHANGES IN markdown VERSION 0.9 134 | 135 | ## BUG FIXES 136 | 137 | - Fixed clang-UBSAN and valgrind issues (thanks, @yixuan, #92). 138 | 139 | # CHANGES IN markdown VERSION 0.8 140 | 141 | ## MINOR CHANGES 142 | 143 | - the MathJax CDN URL was replaced by https://www.bootcdn.cn/mathjax/ 144 | 145 | ## BUG FIXES 146 | 147 | - fixed https://github.com/rstudio/htmltools/issues/30: markdownToHTML() did not work with empty files (thanks, @VermillionAzure) 148 | 149 | # CHANGES IN markdown VERSION 0.7.7 150 | 151 | ## BUG FIXES 152 | 153 | - renderMarkdown() works now even if text = character(0) or "" 154 | 155 | - added an `encoding` argument to renderMarkdown() since multi-byte characters in renderMarkdown() did not work on Windows (thanks, Kohske Takahashi, #63) 156 | 157 | - fixed #64: invalid 'n' argument in rpubsUpload() (thanks, Wouter van Atteveldt) 158 | 159 | ## MAJOR CHANGES 160 | 161 | - if renderMarkdown() returns a character vector, it will be marked with the UTF-8 encoding if it contains multi-byte characters 162 | 163 | # CHANGES IN markdown VERSION 0.7.4 164 | 165 | ## NEW FEATURES 166 | 167 | - when an image is the only element of its parent node in the HTML output document, it is automatically centered on the page 168 | 169 | ## MINOR CHANGES 170 | 171 | - images that have already been base64 encoded will not be encoded again (#61) 172 | 173 | - the URL of the MathJax CDN was updated to cdn.mathjax.org 174 | 175 | # CHANGES IN markdown VERSION 0.7.2 176 | 177 | ## BUG FIXES 178 | 179 | - fixed #60: MathJax may be included even if it is unnecessary when syntax highlighting is enabled (thanks, @aoles) 180 | 181 | - fixed a bug which may hang R when building R Markdown vignettes in a wrong locale (thanks, Dan Tenenbaum, yihui/knitr#782) 182 | 183 | # CHANGES IN markdown VERSION 0.7 184 | 185 | ## BUG FIXES 186 | 187 | - if both the 'file' and 'text' arguments are provided but file = NULL, e.g. markdownToHTML(file = NULL, text = ?), markdownToHTML() can throw an error indicating the file is invalid (thanks, Tyler Rinker, hadley/staticdocs#66) 188 | 189 | - markdownToHTML(text = ?, output = ?) was broken (#54) 190 | 191 | # CHANGES IN markdown VERSION 0.6.5 192 | 193 | ## NEW FEATURES 194 | 195 | - added an argument 'encoding' to markdownToHTML() to specify the character encoding of the input markdown file, and the HTML output file is always encoded in UTF-8 now (thanks, Kohske Takahashi, #50) 196 | 197 | # CHANGES IN markdown VERSION 0.6.4 198 | 199 | ## NEW FEATURES 200 | 201 | - added 'mathjax_embed' to HTML options for embedding the MathJax JavaScript in the HTML document rather than linking to it online. Note the JavaScript code is read from the http instead of https MathJax URL. Contributed by Henrik Bengtsson. 202 | 203 | - added another vignette to show the HTML output of the original vignette (see browseVignettes('markdown')) 204 | 205 | - the default CSS style was tweaked (major changes include: page width is at most 800px, more line height, slightly larger fonts, and a different syntax highlighting theme) 206 | 207 | # CHANGES IN markdown VERSION 0.6.3 208 | 209 | ## NEW FEATURES 210 | 211 | - added a new argument 'template' to markdownToHTML() so that we can customize the HTML template (by default, it uses the template 'resources/markdown.html' in this package); thanks, Nacho Caballero 212 | 213 | - the options markdown.HTML.stylesheet and markdown.HTML.header used in markdownToHTML() can be character vectors (they will be processed by paste(x, collapse = '\n') 214 | 215 | ## MAJOR CHANGES 216 | 217 | - the 'text' argument in markdownToHTML() and renderMarkdown() is treated as lines of input now, i.e. if 'text' is provided, it is passed to the markdown renderer as paste(text, collapse = '\n'); in the previous versions, it was processed by paste(text, collapse = '') 218 | 219 | # CHANGES IN markdown VERSION 0.6 220 | 221 | ## DOCUMENTATION 222 | 223 | - added a package vignette; see browseVignettes(package = 'markdown') 224 | 225 | # CHANGES IN markdown VERSION 0.5.5 226 | 227 | ## NEW FEATURES 228 | 229 | - added a new argument 'header' to markdownToHTML() to insert code into the HTML header (e.g. custom CSS styles) 230 | 231 | ## BUG FIXES 232 | 233 | - fixed #25 and #27: minor documentation problems 234 | 235 | - fixed #26: the HTML output file will be written relative to the current working directory now when it contains images that need to be base64 encoded 236 | 237 | - fixed #28: the image URL should be decoded before the image is based64 encoded 238 | 239 | ## MISC 240 | 241 | - Yihui Xie has taken over the maintainership for this package from Jeffrey Horner 242 | 243 | # CHANGES IN markdown VERSION 0.5.4 244 | 245 | ## NEW FEATURES 246 | 247 | - Both Pandoc title blocks and Jekyll front matter sections are skipped when rendering markdown documents. 248 | 249 | # CHANGES IN markdown VERSION 0.5.3 250 | 251 | ## NEW FEATURES 252 | 253 | - C/C++ is now a supported language for code block highlighting. 254 | 255 | ## MAJOR CHANGES 256 | 257 | - 'hard_wrap' has been dropped while 'mathjax' and 'highlight_code' have been added to the default list of html options. 258 | 259 | ## BUG FIXES 260 | 261 | - fixed parsing of math equations when located at the end of a line. 262 | 263 | # CHANGES IN markdown VERSION 0.5.2 264 | 265 | ## NEW FEATURES 266 | 267 | - with the new 'latex_math' markdown extensions, users can include math equations using several syntaxes. For block level equations, use $$latex ... $$, $$ ... $$, or \[ ... \]. For inline equations, use $latex...$, $...$, or \( ... \). 268 | 269 | ## MAJOR CHANGES 270 | 271 | - the markdown extension 'ignore_math' was replaced with 'latex_math'. 272 | 273 | - users can now use the markdown.HTML.stylesheet option to override the package default stylesheet. 274 | 275 | - setting the fragment_only rendering option or the fragment.only parameter to markdownToHTML will base64 encode images if applicable. version 0.5.1 did not. 276 | 277 | # CHANGES IN markdown VERSION 0.5.1 278 | 279 | ## BUG FIXES 280 | 281 | - fixed a GUIDgenerator bug; for escaping math equations before markdown parsing begins. 282 | 283 | - image encoding was fixed for the case when there are more than one included in a markdown document. 284 | 285 | # CHANGES IN markdown VERSION 0.5 286 | 287 | ## NEW FEATURES 288 | 289 | - added fragment.only parameter to markdownToHTML 290 | 291 | - added new html rendering options base64_images, fragment_only, mathjax, and highlight_code 292 | 293 | - added new markdown extension ignore_math 294 | 295 | ## MAJOR CHANGES 296 | 297 | - removed safelink from default html rendering options 298 | 299 | - the default html rendering options are now hard_wrap, use_xhtml, smartypants, and base64_images. 300 | 301 | ## BUG FIXES 302 | 303 | - fixed syntax errors in C exports 304 | 305 | # CHANGES IN markdown VERSION 0.4 306 | 307 | ## NEW FEATURES 308 | 309 | - added support for post-processing HTML using smartypants filter 310 | 311 | - added optional support for rendering a table of contents 312 | 313 | ## MAJOR CHANGES 314 | 315 | - changed exported C functions to use an rmd_ prefix (eliminating potential symbol conflicts with other packages) 316 | 317 | - changed default html rendering options to use_xhtml, hard_wrap, safelink, and smartypants 318 | 319 | ## BUG FIXES 320 | 321 | - eliminated name collision with render_markdown function in knitr 322 | 323 | -------------------------------------------------------------------------------- /R/old.R: -------------------------------------------------------------------------------- 1 | #' Functions in \pkg{markdown} before `v1.3`. 2 | #' 3 | #' These functions are kept in this package for backward-compatibility. They 4 | #' will not be removed in the foreseeable future, although we recommend that you 5 | #' use their new names instead: `renderMarkdown()` has become [mark()], and 6 | #' `markdownToHTML()` has become [mark_html()]. 7 | #' @param file,output,...,options,template Arguments to be passed to new 8 | #' functions. 9 | #' @param title,stylesheet,header Arguments to be passed to `meta = list(title = 10 | #' , css = , `header-includes` = )`, which is passed to [mark_html()]. 11 | #' @param fragment.only Whether to generate a fragment or a full HTML document. 12 | #' @param encoding Ignored. 13 | #' @export 14 | #' @keywords internal 15 | renderMarkdown = function(file = NULL, output = NULL, ...) mark(file, output = output, ...) 16 | 17 | #' @rdname renderMarkdown 18 | #' @export 19 | markdownToHTML = function( 20 | file = NULL, output = NULL, ..., options = getOption('markdown.HTML.options'), 21 | title = NULL, stylesheet = getOption('markdown.HTML.stylesheet'), 22 | header = getOption('markdown.HTML.header'), 23 | template = getOption('markdown.HTML.template', TRUE), 24 | fragment.only = FALSE, encoding = 'UTF-8' 25 | ) { 26 | if (fragment.only || 'fragment_only' %in% options) template = FALSE 27 | meta = list() 28 | meta$css = stylesheet 29 | meta$title = title 30 | meta$`header-includes` = header 31 | mark_html(file, output = output, ..., options = options, template = template, meta = meta) 32 | } 33 | -------------------------------------------------------------------------------- /R/package.R: -------------------------------------------------------------------------------- 1 | #' Markdown rendering for R 2 | #' 3 | #' \pkg{Markdown} is a plain-text formatting syntax that can be converted to 4 | #' XHTML or other formats. This package provides wrapper functions (mainly 5 | #' [mark()]) based on the \pkg{commonmark} package. 6 | '_PACKAGE' 7 | -------------------------------------------------------------------------------- /R/render.R: -------------------------------------------------------------------------------- 1 | #' Render Markdown to an output format 2 | #' 3 | #' This is a wrapper function based on [litedown::mark()]. You should use 4 | #' `litedown::mark()` directly. 5 | #' @param file,output,text,options,meta Passed to [litedown::mark()]. 6 | #' @param format Output format name. 7 | #' @param template Whether to use a built-in template, or path to a custom 8 | #' template. 9 | #' @import utils 10 | #' @export 11 | mark = function( 12 | file = NULL, output = NULL, text = NULL, format = c('html', 'latex'), 13 | options = NULL, template = FALSE, meta = list() 14 | ) { 15 | format = format[1] 16 | opts = options(stats::setNames( 17 | list(get_option(sprintf('markdown.%s.template', format), template)), 18 | sprintf('litedown.%s.template', format) 19 | )) 20 | on.exit(options(opts), add = TRUE) 21 | if (is.null(output)) output = format 22 | # check if bibutils is available for bibliography, and convert yes/no to Boolean 23 | if (litedown:::is_file(file)) { 24 | is_check = xfun::is_R_CMD_check() 25 | parts = xfun::yaml_body(xfun::read_utf8(file), parse = FALSE) 26 | yaml = parts$yaml 27 | if (length(i <- grep('^bibliography:\\s+', yaml)) && !xfun::loadable('rbibutils')) { 28 | if (is_check) { 29 | yaml = yaml[-i] 30 | } else stop( 31 | 'Detected bibliography in YAML (', file, ') but the rbibutils package is ', 32 | 'unavailable. Please make sure it is installed (and declared in Suggests ', 33 | 'if bibliography is used in package vignettes).' 34 | ) 35 | } 36 | if (length(i <- grep(':\\s+(yes|no)\\s*$', yaml))) { 37 | if (is_check) { 38 | yaml[i] = sub('yes\\s*$', 'true', yaml[i]) 39 | yaml[i] = sub('no\\s*$', 'false', yaml[i]) 40 | } else stop( 41 | 'Detected yes/no in YAML(', file, '). Please replace them with true/false.' 42 | ) 43 | } 44 | if (length(i <- grep('^\\s*- ', yaml)) && xfun::try_error(xfun::taml_load(yaml))) { 45 | yaml[i] = sub('^(\\s*)- ', '\\1 ', yaml[i]) 46 | if (xfun::try_error(xfun::taml_load(yaml))) stop( 47 | 'Cannot parse YAML (', file, '). See https://yihui.org/litedown/#sec:yaml-syntax for the syntax.' 48 | ) else if (!is_check) stop( 49 | "Detected items starting with '- ' in YAML(", file, '). The syntax is ', 50 | 'not supported by litedown: https://yihui.org/litedown/#sec:yaml-syntax' 51 | ) 52 | } 53 | if (is_check) { 54 | xfun::write_utf8(c('---', yaml, '---', parts$body), file) 55 | } 56 | } 57 | res = litedown::mark(file, output, text, options, meta) 58 | if (format == 'html') { 59 | # remove sec/chp prefix in section IDs 60 | res = gsub('()$', '\\1\n', res) 63 | } 64 | as.character(res) 65 | } 66 | 67 | #' @rdname mark 68 | #' @param ... Arguments to be passed to `mark()`. 69 | #' @export 70 | mark_html = function(..., template = TRUE) { 71 | mark(..., format = 'html', template = template) 72 | } 73 | 74 | #' @export 75 | #' @rdname mark 76 | mark_latex = function(..., template = TRUE) { 77 | mark(..., format = 'latex', template = template) 78 | } 79 | 80 | #' Markdown rendering options 81 | #' 82 | #' A wrapper function of [litedown::markdown_options()]. 83 | #' @export 84 | markdown_options = function() litedown::markdown_options() 85 | -------------------------------------------------------------------------------- /R/rmarkdown.R: -------------------------------------------------------------------------------- 1 | output_format = function(to = 'html') { 2 | to 3 | function( 4 | meta = NULL, template = NULL, options = NULL, keep_md = FALSE, 5 | keep_tex = FALSE, latex_engine = 'xelatex' 6 | ) { 7 | opts = rmarkdown::pandoc_options( 8 | to = to, keep_tex = keep_tex, latex_engine = latex_engine, args = '--template' 9 | ) 10 | opts$convert_fun = function(input, output, ...) { 11 | mark(input, output, NULL, to, options, template, meta) 12 | } 13 | rmarkdown::output_format( 14 | NULL, opts, keep_md = keep_md, 15 | clean_supporting = 'local' %in% litedown:::normalize_options(options)[['embed_resources']] 16 | ) 17 | } 18 | } 19 | 20 | #' R Markdown output formats 21 | #' 22 | #' Convenience functions for R Markdown v2 users. 23 | #' 24 | #' We refer to this \pkg{markdown} package plus \pkg{knitr} as \dQuote{R 25 | #' Markdown v1}, and the \pkg{rmarkdown} package as \dQuote{R Markdown v2}. The 26 | #' former uses \pkg{commonmark} to convert Markdown, and the latter uses Pandoc. 27 | #' However, the latter also accept custom converting tools in addition to 28 | #' Pandoc. The output formats here provide the custom converting function 29 | #' [mark()] to \pkg{rmarkdown}, so that users can take advantage of 30 | #' [rmarkdown::render()] and the Knit button in RStudio. It is absolutely not 31 | #' necessary to rely on \pkg{rmarkdown}. The only point is convenience. If you 32 | #' do not use `rmarkdown::render()` or the Knit button, you can definitely just 33 | #' call `markdown::mark()` directly. 34 | #' @param meta,template,options Arguments to be passed to [mark()]. 35 | #' @param keep_md,keep_tex Whether to keep the intermediate \file{.md} and 36 | #' \file{.tex} files generated from \file{.Rmd}. 37 | #' @param latex_engine The LaTeX engine to compile \file{.tex} to \file{.pdf}. 38 | #' This argument and `keep_tex` are for `latex_format()` only, and ignored in 39 | #' `html_format()`. 40 | #' @export 41 | html_format = output_format('html') 42 | 43 | #' @rdname html_format 44 | #' @export 45 | latex_format = output_format('latex') 46 | 47 | # compatibility layers to rmarkdown::[html|pdf]_document 48 | html_document = function(...) do.call(html_format, map_args(...)) 49 | html_vignette = function(...) html_document(...) 50 | pdf_document = function(...) do.call(latex_format, map_args(...)) 51 | 52 | map_args = function(...) { 53 | do.call(litedown:::map_args, convert_yn(list(...))) 54 | } 55 | 56 | convert_yn = function(x) { 57 | lapply(x, function(z) { 58 | if (is.list(z)) convert_yn(z) else if (identical(z, 'yes')) TRUE else 59 | if (identical(z, 'no')) FALSE else z 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /R/rpubs.R: -------------------------------------------------------------------------------- 1 | #' Upload an HTML file to RPubs 2 | #' 3 | #' This function uploads an HTML file to rpubs.com. If the upload succeeds a 4 | #' list that includes an `id` and `continueUrl` is returned. A browser should be 5 | #' opened to the `continueUrl` to complete publishing of the document. If an 6 | #' error occurs then a diagnostic message is returned in the `error` element of 7 | #' the list. 8 | #' @param title The title of the document. 9 | #' @param htmlFile The path to the HTML file to upload. 10 | #' @param id If this upload is an update of an existing document then the id 11 | #' parameter should specify the document id to update. Note that the id is 12 | #' provided as an element of the list returned by successful calls to 13 | #' `rpubsUpload`. 14 | #' @param properties A named list containing additional document properties 15 | #' (RPubs doesn't currently expect any additional properties, this parameter 16 | #' is reserved for future use). 17 | #' @param method Method to be used for uploading. "internal" uses a plain http 18 | #' socket connection; "curl" uses the curl binary to do an https upload; 19 | #' "rcurl" uses the RCurl package to do an https upload; and "auto" uses the 20 | #' best available method searched for in the following order: "curl", "rcurl", 21 | #' and then "internal". The global default behavior can be configured by 22 | #' setting the `rpubs.upload.method` option (the default is "auto"). 23 | #' @return A named list. If the upload was successful then the list contains a 24 | #' `id` element that can be used to subsequently update the document as well 25 | #' as a `continueUrl` element that provides a URL that a browser should be 26 | #' opened to in order to complete publishing of the document. If the upload 27 | #' fails then the list contains an `error` element which contains an 28 | #' explanation of the error that occurred. 29 | #' @export 30 | #' @examples 31 | #' \dontrun{ 32 | #' # upload a document 33 | #' result <- rpubsUpload("My document title", "Document.html") 34 | #' if (!is.null(result$continueUrl)) 35 | #' browseURL(result$continueUrl) else stop(result$error) 36 | #' 37 | #' # update the same document with a new title 38 | #' updateResult <- rpubsUpload("My updated title", "Document.html", result$id) 39 | #' } 40 | rpubsUpload <- function(title, 41 | htmlFile, 42 | id = NULL, 43 | properties = list(), 44 | method = getOption("rpubs.upload.method", "auto")) { 45 | 46 | # validate inputs 47 | if (!is.character(title)) 48 | stop("title must be specified") 49 | if (nzchar(title) == FALSE) 50 | stop("title must be a non-empty string") 51 | if (!is.character(htmlFile)) 52 | stop("htmlFile parameter must be specified") 53 | if (!file.exists(htmlFile)) 54 | stop("specified htmlFile does not exist") 55 | if (!is.list(properties)) 56 | stop("properties parameter must be a named list") 57 | 58 | parseHeader <- function(header) { 59 | split <- strsplit(header, ": ")[[1]] 60 | if (length(split) == 2) 61 | list(name = split[1], value = split[2]) 62 | } 63 | 64 | jsonEscapeString <- function(value) { 65 | chars <- strsplit(value, "")[[1]] 66 | chars <- vapply(chars, function(x) { 67 | if (x %in% c('"', '\\', '/')) 68 | paste('\\', x, sep='') 69 | else if (charToRaw(x) < 20) 70 | paste('\\u', toupper(format(as.hexmode(as.integer(charToRaw(x))), 71 | width=4)), 72 | sep='') 73 | else x 74 | }, character(1)) 75 | paste(chars, sep="", collapse="") 76 | } 77 | 78 | jsonProperty <- function(name, value) { 79 | paste("\"", 80 | jsonEscapeString(enc2utf8(name)), 81 | "\" : \"", 82 | jsonEscapeString(enc2utf8(value)), 83 | "\"", 84 | sep="") 85 | } 86 | 87 | regexExtract <- function(re, input) { 88 | match <- regexec(re, input) 89 | matchLoc <- match[1][[1]] 90 | if (length(matchLoc) > 1) { 91 | matchLen <-attributes(matchLoc)$match.length 92 | url <- substr(input, matchLoc[2], matchLoc[2] + matchLen[2]-1) 93 | url 94 | } 95 | } 96 | 97 | # NOTE: we parse the json naively using a regex because: 98 | # - We don't want to take a dependency on a json library for just this case 99 | # - We know the payload is an ascii url so we don't need a robust parser 100 | parseContinueUrl <- function(continueUrl) { 101 | regexExtract("\\{\\s*\"continueUrl\"\\s*:\\s*\"([^\"]+)\"\\s*\\}", 102 | continueUrl) 103 | } 104 | 105 | parseHttpStatusCode <- function(statusLine) { 106 | statusCode <- regexExtract("HTTP/[0-9]+\\.[0-9]+ ([0-9]+).*", statusLine) 107 | if (is.null(statusCode)) -1 else as.integer(statusCode) 108 | } 109 | 110 | pathFromId <- function(id) { 111 | split <- strsplit(id, "^https?://[^/]+")[[1]] 112 | if (length(split) == 2) split[2] 113 | } 114 | 115 | buildPackage <- function(title, 116 | htmlFile, 117 | properties = list()) { 118 | 119 | # build package.json 120 | packageJson <- "{" 121 | packageJson <- paste(packageJson, jsonProperty("title", title), ",") 122 | for (name in names(properties)) { 123 | if (nzchar(name) == FALSE) 124 | stop("all properties must be named") 125 | value <- properties[[name]] 126 | packageJson <- paste(packageJson, jsonProperty(name, value), ",") 127 | } 128 | packageJson <- substr(packageJson, 1, nchar(packageJson)-1) 129 | packageJson <- paste(packageJson,"}") 130 | 131 | # create a tempdir to build the package in and copy the files to it 132 | fileSep <- .Platform$file.sep 133 | packageDir <- tempfile() 134 | dir.create(packageDir) 135 | packageFile <- function(fileName) { 136 | paste(packageDir,fileName,sep=fileSep) 137 | } 138 | writeLines(packageJson, packageFile("package.json")) 139 | file.copy(htmlFile, packageFile("index.html")) 140 | 141 | # switch to the package dir for building 142 | oldWd <- getwd() 143 | setwd(packageDir) 144 | on.exit(setwd(oldWd)) 145 | 146 | # create the tarball 147 | tarfile <- tempfile("package", fileext = ".tar.gz") 148 | utils::tar(tarfile, files = ".", compression = "gzip") 149 | 150 | # return the full path to the tarball 151 | return (tarfile) 152 | } 153 | 154 | # Use skipDecoding=TRUE if transfer-encoding: chunked but the 155 | # chunk decoding has already been performed on conn 156 | readResponse <- function(conn, skipDecoding) { 157 | # read status code 158 | resp <- readLines(conn, 1) 159 | statusCode <- parseHttpStatusCode(resp[1]) 160 | 161 | # read response headers 162 | contentLength <- NULL 163 | location <- NULL 164 | transferEncoding <- NULL 165 | repeat { 166 | resp <- readLines(conn, 1) 167 | if (nzchar(resp) == 0) 168 | break 169 | 170 | header <- parseHeader(resp) 171 | # Case insensitive header name comparison 172 | headerName <- tolower(header$name) 173 | if (!is.null(header)) { 174 | if (identical(headerName, "content-type")) 175 | contentType <- header$value 176 | if (identical(headerName, "content-length")) 177 | contentLength <- as.integer(header$value) 178 | if (identical(headerName, "location")) 179 | location <- header$value 180 | if (identical(headerName, "transfer-encoding")) 181 | transferEncoding <- tolower(header$value) 182 | } 183 | } 184 | 185 | # read the response content 186 | content <- if (is.null(transferEncoding) || skipDecoding) { 187 | if (!is.null(contentLength)) { 188 | rawToChar(readBin(conn, what = 'raw', n=contentLength)) 189 | } 190 | else { 191 | paste(readLines(conn, warn = FALSE), collapse = "\r\n") 192 | } 193 | } else if (identical(transferEncoding, "chunked")) { 194 | accum <- "" 195 | repeat { 196 | resp <- readLines(conn, 1) 197 | resp <- sub(";.*", "", resp) # Ignore chunk extensions 198 | chunkLen <- as.integer(paste("0x", resp, sep = "")) 199 | if (is.na(chunkLen)) { 200 | stop("Unexpected chunk length") 201 | } 202 | if (identical(chunkLen, 0L)) { 203 | break 204 | } 205 | accum <- paste0(accum, rawToChar(readBin(conn, what = 'raw', n=chunkLen))) 206 | # Eat CRLF 207 | if (!identical("\r\n", rawToChar(readBin(conn, what = 'raw', n=2)))) { 208 | stop("Invalid chunk encoding: missing CRLF") 209 | } 210 | } 211 | accum 212 | } else { 213 | stop("Unexpected transfer encoding") 214 | } 215 | 216 | # return list 217 | list(status = statusCode, 218 | location = location, 219 | contentType = contentType, 220 | content = content) 221 | } 222 | 223 | # internal sockets implementation of upload (supports http-only) 224 | internalUpload <- function(path, 225 | contentType, 226 | headers, 227 | packageFile) { 228 | 229 | # read file in binary mode 230 | fileLength <- file.info(packageFile)$size 231 | fileContents <- readBin(packageFile, what="raw", n=fileLength) 232 | 233 | # build http request 234 | request <- NULL 235 | request <- c(request, paste("POST ", path, " HTTP/1.1\r\n", sep="")) 236 | request <- c(request, "User-Agent: RStudio\r\n") 237 | request <- c(request, "Host: api.rpubs.com\r\n") 238 | request <- c(request, "Accept: */*\r\n") 239 | request <- c(request, paste("Content-Type: ", contentType, "\r\n", sep="")) 240 | request <- c(request, paste("Content-Length: ", fileLength, "\r\n", sep="")) 241 | for (name in names(headers)) { 242 | request <- c(request, 243 | paste(name, ": ", headers[[name]], "\r\n", sep="")) 244 | } 245 | request <- c(request, "\r\n") 246 | 247 | # open socket connection 248 | conn <- socketConnection(host="api.rpubs.com", 249 | port=80, 250 | open="w+b", 251 | blocking=TRUE) 252 | on.exit(close(conn)) 253 | 254 | # write the request header and file payload 255 | writeBin(charToRaw(paste(request,collapse="")), conn, size=1) 256 | writeBin(fileContents, conn, size=1) 257 | 258 | # read the response 259 | readResponse(conn, skipDecoding = FALSE) 260 | } 261 | 262 | 263 | rcurlUpload <- function(path, 264 | contentType, 265 | headers, 266 | packageFile) { 267 | 268 | # url to post to 269 | url <- paste("https://api.rpubs.com", path, sep = "") 270 | 271 | # upload package file 272 | params <- list(file = RCurl::fileUpload(filename = packageFile, 273 | contentType = contentType)) 274 | 275 | # use custom header and text gatherers 276 | sslpath <- system.file("CurlSSL", "cacert.pem", package = "RCurl") 277 | options <- RCurl::curlOptions(url, cainfo = sslpath) 278 | headerGatherer <- RCurl::basicHeaderGatherer() 279 | options$headerfunction <- headerGatherer$update 280 | textGatherer <- RCurl::basicTextGatherer() 281 | options$writefunction <- textGatherer$update 282 | 283 | # add extra headers 284 | extraHeaders <- as.character(headers) 285 | names(extraHeaders) <- names(headers) 286 | options$httpheader <- extraHeaders 287 | 288 | # post the form 289 | RCurl::postForm(paste("https://api.rpubs.com", path, sep=""), 290 | .params = params, 291 | .opts = options, 292 | useragent = "RStudio") 293 | 294 | # return list 295 | headers <- headerGatherer$value() 296 | location <- if ("Location" %in% names(headers)) headers[["Location"]] 297 | list(status = as.integer(headers[["status"]]), 298 | location = location, 299 | contentType <- headers[["Content-Type"]], 300 | content = textGatherer$value()) 301 | 302 | } 303 | 304 | curlUpload <- function(path, 305 | contentType, 306 | headers, 307 | packageFile) { 308 | 309 | fileLength <- file.info(packageFile)$size 310 | 311 | extraHeaders <- character() 312 | for (header in names(headers)) { 313 | extraHeaders <- paste(extraHeaders, "--header") 314 | extraHeaders <- paste(extraHeaders, 315 | paste(header,":",headers[[header]], sep="")) 316 | } 317 | 318 | outputFile <- tempfile() 319 | 320 | command <- paste("curl", 321 | "-X", 322 | "POST", 323 | "--data-binary", 324 | shQuote(paste("@", packageFile, sep="")), 325 | "-i", 326 | "--header", paste("Content-Type:",contentType, sep=""), 327 | "--header", paste("Content-Length:", fileLength, sep=""), 328 | extraHeaders, 329 | "--header", "Expect:", 330 | "--silent", 331 | "--show-error", 332 | "-o", shQuote(outputFile), 333 | paste("https://api.rpubs.com", path, sep="")) 334 | 335 | result <- system(command) 336 | 337 | if (result == 0) { 338 | fileConn <- file(outputFile, "rb") 339 | on.exit(close(fileConn)) 340 | readResponse(fileConn, skipDecoding = TRUE) 341 | } else { 342 | stop(paste("Upload failed (curl error", result, "occurred)")) 343 | } 344 | } 345 | 346 | uploadFunction <- if (is.function(method)) { 347 | method 348 | } else switch( 349 | method, 350 | "auto" = { 351 | if (nzchar(Sys.which("curl"))) curlUpload else { 352 | if (suppressWarnings(requireNamespace("RCurl", quietly = TRUE))) { 353 | rcurlUpload 354 | } else internalUpload 355 | } 356 | }, 357 | "internal" = internalUpload, 358 | "curl" = curlUpload, 359 | "rcurl" = rcurlUpload, 360 | stop(paste("Invalid upload method specified:",method)) 361 | ) 362 | 363 | # build the package 364 | packageFile <- buildPackage(title, htmlFile, properties) 365 | 366 | # determine whether this is a new doc or an update 367 | isUpdate <- FALSE 368 | path <- "/api/v1/document" 369 | headers <- list() 370 | headers$Connection <- "close" 371 | if (!is.null(id)) { 372 | isUpdate <- TRUE 373 | path <- pathFromId(id) 374 | headers$`X-HTTP-Method-Override` <- "PUT" 375 | } 376 | 377 | 378 | # send the request 379 | result <- uploadFunction(path, 380 | "application/x-compressed", 381 | headers, 382 | packageFile) 383 | 384 | # check for success 385 | succeeded <- (isUpdate && (result$status == 200)) || (result$status == 201) 386 | 387 | # mark content as UTF-8 388 | content <- result$content 389 | Encoding(content) <- "UTF-8" 390 | 391 | # return either id & continueUrl or error 392 | if (succeeded) { 393 | list(id = ifelse(isUpdate, id, result$location), 394 | continueUrl = parseContinueUrl(content)) 395 | } else list(error = content) 396 | } 397 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | #' Convert some ASCII strings to HTML entities 2 | #' 3 | #' Transform ASCII strings `(c)` (copyright), `(r)` (registered trademark), 4 | #' `(tm)` (trademark), and fractions `n/m` into *smart* typographic HTML 5 | #' entities. 6 | #' @param text A character vector of the Markdown text. 7 | #' @return A character vector of the transformed text. 8 | #' @export 9 | #' @examples 10 | #' cat(smartypants("1/2 (c)\n")) 11 | smartypants = function(text) litedown:::smartypants(text) 12 | 13 | # get an option using a case-insensitive name 14 | get_option = function(name, default = NULL) { 15 | x = options() 16 | i = match(tolower(name), tolower(names(x))) 17 | i = i[!is.na(i)] 18 | if (length(i) == 0) default else x[[i[1]]] 19 | } 20 | 21 | pkg_file = function(...) { 22 | system.file(..., package = 'markdown', mustWork = TRUE) 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Markdown rendering for R 2 | 3 | 4 | 5 | [![R-CMD-check](https://github.com/rstudio/markdown/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/rstudio/markdown/actions/workflows/R-CMD-check.yaml) 6 | [![CRAN 7 | release](https://www.r-pkg.org/badges/version/markdown)](https://cran.r-project.org/package=markdown) 8 | 9 | 10 | 11 | ## Overview 12 | 13 | Markdown is a plain-text formatting syntax that can be converted to HTML or 14 | other formats. This package provides wrappers based on the 15 | [commonmark](https://github.com/r-lib/commonmark) package. 16 | 17 | Please note that this package is no longer actively developed. New development 18 | will continue only in [the **litedown** 19 | package](https://github.com/yihui/litedown), which is a new implementation of R 20 | Markdown. 21 | 22 | ## License 23 | 24 | The **markdown** package is licensed under MIT. 25 | -------------------------------------------------------------------------------- /inst/examples/render-options.R: -------------------------------------------------------------------------------- 1 | library(markdown) 2 | 3 | # toc example 4 | mkd <- c("# Header 1", "p1", "## Header 2", "p2") 5 | 6 | cat(mark(mkd, options = "+number_sections")) 7 | cat(mark(mkd, options = "+number_sections+toc")) 8 | 9 | # hard_wrap example 10 | cat(mark("foo\nbar\n")) 11 | cat(mark("foo\nbar\n", options = "hardbreaks")) 12 | 13 | # latex math example 14 | mkd <- c( 15 | "`$x$` is inline math $x$!", "", "Display style:", "", "$$x + y$$", "", 16 | "\\begin{eqnarray} 17 | a^{2}+b^{2} & = & c^{2}\\\\ 18 | \\sin^{2}(x)+\\cos^{2}(x) & = & 1 19 | \\end{eqnarray}" 20 | ) 21 | 22 | cat(mark(mkd)) 23 | cat(mark(mkd, options = "-latex_math")) 24 | 25 | # tables example (need 4 spaces at beginning of line here) 26 | cat(mark(" 27 | First Header | Second Header 28 | ------------- | ------------- 29 | Content Cell | Content Cell 30 | Content Cell | Content Cell 31 | ")) 32 | 33 | # but not here 34 | cat(mark(" 35 | First Header | Second Header 36 | ------------- | ------------- 37 | Content Cell | Content Cell 38 | Content Cell | Content Cell 39 | ", options = '-table')) 40 | 41 | # autolink example 42 | cat(mark("https://www.r-project.org/")) 43 | cat(mark("https://www.r-project.org/", options = "-autolink")) 44 | 45 | # strikethrough example 46 | cat(mark("~~awesome~~")) 47 | cat(mark("~~awesome~~", options = "-strikethrough")) 48 | 49 | # superscript and subscript examples 50 | cat(mark("2^10^")) 51 | cat(mark("2^10^", options = "-superscript")) 52 | cat(mark("H~2~O")) 53 | cat(mark("H~2~O", options = "-subscript")) 54 | 55 | # code blocks 56 | cat(mark('```r\n1 + 1;\n```')) 57 | cat(mark('```{.r}\n1 + 1;\n```')) 58 | cat(mark('```{.r .js}\n1 + 1;\n```')) 59 | cat(mark('```{.r .js #foo}\n1 + 1;\n```')) 60 | cat(mark('```{.r .js #foo style="color:red;"}\n1 + 1;\n```')) 61 | cat(mark('````\n```{r, echo=TRUE}\n1 + 1;\n```\n````')) 62 | 63 | # raw blocks 64 | cat(mark('```{=html}\n

raw HTML

\n```')) 65 | cat(mark('```{=latex}\n

raw HTML

\n```')) 66 | 67 | # skip_html tags 68 | mkd = '\n[Hello](#)' 69 | cat(mark(mkd)) 70 | # TODO: wait for https://github.com/r-lib/commonmark/issues/15 to be fixed 71 | # cat(mark(mkd, options = "tagfilter")) 72 | -------------------------------------------------------------------------------- /inst/resources/default.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | max-width: 800px; 4 | margin: auto; 5 | padding: 1em; 6 | line-height: 1.5; 7 | box-sizing: border-box; 8 | } 9 | body, .footnotes, code { font-size: .9em; } 10 | li li { font-size: .95em; } 11 | *, *:before, *:after { 12 | box-sizing: inherit; 13 | } 14 | pre, img { max-width: 100%; } 15 | pre, pre:hover { 16 | white-space: pre-wrap; 17 | word-break: break-all; 18 | } 19 | pre code { 20 | display: block; 21 | overflow-x: auto; 22 | } 23 | code { font-family: 'DejaVu Sans Mono', 'Droid Sans Mono', 'Lucida Console', Consolas, Monaco, monospace; } 24 | :not(pre) > code, code[class] { background-color: #F8F8F8; } 25 | code.language-undefined, pre > code:not([class]) { 26 | background-color: inherit; 27 | border: 1px solid #eee; 28 | } 29 | table { 30 | margin: auto; 31 | border-top: 1px solid #666; 32 | } 33 | table thead th { border-bottom: 1px solid #ddd; } 34 | th, td { padding: 5px; } 35 | thead, tfoot, tr:nth-child(even) { background: #eee; } 36 | blockquote { 37 | color: #666; 38 | margin: 0; 39 | padding-left: 1em; 40 | border-left: 0.5em solid #eee; 41 | } 42 | hr, .footnotes::before { border: 1px dashed #ddd; } 43 | .frontmatter { text-align: center; } 44 | #TOC .numbered li { list-style: none; } 45 | #TOC .numbered { padding-left: 0; } 46 | #TOC .numbered ul { padding-left: 1em; } 47 | table, .body h2 { border-bottom: 1px solid #666; } 48 | .body .appendix, .appendix ~ h2 { border-bottom-style: dashed; } 49 | .footnote-ref a::before { content: "["; } 50 | .footnote-ref a::after { content: "]"; } 51 | section.footnotes::before { 52 | content: ""; 53 | display: block; 54 | max-width: 20em; 55 | } 56 | 57 | @media print { 58 | body { 59 | font-size: 12pt; 60 | max-width: 100%; 61 | } 62 | tr, img { page-break-inside: avoid; } 63 | } 64 | @media only screen and (min-width: 992px) { 65 | pre { white-space: pre; } 66 | } 67 | -------------------------------------------------------------------------------- /inst/resources/markdown.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $title$ 8 | 9 | $css$ 10 | 11 | $header-includes$ 12 | 13 | 14 | 15 | $include-before$ 16 | 17 |
18 |

$title$

19 |

$author$

20 |

$date$

21 |
22 | 23 |
24 | $body$ 25 |
26 | 27 | $include-after$ 28 | 29 | $js$ 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /inst/resources/markdown.latex: -------------------------------------------------------------------------------- 1 | \documentclass[$classoption$]{$documentclass$} 2 | \usepackage[T1]{fontenc} 3 | \usepackage{graphicx} 4 | $header-includes$ 5 | \title{$title$} 6 | \author{$author$} 7 | \date{$date$} 8 | \begin{document} 9 | \maketitle 10 | $include-before$ 11 | $body$ 12 | $include-after$ 13 | \end{document} 14 | -------------------------------------------------------------------------------- /inst/resources/prism-xcode.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Prism.s theme ported from highlight.js's xcode style 3 | */ 4 | pre code { 5 | padding: 1em; 6 | } 7 | .token.comment { 8 | color: #007400; 9 | } 10 | .token.punctuation { 11 | color: #999; 12 | } 13 | .token.tag, 14 | .token.selector { 15 | color: #aa0d91; 16 | } 17 | .token.boolean, 18 | .token.number, 19 | .token.constant, 20 | .token.symbol { 21 | color: #1c00cf; 22 | } 23 | .token.property, 24 | .token.attr-name, 25 | .token.string, 26 | .token.char, 27 | .token.builtin { 28 | color: #c41a16; 29 | } 30 | .token.inserted { 31 | background-color: #ccffd8; 32 | } 33 | .token.deleted { 34 | background-color: #ffebe9; 35 | } 36 | .token.operator, 37 | .token.entity, 38 | .token.url, 39 | .language-css .token.string, 40 | .style .token.string { 41 | color: #9a6e3a; 42 | } 43 | .token.atrule, 44 | .token.attr-value, 45 | .token.keyword { 46 | color: #836c28; 47 | } 48 | .token.function, 49 | .token.class-name { 50 | color: #DD4A68; 51 | } 52 | .token.regex, 53 | .token.important, 54 | .token.variable { 55 | color: #5c2699; 56 | } 57 | .token.important, 58 | .token.bold { 59 | font-weight: bold; 60 | } 61 | .token.italic { 62 | font-style: italic; 63 | } 64 | -------------------------------------------------------------------------------- /inst/resources/snap.css: -------------------------------------------------------------------------------- 1 | :root { --slide-width: 100%; } 2 | html { scroll-snap-type: y mandatory; } 3 | th, td { padding: .2em .5em; } 4 | .slide { 5 | padding: 1em; 6 | position: relative; 7 | } 8 | .slide > h2, .slide > h3 { margin-top: unset; } 9 | body { 10 | max-width: fit-content; 11 | padding: 0; 12 | } 13 | a { color: #eb4a47; } 14 | :not(pre) > code { background-color: #fdfded; } 15 | #TOC { columns: 2; } 16 | #TOC::before { 17 | font-size: 1.3em; 18 | font-weight: bold; 19 | display: block; 20 | border-bottom: 1px solid #666; 21 | } 22 | .frontmatter, .middle { 23 | display: flex; 24 | justify-content: center; 25 | flex-direction: column; 26 | } 27 | .page-number, .timer { 28 | position: absolute; 29 | bottom: 0; 30 | opacity: .5; 31 | font: .7em monospace; 32 | } 33 | .page-number { right: 0; } 34 | .timer { left: 0; } 35 | .inverse { 36 | background-color: #eee; 37 | filter: invert(1); 38 | } 39 | .fade { 40 | background: repeating-linear-gradient(135deg, white, white 30px, #ddd 32px, #ddd 32px); 41 | opacity: 0.6; 42 | } 43 | .center { text-align: center; } 44 | .slide-container h2 .section-number { 45 | display: inline-block; 46 | background-color: #666; 47 | color: white; 48 | padding: 0 .1em; 49 | margin-right: .3em; 50 | } 51 | .overview { 52 | font-size: .8em; 53 | max-width: none; 54 | } 55 | .overview .slide { 56 | min-height: unset; 57 | scroll-snap-align: unset; 58 | } 59 | .overview .slide-container { 60 | display: flex; 61 | flex-wrap: wrap; 62 | justify-content: space-evenly; 63 | } 64 | .overview .slide-container .slide { 65 | width: var(--slide-width); 66 | border: 1px dotted #ccc; 67 | margin-bottom: 0.5em; 68 | } 69 | .mirrored { transform: scale(-1, 1); } 70 | .spacer { height: 50vh; } 71 | .overview .timer, .overview .spacer { display: none; } 72 | .overview .footnotes { position: unset; } 73 | html:fullscreen::-webkit-scrollbar { display: none; } 74 | html:fullscreen { 75 | -ms-overflow-style: none; 76 | scrollbar-width: none; 77 | } 78 | @media (min-width: 992px) { 79 | :root { --slide-width: 49%; } 80 | body { font-size: 2em; } 81 | .slide { 82 | min-height: 100vh; 83 | scroll-snap-align: start; 84 | } 85 | li li { font-size: .9em; } 86 | .footnotes { 87 | position: absolute; 88 | bottom: 1em; 89 | font-size: .8em; 90 | } 91 | } 92 | @media (min-width: 1400px) { 93 | :root { --slide-width: 33%; } 94 | } 95 | @media (min-width: 1800px) { 96 | :root { --slide-width: 24.67%; } 97 | } 98 | @media print, (max-width: 991.98px) { 99 | .timer, .spacer { display: none; } 100 | } 101 | -------------------------------------------------------------------------------- /inst/resources/snap.js: -------------------------------------------------------------------------------- 1 | (function(d) { 2 | let p = d.body; // container of slides; assume for now 3 | const s1 = ':scope > hr:not([class])', s2 = ':scope > h2'; 4 | // find a container that has at least n "slides" 5 | function findContainer(s, n = 1) { 6 | if (p.querySelectorAll(s).length >= n) return true; 7 | // if body doesn't contain headings or
s, look into children 8 | for (let i = 0; i < p.children.length; i++) { 9 | if (p.children[i].querySelectorAll(s).length >= n) { 10 | p = p.children[i]; break; 11 | } 12 | } 13 | return false; 14 | } 15 | function newEl(tag, cls) { 16 | const el = d.createElement(tag); 17 | if (cls) el.className = cls; 18 | return el; 19 | } 20 | if (!findContainer(s1, 3)) { 21 | // if not enough
s found in children; look for

instead 22 | if (p.tagName === 'BODY') { 23 | // not enough h2 found, this page is not appropriate for slides 24 | if (!findContainer(s2) && p.tagName === 'BODY') return; 25 | p.querySelectorAll(s2).forEach(h2 => h2.before(newEl('hr'))); 26 | } 27 | } 28 | p.classList.add('slide-container'); 29 | // add 'slide' class to the frontmatter div and toc 30 | ['.frontmatter', '#TOC'].forEach(s => { 31 | d.body.querySelector(s)?.classList.add('slide'); 32 | }); 33 | 34 | function newSlide(s) { 35 | return (s?.innerText === '') ? s : newEl('div', 'slide'); 36 | } 37 | function isSep(el) { 38 | return el.tagName === 'HR' && el.attributes.length === 0; 39 | } 40 | let el = p.firstElementChild; if (isSep(el)) el.remove(); 41 | el = p.firstElementChild; if (!el) return; 42 | let s = newSlide(); el.before(s); 43 | while (true) { 44 | let el = s.nextSibling; 45 | if (!el) break; 46 | // remove slide separators (
) and create new slide 47 | if (isSep(el)) { 48 | s = newSlide(s); 49 | el.before(s); el.remove(); 50 | } else if (el.classList?.contains('slide')) { 51 | s = newSlide(s); 52 | el.after(s); 53 | } else { 54 | s.append(el); 55 | } 56 | } 57 | function setAttr(el, attr) { 58 | const m = newEl('div'); 59 | m.innerHTML = `
`; 60 | const attrs = m.firstElementChild.attributes; 61 | for (let i = 0; i < attrs.length; i++) { 62 | let a = attrs[i]; 63 | el.setAttribute(a.name, a.value); 64 | } 65 | m.remove(); 66 | } 67 | const slides = d.querySelectorAll('div.slide'), N = slides.length, 68 | tm = d.querySelector('span.timer'), fn = d.querySelector('.footnotes'); 69 | slides.forEach((s, i) => { 70 | // append footnotes 71 | if (fn) s.querySelectorAll('.footnote-ref > a[href^="#fn"]').forEach(a => { 72 | const li = fn.querySelector('li' + a.getAttribute('href')); 73 | if (!li) return; 74 | let f = s.querySelector('section.footnotes'); 75 | if (!f) { 76 | f = newEl('section', 'footnotes'); s.append(f); 77 | } 78 | f.append(li); 79 | li.firstElementChild?.insertAdjacentHTML('afterbegin', `[${a.innerHTML}] `); 80 | li.outerHTML = li.innerHTML; 81 | }); 82 | // add a timer 83 | s.append(tm ? tm.cloneNode() : newEl('span', 'timer')); 84 | // add page numbers 85 | const n = newEl('span', 'page-number'); 86 | n.innerText = i + 1 + '/' + N; 87 | n.onclick = e => location.hash = i + 1; 88 | s.append(n); 89 | // apply slide attributes in 90 | for (let k in s.childNodes) { 91 | const node = s.childNodes[k]; 92 | if (node.nodeType !== Node.COMMENT_NODE) continue; 93 | let t = node.textContent; 94 | if (!/^#/.test(t)) continue; 95 | t = t.replace(/^#/, ''); 96 | const r = /[\s\n]class="([^"]+)"/, m = t.match(r); 97 | if (m) { 98 | t = t.replace(r, '').trim(); 99 | s.className += ' ' + m[1]; 100 | } 101 | if (t) setAttr(s, t); 102 | break; 103 | } 104 | s.classList.contains('extend') && s.append(newEl('div', 'spacer fade')); 105 | location.hash === ('#' + (i + 1)) && s.scrollIntoView(); 106 | s.addEventListener('click', e => { 107 | if (!e.altKey) return; 108 | d.body.classList.toggle('overview'); 109 | setTimeout(() => e.target.scrollIntoView(), 100); 110 | }); 111 | }); 112 | [...d.querySelectorAll('a.footnote-backref'), fn, tm].forEach(el => el?.remove()); 113 | const tms = d.querySelectorAll('span.timer'), t1 = 1000 * tms[0].dataset.total; 114 | let t0; 115 | function startTimers() { 116 | t0 = new Date(); 117 | setInterval(setTimers, 1000); 118 | } 119 | function setTimers() { 120 | let t = (new Date() - t0); 121 | if (t1) t = t1 - t; 122 | const t2 = new Date(Math.abs(t)).toISOString().substr(11, 8).replace(/^00:/, ''); 123 | tms.forEach(el => { 124 | el.innerText = t2; 125 | if (t < 0) el.style.display = el.style.display === 'none' ? '' : 'none'; 126 | }); 127 | } 128 | // press f for fullscreen mode 129 | d.addEventListener('keyup', (e) => { 130 | if (e.target !== d.body) return; 131 | e.key === 'f' && d.documentElement.requestFullscreen(); 132 | e.key === 'o' && d.body.classList.toggle('overview'); 133 | e.key === 'm' && d.body.classList.toggle('mirrored'); 134 | sessionStorage.setItem('body-class', d.body.className); 135 | }); 136 | // start timer on fullscreen 137 | d.onfullscreenchange = (e) => d.fullscreenElement && !t0 && startTimers(); 138 | tms.forEach(el => el.addEventListener('click', e => startTimers())); 139 | // restore previously saved body class 140 | const bc = sessionStorage.getItem('body-class'); 141 | if (bc) d.body.className += ' ' + bc; 142 | })(document); 143 | -------------------------------------------------------------------------------- /man/html_format.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rmarkdown.R 3 | \name{html_format} 4 | \alias{html_format} 5 | \alias{latex_format} 6 | \title{R Markdown output formats} 7 | \usage{ 8 | html_format( 9 | meta = NULL, 10 | template = NULL, 11 | options = NULL, 12 | keep_md = FALSE, 13 | keep_tex = FALSE, 14 | latex_engine = "xelatex" 15 | ) 16 | 17 | latex_format( 18 | meta = NULL, 19 | template = NULL, 20 | options = NULL, 21 | keep_md = FALSE, 22 | keep_tex = FALSE, 23 | latex_engine = "xelatex" 24 | ) 25 | } 26 | \arguments{ 27 | \item{meta, template, options}{Arguments to be passed to \code{\link[=mark]{mark()}}.} 28 | 29 | \item{keep_md, keep_tex}{Whether to keep the intermediate \file{.md} and 30 | \file{.tex} files generated from \file{.Rmd}.} 31 | 32 | \item{latex_engine}{The LaTeX engine to compile \file{.tex} to \file{.pdf}. 33 | This argument and \code{keep_tex} are for \code{latex_format()} only, and ignored in 34 | \code{html_format()}.} 35 | } 36 | \description{ 37 | Convenience functions for R Markdown v2 users. 38 | } 39 | \details{ 40 | We refer to this \pkg{markdown} package plus \pkg{knitr} as \dQuote{R 41 | Markdown v1}, and the \pkg{rmarkdown} package as \dQuote{R Markdown v2}. The 42 | former uses \pkg{commonmark} to convert Markdown, and the latter uses Pandoc. 43 | However, the latter also accept custom converting tools in addition to 44 | Pandoc. The output formats here provide the custom converting function 45 | \code{\link[=mark]{mark()}} to \pkg{rmarkdown}, so that users can take advantage of 46 | \code{\link[rmarkdown:render]{rmarkdown::render()}} and the Knit button in RStudio. It is absolutely not 47 | necessary to rely on \pkg{rmarkdown}. The only point is convenience. If you 48 | do not use \code{rmarkdown::render()} or the Knit button, you can definitely just 49 | call \code{markdown::mark()} directly. 50 | } 51 | -------------------------------------------------------------------------------- /man/mark.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/render.R 3 | \name{mark} 4 | \alias{mark} 5 | \alias{mark_html} 6 | \alias{mark_latex} 7 | \title{Render Markdown to an output format} 8 | \usage{ 9 | mark( 10 | file = NULL, 11 | output = NULL, 12 | text = NULL, 13 | format = c("html", "latex"), 14 | options = NULL, 15 | template = FALSE, 16 | meta = list() 17 | ) 18 | 19 | mark_html(..., template = TRUE) 20 | 21 | mark_latex(..., template = TRUE) 22 | } 23 | \arguments{ 24 | \item{file, output, text, options, meta}{Passed to \code{\link[litedown:mark]{litedown::mark()}}.} 25 | 26 | \item{format}{Output format name.} 27 | 28 | \item{template}{Whether to use a built-in template, or path to a custom 29 | template.} 30 | 31 | \item{...}{Arguments to be passed to \code{mark()}.} 32 | } 33 | \description{ 34 | This is a wrapper function based on \code{\link[litedown:mark]{litedown::mark()}}. You should use 35 | \code{litedown::mark()} directly. 36 | } 37 | -------------------------------------------------------------------------------- /man/markdown-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package.R 3 | \docType{package} 4 | \name{markdown-package} 5 | \alias{markdown} 6 | \alias{markdown-package} 7 | \title{Markdown rendering for R} 8 | \description{ 9 | \pkg{Markdown} is a plain-text formatting syntax that can be converted to 10 | XHTML or other formats. This package provides wrapper functions (mainly 11 | \code{\link[=mark]{mark()}}) based on the \pkg{commonmark} package. 12 | } 13 | \seealso{ 14 | Useful links: 15 | \itemize{ 16 | \item \url{https://github.com/rstudio/markdown} 17 | \item Report bugs at \url{https://github.com/rstudio/markdown/issues} 18 | } 19 | 20 | } 21 | \author{ 22 | \strong{Maintainer}: Yihui Xie \email{xie@yihui.name} (\href{https://orcid.org/0000-0003-0645-5666}{ORCID}) 23 | 24 | Authors: 25 | \itemize{ 26 | \item JJ Allaire 27 | \item Jeffrey Horner 28 | } 29 | 30 | Other contributors: 31 | \itemize{ 32 | \item Henrik Bengtsson [contributor] 33 | \item Jim Hester [contributor] 34 | \item Yixuan Qiu [contributor] 35 | \item Kohske Takahashi [contributor] 36 | \item Adam November [contributor] 37 | \item Nacho Caballero [contributor] 38 | \item Jeroen Ooms [contributor] 39 | \item Thomas Leeper [contributor] 40 | \item Joe Cheng [contributor] 41 | \item Andrzej Oles [contributor] 42 | \item Posit Software, PBC [copyright holder, funder] 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /man/markdown_options.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/render.R 3 | \name{markdown_options} 4 | \alias{markdown_options} 5 | \title{Markdown rendering options} 6 | \usage{ 7 | markdown_options() 8 | } 9 | \description{ 10 | A wrapper function of \code{\link[litedown:markdown_options]{litedown::markdown_options()}}. 11 | } 12 | -------------------------------------------------------------------------------- /man/renderMarkdown.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/old.R 3 | \name{renderMarkdown} 4 | \alias{renderMarkdown} 5 | \alias{markdownToHTML} 6 | \title{Functions in \pkg{markdown} before \code{v1.3}.} 7 | \usage{ 8 | renderMarkdown(file = NULL, output = NULL, ...) 9 | 10 | markdownToHTML( 11 | file = NULL, 12 | output = NULL, 13 | ..., 14 | options = getOption("markdown.HTML.options"), 15 | title = NULL, 16 | stylesheet = getOption("markdown.HTML.stylesheet"), 17 | header = getOption("markdown.HTML.header"), 18 | template = getOption("markdown.HTML.template", TRUE), 19 | fragment.only = FALSE, 20 | encoding = "UTF-8" 21 | ) 22 | } 23 | \arguments{ 24 | \item{file, output, ..., options, template}{Arguments to be passed to new 25 | functions.} 26 | 27 | \item{title, stylesheet, header}{Arguments to be passed to \verb{meta = list(title = , css = , }header-includes\verb{ = )}, which is passed to \code{\link[=mark_html]{mark_html()}}.} 28 | 29 | \item{fragment.only}{Whether to generate a fragment or a full HTML document.} 30 | 31 | \item{encoding}{Ignored.} 32 | } 33 | \description{ 34 | These functions are kept in this package for backward-compatibility. They 35 | will not be removed in the foreseeable future, although we recommend that you 36 | use their new names instead: \code{renderMarkdown()} has become \code{\link[=mark]{mark()}}, and 37 | \code{markdownToHTML()} has become \code{\link[=mark_html]{mark_html()}}. 38 | } 39 | \keyword{internal} 40 | -------------------------------------------------------------------------------- /man/rpubsUpload.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rpubs.R 3 | \name{rpubsUpload} 4 | \alias{rpubsUpload} 5 | \title{Upload an HTML file to RPubs} 6 | \usage{ 7 | rpubsUpload( 8 | title, 9 | htmlFile, 10 | id = NULL, 11 | properties = list(), 12 | method = getOption("rpubs.upload.method", "auto") 13 | ) 14 | } 15 | \arguments{ 16 | \item{title}{The title of the document.} 17 | 18 | \item{htmlFile}{The path to the HTML file to upload.} 19 | 20 | \item{id}{If this upload is an update of an existing document then the id 21 | parameter should specify the document id to update. Note that the id is 22 | provided as an element of the list returned by successful calls to 23 | \code{rpubsUpload}.} 24 | 25 | \item{properties}{A named list containing additional document properties 26 | (RPubs doesn't currently expect any additional properties, this parameter 27 | is reserved for future use).} 28 | 29 | \item{method}{Method to be used for uploading. "internal" uses a plain http 30 | socket connection; "curl" uses the curl binary to do an https upload; 31 | "rcurl" uses the RCurl package to do an https upload; and "auto" uses the 32 | best available method searched for in the following order: "curl", "rcurl", 33 | and then "internal". The global default behavior can be configured by 34 | setting the \code{rpubs.upload.method} option (the default is "auto").} 35 | } 36 | \value{ 37 | A named list. If the upload was successful then the list contains a 38 | \code{id} element that can be used to subsequently update the document as well 39 | as a \code{continueUrl} element that provides a URL that a browser should be 40 | opened to in order to complete publishing of the document. If the upload 41 | fails then the list contains an \code{error} element which contains an 42 | explanation of the error that occurred. 43 | } 44 | \description{ 45 | This function uploads an HTML file to rpubs.com. If the upload succeeds a 46 | list that includes an \code{id} and \code{continueUrl} is returned. A browser should be 47 | opened to the \code{continueUrl} to complete publishing of the document. If an 48 | error occurs then a diagnostic message is returned in the \code{error} element of 49 | the list. 50 | } 51 | \examples{ 52 | \dontrun{ 53 | # upload a document 54 | result <- rpubsUpload("My document title", "Document.html") 55 | if (!is.null(result$continueUrl)) 56 | browseURL(result$continueUrl) else stop(result$error) 57 | 58 | # update the same document with a new title 59 | updateResult <- rpubsUpload("My updated title", "Document.html", result$id) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /man/smartypants.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{smartypants} 4 | \alias{smartypants} 5 | \title{Convert some ASCII strings to HTML entities} 6 | \usage{ 7 | smartypants(text) 8 | } 9 | \arguments{ 10 | \item{text}{A character vector of the Markdown text.} 11 | } 12 | \value{ 13 | A character vector of the transformed text. 14 | } 15 | \description{ 16 | Transform ASCII strings \code{(c)} (copyright), \code{(r)} (registered trademark), 17 | \code{(tm)} (trademark), and fractions \code{n/m} into \emph{smart} typographic HTML 18 | entities. 19 | } 20 | \examples{ 21 | cat(smartypants("1/2 (c)\n")) 22 | } 23 | -------------------------------------------------------------------------------- /markdown.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageInstallArgs: -v && make -B -C 20 | PackageBuildArgs: -v && Rscript -e "Rd2roxygen::rab()" 21 | PackageCheckArgs: --as-cran 22 | -------------------------------------------------------------------------------- /tests/empty.R: -------------------------------------------------------------------------------- 1 | library(markdown) 2 | f = tempfile() 3 | if (file.create(f)) { 4 | mark_html(f, template = FALSE) 5 | mark_html(f) 6 | unlink(f) 7 | } 8 | -------------------------------------------------------------------------------- /tests/tests.R: -------------------------------------------------------------------------------- 1 | local({ 2 | if (!file.exists(f <- '../inst/examples/render-options.R')) 3 | f = markdown:::pkg_file('examples', 'render-options.R') 4 | source(f, local = TRUE, echo = TRUE) 5 | }) 6 | -------------------------------------------------------------------------------- /tests/tests.Rout.save: -------------------------------------------------------------------------------- 1 | 2 | R version 4.4.3 (2025-02-28) -- "Trophy Case" 3 | Copyright (C) 2025 The R Foundation for Statistical Computing 4 | Platform: aarch64-apple-darwin20 5 | 6 | R is free software and comes with ABSOLUTELY NO WARRANTY. 7 | You are welcome to redistribute it under certain conditions. 8 | Type 'license()' or 'licence()' for distribution details. 9 | 10 | Natural language support but running in an English locale 11 | 12 | R is a collaborative project with many contributors. 13 | Type 'contributors()' for more information and 14 | 'citation()' on how to cite R or R packages in publications. 15 | 16 | Type 'demo()' for some demos, 'help()' for on-line help, or 17 | 'help.start()' for an HTML browser interface to help. 18 | Type 'q()' to quit R. 19 | 20 | > local({ 21 | + if (!file.exists(f <- '../inst/examples/render-options.R')) 22 | + f = markdown:::pkg_file('examples', 'render-options.R') 23 | + source(f, local = TRUE, echo = TRUE) 24 | + }) 25 | 26 | > library(markdown) 27 | 28 | > mkd <- c("# Header 1", "p1", "## Header 2", "p2") 29 | 30 | > cat(mark(mkd, options = "+number_sections")) 31 |

1 Header 1

32 |

p1

33 |

1.1 Header 2

34 |

p2

35 | 36 | > cat(mark(mkd, options = "+number_sections+toc")) 37 |
38 | 45 |
46 |

1 Header 1

47 |

p1

48 |

1.1 Header 2

49 |

p2

50 | 51 | > cat(mark("foo\nbar\n")) 52 |

foo 53 | bar

54 | 55 | > cat(mark("foo\nbar\n", options = "hardbreaks")) 56 |

foo
57 | bar

58 | 59 | > mkd <- c("`$x$` is inline math $x$!", "", "Display style:", 60 | + "", "$$x + y$$", "", "\\begin{eqnarray}\na^{2}+b^{2} & = & c^{2}\\\\\n\\sin^{2}(x ..." ... [TRUNCATED] 61 | 62 | > cat(mark(mkd)) 63 |

$x$ is inline math \(x\)!

64 |

Display style:

65 |

$$x + y$$

66 |

\begin{eqnarray} 67 | a^{2}+b^{2} & = & c^{2}\\ 68 | \sin^{2}(x)+\cos^{2}(x) & = & 1 69 | \end{eqnarray}

70 | 71 | > cat(mark(mkd, options = "-latex_math")) 72 |

$x$ is inline math $x$!

73 |

Display style:

74 |

$$x + y$$

75 |

\begin{eqnarray} 76 | a^{2}+b^{2} & = & c^{2}\ 77 | \sin^{2}(x)+\cos^{2}(x) & = & 1 78 | \end{eqnarray}

79 | 80 | > cat(mark("\nFirst Header | Second Header\n------------- | -------------\nContent Cell | Content Cell\nContent Cell | Content Cell\n")) 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 |
First HeaderSecond Header
Content CellContent Cell
Content CellContent Cell
99 | 100 | > cat(mark("\nFirst Header | Second Header\n------------- | -------------\nContent Cell | Content Cell\nContent Cell | Content Cell\n", 101 | + opti .... [TRUNCATED] 102 |

First Header | Second Header 103 | ———–– | ———–– 104 | Content Cell | Content Cell 105 | Content Cell | Content Cell

106 | 107 | > cat(mark("https://www.r-project.org/")) 108 |

https://www.r-project.org/

109 | 110 | > cat(mark("https://www.r-project.org/", options = "-autolink")) 111 |

https://www.r-project.org/

112 | 113 | > cat(mark("~~awesome~~")) 114 |

awesome

115 | 116 | > cat(mark("~~awesome~~", options = "-strikethrough")) 117 |

~~awesome~~

118 | 119 | > cat(mark("2^10^")) 120 |

210

121 | 122 | > cat(mark("2^10^", options = "-superscript")) 123 |

2^10^

124 | 125 | > cat(mark("H~2~O")) 126 |

H2O

127 | 128 | > cat(mark("H~2~O", options = "-subscript")) 129 |

H~2~O

130 | 131 | > cat(mark("```r\n1 + 1;\n```")) 132 |
1 + 1;
133 | 
134 | 135 | > cat(mark("```{.r}\n1 + 1;\n```")) 136 |
1 + 1;
137 | 
138 | 139 | > cat(mark("```{.r .js}\n1 + 1;\n```")) 140 |
1 + 1;
141 | 
142 | 143 | > cat(mark("```{.r .js #foo}\n1 + 1;\n```")) 144 |
1 + 1;
145 | 
146 | 147 | > cat(mark("```{.r .js #foo style=\"color:red;\"}\n1 + 1;\n```")) 148 |
1 + 1;
149 | 
150 | 151 | > cat(mark("````\n```{r, echo=TRUE}\n1 + 1;\n```\n````")) 152 |
```{r, echo=TRUE}
153 | 1 + 1;
154 | ```
155 | 
156 | 157 | > cat(mark("```{=html}\n

raw HTML

\n```")) 158 |

raw HTML

159 | 160 | > cat(mark("```{=latex}\n

raw HTML

\n```")) 161 | 162 | > mkd = "\n[Hello](#)" 163 | 164 | > cat(mark(mkd)) 165 | 166 |

Hello

167 | > 168 | --------------------------------------------------------------------------------