texlive[67348] Master: luahttp (12jun23)

commits+karl at tug.org commits+karl at tug.org
Mon Jun 12 22:16:13 CEST 2023


Revision: 67348
          http://tug.org/svn/texlive?view=revision&revision=67348
Author:   karl
Date:     2023-06-12 22:16:13 +0200 (Mon, 12 Jun 2023)
Log Message:
-----------
luahttp (12jun23)

Modified Paths:
--------------
    trunk/Master/tlpkg/bin/tlpkg-ctan-check
    trunk/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc

Added Paths:
-----------
    trunk/Master/texmf-dist/doc/lualatex/luahttp/
    trunk/Master/texmf-dist/doc/lualatex/luahttp/README.md
    trunk/Master/texmf-dist/doc/lualatex/luahttp/luahttp-doc.pdf
    trunk/Master/texmf-dist/doc/lualatex/luahttp/luahttp-doc.tex
    trunk/Master/texmf-dist/tex/lualatex/luahttp/
    trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp-display.lua
    trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp-fetch.lua
    trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp.sty
    trunk/Master/tlpkg/tlpsrc/luahttp.tlpsrc

Added: trunk/Master/texmf-dist/doc/lualatex/luahttp/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/luahttp/README.md	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/lualatex/luahttp/README.md	2023-06-12 20:16:13 UTC (rev 67348)
@@ -0,0 +1,102 @@
+# README for LUAHTTP
+Version 1.0.1
+
+## INTRODUCTION
+This is a small package that provides five commands to make HTTP requests using Lua and LuaTeX. Functionalities include API calls, fetching RSS feeds and the possibility to include images using a link. These commands run during the compilation of the PDF-Document and may require user interaction.
+
+This packages requires LuaTeX, Lua5.3, expat, openssl and the following Lua modules called "rocks":
+
+* luasec
+* dkjson
+* ltn12
+* feedparser
+* luaexpat
+
+## MANUAL INSTALLATION
+First the required dependencies need to be installed:
+
+Arch:
+```
+lua53 luarocks expat openssl
+```
+
+Debian:
+```
+liblua5.3-dev libssl-dev lua5.3 libexpat1-dev luarocks
+```
+
+MacOS:
+```
+lua at 5.3 luarocks expat openssl
+```
+
+The Lua rocks can be installed locally using ```luarocks```. To install the Lua rocks locally we need to initialize the directory where the ```.tex``` file(s) is located. In the directory execute the following commands to initialize a new Lua project and install the required dependencies:
+
+```bash
+luarocks init --lua-version=5.3
+luarocks install dkjson --lua-version=5.3
+luarocks install luasec --lua-version=5.3
+luarocks install ltn12 --lua-version=5.3
+luarocks install luaexpat --lua-version=5.3
+luarocks install feedparser --lua-version=5.3
+```
+
+For **MacOS** it may be necessary to add Lua 5.3 to your "$PATH" and install **luaexpat** and **luasec** using the commands bellow:
+```bash
+echo 'export PATH="/usr/local/opt/lua at 5.3/bin:$PATH"' >> ~/.zshrc
+luarocks install luaexpat EXPAT_DIR=/usr/local/Cellar/expat/${YOUR_VERSION_HERE}
+luarocks install luasec OPENSSL_DIR=/usr/local/Cellar/openssl at 3/${YOUR_VERSION_HERE}
+```
+
+## USAGE
+Compilation requires the use of ```lualatex``` with the ```--shell-escape``` option.
+
+```
+lualatex --shell-escape
+```
+
+Add the package to your TeX file:
+```
+\usepackage{luahttp}
+```
+
+Here is a quick overview of the commands and their parameters. For further details and examples please read the package documentation.
+
+* fetchJson, send a GET request and filter the response using optional keys:
+    ```tex
+    \fetchJson{URL}[optional: "key1,key2,.."]
+    ```
+
+* fetchJsonUsingFile, send a request specified in a JSON-file and filter the response using optional keys:
+    ```tex
+    \fetchJsonUsingFile{Path to JSON-file}[optional: "key1,key2,.."]
+    ```
+
+* fetchJsonUsingQuery, send a POST request with up to five query parameters and filter the response using keys:
+    ```tex
+    \fetchJsonUsingQuery{URL}{"key1,key2,.."} [optional: "?queryparameter1=value1"] .. [optional: "&queryparameter5=value5"]
+    ```
+
+* fetchRss, send a GET request and filter the feed and entries (limited by the second argument) using optional keys:
+    ```tex
+    \fetchRss{URL}{limit}[optional: "feedinfokey1,feedinfokey2,.."][optional: "entrykey1,entrykey2,.."]
+    ```
+
+* fetchImage, fetch an image from the internet and specify the width and height in **cm** using optional arguments:
+    ```tex
+    \fetchImage{URL}[optional: width in cm][optional: height in cm]
+    ```
+
+
+## AUTHOR / MAINTAINER
+
+* Johannes Casaburi (johannes.casaburi at protonmail.com)
+
+## LICENSE
+
+LATEX Project Public License, version 1.3c or later.
+
+## KNOWN PROBLEMS
+* Not all URL leading to an image are detected
+* Results that where filtered using keys may not be displayed in the order of the given keys.
+


