Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
589 views
in Technique[技术] by (71.8m points)

latex - How to apply two separate lua filters to Span elements, in R Markdown?

Using lua filters, I'm trying to allow people to do the following to PDF output from R Markdown:

  1. Apply a pre-defined highlighting color to text [like this]{.correction}
  2. Apply a user-defined highlighting color to text [like this]{highlight = "red"}
  3. Apply a custom font color [like this]{color = "blue"}

I can make all three possible with the lua filter shown by the end. However, I want to allow the user to turn off (1).

So I need (1) and (2)+(3) to be supplied by separate .lua files.

When I split my code into two files, however, I can only get the functionality of one or the other. I assume this is because I can only use one filter that targets Span elements at a time?

Any suggestions for solutions is much appreciated!

Here is the lua filter:

Span = function (el)
  -- store the attributes for color and highlight
  color = el.attributes['color']
  highlight = el.attributes['highlight']
  
  -- create a function to check for emptiness
  local function isempty(s)
    return s == nil or s == ''
  end
  
  -- highlight stuff that ends in {.correction}
  if el.classes[1] == "correction" then
    table.insert(
      el.content, 1, 
      pandoc.RawInline('latex', '\hl{')
    )
    table.insert(
      el.content, 
      pandoc.RawInline('latex', '}')
    )
  end
  
  -- highlight stuff with {highlight = "some-color"}
  if not isempty(highlight) then
    -- remove highlight attributes
    el.attributes['highlight'] = nil
    -- encapsulate in latex code
    table.insert(
      el.content, 1,
      pandoc.RawInline('latex', '\sethlcolor{' ..highlight..'}\hl{')
    )
    table.insert(
      el.content,
      pandoc.RawInline('latex', '}\sethlcolor{correctioncolor}')
    )
  end
  
  -- color text with {color = "some-color"}
  if not isempty(color) then
    -- remove color attributes
    el.attributes['color'] = nil
    -- encapsulate in latex code
    table.insert(
      el.content, 1,
      pandoc.RawInline('latex', '\textcolor{'..color..'}{')
    )
    table.insert(
      el.content,
      pandoc.RawInline('latex', '}')
    )
  end

  return el.content 
end

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Using the same filter but having a conditional part

This will answer your question not in the title but in the post.

I can make all three possible with the lua filter shown by the end. However, I want to allow the user to turn off (1).

You can do that from within the same filter by using Metadata that can allow to pass value to Pandoc which the Lua filter can use. See about Pandoc's metadata

You can take a value from your user in R, pass it to Pandoc using the -M arg (or rmarkdown::pandoc_metadata_arg()) - it would then be known by Pandoc. Lua filter can have access using Meta. You just need to take care of the execution order

For example, this example will take the value directly in the yaml header and it will be processed in Lua to create a variable acting as a switch to activated or not one part of the filter.

Setting to false below will not run the part in Lua. Setting to true (or nothing as it is the default) will run the part in Lua and highlight.

---
title: several Lua
output: 
  pdf_document: 
    keep_tex: true
    pandoc_args: ["--lua-filter", "custom.lua"]
    extra_dependencies: soul
correction: false
---

# write the lua files

## Correction

```{cat, engine.opts = list(file = "custom.lua")}

local correction_activated = true

Meta = function(m)
  if m['correction'] ~= nil then 
    correction_activated = m['correction']
  end
end
  

Span = function (el)
  -- store the attributes for color and highlight
  color = el.attributes['color']
  highlight = el.attributes['highlight']
  
  -- create a function to check for emptiness
  local function isempty(s)
    return s == nil or s == ''
  end
  
  -- highlight stuff that ends in {.correction}
  if correction_activated and el.classes[1] == "correction" then
    table.insert(
      el.content, 1, 
      pandoc.RawInline('latex', '\hl{')
    )
    table.insert(
      el.content, 
      pandoc.RawInline('latex', '}')
    )
  end
  
  -- highlight stuff with {highlight = "some-color"}
  if not isempty(highlight) then
    -- remove highlight attributes
    el.attributes['highlight'] = nil
    -- encapsulate in latex code
    table.insert(
      el.content, 1,
      pandoc.RawInline('latex', '\sethlcolor{' ..highlight..'}\hl{')
    )
    table.insert(
      el.content,
      pandoc.RawInline('latex', '}\sethlcolor{correctioncolor}')
    )
  end
  
  -- color text with {color = "some-color"}
  if not isempty(color) then
    -- remove color attributes
    el.attributes['color'] = nil
    -- encapsulate in latex code
    table.insert(
      el.content, 1,
      pandoc.RawInline('latex', '\textcolor{'..color..'}{')
    )
    table.insert(
      el.content,
      pandoc.RawInline('latex', '}')
    )
  end

  return el.content 
end

return {
  { Meta = Meta },
  { Span = Span }
}

```


Something [here]{.correction}

With what I mentioned earlier you can pass as metadata from within R directly if you prefer a switch as an argument in your format function.

This is one way of doing it and keep one filter.

Using several filters to pass to Pandoc conditionally

This is now in several filters

---
title: several Lua
output: 
  pdf_document: 
    keep_tex: true
    pandoc_args: ["--lua-filter", "correction.lua", "--lua-filter", "hl-color.lua"]
    extra_dependencies: soul
---

# write the lua files

## Correction

```{cat, engine.opts = list(file = "correction.lua")}

Span = function (el)
  -- create a function to check for emptiness
  local function isempty(s)
    return s == nil or s == ''
  end
  
  -- highlight stuff that ends in {.correction}
  if el.classes[1] == "correction" then
    table.insert(
      el.content, 1, 
      pandoc.RawInline('latex', '\hl{')
    )
    table.insert(
      el.content, 
      pandoc.RawInline('latex', '}')
    )
  end
  
  return el 
end
```

## Highlight + colors

```{cat, engine.opts = list(file = "hl-color.lua")}
Span = function (el)
  -- store the attributes for color and highlight
  color = el.attributes['color']
  highlight = el.attributes['highlight']
  
  -- create a function to check for emptiness
  local function isempty(s)
    return s == nil or s == ''
  end
  
  -- highlight stuff with {highlight = "some-color"}
  if not isempty(highlight) then
    -- remove highlight attributes
    el.attributes['highlight'] = nil
    -- encapsulate in latex code
    table.insert(
      el.content, 1,
      pandoc.RawInline('latex', '\sethlcolor{' ..highlight..'}\hl{')
    )
    table.insert(
      el.content,
      pandoc.RawInline('latex', '}\sethlcolor{correctioncolor}')
    )
  end
  
  -- color text with {color = "some-color"}
  if not isempty(color) then
    -- remove color attributes
    el.attributes['color'] = nil
    -- encapsulate in latex code
    table.insert(
      el.content, 1,
      pandoc.RawInline('latex', '\textcolor{'..color..'}{')
    )
    table.insert(
      el.content,
      pandoc.RawInline('latex', '}')
    )
  end

  return el
end
```


Something [here]{.correction}

Something [like this]{highlight="red"}

Or [like this]{color="blue"}

Put it in test.Rmd

Try

  • both
rmarkdown::render("test.Rmd")
  • Only correction
rmarkdown::render("test.Rmd", output_options = list(pandoc_args = rmarkdown::pandoc_lua_filter_args("correction.lua")))
  • Only hl+color
rmarkdown::render("test.Rmd", output_options = list(pandoc_args = rmarkdown::pandoc_lua_filter_args("hl-color.lua")))

I hope you'll manage to integrate this into your tool.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...