diff --git a/src/resources/filters/main.lua b/src/resources/filters/main.lua
index fc7e40d382..d949b2f61d 100644
--- a/src/resources/filters/main.lua
+++ b/src/resources/filters/main.lua
@@ -85,6 +85,7 @@ import("./quarto-post/html.lua")
import("./quarto-post/dashboard.lua")
import("./quarto-post/email.lua")
import("./quarto-post/pptx.lua")
+import("./quarto-post/landscape.lua")
import("./quarto-finalize/dependencies.lua")
import("./quarto-finalize/book-cleanup.lua")
@@ -333,6 +334,10 @@ local quarto_post_filters = {
bibliography()
})
},
+ { name = "post-landscape-div",
+ filter = landscape_div(),
+ flags = { "has_landscape" }
+ },
{ name = "post-ipynb", filters = ipynb()},
{ name = "post-figureCleanupCombined", filter = combineFilters({
latexDiv(),
diff --git a/src/resources/filters/normalize/flags.lua b/src/resources/filters/normalize/flags.lua
index 127a3f07c0..a57a00e648 100644
--- a/src/resources/filters/normalize/flags.lua
+++ b/src/resources/filters/normalize/flags.lua
@@ -92,6 +92,10 @@ function compute_flags()
flags.has_lightbox = true
end
+ if node.attr.classes:find("landscape") then
+ flags.has_landscape = true
+ end
+
if node.attr.classes:find("hidden") then
flags.has_hidden = true
end
diff --git a/src/resources/filters/quarto-post/landscape.lua b/src/resources/filters/quarto-post/landscape.lua
new file mode 100644
index 0000000000..17c41d7966
--- /dev/null
+++ b/src/resources/filters/quarto-post/landscape.lua
@@ -0,0 +1,64 @@
+-- landscape.lua
+-- Copyright (C) 2024-2024 Posit Software, PBC
+--
+-- Author: [Edvin Syk](https://github.com/edvinsyk/)
+
+function landscape_div()
+ local ooxml = function(s)
+ return pandoc.RawBlock('openxml', s)
+ end
+
+ -- Define the end of a portrait section for DOCX
+ local end_portrait_section = ooxml ''
+
+ -- Define the end of a landscape section for DOCX
+ local end_landscape_section = ooxml [[
+
+
+
+
+
+
+
+ ]]
+
+ -- LateX commands for starting and ending a landscape section
+ local landscape_start_pdf = pandoc.RawBlock('latex', '\\begin{landscape}')
+ local landscape_end_pdf = pandoc.RawBlock('latex', '\\end{landscape}')
+
+ local landscape_start_typst = pandoc.RawBlock('typst', '#set page(flipped: true)')
+ local landscape_end_typst = pandoc.RawBlock('typst', '#set page(flipped: false)')
+
+ local function Meta(meta)
+ metaInjectLatex(meta, function(inject)
+ inject("\\usepackage{pdflscape}")
+ end)
+ return meta
+ end
+
+ local function Div(div)
+ if div.classes:includes('landscape') then
+ if FORMAT:match 'docx' then
+ -- DOCX-specific landscape orientation
+ div.content:insert(1, end_portrait_section)
+ div.content:insert(end_landscape_section)
+ elseif FORMAT:match 'latex' then
+ -- PDF-specific landscape orientation using KOMA-Script
+
+ div.content:insert(1, landscape_start_pdf)
+ div.content:insert(landscape_end_pdf)
+ elseif FORMAT:match 'typst' then
+ -- Insert the landscape start command before the Div content
+ table.insert(div.content, 1, landscape_start_typst)
+ table.insert(div.content, landscape_end_typst)
+ return div.content
+ end
+ return div
+ end
+ end
+
+ return {
+ Meta = Meta,
+ Div = Div
+ }
+end
diff --git a/tests/docs/smoke-all/landscape/landscape.qmd b/tests/docs/smoke-all/landscape/landscape.qmd
new file mode 100644
index 0000000000..0e793b7f8c
--- /dev/null
+++ b/tests/docs/smoke-all/landscape/landscape.qmd
@@ -0,0 +1,48 @@
+---
+title: "Landscape Example"
+keep-typ: true
+format:
+ docx: default
+ typst: default
+ latex: default
+_quarto:
+ tests:
+ docx:
+ ensureDocxRegexMatches:
+ - ['w:orient="landscape"']
+ - []
+ latex:
+ ensureFileRegexMatches:
+ -
+ - '\\usepackage{pdflscape}'
+ - '\\begin{landscape}'
+ - '\\end{landscape}'
+ - []
+ typst:
+ ensureTypstFileRegexMatches:
+ -
+ - '#set page\(flipped: true\)'
+ - '#set page\(flipped: false\)'
+ - []
+---
+
+# Examples
+
+This is in portrait mode.
+
+1. Things
+2. Stuff
+
+
+::: { .landscape }
+
+This should appear in landscape mode.
+
+1. Things
+
+:::
+
+
+Things should be back to normal here.
+
+1. Things
\ No newline at end of file