Property changes on: trunk/Master/texmf-dist/doc/lualatex/luahttp/README.md
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/lualatex/luahttp/luahttp-doc.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/doc/lualatex/luahttp/luahttp-doc.pdf
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/luahttp/luahttp-doc.pdf	2023-06-12 20:15:41 UTC (rev 67347)
+++ trunk/Master/texmf-dist/doc/lualatex/luahttp/luahttp-doc.pdf	2023-06-12 20:16:13 UTC (rev 67348)

Property changes on: trunk/Master/texmf-dist/doc/lualatex/luahttp/luahttp-doc.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/lualatex/luahttp/luahttp-doc.tex
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/luahttp/luahttp-doc.tex	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/lualatex/luahttp/luahttp-doc.tex	2023-06-12 20:16:13 UTC (rev 67348)
@@ -0,0 +1,227 @@
+\documentclass[11pt]{article}
+\usepackage[english]{babel}
+\usepackage{listings}
+
+\lstset{
+  frame=shadowbox,
+  numbers=left,
+  basicstyle=\ttfamily,
+  keywordstyle=\bfseries,
+  breakatwhitespace=false,
+  breaklines=true,
+  keepspaces=true,
+}
+
+% Document
+\begin{document}
+
+\setlength{\parindent}{0pt}
+\setlength{\parskip}{5pt plus 2pt minus 1pt}
+
+\title{The \textsf{LuaHTTP} package}
+\author{Johannes Casaburi \\ \texttt{johannes.casaburi at protonmail.com}}
+\date{\today\\Version 1.0.1}
+
+\maketitle
+
+\tableofcontents
+
+\newpage
+
+\section{Introduction}
+This is a small package that is the result of a case study on Internet-Interactive \LaTeX\ PDF-documents. It provides five commands to make API calls, fetch data from the internet and insert that data into the PDF-document. This package depends on LuaTex, Lua and additional dependencies that need to be installed.
+
+\section{Manual Installation}
+First the required dependencies need to be installed:
+
+\texttt{MacOS using Homebrew}
+\begin{lstlisting}[language=bash]
+brew install lua at 5.3 luarocks expat openssl
+\end{lstlisting}
+
+\texttt{Arch}
+\begin{lstlisting}[language=bash]
+pacman -S lua53 luarocks expat openssl
+\end{lstlisting}
+
+\texttt{Debian}
+\begin{lstlisting}[language=bash]
+apt install liblua5.3-dev libssl-dev lua5.3 libexpat1-dev luarocks
+\end{lstlisting}
+
+Copy the \texttt{.sty} and \texttt{.lua} files in the directory where the \texttt{.tex} file is located.
+
+In order to get the package working, a few dependencies first need to be installed. An easy way to do so, is by using \texttt{luarocks} which is the package manager for Lua modules which are called \texttt{rocks}.
+
+To install the Lua rocks locally in the directory where the \lstinline{.tex} file(s) is located, execute the following commands. This will initialize a new Lua project and install the required dependencies (See below for MacOS):
+
+\begin{lstlisting}[language=bash]
+luarocks --lua-version=5.3 init
+luarocks --lua-version=5.3 install dkjson
+luarocks --lua-version=5.3 install luasec
+luarocks --lua-version=5.3 install ltn12
+luarocks --lua-version=5.3 install luaexpat
+luarocks --lua-version=5.3 install feedparser
+\end{lstlisting}
+
+The Lua version may be changed if the Lua rocks support the version.
+
+For \texttt{MacOS} you may need to add the Lua5.3 executable to your \texttt{\$PATH}.
+\begin{lstlisting}[language=bash]
+echo 'export PATH="/usr/local/opt/lua at 5.3/bin:$PATH"' >> ~/.zshrc
+\end{lstlisting}
+
+And it may also be necessary to install \texttt{luaexpat} and \texttt{luasec} using the commands below:
+\begin{lstlisting}[language=bash]
+luarocks install luaexpat EXPAT_DIR=/usr/local/Cellar/expat/${YOUR_VERSION_HERE}
+luarocks install luasec OPENSSL_DIR=/usr/local/Cellar/openssl at 3/${YOUR_VERSION_HERE}
+\end{lstlisting}
+
+\section{Usage}
+The LaTeX package can be used by adding the following line to the \texttt{.tex} file.
+\begin{lstlisting}[language=tex]
+\usepackage{luahttp}
+\end{lstlisting}
+
+To compile the PDF-document use:
+\begin{lstlisting}[language=bash]
+lualatex --shell-escape
+\end{lstlisting}
+
+\subsection{fetchJson}
+
+\begin{lstlisting}[language=tex]
+\fetchJson{URL}[optional: "key1,key2,.."]
+\end{lstlisting}
+
+This command takes an URL as the first argument and a list of comma separated keys as a second optional argument. Upon execution a GET request will be sent to the specified address. The accept header is set to \texttt{application/json}. The keys which are to be specified like \texttt{key1,key2,..} are used to filter out the desired values from the response. They have to exactly math the keys in the response otherwise the value will not be written to the PDF-Document. The same key can occur multiple times, this will output all values found to the PDF-Document.
+
+Values with special characters are escaped. Values are searched for URLs. If an URL pointing to a image is detected the user will be prompted to either display the image or the actual URL.
+
+\subsection{fetchJsonUsingFile}
+
+\begin{lstlisting}[language=tex]
+\fetchJsonUsingFile{Path to JSON file}[optional: "key1,key2,.."]
+\end{lstlisting}
+
+This is very similar to the \texttt{fetchJson} command but takes a path to a JSON file instead of an URL. This allows the user to specify the request method, headers and body. The file should look something like this:
+
+\begin{lstlisting}
+{
+    "method": "POST",
+    "url": "https://httpbin.org/post",
+    "redirect": false,
+    "headers": {
+        "Accept": "application/json",
+        "Content-Type": "application/json"
+    },
+    "body": {
+        "name": "john",
+        "id" : 1234
+    }
+}
+\end{lstlisting}
+
+Like the command before this command also takes and optional argument where the \texttt{keys} can be specified. If a value contains an URL leading to an image the user will be asked to either display the image or the URL.
+
+\subsection{fetchJsonUsingQuery}
+
+\begin{lstlisting}[language=tex]
+\fetchJsonUsingQuery{URL}{"key1,key2,.."} [optional: "?queryparameter1=value1"] .. [optional: "&queryparameter5=value5"]
+\end{lstlisting}
+
+This command can be used to send up to five query parameters using a POST request. The required arguments are the \texttt{URL} and a list of keys. The optional query parameters need to be written in key-value pairs:
+
+\begin{lstlisting}[language=tex]
+\fetchJsonUsingQuery{URL}{key1,key2}[?q=The first value][&second=The second value and so on"]
+\end{lstlisting}
+
+The values after the \texttt{=} sign are URL-encoded and sent using the \lstinline{application/json} Accept header and \texttt{application/x-www-form-urlencoded} Content-Type header. Like in the \texttt{fetchJson} command the user will be prompted if an URL leading to an image is detected.
+
+\subsection{fetchRss}
+
+\begin{lstlisting}[language=tex]
+\fetchRss{URL}{limit}[optional: "feedinfokey1,feedinfokey2,.."][optional: "entrykey1,entrykey2,.."]
+\end{lstlisting}
+
+This command takes an \texttt{URL} and a \texttt{limit}. The limit describes how many entries of the feed are written to the PDF-Document. There are two optional arguments. The first argument is a list of keys used to filter out feed information. At this time possible values are \texttt{title, subtitle, rights, generator, author, author\_detail, link, links, updated\_parsed, updated, id, contributors}. Every entry can be filtered using the second argument. Possible values for the entry keys are \texttt{id, link, links, updated, updated\_parsed, title, summary, contributors}. These values may change if the Lua rock called \texttt{feedparser} that is used to parse the feed changes. More information about \texttt{feedparser} can be found here: \lstinline{https://github.com/LuaDist-testing/feedparser}
+
+The \texttt{summary} from the entries may contain HTML which will just be written to the PDF-Document. As described in the \texttt{fetchJson} command you may be promoted when a link to an image is detected.
+
+
+\subsection{fetchImage}
+
+\begin{lstlisting}[language=tex]
+\fetchImage{URL}[optional: width in cm][optional: height in cm]
+\end{lstlisting}
+
+Images can be inserted into the PDF-Document using the URL. To resize the image, one or two optional arguments can be given to specify the width or width and height in \texttt{centimeters}:
+
+\begin{lstlisting}[language=tex]
+\fetchImage{URL}[7cm][5cm]
+\end{lstlisting}
+
+\section{Examples}
+
+\subsection{fetchJson}
+
+To display the title and summary of the first article returned by the space-news API we have to specify the URL and the keys that hold the desired values:
+
+\begin{lstlisting}[language=tex]
+\fetchJson{https://api.spaceflightnewsapi.net/v3/articles?_limit=1}[title,summary]
+\end{lstlisting}
+
+\subsection{fetchJsonUsingFile}
+Like the example above the same can be done using \texttt{fetchJsonUsingFile}. For that we need a \texttt{JSON} file with the following content:
+
+\begin{lstlisting}
+{
+    "method": "GET",
+    "url": "https://api.spaceflightnewsapi.net/v3/articles?_limit=1",
+    "redirect": false,
+    "headers": {
+        "Accept": "application/json"
+    }
+}
+\end{lstlisting}
+
+If the \texttt{JSON} file is located in the same directory as the \texttt{.lua} files the command looks like this:
+
+\begin{lstlisting}[language=tex]
+\fetchJsonUsingFile{myjsonfile.json}[title,summary]
+\end{lstlisting}
+
+If the package was not manually installed the absolute path must be used.
+
+\subsection{fetchJsonUsingQuery}
+
+Here is an example of an API call using query parameters:
+
+\begin{lstlisting}[language=tex]
+\fetchJsonUsingQuery{http://127.0.0.1:5000/translate}{translatedText}[?q=I want to translate this text][&source=en][&target=de][&format=text]
+\end{lstlisting}
+
+First we need to specify the first part of the URL, followed by the key(s) that hold the desired return values. The values of the query parameters get URL-encoded and put back together in a single URL. We may need to add an \texttt{\&} before the query parameter or an \texttt{?} depending on the location.
+
+\subsection{fetchRss}
+
+In this example the first the subtitle from the feed information is printed as a subsection. The second command prints the \texttt{title, link} and \texttt{updated} information of the first three entries received.
+
+\begin{lstlisting}[language=tex]
+\subsection{
+    \fetchRss{https://www.eff.org/rss/updates.xml}{1}[subtitle][none]
+}
+
+\fetchRss{https://www.eff.org/rss/updates.xml}{3}[none][title,link,updated]
+\end{lstlisting}
+
+Note that \texttt{none} is not a special keyword, if there was a key called \texttt{none} then its value would be printed to the PDF-Document.
+
+\subsection{fetchImage}
+Here is an example how to embed an image and scaling it to have a width of 5cm:
+
+\begin{lstlisting}[language=tex]
+\fetchImage{https://i0.wp.com/spacenews.com/wp-content/uploads/2023/03/vorb-march2023.jpg}[5cm]
+\end{lstlisting}
+
+\end{document}


Property changes on: trunk/Master/texmf-dist/doc/lualatex/luahttp/luahttp-doc.tex
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp-display.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp-display.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp-display.lua	2023-06-12 20:16:13 UTC (rev 67348)
@@ -0,0 +1,278 @@
+--[[
+-- This module is part of the LuaHTTP package
+-- The purpose of this module is to correctly display the data reveived from the fetch module.
+]]
+
+local moduleName = display
+local M = {}
+
+---------- Dependencies ------------------------
+local fetch = require("luahttp-fetch")
+
+---------- Local variables ---------------------
+local tmp_image_counter = 0 -- Counter for image names
+
+---------- Helper functions --------------------
+
+--- Displays an image using LuaTeX img.write function.
+-- The image has to be saved first in order to be written to the PDF-Document using LuaTeX.
+-- @param data image data
+-- @param width optional width in cm
+-- @param height optional height in cm
+-- @see search_and_escape
+local function display_image(data, width, height)
+    local tmp_image_name = '/tmp/tmp_image' -- filename of image saved temporarly
+    tmp_image_name = tmp_image_name .. tmp_image_counter
+
+    local width = width or nil
+    local height = height or nil
+    local f = assert(io.open(tmp_image_name, 'wb'))
+    f:write(data)
+    f:close()
+
+    -- LuaTeX does not provide built-in image scaling functions
+    local image = img.new({filename = tmp_image_name, width = width, height = height})
+    if image then
+        img.write(image)
+        tmp_image_counter = tmp_image_counter + 1
+    end
+end
+
+--- Prompts the user to display an image.
+-- If an image-URL is detected the user is asked to display the image or the plain URL.
+-- @see is_image_url
+local function prompt_user()
+    while true do
+        print("Do you want to display the image? (y/n)")
+        local answer = io.read()
+
+        if answer == 'y' then
+            return true
+        elseif answer == 'n' then
+            return false
+        else
+            print("Invalid answer. Please enter 'y' or 'n'.")
+        end
+    end
+end
+
+--- Searches the given URL for image extensions.
+-- @param url some URL
+-- @return true if an image extension was found, false otherwise
+-- @see search_and_escape
+local function is_image_url(url)
+    local image_extensions = { "jpg", "jpeg", "png", "gif" }
+    for _, ext in ipairs(image_extensions) do
+        if string.match(url, "%." .. ext) then
+            return true
+        end
+    end
+    return false
+end
+
+--- Searches the given value for special characters that cause problems in LaTeX-Documents.
+-- @param value single value of a table
+-- @return if no special characters where found the value is retured unchanged,
+-- if special characters where found the escaped value is returned,
+-- if an image-URL is detected and the user chooses to display that image nil is returned
+-- @see is_image_url, prompt_user
+local function search_and_escape(value)
+    local value = tostring(value)
+    if string.find(value, "^http") then
+        if is_image_url(value) then
+            print("\nLooks like this URL leads to an image: " .. value)
+            if prompt_user() then
+                local body = fetch.image(value)
+                display_image(body, "5cm")
+                return nil
+            else
+                print("\nEscaping URL: " .. value)
+                value = [[\url{]] .. value .. [[}]]
+            end
+        else
+            print("\nEscaping URL: " .. value)
+            value = [[\url{]] .. value .. [[}]]
+        end
+    else
+        local latex_special_chars = '([%%$%{%}&%#_%^%~])'
+        value = value:gsub(latex_special_chars, "\\%1")
+    end
+    return value
+end
+
+--- Prints a table to stdout.
+-- @param t tagle to print
+-- @param indent optional string used for indents
+local function print_table(t, indent)
+    indent = indent or ""
+    for k, v in pairs(t) do
+        if type(v) == "table" then
+            print(indent .. k .. ":")
+            print_table(v, indent .. "  ")
+        else
+            print(indent .. k .. ": " .. tostring(v))
+        end
+    end
+end
+
+--- Converts a table to text which can be written to the PDF-Document
+-- The values of the table are first searched for special characters.
+-- @param tbl table to be converted
+-- @return text
+-- @see search_and_escape
+local function table_to_text(tbl)
+    local results = {}
+    for k, v in pairs(tbl) do
+        if type(v) == "table" then
+            table.insert(results, table_to_text(v))
+        else
+            v = search_and_escape(v)
+            if v then
+                table.insert(results, v .. " \\\\ ")
+            end
+        end
+    end
+    return table.concat(results, " \\ ")
+end
+
+--- Check if a table contains a certain value.
+-- @param table input table
+-- @param target_value value to be searched
+-- @return true if value was found, false otherwise
+local function table_contains(table, target_value)
+    for _, value in pairs(table) do
+        if type(value) == "table" then
+            table_contains(value, target_value)
+        elseif value == target_value then
+            return true
+        end
+    end
+    return false
+end
+
+--- Filter out table entries that are not in the provided target keys.
+-- @param input_table
+-- @param target_keys array of target keys
+-- @param results used for recursion
+-- @return return a new table containing only the target keys and their values
+local function filter_table(input_table, target_keys, results)
+    local results = results or {}
+
+    for _, target_key in ipairs(target_keys) do
+        for key, value in pairs(input_table) do
+            if type(value) == "table" then
+                filter_table(value, target_keys, results)
+            elseif tostring(key) == target_key then
+                if not table_contains(results, value) then
+                    table.insert(results, value)
+                    break
+                end
+            end
+        end
+    end
+    return results
+end
+
+--- Converts a string containing a comma seperated list of elements to an array (ipairs).
+-- @param str input string
+-- @return table containing the elements as values
+local function string_to_ipairs(str)
+    local t = {}
+    for value in string.gmatch(str, "([^,]+)") do
+        table.insert(t, value)
+    end
+    return t
+end
+
+---------- Module functions --------------------
+
+--- Reads the contents of a JSON-file, filters the response and prints the result to the PDF-Document.
+-- @param json_file_path path to the JSON-file
+-- @param keys optional keys to filter out the relevant values from the response
+function M.json_using_file(json_file_path, keys)
+    local data = fetch.json_using_file(json_file_path)
+    print_table(data)
+    if keys then
+        local keys = string_to_ipairs(tostring(keys))
+        local values = filter_table(data, keys)
+        tex.sprint(table_to_text(values))
+    else
+        tex.sprint(table_to_text(data))
+    end
+end
+
+--- Prints the response filtered by the keys to the PDF-Document.
+-- @param url URL of the API
+-- @param keys optional keys to filter out the relevant values from the response
+function M.json(url, keys)
+    local data = fetch.json(tostring(url))
+    print_table(data)
+    if keys then
+        local keys = string_to_ipairs(tostring(keys))
+        local values = filter_table(data, keys)
+        tex.sprint(table_to_text(values))
+    else
+        tex.sprint(table_to_text(data))
+    end
+end
+
+--- Print an image to the PDF-Document.
+-- @param url URL of the image
+-- @param width optional width in cm
+-- @param height optional height in cm
+function M.image(url, width, height)
+    local data = fetch.image(tostring(url))
+    display_image(data, width, height)
+end
+
+--- Print values from an rss-feed to the PDF-Document.
+-- @param url URL of the feed
+-- @param limit limits the amount of entries that get printed to the PDF-Document
+-- @param feed_info_keys keys used to filter the feed information
+-- @param entry_keys keys used to filter the feed entries
+function M.rss(url, limit, feed_info_keys, entry_keys)
+    local data = fetch.rss(tostring(url))
+
+    if feed_info_keys then
+        local feed_info_keys = string_to_ipairs(tostring(feed_info_keys))
+        local feed = data.feed
+        local feed_info_filtered = filter_table(feed, feed_info_keys)
+
+        tex.sprint(table_to_text(feed_info_filtered))
+    end
+
+    local entries = {}
+
+    for i = 1, limit do
+        if data.entries[i] then
+            table.insert(entries, data.entries[i])
+        end
+    end
+
+    if entry_keys then
+        local entry_keys = string_to_ipairs(tostring(entry_keys))
+        local entries_filtered = filter_table(entries, entry_keys)
+
+        print_table(entries_filtered)
+        tex.sprint(table_to_text(entries_filtered))
+    else
+        tex.sprint(table_to_text(entries))
+    end
+end
+
+--- Print the reponse from a request using query parameters to the PDF-Document.
+-- @param url URL of the API
+-- @param keys keys to filter out the relevant values
+-- @param ... multiple optional query parameters used in the request
+function M.json_using_query(url, keys, ...)
+    local query_parameters = { ... }
+    local data = fetch.json_using_query(url, query_parameters)
+
+    print_table(data)
+
+    local keys = string_to_ipairs(tostring(keys))
+    local values = filter_table(data, keys)
+    tex.sprint(table_to_text(values))
+end
+
+return M


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp-display.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp-fetch.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp-fetch.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp-fetch.lua	2023-06-12 20:16:13 UTC (rev 67348)
@@ -0,0 +1,199 @@
+--[[
+-- This module is part of the LuaHTTP package
+-- The purpose of this module is to make HTTP requests and return the response.
+--
+-- Dependencies:
+--  dkjson
+--  luasec
+--  ltn12
+--  feedparser
+]]
+
+local moduleName = fetch
+local M = {}
+
+---------- Dependencies ------------------------
+local http = require("socket.http")
+local urlsocket = require("socket.url")
+local https = require("ssl.https")
+local dkjson = require("dkjson")
+local ltn12 = require("ltn12")
+local feedparser = require("feedparser")
+
+---------- Local variables ---------------------
+
+---------- Helper functions --------------------
+
+--- Makes an HTTP request using the provided request parameter.
+-- @param request table containing the request parameters
+-- @return the response as a table
+local function http_request(request)
+    local url = request.url
+    print("\nConnecting to " .. url)
+
+    -- Detect HTTPS
+    local client = http
+    if url:lower():find("^https://") then
+        client = https
+    end
+
+    -- Save optional body
+    local body = request.body
+
+    -- Prepare request
+    local response = {}
+    local request = {
+        method = request.method or "GET",
+        url = url,
+        headers = request.headers or nil,
+        redirect = request.redirect or false,
+        sink = ltn12.sink.table(response)
+    }
+
+    -- Send optional body
+    if body then
+        if type(body) == "table" then
+            body = dkjson.encode(body)
+        end
+        request.source = ltn12.source.string(body)
+        request.headers["Content-Length"] = #body
+    end
+
+    -- Make the request
+    local response_status, response_code, response_header, response_message = client.request(request)
+
+    local message = response_message or "(No response message recieved)"
+
+    if response_status == nil then
+        error("\n!!! Error connecting to " .. url .. "\nResponse: " .. response_code .. "\nMessage: " .. message)
+    end
+
+    -- Check for redirects and return body
+    if response_code == 301 or response_code == 302 or response_code == 303 then
+        print("\nResponse " .. message)
+        local redirect_url = response_header["location"]
+        if redirect_url == url then
+            error("\n!!! Error connecting to " .. url .. " results in a redirection loop")
+        else
+            print("\n!! Warning: redirecting to " .. redirect_url)
+            request.url = redirect_url
+            return http_request(request)
+        end
+    elseif response_code == 200 then
+        print("\nResponse " .. message)
+        if response == null or not next(response) then
+            error("\n!!! Error empty response")
+        end
+        return response
+    else
+        error("\n!!! Error connecting to " .. url .. "\nResponse: " .. response_code .. "\nMessage: " .. message)
+    end
+end
+
+--- Parse the given JSON-file.
+-- @param file_path path to JSON-file
+-- @return table containing the JSON data
+local function parse_json_file(file_path)
+    local file = io.open(file_path, "r")
+    local content = file:read("*all")
+    file:close()
+    return dkjson.decode(content)
+end
+
+--- Split a given string on the first occurence of a given character.
+-- @param str string containing the given character
+-- @param char target character at which the string gets split
+-- @return table containing the first part of the string as the key and the second part as the value
+local function split_first(str, char)
+    local result = {}
+    local pos = str:find(char)
+    local key = str:sub(1, pos - 1)
+    local value = str:sub(pos + 1)
+    result[key] = value
+    return result
+end
+
+---------- Module functions --------------------
+
+--- Make a GET request using the provided URL
+-- @param url target URL
+-- @return table containg the response
+function M.json(url)
+    local request = {
+        method = "GET",
+        url = url,
+        headers = {
+            ["Accept"] = "application/json"
+        },
+    }
+    local response = http_request(request)
+    return dkjson.decode(table.concat(response))
+end
+
+--- Make a request using the provided JSON-file
+-- @param json_file_path path to JSON-file
+-- @return table containg the response
+function M.json_using_file(json_file_path)
+    print("\nUsing file " .. json_file_path)
+    local request = parse_json_file(json_file_path)
+    local response = http_request(request)
+    return dkjson.decode(table.concat(response))
+end
+
+--- Make a GET request using the provided URL
+-- @param url target URL
+-- @return table containg the response
+function M.rss(url)
+    local request = {
+        method = "GET",
+        url = url,
+        headers = {
+            ["Accept"] = "application/rss+xml"
+        },
+    }
+    local response = http_request(request)
+    return feedparser.parse(table.concat(response))
+end
+
+--- Fetch image data using the provided URL
+-- @param url target URL leading to an image
+-- @return image data
+function M.image(url)
+    local request = {
+        method = "GET",
+        url = url
+    }
+    local response = http_request(request)
+    return table.concat(response)
+end
+
+--- Make a POST request using the provided URL and query parameters
+-- @param url target URL
+-- @param query_parameters parameters sent in the URL
+-- @return table containg the response
+function M.json_using_query(url, query_parameters)
+    local url = url
+    for _, value in ipairs(query_parameters) do
+        local params = split_first(value, "=")
+        for k, v in pairs(params) do
+            v = string.gsub(v, "\n", "")
+            url = url .. k .. "=" .. urlsocket.escape(v)
+        end
+    end
+
+    print("\nURL: " .. url)
+
+    local request = {
+        method = "POST",
+        url = url,
+        headers = {
+            ["Accept"] = "application/json",
+            ["Content-Type"] = "application/x-www-form-urlencoded";
+        },
+        redirect = false
+    }
+    local response = http_request(request)
+    return dkjson.decode(table.concat(response))
+end
+
+return M


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp-fetch.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp.sty	2023-06-12 20:16:13 UTC (rev 67348)
@@ -0,0 +1,91 @@
+%% luahttp.sty
+%% Copyright 2023 Johannes Casaburi
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+%   https://www.latex-project.org/lppl.txt
+% and version 1.3c or later is part of all distributions of LaTeX
+% version 2008 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Johannes Casaburi (johannes.casaburi at protonmail.com).
+%
+% This work consists of the files luahttp.sty, display.lua and fetch.lua.
+
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{luahttp}[LuaHTTP Package, Version 1.0.1]
+
+\RequirePackage{ifluatex}
+\RequirePackage{url}
+\RequirePackage{xparse}
+
+\ifluatex
+    \RequirePackage{luapackageloader}
+    \directlua{
+        version = 5.3
+        package.path = 'lua_modules/share/lua/' .. version .. '/?.lua;lua_modules/share/lua/' .. version .. '/?/init.lua;' .. package.path
+        package.cpath = 'lua_modules/lib/lua/' .. version .. '/?.so;' .. package.cpath
+        display = require("luahttp-display")
+    }
+
+    % \fetchJson{URL}[optional: "key1,key2,.."]
+    \NewDocumentCommand{\fetchJson}{m o}{
+        \IfNoValueTF{#2}
+            {\directlua{display.json("\luaescapestring{#1}")}} % without arg 2
+            {\directlua{display.json("\luaescapestring{#1}", "\luaescapestring{#2}")}}
+    }
+
+    % \fetchJsonUsingFile{JSON file}[optional: "key1,key2,.."]
+    \NewDocumentCommand{\fetchJsonUsingFile}{m o}{
+        \IfNoValueTF{#2}
+            {\directlua{display.json_using_file("\luaescapestring{#1}")}} % without arg 2
+            {\directlua{display.json_using_file("\luaescapestring{#1}", "\luaescapestring{#2}")}}
+    }
+
+    % \fetchJsonUsingQuery{URL}{"key1,key2,.."} [optional: "queryparameter1=value1"] .. [optional: "queryparameter5=value5"]
+    \NewDocumentCommand{\fetchJsonUsingQuery}{m m o o o o o}{
+        \IfNoValueTF{#7}
+            {\IfNoValueTF{#6}
+                {\IfNoValueTF{#5}
+                    {\IfNoValueTF{#4}
+                        {\IfNoValueTF{#3}
+                            {\directlua{display.json_using_query("\luaescapestring{#1}", "\luaescapestring{#2}")}}
+                            {\directlua{display.json_using_query("\luaescapestring{#1}", "\luaescapestring{#2}", "\luaescapestring{#3}")}}
+                        }
+                        {\directlua{display.json_using_query("\luaescapestring{#1}", "\luaescapestring{#2}", "\luaescapestring{#3}", "\luaescapestring{#4}")}}
+                    }
+                    {\directlua{display.json_using_query("\luaescapestring{#1}", "\luaescapestring{#2}", "\luaescapestring{#3}", "\luaescapestring{#4}", "\luaescapestring{#5}")}}
+                }
+                {\directlua{display.json_using_query("\luaescapestring{#1}", "\luaescapestring{#2}", "\luaescapestring{#3}", "\luaescapestring{#4}", "\luaescapestring{#5}", "\luaescapestring{#6}")}}
+            }
+            {\directlua{display.json_using_query("\luaescapestring{#1}", "\luaescapestring{#2}", "\luaescapestring{#3}", "\luaescapestring{#4}", "\luaescapestring{#5}", "\luaescapestring{#6}",  "\luaescapestring{#7}")}}
+    }
+
+    % \fetchRss{URL}{limit}[optional: "feedinfokey1,feedinfokey2,.."][optional: "entrykey1,entrykey2,.."]
+    \NewDocumentCommand{\fetchRss}{m m o o}{
+        \IfNoValueTF{#4}
+            {\IfNoValueTF{#3}
+                {\directlua{display.rss("\luaescapestring{#1}", #2)}}
+                {\directlua{display.rss("\luaescapestring{#1}", #2, "\luaescapestring{#3}")}}
+            } % without arg 4
+            {\directlua{display.rss("\luaescapestring{#1}", #2, "\luaescapestring{#3}", "\luaescapestring{#4}")}
+        }
+    }
+
+    %\fetchImage{URL}[optional: width][optional: height]
+    \NewDocumentCommand{\fetchImage}{m o o}{
+        \IfNoValueTF{#2}
+            {\directlua{display.image("\luaescapestring{#1}")}} % whithout arg 2
+            {\IfNoValueTF{#3}
+                {\directlua{display.image("\luaescapestring{#1}", "\luaescapestring{#2}")}} % without arg 3
+                {\directlua{display.image("\luaescapestring{#1}", "\luaescapestring{#2}", "\luaescapestring{#3}")}}
+            }
+    }
+
+\else
+    \PackageError{luatexhttp}{LuaTeX is required}\@ehd
+    \expandafter\endinput % abort early
+\fi


Property changes on: trunk/Master/texmf-dist/tex/lualatex/luahttp/luahttp.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/tlpkg/bin/tlpkg-ctan-check
===================================================================
--- trunk/Master/tlpkg/bin/tlpkg-ctan-check	2023-06-12 20:15:41 UTC (rev 67347)
+++ trunk/Master/tlpkg/bin/tlpkg-ctan-check	2023-06-12 20:16:13 UTC (rev 67348)
@@ -515,7 +515,7 @@
     lua-tinyyaml lua-typo lua-uca lua-ul
     lua-uni-algos lua-visual-debug lua-widow-control luaaddplot
     luabibentry luabidi luacas luacensor luacode luacolor luacomplex
-    luafindfont luagcd luahyphenrules
+    luafindfont luagcd luahttp luahyphenrules
     luaimageembed luaindex luainputenc luaintro luakeys
     lualatex-doc lualatex-doc-de
     lualatex-math lualatex-truncate lualibs lualinalg luamathalign luamaths

Modified: trunk/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
===================================================================
--- trunk/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc	2023-06-12 20:15:41 UTC (rev 67347)
+++ trunk/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc	2023-06-12 20:16:13 UTC (rev 67348)
@@ -44,6 +44,7 @@
 depend luacolor
 depend luacomplex
 depend luagcd
+depend luahttp
 depend luahyphenrules
 depend luaimageembed
 depend luaindex

Added: trunk/Master/tlpkg/tlpsrc/luahttp.tlpsrc
===================================================================


More information about the tex-live-commits mailing list.