summaryrefslogtreecommitdiff
path: root/macros/luatex
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2022-12-13 03:02:44 +0000
committerNorbert Preining <norbert@preining.info>2022-12-13 03:02:44 +0000
commit7b8bd496c62a5a8e18fb7a038217d4118c02b1e2 (patch)
treeef531a0b7c481a8139f7603155e33684ad95e40e /macros/luatex
parentd9c472147310b678beb9f80f0e90fc82f8825ec5 (diff)
CTAN sync 202212130302
Diffstat (limited to 'macros/luatex')
-rw-r--r--macros/luatex/latex/luaquotes/README3
-rw-r--r--macros/luatex/latex/luaquotes/luaquotes-documentation.pdfbin111052 -> 113378 bytes
-rw-r--r--macros/luatex/latex/luaquotes/luaquotes-documentation.tex320
-rw-r--r--macros/luatex/latex/luaquotes/luaquotes.sty58
-rw-r--r--macros/luatex/latex/scikgtex/LICENSE21
-rw-r--r--macros/luatex/latex/scikgtex/README21
-rw-r--r--macros/luatex/latex/scikgtex/scikgtex.lua685
-rw-r--r--macros/luatex/latex/scikgtex/scikgtex.sty149
8 files changed, 1243 insertions, 14 deletions
diff --git a/macros/luatex/latex/luaquotes/README b/macros/luatex/latex/luaquotes/README
index b4095e8a56..963efc971d 100644
--- a/macros/luatex/latex/luaquotes/README
+++ b/macros/luatex/latex/luaquotes/README
@@ -1,6 +1,6 @@
luaquotes - smart quotes with lua
-version: 1.2.1
+version: 1.2.2
This package automatically creates smart quotes using lua filters and provides auxiliary commands for typesetting specific lua punctuation
@@ -12,6 +12,7 @@ For suggestions, feature requests, and bug reports, please use the project's
GitHub site: https://github.com/ezgranet/luaquotes
Version history:
+12/12/2022-added degm function
23/11/2022-walking back automatic elision for now
25/10/2022—support for elision
30/08/2022—resolving bug with language versions
diff --git a/macros/luatex/latex/luaquotes/luaquotes-documentation.pdf b/macros/luatex/latex/luaquotes/luaquotes-documentation.pdf
index 4d5e961eac..991ae4c461 100644
--- a/macros/luatex/latex/luaquotes/luaquotes-documentation.pdf
+++ b/macros/luatex/latex/luaquotes/luaquotes-documentation.pdf
Binary files differ
diff --git a/macros/luatex/latex/luaquotes/luaquotes-documentation.tex b/macros/luatex/latex/luaquotes/luaquotes-documentation.tex
index 3a92f1df05..f77f8f956a 100644
--- a/macros/luatex/latex/luaquotes/luaquotes-documentation.tex
+++ b/macros/luatex/latex/luaquotes/luaquotes-documentation.tex
@@ -80,6 +80,7 @@ breaklines,
{latex}
\usepackage[fr]{luaquotes} %French
\usepackage[de]{luaquotes} % German
+\usepackage[degm]{luaquotes} % German with French-style quotes
\end{minted}
\subsubsection{English Features}
The English features are designed to smartly recognise English punctuation:
@@ -110,7 +111,7 @@ The French option produces the following output, including the extra space aroun
\end{tabular}
\end{center}
\subsubsection{The German option}
-The German option produces the following outpu:
+The German option produces the following output:
\begin{center}
\renewcommand{\arraystretch}{2}
@@ -123,6 +124,20 @@ The German option produces the following outpu:
\end{tabular}
\end{center}
+\subsubsection{The German (\texttt{degm}) option}
+The German (\texttt{degm}) option produces the following output:
+
+\begin{center}
+\renewcommand{\arraystretch}{2}
+\begin{tabular}{cc}
+\ttfamily User input &Output\\
+\LARGE\texttt{"Hallo!"}%
+& \LARGE \degmdouble Hallo!\degmtr\\
+\LARGE\texttt{'Hallo!'}%
+& \LARGE \degmsingle Hallo!\desgmtr\\
+
+\end{tabular}
+\end{center}
@@ -140,6 +155,7 @@ breaklines,
\dumbquotes %English
\frdumbquotes %French
\dedumbquotes %German
+\degmdumbquotes %German with French-style quotes
\end{minted}
The following commands re-activate the smart quotes function:
@@ -155,12 +171,13 @@ breaklines,
\smartquotes %English
\frsmartquotes %French
\desmartquotes % German
+\degmsmartquotes % German with French-style quotes
\end{minted}
A limitation on the (de-)activation of the package is that the Lua filters will not deactivate within the same paragraph, so the function can only be changed across paragraphs.
\subsection{Monospace}
-As a general rule, smart quotes are rather undesirable in monospace text, and therefore, within the \color{darkspringgreen}\verb! \texttt!\color{black} environment the package does not apply smart quotes. Thus, the same input produces in roman face \textcolor{darkspringgreen}{"Hello World"} but in monospace \texttt{"Hello World"}.
+As a general rule, smart quotes are rather undesirable in monospace text, and therefore, within the \color{darkspringgreen} \verb!\texttt!\color{black}\ environment the package does not apply smart quotes. Thus, the same input produces in roman face \textcolor{darkspringgreen}{"Hello World"} but in monospace \texttt{"Hello World"}.
As the example above shows, the default behaviour of this package forces straight quotes in monospace, and disables \TeX\ quote ligatures (but not other \TeX\ ligatures) to do so, on the assumption that any form of curved quotes are undesirable.
@@ -201,10 +218,12 @@ Single low quote & \ttfamily U+201A & \verb!\desingle! & \Huge\desingle\thebox\\
Right double quote & \ttfamily U+201D & \verb!\sqtworight! & \Huge\thebox\sqtworight\\
Left guillemet [w/ space]& \ttfamily U+00AB & \verb!\glmtl! & \Huge\glmtl\thebox\\
Right guillemet [w/ space]& \ttfamily U+00BB & \verb!\glmtr! & \Huge\thebox\glmtr\\
-Single left guillemet [w/ space]& \ttfamily U+2039 & \verb!\sglmtl! & \Huge\sglmtl\thebox\\
-
-Single right guillemet [w/ space]& \ttfamily U+203A & \verb!\sglmtr! & \Huge\thebox\sglmtr\\
-
+ \texttt{degm} left guillemet & \ttfamily U+00BB & \verb!\degmtl! & \Huge\degmtl\thebox\\
+ \texttt{degm} right guillemet & \ttfamily U+00AB & \verb!\degmtr! & \Huge\thebox\degmtr\\
+ Single left guillemet [w/ space]& \ttfamily U+2039 & \verb!\sglmtl! & \Huge\sglmtl\thebox\\
+ Single right guillemet [w/ space]& \ttfamily U+203A & \verb!\sglmtr! & \Huge\thebox\sglmtr\\
+ \texttt{degm} single left guillemet & \ttfamily U+203A & \verb!\desgmtl! & \Huge\desgmtl\thebox\\
+ \texttt{degm} single right guillemet & \ttfamily U+2039 & \verb!\desgmtr! & \Huge\thebox\desgmtr\\
\end{longtable}
\end{center}
@@ -236,7 +255,7 @@ For typesetting US/Imperial measurements like feet and inches, the correct symbo
-For certain Polynesian langauges, a letter called the \okina Okina is used; while this appears identical to a left single quotation mark in many fonts, it is encoded differently in Unicode because it is properly a letter, not a punctuation mark.
+For certain Polynesian languages, a letter called the \okina Okina is used; while this appears identical to a left single quotation mark in many fonts, it is encoded differently in Unicode because it is properly a letter, not a punctuation mark.
@@ -254,7 +273,7 @@ For certain Polynesian langauges, a letter called the \okina Okina is used; whil
\section{Future Development and Localisation}
- The package's online repository is the best place to report bugs, feature requests, or other contributions, and is located at: \\\url{github.com/ezgranet/luaquotes}.
+ The package's online repository is the best place to report bugs, feature requests, or other contributions, and is located at: \\\url{https://github.com/ezgranet/luaquotes}.
One obvious point of future development is the addition of other language localisations; this is technologically easy in principle but difficult for me to accomplish without assistance because it requires detailed knowledge of typographic conventions for any given language standard, and therefore the aid of contributors and collaborators. \section{Licence}
This project is licensed under the Latex Public Project Licence version 1.3\textit{c}. This documentation is copyright of the author but licensed under CC-BY-SA 3.0.
@@ -829,7 +848,10 @@ firstnumber=last
\frsmartquotes
\frdumbquotes
\desmartquotes
-\dedumbquotes\smartquotes}
+\dedumbquotes
+\degmsmartquotes
+\degmdumbquotes
+\smartquotes}
\renewcommand{\texttt}[1]{{\ttfamily\addfontfeature{RawFeature={+qtbye,-tlig}} #1}}
}
@@ -1195,6 +1217,283 @@ firstnumber=last
\renewcommand{\texttt}[1]{{\ttfamily\addfontfeature{RawFeature={+qtbye,-tlig}} #1}}
}
\end{minted}
+ \subsection{The German with French-style quotes (\texttt{degm}) option}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{latex}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% SCHÖNERES DEUTSCH
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\luaexec{
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{lua}
+function degmdoublequotes ( s )
+ return ( s:gsub ( '"(..-)"' , "»\%1«" ) )
+ end
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{latex}
+ }
+\luaexec{
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{lua}
+function degmsinglequotelinestart ( s )
+ return (s:gsub ("^'","›" ) )
+ end
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{latex}
+ }
+\luaexec{
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{lua}
+function degmsinglequotesclose( s )
+return ( s:gsub ( " '(..-)'", " ‚\%1‹" ) )
+ end
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{latex}
+ }
+
+%% Two utility macros to activate/deactivate the Lua function:
+\newcommand\degmdoublequoteson{\directlua{
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{lua}
+luatexbase.add_to_callback (
+ "process_input_buffer" , degmdoublequotes , "degmdoublequotes" )
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{latex}
+ }}
+\newcommand\degmdoublequotesoff{\directlua{luatexbase.remove_from_callback (
+ "process_input_buffer" , "degmdoublequotes" )
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{latex}
+}}
+\newcommand\degmsinglequotelinestarton{\directlua{
+\end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{lua}
+luatexbase.add_to_callback (
+ "process_input_buffer" , degmsinglequotelinestart , "degmsinglequotelinestart" )\end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{latex}
+ }}
+\newcommand\degmsinglequotelinestartoff{\directlua{
+\end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{lua}
+luatexbase.remove_from_callback (
+ "process_input_buffer" , "degmsinglequotelinestart" )
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{latex}
+ }}
+\newcommand\degmsinglequotescloseon{\directlua{
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{lua}
+ luatexbase.add_to_callback (
+ "process_input_buffer" , degmsinglequotesclose , "degmsinglequotesclose" )
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{latex}
+ }}
+\newcommand\degmsinglequotescloseoff{\directlua{
+\end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{lua}
+luatexbase.remove_from_callback (
+ "process_input_buffer" , "degmsinglequotesclose" )
+ \end{minted}
+ \begin{minted}[
+frame=lines,
+framesep=2mm,
+baselinestretch=1.2,
+bgcolor=LightGray,
+fontsize=\footnotesize,
+linenos,
+breaklines,
+firstnumber=last
+]
+{latex}
+ }}
+ \newcommand{\degmsmartquotes}{\degmdoublequoteson
+\degmsinglequotelinestarton
+\degmsinglequotescloseon}
+ \newcommand{\degmdumbquotes}{\degmdoublequotesoff
+\degmsinglequotelinestartoff
+\degmsinglequotescloseoff}
+ \DeclareOption{degm}{
+\AtBeginDocument{
+\frsmartquotes
+\frdumbquotes
+\smartquotes
+\dumbquotes\dumbquotes
+\degmsmartquotes}
+\renewcommand{\texttt}[1]{{\ttfamily\addfontfeature{RawFeature={+qtbye,-tlig}} #1}}
+}
+ \end{minted}
\subsection{The French option}
\begin{minted}[
frame=lines,
@@ -1502,6 +1801,9 @@ firstnumber=last
\end{minted}
\section{Version History}
+\subsection{\normalfont\texttt{1.2.2}}
+\ttfamily Added the "degm" option
+
\subsection{\normalfont\texttt{1.2.1}}
\ttfamily Suspended automatic elision support due to implementation issues
diff --git a/macros/luatex/latex/luaquotes/luaquotes.sty b/macros/luatex/latex/luaquotes/luaquotes.sty
index f26758b909..c07785bcd6 100644
--- a/macros/luatex/latex/luaquotes/luaquotes.sty
+++ b/macros/luatex/latex/luaquotes/luaquotes.sty
@@ -1,6 +1,6 @@
-\def\luaquotesversionnumber{1.2.1}
+\def\luaquotesversionnumber{1.2.2}
\ProvidesPackage{luaquotes}
- [2022/11/23\luaquotesversionnumber smart quotes with lua]
+ [2022/12/09\luaquotesversionnumber smart quotes with lua]
% !TeX program = lualatex
% !TeX encoding = utf8
% This work may be distributed and/or modified under the
@@ -153,7 +153,7 @@ luatexbase.add_to_callback (
\singlequotesoff}
\DeclareOption{en}{
-\AtBeginDocument{\frsmartquotes\frdumbquotes\desmartquotes\dedumbquotes\smartquotes}
+\AtBeginDocument{\frsmartquotes\frdumbquotes\desmartquotes\dedumbquotes\degmsmartquotes\degmdumbquotes\smartquotes}
\renewcommand{\texttt}[1]{{\ttfamily\addfontfeature{RawFeature={+qtbye,-tlig}} #1}}
}
@@ -180,6 +180,8 @@ luatexbase.add_to_callback (
%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newcommand{\desingle}{{\addfontfeature{RawFeature={-qtbye,-tlig}}\symbol{"201A}}}
\newcommand{\dedouble}{{\addfontfeature{RawFeature={-qtbye,-tlig}}\symbol{"201E}}}
+\newcommand{\degmsingle}{{\addfontfeature{RawFeature={-qtbye,-tlig}}\symbol{"203A}}}
+\newcommand{\degmdouble}{{\addfontfeature{RawFeature={-qtbye,-tlig}}\symbol{"00BB}}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% backtick
@@ -227,6 +229,10 @@ luatexbase.add_to_callback (
\newcommand{\glmtr}{{\addfontfeature{RawFeature={-qtbye,-tlig}}\,»}}
\newcommand{\sglmtl}{{\addfontfeature{RawFeature={-qtbye,-tlig}}‹\,}}
\newcommand{\sglmtr}{{\addfontfeature{RawFeature={-qtbye,-tlig}}\,›}}
+\newcommand{\degmtl}{{\addfontfeature{RawFeature={-qtbye,-tlig}}»}}
+\newcommand{\degmtr}{{\addfontfeature{RawFeature={-qtbye,-tlig}}«}}
+\newcommand{\desgmtl}{{\addfontfeature{RawFeature={-qtbye,-tlig}}›}}
+\newcommand{\desgmtr}{{\addfontfeature{RawFeature={-qtbye,-tlig}}‹}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% DEUTSCH
@@ -273,6 +279,50 @@ return ( s:gsub ( " '(..-)'", " ‚\%1`" ) )
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% SCHÖNERES DEUTSCH
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\luaexec{function degmdoublequotes ( s )
+ return ( s:gsub ( '"(..-)"' , "»\%1«" ) )
+ end}
+\luaexec{function degmsinglequotelinestart ( s )
+ return (s:gsub ("^'","›" ) )
+ end}
+\luaexec{function degmsinglequotesclose( s )
+return ( s:gsub ( " '(..-)'", " ‚\%1‹" ) )
+ end}
+
+%% Two utility macros to activate/deactivate the Lua function:
+\newcommand\degmdoublequoteson{\directlua{luatexbase.add_to_callback (
+ "process_input_buffer" , degmdoublequotes , "degmdoublequotes" )}}
+\newcommand\degmdoublequotesoff{\directlua{luatexbase.remove_from_callback (
+ "process_input_buffer" , "degmdoublequotes" )}}
+\newcommand\degmsinglequotelinestarton{\directlua{luatexbase.add_to_callback (
+ "process_input_buffer" , degmsinglequotelinestart , "degmsinglequotelinestart" )}}
+\newcommand\degmsinglequotelinestartoff{\directlua{luatexbase.remove_from_callback (
+ "process_input_buffer" , "degmsinglequotelinestart" )}}
+ \newcommand\degmsinglequotescloseon{\directlua{luatexbase.add_to_callback (
+ "process_input_buffer" , degmsinglequotesclose , "degmsinglequotesclose" )}}
+\newcommand\degmsinglequotescloseoff{\directlua{luatexbase.remove_from_callback (
+ "process_input_buffer" , "degmsinglequotesclose" )}}
+ \newcommand{\degmsmartquotes}{\degmdoublequoteson
+\degmsinglequotelinestarton
+\degmsinglequotescloseon}
+ \newcommand{\degmdumbquotes}{\degmdoublequotesoff
+\degmsinglequotelinestartoff
+\degmsinglequotescloseoff}
+ \DeclareOption{degm}{
+\AtBeginDocument{
+\frsmartquotes
+\frdumbquotes
+\smartquotes
+\dumbquotes\dumbquotes
+\degmsmartquotes}
+\renewcommand{\texttt}[1]{{\ttfamily\addfontfeature{RawFeature={+qtbye,-tlig}} #1}}
+}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Français
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -308,7 +358,7 @@ return ( s:gsub ( " '(..-)'", " ‹\\,\%1\\,›" ) )
\frsinglequotelinestartoff
\frsinglequotescloseoff}
\DeclareOption{fr}{
-\AtBeginDocument{\desmartquotes\dedumbquotes\smartquotes\dumbquotes\dumbquotes\frsmartquotes}
+\AtBeginDocument{\desmartquotes\dedumbquotes\degmsmartquotes\degmdumpquotes\smartquotes\dumbquotes\dumbquotes\frsmartquotes}
\renewcommand{\texttt}[1]{{\ttfamily\addfontfeature{RawFeature={+qtbye,-tlig}} #1}}
diff --git a/macros/luatex/latex/scikgtex/LICENSE b/macros/luatex/latex/scikgtex/LICENSE
new file mode 100644
index 0000000000..bde9a445ef
--- /dev/null
+++ b/macros/luatex/latex/scikgtex/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Christof Bless
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/macros/luatex/latex/scikgtex/README b/macros/luatex/latex/scikgtex/README
new file mode 100644
index 0000000000..e754496c6f
--- /dev/null
+++ b/macros/luatex/latex/scikgtex/README
@@ -0,0 +1,21 @@
+SciKGTeX
+-------------
+AUTHOR
+ Christof Bless, christofbless@gmail.com
+
+DESCRIPTION
+ Scientific Knowledge Graph TeX (SciKgTeX) is a LuaLaTeX package which makes
+ it possible to annotate specific research contributions in scientific documents.
+ SciKGTeX will enrich the document by adding the marked contributions to PDF metadata
+ in a structured XMP format which can be picked up by Search Engines and
+ Knowledge Graphs.
+ More information on how to use the package can be found here:
+ https://github.com/Christof93/SciKGTeX#readme
+
+VERSION
+ 2.1.1 (13.11.2022)
+
+LICENSE
+ This work is published under MIT License. https://opensource.org/licenses/MIT
+
+ Copyright (C) 2022 Christof Bless
diff --git a/macros/luatex/latex/scikgtex/scikgtex.lua b/macros/luatex/latex/scikgtex/scikgtex.lua
new file mode 100644
index 0000000000..94bd0c1f40
--- /dev/null
+++ b/macros/luatex/latex/scikgtex/scikgtex.lua
@@ -0,0 +1,685 @@
+RANDOM_SEED = math.randomseed(os.time())
+MATRIX_AND = {{0,0},{0,1}}
+MATRIX_OR = {{0,1},{1,1}}
+HEXES = '0123456789abcdef'
+local SciKGTeX = {}
+SciKGTeX.whole_string = ""
+SciKGTeX.properties_used = {}
+SciKGTeX.property_commands = {}
+SciKGTeX.mandatory_properties = {
+ 'researchproblem',
+ 'objective',
+ 'method',
+ 'result',
+ 'conclusion'
+}
+SciKGTeX.PRODUCE_XMP_FILE = true
+SciKGTeX.WARNING_LEVEL = 1
+
+local XMP = {}
+XMP.lines = {}
+XMP.namespaces = {}
+XMP.property_ns = {}
+XMP.XMP_TOP = [[<x:xmpmeta xmlns:x="adobe:ns:meta/">]]
+XMP.XMP_BOTTOM = [[</rdf:RDF>
+</x:xmpmeta>]]
+XMP.PACKET_END = [[<?xpacket end="r"?>]]
+
+local UUID = {}
+
+---------------------------- utilities -------------------------------
+
+-- performs the bitwise operation specified by truth matrix on two numbers.
+function BITWISE(x, y, matrix)
+ local z = 0
+ local pow = 1
+ while x > 0 or y > 0 do
+ z = z + (matrix[x%2+1][y%2+1] * pow)
+ pow = pow * 2
+ x = math.floor(x/2)
+ y = math.floor(y/2)
+ end
+ return z
+end
+
+function INT2HEX(x)
+ local s,base,pow = '',16,0
+ local d
+ while x > 0 do
+ d = x % base + 1
+ x = math.floor(x/base)
+ s = string.sub(HEXES, d, d)..s
+ end
+ if #s == 1 then s = "0" .. s end
+ return s
+end
+
+function get_output_dir()
+ if arg ~= nil then
+ for k,v in ipairs(arg) do
+ val, is_output_argument = v:gsub('%-%-output%-directory=(.*)','%1')
+ if is_output_argument > 0 then
+ return val
+ end
+ end
+ return nil
+ end
+ return nil
+end
+
+function read_header_of_file(path)
+ local fh = io.open(path, "rb")
+ if fh then
+ local first_line = assert(fh:read())
+ fh:close()
+ return first_line
+ else
+ print ("No xmp metadata file found!")
+ return nil
+ end
+end
+
+function extract_uuid_from_header(header)
+ return header:gsub('.*id=\"(.-)\".*','%1')
+end
+
+function generate_UUID()
+ UUID:initialize('00:0c:29:69:41:c6')
+ return UUID:toString()
+end
+
+function string:split(sep)
+ if sep == nil then
+ sep = "%s"
+ end
+ local t = {}
+ for str in self:gmatch("([^"..sep.."]+)") do
+ table.insert(t, str)
+ end
+ return t
+end
+
+function spaces_to_underscores(s)
+ return s:gsub('%s+','_')
+end
+
+function remove_environments(s)
+ s,c = s:gsub('\\begin%s*{.-}{.-}%s*','')
+ s,c = s:gsub('\\begin%s*{.-}%s*','')
+ s,c = s:gsub('\\end%s*{.-}%s*','')
+ return s
+end
+
+function remove_any_latex_command(s)
+ s, c = s:gsub('\\%w+%s*%[%d*%]%s*{(.*)}','%1')
+ if c > 0 then
+ return remove_latex_commands(s)
+ end
+ s, c = s:gsub('\\%w+%s*{(.*)}','%1')
+ if c > 0 then
+ return remove_latex_commands(s)
+ end
+ s, c = s:gsub('\\%w+%s*','')
+ if c > 0 then
+ return remove_latex_commands(s)
+ end
+ return s
+end
+
+function find_last_occurence(s, repls)
+ occurences = {}
+ for pattern, repl in pairs(repls) do
+ i, j = s:find(pattern)
+ if i ~= nil then
+ table.insert(occurences, {i,j,pattern})
+ end
+ end
+ table.sort(occurences, function(l, r) return l[1]>r[1] end)
+ if #occurences > 0 then
+ return occurences[1]
+ else
+ return nil
+ end
+end
+
+function exhaustively_replace_last_occurence_of_pattern(s, repls)
+ last_occurence = find_last_occurence(s, repls)
+ if last_occurence ~= nil then
+ starts, ends, pattern = table.unpack(last_occurence)
+ to_replace = s:sub(starts,ends)
+ else
+ return s
+ end
+ new_string = s:sub(0,starts-1) .. to_replace:gsub(pattern, repls[pattern], 1) .. s:sub(ends+1)
+ return exhaustively_replace_last_occurence_of_pattern(new_string, repls)
+end
+
+function remove_latex_commands(s)
+ replacements = {
+ -- contribution with * and []
+ ['\\contribution%s*%*%s*%[%d*%]%s*{.-}{.*}%s*'] = '',
+ -- contribution with *
+ ['\\contribution%s*%*%s*{.-}%s*{.*}%s*'] = '',
+ -- contribution with []
+ ['\\contribution%s*%[%d*%]%s*{.-}{(.*)}'] = '%1',
+ -- contribution normal
+ ['\\contribution%s*{.-}%s*{(.*)}'] = '%1',
+
+ ['\\uri%s*{.-}%s*{(.*)}'] = '%1',
+ }
+ for cmd, used in pairs(SciKGTeX.property_commands) do
+ -- []
+ replacements['\\'.. cmd .. '%s*%[%d*%]%s*{(.*)}'] = '%1'
+ -- normal command
+ replacements['\\'.. cmd .. '%s*{(.*)}'] = '%1'
+ -- with * and []
+ replacements['\\'.. cmd .. '%s*%*%s*%[%d*%]%s*{.*}%s*'] = ''
+ -- with *
+ replacements['\\'.. cmd .. '%s*%*%s*{.*}%s*'] =''
+ end
+ s = remove_environments(s)
+ s = exhaustively_replace_last_occurence_of_pattern(s, replacements)
+ s = remove_any_latex_command(s)
+ -- remove escape chars
+ s = s:gsub('\\','')
+ return s
+end
+
+function uri_valid(s)
+ if s:find('http') ~= 1 then
+ return false
+ else
+ return true
+ end
+end
+
+function resolve_entity(s)
+ -- make sure the entity is only resolved at the innermost of nested commands.
+ for _, cmd in ipairs(SciKGTeX.mandatory_properties) do
+ if s:find('\\' .. cmd) then
+ return false
+ end
+ end
+ if s:find('\\contribution') then
+ return false
+ end
+ uri, found = s:gsub('.*\\uri%s*{(.-)}%s*{.*}.*', '%1')
+ if found == 1 then
+ label = s:gsub('.*\\uri%s*{.-}%s*{(.*)}.*', '%1')
+ entity = string.format('<rdf:Description rdf:about=\"%s\"><rdfs:label>%s</rdfs:label></rdf:Description>', uri, label)
+ return entity
+ else
+ uri, found = s:gsub('.*\\uri%s*{(.-)}.*', '%1')
+ if found == 1 then
+ entity = string.format('<rdf:Description rdf:about=\"%s\"></rdf:Description>', uri)
+ return entity
+ else
+ return false
+ end
+ end
+end
+
+---------------------------- UUID class methods -------------------------------------
+
+-- hwaddr is a string: hexes delimited by colons. e.g.: 00:0c:29:69:41:c6
+function UUID:initialize(hwaddr)
+ self._bytes = {
+ math.random(0, 255),
+ math.random(0, 255),
+ math.random(0, 255),
+ math.random(0, 255),
+ math.random(0, 255),
+ math.random(0, 255),
+ math.random(0, 255),
+ math.random(0, 255),
+ math.random(0, 255),
+ math.random(0, 255),
+ -- should come from mac address
+ tonumber(hwaddr:sub(1, 2), 16),
+ tonumber(hwaddr:sub(4, 5), 16),
+ tonumber(hwaddr:sub(7, 8), 16),
+ tonumber(hwaddr:sub(10, 11), 16),
+ tonumber(hwaddr:sub(13, 14), 16),
+ tonumber(hwaddr:sub(16, 17), 16)
+ }
+ -- set the version
+ self._bytes[7] = BITWISE(self._bytes[7], 0x0f, MATRIX_AND)
+ self._bytes[7] = BITWISE(self._bytes[7], 0x40, MATRIX_OR)
+ -- set the variant
+ self._bytes[9] = BITWISE(self._bytes[7], 0x3f, MATRIX_AND)
+ self._bytes[9] = BITWISE(self._bytes[7], 0x80, MATRIX_OR)
+ self._string = nil
+ end
+
+ -- lazy string creation.
+ function UUID:toString()
+ if self._string == nil then
+ self._string = INT2HEX(self._bytes[1])..INT2HEX(self._bytes[2])..INT2HEX(self._bytes[3])..INT2HEX(self._bytes[4]).."-"..
+ INT2HEX(self._bytes[5])..INT2HEX(self._bytes[6]).."-"..
+ INT2HEX(self._bytes[7])..INT2HEX(self._bytes[8]).."-"..
+ INT2HEX(self._bytes[9])..INT2HEX(self._bytes[10]).."-"..
+ INT2HEX(self._bytes[11])..INT2HEX(self._bytes[12])..INT2HEX(self._bytes[13])..INT2HEX(self._bytes[14])..INT2HEX(self._bytes[15])..INT2HEX(self._bytes[16])
+ end
+ return self._string
+ end
+
+---------------------------- Main class methods -------------------------------
+
+function SciKGTeX:set_warning_level(wl)
+ self.WARNING_LEVEL = wl
+end
+
+function SciKGTeX:warn(warning_message, ...)
+ if self.WARNING_LEVEL > 0 then
+ texio.write_nl("term and log",
+ [[Package SciKGTeX Warning: ]] .. string.format(warning_message, ...))
+ texio.write_nl("term and log","\n")
+ end
+end
+
+function SciKGTeX:error(warning_message, ...)
+ tex.error([[Package SciKGTeX Error: ]] .. string.format(warning_message, ...))
+end
+
+SciKGTeX.command_factory = {}
+
+SciKGTeX.command_factory.cmd_top = [[\newcommand{\%s}[2][]{]]
+
+SciKGTeX.command_factory.cmd_top_star = [[\WithSuffix\newcommand\%s*[2][]{]]
+
+SciKGTeX.command_factory.cmd_top_override = [[\renewcommand{\%s}[2][]{]]
+
+SciKGTeX.command_factory.cmd_top_star_override = [[\WithSuffix\renewcommand\%s*[2][]{]]
+
+SciKGTeX.command_factory.directlua_part = [[ \directlua{
+ local content = "\luaescapestring{\unexpanded{#2}}"
+ local belongs_to_contribution = "\luaescapestring{\unexpanded{#1}}"
+ SciKGTeX.XMP:add_annotation(belongs_to_contribution, '%s', content, 'annotation-id')
+ }]]
+
+SciKGTeX.command_factory.cmd_bottom = [[}]]
+SciKGTeX.command_factory.cmd_bottom_star = [[\ignorespaces}]]
+
+function SciKGTeX.command_factory:build_command(command_name)
+ full_cmd = self.cmd_top .. "\n" .. self.directlua_part .. "\n #2\n" .. self.cmd_bottom
+ formatted_cmd = string.format(full_cmd, command_name, command_name)
+ for i, line in ipairs(formatted_cmd:split("\n")) do
+ tex.print(line .. "%")
+ end
+end
+
+function SciKGTeX.command_factory:build_star_command(command_name)
+ full_cmd = self.cmd_top_star .. "\n" .. self.directlua_part .. "\n" .. self.cmd_bottom_star
+ formatted_cmd = string.format(full_cmd, command_name, command_name)
+ for i, line in ipairs(formatted_cmd:split("\n")) do
+ tex.print(line .. "%")
+ end
+end
+
+function SciKGTeX.command_factory:override_command(command_name)
+ full_cmd = self.cmd_top_override .. "\n" .. self.directlua_part .. "\n #2\n" .. self.cmd_bottom
+ formatted_cmd = string.format(full_cmd, command_name, command_name)
+ for i, line in ipairs(formatted_cmd:split("\n")) do
+ tex.print(line .. "%")
+ end
+end
+
+function SciKGTeX.command_factory:override_star_command(command_name)
+ full_cmd = self.cmd_top_star_override .. "\n" .. self.directlua_part .. "\n" .. self.cmd_bottom_star
+ formatted_cmd = string.format(full_cmd, command_name, command_name)
+ for i, line in ipairs(formatted_cmd:split("\n")) do
+ tex.print(line .. "%")
+ end
+end
+
+function SciKGTeX:make_new_command(new_property, namespace)
+ -- check if property already exists
+ if self.property_commands[new_property]~=nil then
+ self:warn([[Method newpropertycommand: Repeated definition.
+ Command %s already exists!
+ Are you sure you want to override it?]], new_property)
+ self:add_property(new_property, namespace)
+ self.command_factory:override_command(new_property, namespace)
+ self.command_factory:override_star_command(new_property, namespace)
+ else
+ self.property_commands[new_property] = false
+ self:add_property(new_property, namespace)
+ self.command_factory:build_command(new_property, namespace)
+ self.command_factory:build_star_command(new_property, namespace)
+ end
+end
+
+function SciKGTeX:add_property(new_property, namespace)
+ new_property = self.XMP:escape_xml_tags(new_property)
+ -- check if property already exists
+ if self.properties_used[new_property]~=nil then
+ self:warn([[Method addmetaproperty: Repeated definition.
+ Property %s already added!
+ Are you sure you want to replace it?]], new_property)
+ -- if not make it known to the object
+ else
+ self.properties_used[new_property] = false
+ end
+ ns_prefix = self.XMP:extract_namespace_prefix(namespace)
+ self.XMP.property_ns[new_property] = ns_prefix
+end
+
+function SciKGTeX:register_property(prop_type)
+ self.properties_used[prop_type] = true
+end
+
+function SciKGTeX:warn_unused_command()
+ warning_message = [[No %s annotation found!
+ Are you sure you don't want to mark an entity with %s?]]
+ for i, p in ipairs(self.mandatory_properties) do
+ used = self.properties_used[p]
+ if not used then
+ self:warn(warning_message, p, p);
+ end
+ end
+end
+
+function SciKGTeX:print_entity(uri, label)
+ if label ~= "" then
+ tex.print(string.format('\\href{%s}{%s}',uri , label))
+ else
+ tex.print(string.format('\\url{%s}',uri))
+ end
+end
+
+---------------------------- XMP class methods -------------------------------
+
+function XMP:escape_xml_tags(s)
+ s = spaces_to_underscores(s)
+ s, i = s:gsub('[^%a%d%.-_]','')
+ if i > 0 then
+ SciKGTeX:warn([[Method escape_xml_tags: Forbidden characters.
+ Property %s can only contain letters, digits, underscores, hyphens and periods!
+ Forbidden characters removed.]], s)
+ end
+ s, i = s:gsub('^([Xx][Mm][Ll])','_%1')
+ if i > 0 then
+ SciKGTeX:warn([[Method escape_xml_tags: Forbidden characters.
+ Property %s can not start with xml!
+ Changed to _xml.]], s)
+ end
+ return s
+end
+
+function XMP:escape_xml_content(s)
+ s = s:gsub('&', '&amp;')
+ s = s:gsub('>', '&gt;')
+ return s:gsub('<', '&lt;')
+end
+
+function XMP:add_line(...)
+ table.insert(self.lines, string.format(...))
+end
+
+function XMP:add_paper_node(paper_iri)
+ self.paper = {}
+ self.paper.contributions = {}
+ self.paper.id = paper_iri
+ self.paper.title = nil
+ self.paper.authors = {}
+ self.paper.researchfield = nil
+end
+
+function XMP:add_contribution(key, contribution_iri)
+ local contribution = {}
+ contribution.properties = {}
+ contribution.id = contribution_iri:gsub("<(default_contribution)>", "ORKG_default")
+ self.paper.contributions[key] = contribution
+end
+
+function XMP:extract_namespace_prefix(ns_arg)
+ if ns_arg == '' then
+ return nil
+ end
+ uri_and_prefix = ns_arg:split(',%s+?')
+
+ if #uri_and_prefix < 2 then
+ SciKGTeX:error([[Method addmetaproperty: No prefix found.
+ Unknown prefix, URI specification: %s.
+ Please specify the arguments as [prefix, URI]!]], ns_arg)
+ return nil
+ elseif #uri_and_prefix > 2 then
+ SciKGTeX:warn([[Method addmetaproperty: Too many arguments.
+ Too many arguments in prefix, URI specification: %s.
+ Excess arguments are ignored.]], ns_arg)
+ end
+
+ if not uri_valid(uri_and_prefix[2]) then
+ message = [[Method addmetaproperty: Invalid URI.
+ The given URI %s is not a valid choice!
+ Please use a resolvable URI starting with 'http'.]]
+ SciKGTeX:error(message, uri_and_prefix[2])
+ return nil
+ end
+ -- add the namespace if it has not been added yet
+ if self.namespaces[uri_and_prefix[1]]==nil then
+ self:add_namespace(uri_and_prefix[1], uri_and_prefix[2])
+ end
+ return uri_and_prefix[1]
+end
+
+function XMP:process_content(c)
+ c = self:escape_xml_content(c)
+ entity = resolve_entity(c)
+ if entity ~= false then
+ return entity
+ end
+ c = remove_latex_commands(c)
+ return c
+end
+
+function XMP:property_has_namespace(annotation_type)
+ annotation_type_t = annotation_type:split(':')
+
+ if #annotation_type_t > 1 then
+ annotation_type = annotation_type_t[2]
+ prefix = annotation_type_t[1]
+ else
+ annotation_type = annotation_type_t[1]
+ prefix = nil
+ end
+ return prefix, annotation_type
+end
+
+function XMP:set_title(title)
+ self.paper.title = title
+end
+
+function XMP:add_author(author)
+ table.insert(self.paper.authors, author)
+end
+
+function XMP:set_researchfield(researchfield)
+ self.paper.researchfield = researchfield
+end
+
+function XMP:add_annotation(contribution_ids, annotation_type, content, annotation_id)
+ local annotation = {}
+ -- check if a namespace is attached to the property specification
+ prefix, annotation_type = self:property_has_namespace(annotation_type)
+
+ annotation.content = content
+ annotation.id = annotation_id
+ annotation.type = self:escape_xml_tags(annotation_type)
+
+ -- take the prefix given, the prefix saved in the namespace dictionary or the default ns
+ annotation.prefix = prefix or self.property_ns[annotation.type] or 'orkg_property'
+
+ -- register the use of the property in text
+ SciKGTeX:register_property(annotation.type)
+
+ -- check if the annotation was numbered
+ if contribution_ids == '' then
+ contribution_ids = '<default_contribution>'
+ end
+ contributions_ids_t = contribution_ids:split(',%s+?')
+ -- add the annotations at the specified contribution
+ for i, contribution_id in ipairs(contributions_ids_t) do
+ -- add a new contribution if it has not been added yet
+ if self.paper.contributions[contribution_id] == nil then
+ self:add_contribution(contribution_id, 'contribution_'..contribution_id)
+ end
+ -- add the property annotation to the list of properties of a contribution
+ -- check if the same annotation already exists (in case of double evaluation of the LaTeX command for example)
+ already_there = false
+ for _, prop in pairs(self.paper.contributions[contribution_id].properties) do
+ if prop.content == annotation.content and prop.type == annotation.type then
+ already_there = true
+ break
+ end
+ end
+ if not already_there then
+ table.insert(self.paper.contributions[contribution_id].properties, annotation)
+ end
+ end
+end
+
+function XMP:add_namespace(abbr, uri)
+ self.namespaces[abbr] = uri
+end
+
+function XMP:generate_rdf_root()
+ ns_key_array = {}
+ for ns, uri in pairs(self.namespaces) do table.insert(ns_key_array, ns) end
+ root_string = [[<rdf:RDF ]]
+ table.sort(ns_key_array)
+ for i, key in ipairs(ns_key_array) do
+ root_string = root_string .. "\n xmlns:" .. key .. [[="]] .. self.namespaces[key] .. [["]]
+ end
+ root_string = root_string .. [[>]]
+ return root_string
+end
+
+function XMP:generate_xmp_string(lb_char)
+ lb_char = lb_char or "\n"
+ if lb_char == "r" then
+ lb_char = "\r"
+ end
+ output_string = ""
+ sorted_contributions = {}
+ for cb_id, contribution in pairs(XMP.paper.contributions) do
+ table.insert(sorted_contributions,cb_id)
+ end
+ table.sort(sorted_contributions)
+ self:add_line('<?xpacket begin="?" id="%s"?>',self.paper.id)
+ self:add_line(self.XMP_TOP)
+ self:add_line(self:generate_rdf_root())
+ --print(debug.traceback())
+ if self.paper then
+ self:add_line(
+ ' <rdf:Description rdf:about="https://www.orkg.org/orkg/paper/%s">',
+ self.paper.id
+ )
+ self:add_line(' <rdf:type rdf:resource="http://orkg.org/core#Paper"/>')
+ if self.paper.title ~= nil then
+ self:add_line(
+ ' <orkg:hasTitle>%s</orkg:hasTitle>',
+ self:process_content(self.paper.title)
+ )
+ end
+ for i, author in ipairs(self.paper.authors) do
+ self:add_line(
+ ' <orkg:hasAuthor>%s</orkg:hasAuthor>',
+ self:process_content(author))
+ end
+ if self.paper.researchfield ~= nil then
+ self:add_line(
+ ' <orkg:hasResearchField>%s</orkg:hasResearchField>',
+ self:process_content(self.paper.researchfield)
+ )
+ end
+ for i, cb_id in pairs(sorted_contributions) do
+ contribution = self.paper.contributions[cb_id]
+ self:add_line(' <orkg:hasResearchContribution>')
+ self:add_line(
+ ' <orkg:ResearchContribution rdf:about="https://www.orkg.org/orkg/paper/%s">',
+ self.paper.id .. "/" ..contribution.id
+ )
+ for j, property in ipairs(contribution.properties) do
+ self:add_line(
+ ' <%s:%s>%s</%s:%s>',
+ property.prefix,
+ property.type,
+ self:process_content(property.content),
+ property.prefix,
+ property.type
+ )
+ end
+ self:add_line(' </orkg:ResearchContribution>')
+ self:add_line(' </orkg:hasResearchContribution>')
+ end
+ self:add_line(' </rdf:Description>')
+ end
+ self:add_line(self.XMP_BOTTOM)
+ self:add_line(self.PACKET_END)
+
+ return table.concat(self.lines, lb_char)
+
+end
+
+function XMP:attach_metadata_pdfstream()
+ local xmp_string = self:generate_xmp_string()
+ local new_pdf = pdf.obj {
+ type = 'stream',
+ attr = '/Type/Metadata /Subtype/XML',
+ immediate = true,
+ compresslevel = 0,
+ string = xmp_string,
+ }
+ self.lines = {}
+ return new_pdf
+end
+
+function XMP:dump_metadata()
+ local xmp_string = self:generate_xmp_string()
+ local dir = get_output_dir() or '.'
+ f = io.open(dir .. '/' .. tex.jobname .. '.xmp_metadata.xml','w')
+ io.output(f)
+ io.write(xmp_string)
+ io.close(f)
+end
+
+luatexbase.add_to_callback('stop_run', function()
+ SciKGTeX:warn_unused_command()
+ if SciKGTeX.PRODUCE_XMP_FILE then
+ XMP:dump_metadata()
+ end
+end, 'at_end')
+
+-- Writing metadata packets
+luatexbase.add_to_callback('finish_pdffile', function()
+ if XMP.paper then
+ local metadata_obj = XMP:attach_metadata_pdfstream()
+ local catalog = pdf.getcatalog() or ''
+ pdf.setcatalog(catalog..string.format('/Metadata %s 0 R', metadata_obj))
+ --if SciKGTeX.PRODUCE_XMP_FILE then
+ -- XMP:dump_metadata()
+ --end
+ end
+end, 'finish')
+
+-- TODO: real identifier assigned
+
+-- get the id or generate UUID
+local output_dir = get_output_dir() or '.'
+local header = read_header_of_file(output_dir .. '/' .. tex.jobname .. '.xmp_metadata.xml')
+if header ~= nil then
+ id = extract_uuid_from_header(header)
+end
+if id == nil then
+ id = generate_UUID()
+ print('generate new id:', id)
+end
+XMP:add_paper_node(id)
+XMP:add_namespace("rdf","http://www.w3.org/1999/02/22-rdf-syntax-ns#")
+XMP:add_namespace("rdfs","http://www.w3.org/2000/01/rdf-schema#")
+XMP:add_namespace("orkg","http://orkg.org/core#")
+XMP:add_namespace("orkg_property","http://orkg.org/property/")
+
+SciKGTeX.XMP = XMP
+return SciKGTeX \ No newline at end of file
diff --git a/macros/luatex/latex/scikgtex/scikgtex.sty b/macros/luatex/latex/scikgtex/scikgtex.sty
new file mode 100644
index 0000000000..9f94c11a1b
--- /dev/null
+++ b/macros/luatex/latex/scikgtex/scikgtex.sty
@@ -0,0 +1,149 @@
+%% ----------------------------------------------------------------
+%% scikgtex --- Scientific Knowledge Graph TeX (SciKgTeX) is a
+%% LuaLaTeX package which makes it possible to annotate specific
+%% research contributions in scientific documents. SciKGTeX will
+%% enrich the document by adding marked contributions to PDF
+%% metadata in a structured XMP format which can be picked up by
+%% Search Engines and Knowledge Graphs.
+%% More information on how to use the package can be found here:
+%% https://github.com/Christof93/SciKGTeX#readme
+%%
+%% Author: Christof Bless
+%% E-mail: christofbless@gmail.com
+%% Released under the MIT License
+%% See https://opensource.org/licenses/MIT
+%% ----------------------------------------------------------------
+%%
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{scikgtex}[2022/11/13 v2.1.1]
+
+\def\useignorespacesandallpars#1\ignorespaces\fi{%
+#1\fi\ignorespacesandallpars}
+
+\makeatletter
+\def\ignorespacesandallpars{%
+ \@ifnextchar\par
+ {\expandafter\ignorespacesandallpars\@gobble}%
+ {}%
+}
+\makeatother
+
+\RequirePackage{suffix}
+\RequirePackage{hyperref}
+
+\errorcontextlines=5
+
+\directlua {
+ SciKGTeX = require('scikgtex')
+}
+
+\newcommand{\addmetaproperty}[2][]{%
+ \directlua{%
+ local new_property = "\luaescapestring{\unexpanded{#2}}"%
+ local ns = "\luaescapestring{\unexpanded{#1}}"%
+ SciKGTeX:add_property(new_property, ns)%
+ }%
+ \ignorespaces
+}%
+
+\newcommand{\newpropertycommand}[2][]{%
+ \directlua{%
+ local new_property = "\luaescapestring{\unexpanded{#2}}"%
+ local ns = "\luaescapestring{\unexpanded{#1}}"%
+ SciKGTeX:make_new_command(new_property, ns)%
+ }%
+ \ignorespaces
+}%
+
+\newcommand{\metatitle}[2][]{%
+ \directlua{%
+ local value = "\luaescapestring{\unexpanded{#2}}"%
+ SciKGTeX.XMP:set_title(value)%
+ }%
+ #2%
+}%
+
+\WithSuffix\newcommand\metatitle*[2][]{%
+ \directlua{%
+ local value = "\luaescapestring{\unexpanded{#2}}"%
+ SciKGTeX.XMP:set_title(value)%
+ }%
+ \ignorespaces
+}%
+
+\newcommand{\metaauthor}[2][]{%
+ \directlua{%
+ local value = "\luaescapestring{\unexpanded{#2}}"%
+ SciKGTeX.XMP:add_author(value)%
+ }%
+ #2%
+}%
+
+\WithSuffix\newcommand\metaauthor*[2][]{%
+ \directlua{%
+ local value = "\luaescapestring{\unexpanded{#2}}"%
+ SciKGTeX.XMP:add_author(value)%
+ }%
+ \ignorespaces
+}%
+
+\newcommand{\researchfield}[2][]{%
+ \directlua{%
+ local value = "\luaescapestring{\unexpanded{#2}}"%
+ SciKGTeX.XMP:set_researchfield(value)%
+ }%
+ #2%
+}%
+
+\WithSuffix\newcommand\researchfield*[2][]{%
+ \directlua{%
+ local value = "\luaescapestring{\unexpanded{#2}}"%
+ SciKGTeX.XMP:set_researchfield(value)%
+ }%
+ \ignorespaces
+}%
+
+\newcommand{\uri}[2]{%
+ \directlua{%
+ local label = "\luaescapestring{\unexpanded{#2}}"%
+ local uri = "\luaescapestring{\unexpanded{#1}}"%
+ SciKGTeX:print_entity(uri, label)%
+ }%
+}%
+
+\newcommand{\contribution}[3][]{%
+ \directlua{%
+ local contrib_n = "\luaescapestring{\unexpanded{#1}}"%
+ local property = "\luaescapestring{\unexpanded{#2}}"%
+ local value = "\luaescapestring{\unexpanded{#3}}"%
+ SciKGTeX.XMP:add_annotation(contrib_n, property, value, 'annotation-id')%
+ }%
+ #3%
+}%
+
+\WithSuffix\newcommand\contribution*[3][]{%
+ \directlua{%
+ local contrib_n = "\luaescapestring{\unexpanded{#1}}"%
+ local property = "\luaescapestring{\unexpanded{#2}}"%
+ local value = "\luaescapestring{\unexpanded{#3}}"%
+ SciKGTeX.XMP:add_annotation(contrib_n, property, value, 'annotation-id')%
+ }%
+ \ignorespaces
+}%
+
+\newpropertycommand{researchproblem}
+\newpropertycommand{result}
+\newpropertycommand{method}
+\newpropertycommand{objective}
+\newpropertycommand{conclusion}
+
+%% Copyright (C) 2022 by Christof Bless <christofbless@gmail.com>
+%%
+%% This work may be distributed and/or modified under the
+%% conditions of the MIT License.
+%%
+%% This work consists of the file scikgtex.sty
+%% and the lua file scikgtex.lua.
+%%
+%%
+%% End of file `scikgtex.sty'.