├── .github ├── DISCUSSION_TEMPLATE │ ├── ideas.yml │ └── q-a.yml ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug.yml │ └── config.yml ├── dependabot.yml └── workflows │ └── release.yml ├── .gitignore ├── CITATION.cff ├── LICENSE ├── README.md ├── _extensions └── highlight-text │ ├── LICENSE │ ├── _extension.yml │ └── highlight-text.lua └── example.qmd /.github/DISCUSSION_TEMPLATE/ideas.yml: -------------------------------------------------------------------------------- 1 | title: "The (missing) feature you want to discuss" 2 | labels: 3 | - "Type: Enhancement :bulb:" 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | First, please check if your feature request has already been discussed. 9 | If it has, please consider adding a comment to the existing discussion instead of creating a new one. 10 | 11 | If you want to ask for help, please consider using the Q&A GitHub Discussions, as it is better suited for answering your question. 12 | 13 | Finally, try to describe the best you can what you want to achieve and why you think it is important. 14 | This will help us to understand your request and prioritise it. 15 | 16 | _Thank you for opening this feature request!_ 17 | - type: textarea 18 | attributes: 19 | label: Description 20 | description: Start your discussion! 21 | validations: 22 | required: true 23 | - type: markdown 24 | attributes: 25 | value: | 26 | _Thank you for opening this feature request!_ 27 | -------------------------------------------------------------------------------- /.github/DISCUSSION_TEMPLATE/q-a.yml: -------------------------------------------------------------------------------- 1 | title: "Your question in one sentence" 2 | body: 3 | - type: markdown 4 | attributes: 5 | value: | 6 | First, please check if your question has already been asked. 7 | If it has, please consider adding a comment to the existing discussion instead of creating a new one. 8 | 9 | Finally, try to describe the best you can what you want to achieve or what is your issue, especially by providing a complete self-contained reproducible example. 10 | This will help us to understand your question and answer it. 11 | 12 | `````md 13 | ````qmd 14 | --- 15 | title: "Reproducible Quarto Document" 16 | format: html 17 | engine: jupyter 18 | --- 19 | 20 | This is a reproducible Quarto document using `format: html`. 21 | It is written in Markdown and contains embedded Python code. 22 | When you run the code, it will produce a message. 23 | 24 | ```{python} 25 | print("Hello, world!") 26 | ``` 27 | 28 | ![An image]({{< placeholder 600 400 >}}){#fig-placeholder} 29 | 30 | {{< lipsum 1 >}} 31 | 32 | A reference to @fig-placeholder. 33 | 34 | The end. 35 | ```` 36 | ````` 37 | 38 | You can add some additional information that can help us to help you: 39 | 40 | - What Quarto version are you using? 41 | You can find the version of Quarto you used by running `quarto --version` in your terminal. 42 | - What version of the Quarto extension are you using? 43 | You can find the version of the Quarto extension you used by running `quarto list extensions` in your terminal. 44 | - What operating system are you using? 45 | Linux Ubuntu 20.04, macOS 11.2.3, Windows 10, _etc._ 46 | 47 | _Thank you for opening this discussion either to ask for help or to report a possible bug!_ 48 | - type: textarea 49 | attributes: 50 | label: Description 51 | description: Start your question/report! 52 | placeholder: | 53 | You can include Quarto document code which includes code blocks like this: 54 | 55 | `````md 56 | ````qmd 57 | --- 58 | title: "Reproducible Quarto Document" 59 | format: html 60 | engine: jupyter 61 | --- 62 | 63 | This is a reproducible Quarto document using `format: html`. 64 | It is written in Markdown and contains embedded Python code. 65 | When you run the code, it will produce a message. 66 | 67 | ```{python} 68 | print("Hello, world!") 69 | ``` 70 | 71 | ![An image]({{< placeholder 600 400 >}}){#fig-placeholder} 72 | 73 | {{< lipsum 1 >}} 74 | 75 | A reference to @fig-placeholder. 76 | 77 | The end. 78 | ```` 79 | ````` 80 | 81 | --- 82 | 83 | ### Additional information that can help us to help you 84 | 85 | - What Quarto version are you using? 86 | You can find the version of Quarto you used by running `quarto --version` in your terminal. 87 | - What version of the Quarto extension are you using? 88 | You can find the version of the Quarto extension you used by running `quarto list extensions` in your terminal. 89 | - What operating system are you using? 90 | Linux Ubuntu 20.04, macOS 11.2.3, Windows 10, _etc._ 91 | validations: 92 | required: true 93 | - type: markdown 94 | attributes: 95 | value: | 96 | _Thank you for opening this discussion either to ask for help or to report a possible bug!_ 97 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: mcanouil 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Report an error or unexpected behaviour 3 | labels: 4 | - "Type: Bug :bug:" 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Welcome! 11 | 12 | - type: checkboxes 13 | attributes: 14 | label: "I Have checked the following" 15 | options: 16 | - label: I have searched the issue tracker for similar issues 17 | required: true 18 | - label: I have the latest version of [Quarto CLI](https://github.com/quarto-dev/quarto-cli/releases/latest) 19 | required: true 20 | - label: I have the latest version of the Quarto extension 21 | required: true 22 | 23 | - type: textarea 24 | attributes: 25 | label: Bug description 26 | description: Description of the bug. 27 | placeholder: Please describe the bug here. 28 | 29 | - type: textarea 30 | attributes: 31 | label: Steps to reproduce 32 | description: | 33 | Tell us how to reproduce this bug. 34 | 35 | - type: textarea 36 | attributes: 37 | label: Actual behaviour 38 | description: Tell us what happens instead. 39 | 40 | - type: textarea 41 | attributes: 42 | label: Expected behaviour 43 | description: Tell us what should happen. 44 | 45 | - type: textarea 46 | attributes: 47 | label: Your environment 48 | description: | 49 | Please document the IDE (_e.g._ RStudio, Positron, VSCode, NVim), its version, and the operating system you're running (_e.g., MacOS Ventura 13.4, Windows 11, Linux Debian 11, _etc._). 50 | placeholder: | 51 | - IDE: RStudio 2023.03.1+446 | VSCode | Positron 52 | - OS: MacOS Ventura 13.4 | Windows | Ubuntu 53 | 54 | - type: markdown 55 | attributes: 56 | value: "_Thanks for submitting this bug report!_" 57 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Support 4 | url: https://github.com/mcanouil/quarto-highlight-text/discussions 5 | about: Please ask and answer questions here. 6 | - name: Issue with Quarto CLI 7 | url: https://github.com/quarto-dev/quarto-cli 8 | about: Please report issues with the Quarto CLI here 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | labels: 8 | - "Type: Dependencies :arrow_up:" 9 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release Quarto Extension 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | type: choice 8 | description: "Version" 9 | required: false 10 | default: "patch" 11 | options: 12 | - "patch" 13 | - "minor" 14 | - "major" 15 | quarto: 16 | type: choice 17 | description: "Quarto version" 18 | required: false 19 | default: "release" 20 | options: 21 | - "release" 22 | - "pre-release" 23 | 24 | permissions: 25 | contents: write 26 | pull-requests: write 27 | id-token: write 28 | pages: write 29 | 30 | jobs: 31 | release: 32 | uses: mcanouil/quarto-workflows/.github/workflows/release-extension.yml@main 33 | secrets: inherit 34 | with: 35 | version: "${{ github.event.inputs.version }}" 36 | formats: "html typst pdf-xelatex pdf-lualatex pdf-pdflatex docx revealjs beamer pptx" 37 | tinytex: true 38 | quarto: "${{ github.event.inputs.quarto }}" 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.luarc.json 2 | /.quarto/ 3 | _quarto.yml 4 | *.html 5 | *.pdf 6 | *.docx 7 | *.pptx 8 | *_files/ 9 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: "Highlight-text Extension For Quarto" 3 | message: "If you use this project, please cite it as below." 4 | type: software 5 | authors: 6 | - family-names: "Canouil" 7 | given-names: "Mickaël" 8 | orcid: "https://orcid.org/0000-0002-3396-4549" 9 | repository-code: "https://github.com/mcanouil/quarto-highlight-text" 10 | url: "http://m.canouil.dev/quarto-highlight-text/" 11 | license: "MIT" 12 | date-released: "2025-05-05" 13 | version: 1.3.3 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Mickaël Canouil 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Highlight-text Extension For Quarto 2 | 3 | This is a Quarto extension that allows to highlight text in a document for various format: HTML, LaTeX, Typst, and Docx. 4 | 5 | ## Installing 6 | 7 | ```bash 8 | quarto add mcanouil/quarto-highlight-text 9 | ``` 10 | 11 | This will install the extension under the `_extensions` subdirectory. 12 | If you're using version control, you will want to check in this directory. 13 | 14 | ## Using 15 | 16 | To use the extension, add the following to your document's front matter: 17 | 18 | ```yaml 19 | filters: 20 | - highlight-text 21 | ``` 22 | 23 | Then you can use the span syntax markup to highlight text in your document, *e.g.*: 24 | 25 | ```markdown 26 | [Red]{colour="#b22222" bg-colour="#abc123"} # UK 27 | [Blue]{color="#0000FF" bg-color="#ABC123"} # US 28 | ``` 29 | 30 | You can also use the shorter syntax ([v1.1.1](../../releases/tag/1.1.1)): 31 | 32 | ```markdown 33 | [Red]{fg="red" bg="primary"} 34 | ``` 35 | 36 | Using colours from `_brand.yml` ([v1.1.0](../../releases/tag/1.1.0)): 37 | 38 | ```yaml 39 | color: 40 | palette: 41 | red: "#b22222" 42 | primary: "#abc123" 43 | ``` 44 | 45 | ```markdown 46 | [Red]{colour="brand-color.red" bg-colour="brand-color.primary"} 47 | ``` 48 | 49 | Using colours from light/dark themes with Quarto CLI >=1.7.28 ([v1.2.0](../../releases/tag/1.2.0)): 50 | 51 | - From document front matter: 52 | 53 | ```yaml 54 | brand: 55 | light: 56 | color: 57 | palette: 58 | fg: "#ffffff" 59 | bg: "#b22222" 60 | dark: 61 | color: 62 | palette: 63 | fg: "#b22222" 64 | bg: "#ffffff" 65 | ``` 66 | 67 | - From `_quarto.yml` and `_brand.yml` file 68 | 69 | ```yaml 70 | brand: 71 | dark: _brand.yml 72 | ``` 73 | 74 | ```markdown 75 | [Light: White/Red | Dark: Red/White]{colour="brand-color.fg" bg-colour="brand-color.bg"} 76 | ``` 77 | 78 | > [!NOTE] 79 | > Only the `html` support light/dark mode switching. 80 | > The other formats will use the light mode colours if available or the dark mode colours otherwise. 81 | 82 | ## Limitations 83 | 84 | LaTeX `\colorbox` command does not support wrapping/line breaks in the text to be highlighted. 85 | This means that the above example will not work well in LaTeX output. 86 | In order to get a slightly better result, you can use the `par=true` attribute to add `\parbox{\linewidth}`: 87 | 88 | ```markdown 89 | [Red]{colour="#b22222" bg-colour="#abc123" par=true} 90 | ``` 91 | 92 | Use `pdf-engine: lualatex` in your YAML front matter to use the `luatex` engine and fully alleviate the above issue. 93 | 94 | ## Examples 95 | 96 | Here is the source code for a minimal example: [`example.qmd`](example.qmd). 97 | 98 | Outputs of `example.qmd`: 99 | 100 | - [HTML](https://m.canouil.dev/quarto-highlight-text/) 101 | - [Typst/PDF](https://m.canouil.dev/quarto-highlight-text/highlight-typst.pdf) 102 | - [LaTeX/PDF (XeLaTeX)](https://m.canouil.dev/quarto-highlight-text/highlight-xelatex.pdf) 103 | - [LaTeX/PDF (LuaLaTeX)](https://m.canouil.dev/quarto-highlight-text/highlight-lualatex.pdf) 104 | - [LaTeX/PDF (PDFLaTeX)](https://m.canouil.dev/quarto-highlight-text/highlight-pdflatex.pdf) 105 | - [Word/Docx](https://m.canouil.dev/quarto-highlight-text/highlight-openxml.docx) (**only supports plain text, *i.e.*, no URLs**) 106 | - [Reveal.js](https://m.canouil.dev/quarto-highlight-text/highlight-revealjs.html) 107 | - [Beamer/PDF](https://m.canouil.dev/quarto-highlight-text/highlight-beamer.pdf) 108 | - [PowerPoint/Pptx](https://m.canouil.dev/quarto-highlight-text/highlight-pptx.pptx) (**only supports plain text, *i.e.*, no URLs**) 109 | -------------------------------------------------------------------------------- /_extensions/highlight-text/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Mickaël Canouil 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 | -------------------------------------------------------------------------------- /_extensions/highlight-text/_extension.yml: -------------------------------------------------------------------------------- 1 | title: Highlight Text 2 | author: Mickaël Canouil 3 | version: 1.3.3 4 | quarto-required: ">=1.7.29" 5 | contributes: 6 | filters: 7 | - highlight-text.lua 8 | -------------------------------------------------------------------------------- /_extensions/highlight-text/highlight-text.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | # MIT License 3 | # 4 | # Copyright (c) 2025 Mickaël Canouil 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | ]] 24 | 25 | --- Gets a colour value from brand theme or formats it for later use 26 | --- @param theme string The brand theme to use (light/dark) 27 | --- @param colour string The colour value or brand colour reference 28 | --- @return string The processed colour value 29 | local function get_brand_colour(theme, colour) 30 | local brand = require('modules/brand/brand') 31 | if colour and colour:match('^brand%-color.') then 32 | colour = brand.get_color(theme, colour:gsub('^brand%-color%.', '')) 33 | else 34 | if FORMAT:match 'typst' and colour ~= nil then 35 | colour = 'rgb("' .. colour .. '")' 36 | end 37 | end 38 | return colour 39 | end 40 | 41 | --- Applies text and background colour styling for HTML-based outputs 42 | --- @param span table The span element to modify 43 | --- @param settings table The highlight settings containing colour and background colour 44 | --- @return table The modified span with HTML styling 45 | local function highlight_html(span, settings) 46 | local result = {} 47 | local theme_keys = {} 48 | 49 | for key, _ in pairs(settings) do 50 | table.insert(theme_keys, key) 51 | end 52 | 53 | for _, theme in ipairs(theme_keys) do 54 | local theme_span = pandoc.Span(span.content) 55 | local colour = settings[theme].colour 56 | local bg_colour = settings[theme].bg_colour 57 | 58 | for k, v in pairs(span.attributes) do 59 | theme_span.attributes[k] = v 60 | end 61 | 62 | if theme_span.attributes['style'] == nil then 63 | theme_span.attributes['style'] = '' 64 | elseif theme_span.attributes['style']:sub(-1) ~= ';' then 65 | theme_span.attributes['style'] = theme_span.attributes['style'] .. ';' 66 | end 67 | 68 | theme_span.classes = theme_span.classes or {} 69 | table.insert(theme_span.classes, theme .. '-content') 70 | 71 | if colour ~= nil then 72 | theme_span.attributes['colour'] = nil 73 | theme_span.attributes['color'] = nil 74 | theme_span.attributes['style'] = theme_span.attributes['style'] .. 'color: ' .. colour .. ';' 75 | end 76 | 77 | if bg_colour ~= nil then 78 | theme_span.attributes['bg-colour'] = nil 79 | theme_span.attributes['bg-color'] = nil 80 | theme_span.attributes['style'] = theme_span.attributes['style'] .. 81 | 'border-radius: 0.2rem; padding: 0 0.2rem 0 0.2rem;' .. 'background-color: ' .. bg_colour .. ';' 82 | end 83 | 84 | table.insert(result, theme_span) 85 | end 86 | 87 | if #result == 1 then 88 | return result[1] 89 | else 90 | return result 91 | end 92 | end 93 | 94 | --- Applies text and background colour styling for LaTeX-based outputs 95 | --- @param span table The span element to modify 96 | --- @param colour string The text colour to apply 97 | --- @param bg_colour string The background colour to apply 98 | --- @param par boolean Whether to wrap in a paragraph box 99 | --- @return table The span content with LaTeX markup 100 | local function highlight_latex(span, colour, bg_colour, par) 101 | local is_lualatex = quarto.doc.pdf_engine() == 'lualatex' 102 | 103 | if is_lualatex and bg_colour ~= nil then 104 | quarto.doc.use_latex_package('luacolor, lua-ul') 105 | end 106 | 107 | if colour == nil then 108 | colour_open = '' 109 | colour_close = '' 110 | else 111 | colour_open = '\\textcolor[HTML]{' .. colour:gsub('^#', '') .. '}{' 112 | colour_close = '}' 113 | end 114 | 115 | if bg_colour == nil then 116 | bg_colour_open = '' 117 | bg_colour_close = '' 118 | else 119 | if is_lualatex then 120 | bg_colour_open = '\\highLight[{[HTML]{' .. bg_colour:gsub('^#', '') .. '}}]{' 121 | bg_colour_close = '}' 122 | else 123 | bg_colour_open = '\\colorbox[HTML]{' .. bg_colour:gsub('^#', '') .. '}{' 124 | bg_colour_close = '}' 125 | end 126 | end 127 | 128 | if par and not is_lualatex then 129 | bg_colour_open = bg_colour_open .. '\\parbox{\\linewidth}{' 130 | bg_colour_close = '}' .. bg_colour_close 131 | end 132 | 133 | table.insert( 134 | span.content, 1, 135 | pandoc.RawInline('latex', colour_open .. bg_colour_open) 136 | ) 137 | table.insert(span.content, pandoc.RawInline('latex', bg_colour_close .. colour_close)) 138 | 139 | return span.content 140 | end 141 | 142 | --- Applies text and background colour styling for Word documents 143 | --- @param span table The span element to modify 144 | --- @param colour string The text colour to apply 145 | --- @param bg_colour string The background colour to apply 146 | --- @return table The span content with OpenXML markup for Word 147 | local function highlight_openxml_docx(span, colour, bg_colour) 148 | local spec = '' 149 | if bg_colour ~= nil then 150 | spec = spec .. '' 151 | end 152 | if colour ~= nil then 153 | spec = spec .. '' 154 | end 155 | spec = spec .. '' 156 | 157 | table.insert(span.content, 1, pandoc.RawInline('openxml', spec)) 158 | table.insert(span.content, pandoc.RawInline('openxml', '')) 159 | 160 | return span.content 161 | end 162 | 163 | --- Applies text and background colour styling for PowerPoint presentations 164 | --- @param span table The span element to modify 165 | --- @param colour string The text colour to apply 166 | --- @param bg_colour string The background colour to apply 167 | --- @return table Raw inline containing OpenXML markup for PowerPoint 168 | local function highlight_openxml_pptx(span, colour, bg_colour) 169 | local spec = '' 170 | if colour ~= nil then 171 | spec = spec .. '' 172 | end 173 | if bg_colour ~= nil then 174 | spec = spec .. '' 175 | end 176 | spec = spec .. '' 177 | 178 | -- table.insert(span.content, 1, pandoc.RawInline('openxml', spec)) 179 | -- table.insert(span.content, pandoc.RawInline('openxml', '')) 180 | 181 | local span_content_string = '' 182 | for i, inline in ipairs(span.content) do 183 | span_content_string = span_content_string .. pandoc.utils.stringify(inline) 184 | end 185 | 186 | return pandoc.RawInline('openxml', spec .. span_content_string .. '') 187 | end 188 | 189 | --- Applies text and background colour styling for Typst output 190 | --- @param span table The span element to modify 191 | --- @param colour string The text colour to apply 192 | --- @param bg_colour string The background colour to apply 193 | --- @return table The span content with Typst markup 194 | local function highlight_typst(span, colour, bg_colour) 195 | if colour == nil then 196 | colour_open = '' 197 | colour_close = '' 198 | else 199 | colour_open = '#text(' .. colour .. ')[' 200 | colour_close = ']' 201 | end 202 | 203 | if bg_colour == nil then 204 | bg_colour_open = '' 205 | bg_colour_close = '' 206 | else 207 | bg_colour_open = '#highlight(fill: ' .. bg_colour .. ')[' 208 | bg_colour_close = ']' 209 | end 210 | 211 | table.insert( 212 | span.content, 1, 213 | pandoc.RawInline('typst', colour_open .. bg_colour_open) 214 | ) 215 | table.insert( 216 | span.content, 217 | pandoc.RawInline('typst', bg_colour_close .. colour_close) 218 | ) 219 | 220 | return span.content 221 | end 222 | 223 | --- Main filter function that processes span elements and applies highlighting 224 | --- based on the output format and specified attributes 225 | --- @param span table The span element from the document 226 | --- @return table The modified span or span content with appropriate styling 227 | local function highlight(span) 228 | local colour = span.attributes['fg'] 229 | if colour == nil then 230 | colour = span.attributes['colour'] 231 | end 232 | if colour == nil then 233 | colour = span.attributes['color'] 234 | end 235 | 236 | local bg_colour = span.attributes['bg'] 237 | if bg_colour == nil then 238 | bg_colour = span.attributes['bg-colour'] 239 | end 240 | if bg_colour == nil then 241 | bg_colour = span.attributes['bg-color'] 242 | end 243 | 244 | local highlight_settings = {} 245 | if quarto.brand.has_mode('light') or quarto.brand.has_mode('dark') then 246 | local modes = {'light', 'dark'} 247 | 248 | for _, mode in ipairs(modes) do 249 | if quarto.brand.has_mode(mode) then 250 | highlight_settings[mode] = { 251 | colour = get_brand_colour(mode, colour), 252 | bg_colour = get_brand_colour(mode, bg_colour) 253 | } 254 | end 255 | end 256 | else 257 | highlight_settings.light = { 258 | colour = get_brand_colour('light', colour), 259 | bg_colour = get_brand_colour('light', bg_colour) 260 | } 261 | end 262 | 263 | if highlight_settings.light == nil and highlight_settings.dark == nil then 264 | return span 265 | end 266 | 267 | if highlight_settings.light == nil then 268 | highlight_settings.light = highlight_settings.dark 269 | end 270 | 271 | colour = highlight_settings.light.colour 272 | bg_colour = highlight_settings.light.bg_colour 273 | 274 | if colour == nil and bg_colour == nil then return span end 275 | 276 | if span.attributes['par'] == nil then 277 | par = false 278 | else 279 | par = true 280 | span.attributes['par'] = nil 281 | end 282 | 283 | if quarto.doc.is_format('html') or quarto.doc.is_format('revealjs') then 284 | return highlight_html(span, highlight_settings) 285 | elseif quarto.doc.is_format('latex') or quarto.doc.is_format('beamer') then 286 | return highlight_latex(span, colour, bg_colour, par) 287 | elseif quarto.doc.is_format('docx') then 288 | return highlight_openxml_docx(span, colour, bg_colour) 289 | elseif quarto.doc.is_format('pptx') then 290 | return highlight_openxml_pptx(span, colour, bg_colour) 291 | elseif quarto.doc.is_format('typst') then 292 | return highlight_typst(span, colour, bg_colour) 293 | else 294 | return span 295 | end 296 | end 297 | 298 | return { 299 | { Span = highlight }, 300 | } 301 | -------------------------------------------------------------------------------- /example.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Highlight-text Quarto Extension" 3 | format: 4 | html: 5 | output-file: index 6 | typst: 7 | output-file: highlight-typst 8 | papersize: a4 9 | margin: 10 | x: 2.5cm 11 | y: 2.5cm 12 | pdf-xelatex: 13 | output-file: highlight-xelatex 14 | papersize: a4 15 | margin: 16 | x: 2.5cm 17 | y: 2.5cm 18 | pdf-lualatex: 19 | output-file: highlight-lualatex 20 | pdf-engine: lualatex 21 | papersize: a4 22 | margin: 23 | x: 2.5cm 24 | y: 2.5cm 25 | pdf-pdflatex: 26 | output-file: highlight-pdflatex 27 | pdf-engine: lualatex 28 | papersize: a4 29 | margin: 30 | x: 2.5cm 31 | y: 2.5cm 32 | docx: 33 | output-file: highlight-docx 34 | revealjs: 35 | output-file: highlight-revealjs 36 | beamer: 37 | output-file: highlight-beamer 38 | aspectratio: 169 39 | pptx: 40 | output-file: highlight-pptx 41 | format-links: 42 | - html 43 | - typst 44 | - format: pdf-xelatex 45 | text: "PDF (XeLaTeX)" 46 | - format: pdf-lualatex 47 | text: "PDF (LuaLaTeX)" 48 | - format: pdf-pdflatex 49 | text: "PDF (PDFLaTeX)" 50 | - docx 51 | - revealjs 52 | - beamer 53 | - pptx 54 | embed-resources: true 55 | execute: 56 | echo: true 57 | filters: 58 | - highlight-text 59 | brand: 60 | light: 61 | color: 62 | palette: 63 | fg: "#ffffff" 64 | bg: "#b22222" 65 | foreground: "#333333" 66 | background: "#fafafa" 67 | dark: 68 | color: 69 | palette: 70 | fg: "#b22222" 71 | bg: "#ffffff" 72 | foreground: "#fafafa" 73 | background: "#333333" 74 | --- 75 | 76 | This is a Quarto extension that allows to highlight text in a document for various format: HTML, LaTeX, Typst, and Docx. 77 | 78 | ## Installing 79 | 80 | ```bash 81 | quarto add mcanouil/quarto-highlight-text 82 | ``` 83 | 84 | This will install the extension under the `_extensions` subdirectory. 85 | If you're using version control, you will want to check in this directory. 86 | 87 | ## Using 88 | 89 | To use the extension, add the following to your document's front matter: 90 | 91 | ```yaml 92 | filters: 93 | - highlight-text 94 | ``` 95 | 96 | Then you can use the span syntax markup to highlight text in your document. 97 | 98 | ```markdown 99 | [Red]{colour="#b22222" bg-colour="#abc123"} # UK 100 | [Red]{fg="#b22222" bg="#abc123"} 101 | [Blue]{color="#0000FF" bg-color="#ABC123"} # US 102 | ``` 103 | 104 | ## Font Colour 105 | 106 | ```markdown 107 | [Red text]{colour="#b22222"} 108 | ``` 109 | 110 | [Red text]{colour="#b22222"} 111 | 112 | ```markdown 113 | [Blue text]{color="#0000FF"} 114 | ``` 115 | 116 | [Blue text]{color="#0000FF"} 117 | 118 | ## Background Colour 119 | 120 | ```markdown 121 | [Red background]{bg-colour="#b22222"} 122 | ``` 123 | 124 | [Red background]{bg-colour="#b22222"} 125 | 126 | ```markdown 127 | [Blue background]{bg-color="#0000FF"} 128 | ``` 129 | 130 | [Blue background]{bg-color="#0000FF"} 131 | 132 | ## Font and Background Colour 133 | 134 | ```markdown 135 | [White text, Red background]{ 136 | fg="#FFFFFF" bg="#b22222" 137 | } 138 | ``` 139 | 140 | [White text, Red background]{fg="#FFFFFF" bg="#b22222"} 141 | 142 | ```markdown 143 | [White text, Blue background]{ 144 | colour="#FFFFFF" bg-colour="#0000FF" 145 | } 146 | ``` 147 | 148 | [White text, Blue background]{colour="#FFFFFF" bg-colour="#0000FF"} 149 | 150 | ## More Examples 151 | 152 | ```markdown 153 | [text [with a link](https://quarto.org/)]{ 154 | color="#FFFFFF" bg-color="#00FFFF" 155 | } 156 | ``` 157 | 158 | [text [with a link](https://quarto.org/)]{ 159 | color="#FFFFFF" bg-color="#00FFFF" 160 | } 161 | 162 | ## `_brand.yml` 163 | 164 | ```markdown 165 | [Light: White/Red | Dark: Red/White]{ 166 | colour="brand-color.fg" 167 | bg-colour="brand-color.bg" 168 | } 169 | ``` 170 | 171 | [Light: White/Red | Dark: Red/White]{colour="brand-color.fg" bg-colour="brand-color.bg"} 172 | 173 | ## Limitations `xelatex` and `pdflatex` 174 | 175 | LaTeX `\colorbox` command does not support wrapping/line breaks in the text to be highlighted. 176 | This means that the above example will not work well in LaTeX output. 177 | 178 | ```markdown 179 | [Your long text]{colour="#b22222" bg-colour="#abc123"} 180 | ``` 181 | [ 182 | LaTeX `\colorbox` command does not support wrapping/line breaks in the text to be highlighted. 183 | This means that the above example will not work well in LaTeX output. 184 | ]{colour="#b22222" bg-colour="#abc123"} 185 | 186 | In order to get a slightly better result, you can use the `par=true` attribute to add `\parbox{\linewidth}`: 187 | 188 | ```markdown 189 | [Your long text]{colour="#b22222" bg-colour="#abc123" par=true} 190 | ``` 191 | 192 | [ 193 | LaTeX `\colorbox` command does not support wrapping/line breaks in the text to be highlighted. 194 | This means that the above example will not work well in LaTeX output. 195 | ]{colour="#b22222" bg-colour="#abc123" par=true} 196 | 197 | Use `pdf-engine: lualatex` in the YAML front matter to get the best result. 198 | --------------------------------------------------------------------------------