aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbert Krewinkel <[email protected]>2024-01-17 12:19:20 +0100
committerJohn MacFarlane <[email protected]>2024-04-16 10:23:08 -0700
commit5f937eae617d72f5f01e24f5a72bafc5b04fde15 (patch)
tree99a21e079e428b93561e12a45ecc6a0961cc5f09
parent9a09c89636e78edaed52276a2b3e00fb7368631b (diff)
Lua: add new module `pandoc.image`
The module provides basic querying functions for image properties.
-rw-r--r--doc/lua-filters.md61
-rw-r--r--pandoc-lua-engine/pandoc-lua-engine.cabal2
-rw-r--r--pandoc-lua-engine/src/Text/Pandoc/Lua/Init.hs2
-rw-r--r--pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/ImageSize.hs31
-rw-r--r--pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Image.hs98
-rw-r--r--pandoc-lua-engine/test/Tests/Lua/Module.hs2
-rw-r--r--pandoc-lua-engine/test/lua/module/pandoc-image.lua68
7 files changed, 264 insertions, 0 deletions
diff --git a/doc/lua-filters.md b/doc/lua-filters.md
index e1504ff13..3872e1cba 100644
--- a/doc/lua-filters.md
+++ b/doc/lua-filters.md
@@ -4607,6 +4607,67 @@ Returns:
<!-- END: AUTOGENERATED CONTENT -->
+<!-- BEGIN: AUTOGENERATED CONTENT for module pandoc.image -->
+
+# Module pandoc.image
+
+Basic image querying functions.
+
+## Functions {#pandoc.image-functions}
+
+### size {#pandoc.image.size}
+
+`size (image[, opts])`
+
+Returns a table containing the size and resolution of an image;
+throws an error if the given string is not an image, or if the
+size of the image cannot be determined.
+
+The resulting table has four entires: *width*, *height*,
+*dpi_horz*, and *dpi_vert*.
+
+The `opts` parameter, when given, should be either a WriterOptions
+object such as `PANDOC_WRITER_OPTIONS`, or a table with a `dpi`
+entry. It affects the calculation for vector image formats such as
+SVG.
+
+Parameters:
+
+`image`
+: image data (string)
+
+`opts`
+: writer options ([WriterOptions]\|table)
+
+Returns:
+
+- image size information or error message (table)
+
+*Since: 3.1.13*
+
+### format {#pandoc.image.format}
+
+`format (image)`
+
+Returns the format of an image as a lowercase string.
+
+Formats recognized by pandoc include *png*, *gif*, *tiff*, *jpeg*,
+*pdf*, *svg*, *eps*, and *emf*.
+
+Parameters:
+
+`image`
+: binary image data (string)
+
+Returns:
+
+- image format, or nil if the format cannot be determined
+ (string\|nil)
+
+*Since: 3.1.13*
+
+<!-- END: AUTOGENERATED CONTENT -->
+
<!-- BEGIN: AUTOGENERATED CONTENT for module pandoc.json -->
# Module pandoc.json
diff --git a/pandoc-lua-engine/pandoc-lua-engine.cabal b/pandoc-lua-engine/pandoc-lua-engine.cabal
index 3e101a4f2..27cfb75cc 100644
--- a/pandoc-lua-engine/pandoc-lua-engine.cabal
+++ b/pandoc-lua-engine/pandoc-lua-engine.cabal
@@ -74,6 +74,7 @@ library
, Text.Pandoc.Lua.Marshal.CommonState
, Text.Pandoc.Lua.Marshal.Context
, Text.Pandoc.Lua.Marshal.Format
+ , Text.Pandoc.Lua.Marshal.ImageSize
, Text.Pandoc.Lua.Marshal.PandocError
, Text.Pandoc.Lua.Marshal.ReaderOptions
, Text.Pandoc.Lua.Marshal.Reference
@@ -82,6 +83,7 @@ library
, Text.Pandoc.Lua.Marshal.WriterOptions
, Text.Pandoc.Lua.Module.CLI
, Text.Pandoc.Lua.Module.Format
+ , Text.Pandoc.Lua.Module.Image
, Text.Pandoc.Lua.Module.JSON
, Text.Pandoc.Lua.Module.MediaBag
, Text.Pandoc.Lua.Module.Pandoc
diff --git a/pandoc-lua-engine/src/Text/Pandoc/Lua/Init.hs b/pandoc-lua-engine/src/Text/Pandoc/Lua/Init.hs
index 2900fea27..0ea04b635 100644
--- a/pandoc-lua-engine/src/Text/Pandoc/Lua/Init.hs
+++ b/pandoc-lua-engine/src/Text/Pandoc/Lua/Init.hs
@@ -41,6 +41,7 @@ import qualified HsLua.Module.Path as Module.Path
import qualified HsLua.Module.Zip as Module.Zip
import qualified Text.Pandoc.Lua.Module.CLI as Pandoc.CLI
import qualified Text.Pandoc.Lua.Module.Format as Pandoc.Format
+import qualified Text.Pandoc.Lua.Module.Image as Pandoc.Image
import qualified Text.Pandoc.Lua.Module.JSON as Pandoc.JSON
import qualified Text.Pandoc.Lua.Module.MediaBag as Pandoc.MediaBag
import qualified Text.Pandoc.Lua.Module.Pandoc as Module.Pandoc
@@ -91,6 +92,7 @@ loadedModules :: [Module PandocError]
loadedModules =
[ Pandoc.CLI.documentedModule
, Pandoc.Format.documentedModule
+ , Pandoc.Image.documentedModule
, Pandoc.JSON.documentedModule
, Pandoc.MediaBag.documentedModule
, Pandoc.Scaffolding.documentedModule
diff --git a/pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/ImageSize.hs b/pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/ImageSize.hs
new file mode 100644
index 000000000..e56b5cd25
--- /dev/null
+++ b/pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/ImageSize.hs
@@ -0,0 +1,31 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{- |
+ Module : Text.Pandoc.Lua.Marshal.ImageSize
+ Copyright : © 2024 Albert Krewinkel
+ License : GPL-2.0-or-later
+ Maintainer : Albert Krewinkel <[email protected]>
+
+Marshaling image properties.
+-}
+module Text.Pandoc.Lua.Marshal.ImageSize
+ ( pushImageType
+ , pushImageSize
+ ) where
+
+import Data.Char (toLower)
+import HsLua
+import Text.Pandoc.ImageSize
+
+-- | Pushes an 'ImageType' as a string value.
+pushImageType :: LuaError e => Pusher e ImageType
+pushImageType = pushString . map toLower . show
+
+-- | Pushes a dimensional value.
+pushImageSize :: LuaError e => Pusher e ImageSize
+pushImageSize = pushAsTable
+ [ ("width", pushIntegral . pxX)
+ , ("height", pushIntegral . pxY)
+ , ("dpi_horz", pushIntegral . dpiX)
+ , ("dpi_vert", pushIntegral . dpiY)
+ ]
diff --git a/pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Image.hs b/pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Image.hs
new file mode 100644
index 000000000..3b2465ccc
--- /dev/null
+++ b/pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Image.hs
@@ -0,0 +1,98 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-|
+Module : Text.Pandoc.Lua.Module.Image
+Copyright : © 2024 Albert Krewinkel
+License : MIT
+Maintainer : Albert Krewinkel <[email protected]>
+
+Lua module for basic image operations.
+-}
+module Text.Pandoc.Lua.Module.Image (
+ -- * Module
+ documentedModule
+
+ -- ** Functions
+ , size
+ , format
+ )
+where
+
+import Prelude hiding (null)
+import Data.Default (Default (def))
+import Data.Maybe (fromMaybe)
+import Data.Version (makeVersion)
+import HsLua.Core
+import HsLua.Marshalling
+import HsLua.Packaging
+import Text.Pandoc.Error (PandocError)
+import Text.Pandoc.ImageSize (imageType, imageSize)
+import Text.Pandoc.Lua.PandocLua ()
+import Text.Pandoc.Lua.Marshal.ImageSize (pushImageType, pushImageSize)
+import Text.Pandoc.Lua.Marshal.WriterOptions (peekWriterOptions)
+
+import qualified Data.Text as T
+
+-- | The @pandoc.image@ module specification.
+documentedModule :: Module PandocError
+documentedModule = Module
+ { moduleName = "pandoc.image"
+ , moduleDescription = "Basic image querying functions."
+ , moduleFields = fields
+ , moduleFunctions = functions
+ , moduleOperations = []
+ , moduleTypeInitializers = []
+ }
+
+--
+-- Fields
+--
+
+-- | Exported fields.
+fields :: LuaError e => [Field e]
+fields = []
+
+--
+-- Functions
+--
+
+functions :: [DocumentedFunction PandocError]
+functions =
+ [ size `since` makeVersion [3, 1, 13]
+ , format `since` makeVersion [3, 1, 13]
+ ]
+
+-- | Find the size of an image.
+size :: DocumentedFunction PandocError
+size = defun "size"
+ ### liftPure2 (\img mwriterOpts -> imageSize (fromMaybe def mwriterOpts) img)
+ <#> parameter peekByteString "string" "image" "image data"
+ <#> opt (parameter peekWriterOptions "WriterOptions|table" "opts"
+ "writer options")
+ =#> functionResult (either (failLua . T.unpack) pushImageSize) "table"
+ "image size information or error message"
+ #? T.unlines
+ [ "Returns a table containing the size and resolution of an image;"
+ , "throws an error if the given string is not an image, or if the size"
+ , "of the image cannot be determined."
+ , ""
+ , "The resulting table has four entires: *width*, *height*, *dpi\\_horz*,"
+ , "and *dpi\\_vert*."
+ , ""
+ , "The `opts` parameter, when given, should be either a WriterOptions"
+ , "object such as `PANDOC_WRITER_OPTIONS`, or a table with a `dpi` entry."
+ , "It affects the calculation for vector image formats such as SVG."
+ ]
+
+-- | Returns the format of an image.
+format :: LuaError e => DocumentedFunction e
+format = defun "format"
+ ### liftPure imageType
+ <#> parameter peekByteString "string" "image" "binary image data"
+ =#> functionResult (maybe pushnil pushImageType) "string|nil"
+ "image format, or nil if the format cannot be determined"
+ #? T.unlines
+ [ "Returns the format of an image as a lowercase string."
+ , ""
+ , "Formats recognized by pandoc include *png*, *gif*, *tiff*, *jpeg*,"
+ , "*pdf*, *svg*, *eps*, and *emf*."
+ ]
diff --git a/pandoc-lua-engine/test/Tests/Lua/Module.hs b/pandoc-lua-engine/test/Tests/Lua/Module.hs
index e3556f2cd..ff85f1292 100644
--- a/pandoc-lua-engine/test/Tests/Lua/Module.hs
+++ b/pandoc-lua-engine/test/Tests/Lua/Module.hs
@@ -25,6 +25,8 @@ tests =
("lua" </> "module" </> "pandoc-list.lua")
, testPandocLua "pandoc.format"
("lua" </> "module" </> "pandoc-format.lua")
+ , testPandocLua "pandoc.image"
+ ("lua" </> "module" </> "pandoc-image.lua")
, testPandocLua "pandoc.json"
("lua" </> "module" </> "pandoc-json.lua")
, testPandocLua "pandoc.mediabag"
diff --git a/pandoc-lua-engine/test/lua/module/pandoc-image.lua b/pandoc-lua-engine/test/lua/module/pandoc-image.lua
new file mode 100644
index 000000000..72f261449
--- /dev/null
+++ b/pandoc-lua-engine/test/lua/module/pandoc-image.lua
@@ -0,0 +1,68 @@
+--
+-- Tests for the system module
+--
+local image = require 'pandoc.image'
+local tasty = require 'tasty'
+
+local group = tasty.test_group
+local test = tasty.test_case
+local assert = tasty.assert
+
+local svg_image = [==[<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ height="70" width="70"
+ viewBox="-35 -35 70 70">
+ <title>test</title>
+ <!-- document shape -->
+ <polygon points="-10,-31.53 -10,-3.25 0,0 10,-3.25 10,-23.53 2,-31.53" />
+</svg>
+]==]
+
+return {
+ -- Check existence of static fields
+ group 'static fields' {
+ },
+
+ group 'size' {
+ test('returns a table', function ()
+ local imgsize = {
+ width = 70,
+ height = 70,
+ dpi_horz = 96,
+ dpi_vert = 96,
+ }
+ assert.are_same(image.size(svg_image), imgsize)
+ end),
+ test('fails on faulty eps', function ()
+ assert.error_matches(
+ function () image.size('%!PS EPSF') end,
+ 'could not determine EPS size'
+ )
+ end),
+ test('fails if input is not an image', function ()
+ assert.error_matches(
+ function () image.size('not an image') end,
+ 'could not determine image type'
+ )
+ end),
+ test('respects the dpi setting', function ()
+ local imgsize = {
+ width = 70,
+ height = 70,
+ dpi_horz = 300,
+ dpi_vert = 300,
+ }
+ assert.are_same(image.size(svg_image, {dpi=300}), imgsize)
+ end),
+ },
+
+ group 'format' {
+ test('SVG', function ()
+ assert.are_equal(image.format(svg_image), 'svg')
+ end),
+ test('returns nil if input is not an image', function ()
+ assert.is_nil(image.format('not an image'))
+ end),
+ },
+}