% \iffalse meta-comment
% 
% ┌─────────────────────────────────────────────────────────────┐
% │                                                             │
% │                  The “fits-astro” package                   │
% │        Retrieving FITS file header information in LuaTeX    │
% │                                                             │
% │  Copyright (C) 2026 Valentin Dao                            │
% │                                                             │
% │  This file may be distributed and/or modified under the     │
% │  conditions of the LaTeX Project Public License, either     │
% │  version 1.3c of this license or (at your option) any       │
% │  later version.                                             │
% │                                                             │
% │  The latest version of this license is in:                  │
% │                                                             │
% │    > http://www.latex-project.org/lppl.txt                  │
% │                                                             │
% │  This work has the LPPL maintenance status 'maintained'     │
% │  and the current maintainer is Valentin Dao                 │
% │  (vdao.texdev@gmail.com).                                   │
% │                                                             │
% │  This work consists of the files listed in the              │
% │  'MANIFEST.md' file.                                        │
% │                                                             │
% │  The repository of this work can be found here:             │
% │                                                             │
% │    > https://github.com/TeXackers/fits-astro                │
% │                                                             │
% └─────────────────────────────────────────────────────────────┘
%
%<*driver>
% \fi
% \iffalse
\documentclass[11pt, a4paper]{l3doc}

\catcode`\@=11

\usepackage{fontspec}

\IfFontExistsTF{ChronicleTextG3}
	{
		\setmainfont{ChronicleTextG3}[
			Extension             = .otf,
			UprightFont           = *-Roman-Pro,
			UprightFeatures       = { Kerning = On },
			ItalicFont            = *-Italic-Pro,
			BoldFont              = *-Bold-Pro,
			BoldItalicFont        = *-BoldIta-Pro,
		]
	}{}

\IfFontExistsTF{Whitney}
	{
		\setsansfont{Whitney}[
			Extension        = .otf,
			UprightFont      = *-Medium,
			ItalicFont       = *-MediumItalic,
			BoldFont         = *-Bold,
			BoldItalicFont   = *-BoldItalic,
			UprightFeatures  = 
				{
					StylisticSet = 1,
					StylisticSet = 10,
					StylisticSet = 11,
				},
			BoldFeatures     =
				{
					StylisticSet = 1,
					StylisticSet = 10,
					StylisticSet = 11,
				},
			ItalicFeatures   =
				{
					StylisticSet = 1,
					StylisticSet = 10,
					StylisticSet = 11,
					StylisticSet = 8,
					StylisticSet = 9
				}
		]
	}{}

\IfFontExistsTF{MonospaceNeon}
	{
		\setmonofont{MonaspaceNeon}[
			Extension              = .otf,
			Scale                  = 0.8,
			UprightFont            = *-Regular,
			UprightFeatures        = { Kerning = On },
			ItalicFont             = *-Italic,
			BoldFont               = *-Bold,
			BoldItalicFont         = *-BoldItalic,
		]
	}{}

\usepackage{microtype}

\ExplSyntaxOn
\debug_on:n { all }
\usepackage{fits-astro}
\debug_off:n { all }
\ExplSyntaxOff

\usepackage[
	a4paper,
	marginparwidth  = 32mm,
	top             = 3cm,
	bottom          = 2cm,
	left            = 44mm,
	right           = 30mm,
	marginparsep    = 2mm,
	]{geometry}

\usepackage{fancyhdr}
\fancyhf{}
\fancyfoot[C]{\small\sffamily\itshape---\space\thepage\space\kern0.4mm\/---}
\renewcommand{\headrulewidth}{0pt}
\pagestyle{fancy}

\usepackage[silence]{codedescribe}
\usepackage{hologo}

\usepackage[svgnames]{xcolor}

\definecolor{RoyalBlue}{RGB}{0, 35, 102}
\definecolor{RoyalRed}{RGB}{157, 16, 45}
\definecolor{RoyalGreen}{HTML}{48845F}
\definecolor{urlcolour}{HTML}{B42F5E}

\definecolor{packagecolour}{HTML}{B86B00}
\definecolor{keycolour}{HTML}{004766}
\definecolor{macrocolour}{HTML}{047658}
\definecolor{macro3colour}{HTML}{27262E}
\definecolor{optioncolour}{HTML}{630476}

\defobjectfmt{macro}{code}{color=macrocolour}
\defobjectfmt{macro3}{code}{color=macro3colour}
\defobjectfmt{variable3}{code}{color=SlateGrey}
\defobjectfmt{pkg}{pkg}{font=\ttfamily}
\defobjectfmt{option}{option}{color=optioncolour, shape preadj=thin, shape posadj=thin, noshape}
\defgroupfmt{keys}{color=keycolour, noshape}

\newcodekey{fits}{
	ruleht        = 0,
	texcsstyle    = \bfseries\color{RoyalBlue},
	texcs         =
		[2]{
			loadheader,
			showheader,
			headerkey,
			setheaderkey,
			gsetheaderkey,
			HST,
			ra,
			dec,
			exptime
		},
	texcsstyle   = [2]{\color{macrocolour}},
	codeprefix    = {},
	resultprefix  = {}
}

\setcodelabels{
	new       = \NoAutoSpacing\textsf{nouveau},
	update    = \NoAutoSpacing\textsf{mise à jour},
	note      = \NoAutoSpacing\textsf{N.B},
	remark    = \NoAutoSpacing\textsf{remarque},
	and       = \NoAutoSpacing\textsf{et},
	or        = \NoAutoSpacing\textsf{ou},
	months    = 
		{
			janvier, février, mars, avril,
			mai, juin, juillet, août,
			septembre, octobre, novembre, décembre
		}
}

\lstset{
	gobble=2,
}

\usepackage{titlesec}
\titleformat*{\section}{\sffamily\LARGE\bfseries}
\titleformat*{\subsection}{\sffamily\Large\bfseries}

\usepackage{changelog}
\usepackage{enumitem}
\renewenvironment{changelogitemize}
	{\begin{itemize}[label=\raisebox{0.5ex}{$\scriptscriptstyle\blacktriangleright$}]}
	{\end{itemize}}

\DeclareTranslation{French}{changelog}{Historique des modifications}

\setlength{\parindent}{0pt}

\usepackage{polyglossia}
\setmainlanguage[
	variant               = french,
	autospacetypewriter   = false,
	frenchfootnote        = true,
	frenchpart            = true,
	]{french}

\usepackage[perpage]{footmisc}

\usepackage{hyperref}
\hypersetup{
  pdfauthor   = {Valentin Dao},
  pdftitle    = {L'extension fits-astro},
  pdfcreator  = {LuaLaTeX with hyperref package},
  colorlinks  = true,
  linkcolor   = RoyalBlue,
  urlcolor    = urlcolour,
}

\title{L'extension \href{https://ctan.org/pkg/fits-astro}{\textsf{\fitsastro@module}} \medbreak{\Large\fitsastro@version}}
\author{Valentin Dao\footnote{E-mail: \href{mailto:vdao.texdev@gmail.com}{\ttfamily vdao.texdev@gmail.com}}}
\date{\today}

\listfiles

\EnableCrossrefs
\EnableDocumentation
\EnableImplementation

\begin{document}
	\DocInput{fits-astro-fr.dtx}
\end{document}
%</driver>
% \fi
% 
% \begin{documentation}
% 
% \maketitle
% L'extension \tsobj[pkg]{\fitsastro@module} offre une interface simple permettant de lire des fichiers \href{https://www.stsci.edu/hst/wfpc2/Wfpc2_dhb/intro_ch23.html}{\em Flexible Image Transport System} (FITS) et d'accéder à leur en-tête afin d'y récupérer des informations. Elle dépend d'un script Lua personnalisé et requiert donc le moteur Lua\hologo{LaTeXTeX}. Contrairement à des bibliothèques plus complexes comme \texttt{astropy.io.fits} en Python, cette extension est uniquement conçue pour récupérer directement les paires clé-valeur des en-têtes. Elle n'est \emph{pas} destinée à les modifier ou à corriger les erreurs de standardisation, et encore moins à lire la partie des données. Veuillez également noter qu'à l'heure actuelle, le paquet ne \emph{prend pas} en charge les fichiers comportant plusieurs \emph{Header Data Units} (HDUs), et seul l'en-tête principal sera lu ; c.-à-d. que les extensions seront ignorées.

% \section{Importer le paquet}
% 
% Le paquet peut être importé de l'une des manières suivantes :
% \begin{itemize}
%		\item \tsobj[code]{\usepackage{fits-astro}} si compilation avec Lua\LaTeX.
%		\item \tsobj[code]{\input{fits-astro.tex}} si compilation avec Lua\TeX.
% \end{itemize}
% 
% \section{Macros du paquet}
% 
% \begin{codedescribe}[macro]{\loadheader}
% \begin{codesyntax}
%		\tsobj[code]{\loadheader}\tsargs[marg]{identification FITS}\tsargs[marg]{chemin du fichier FITS}
% \end{codesyntax}
%		Cette macro lit le fichier \texttt{FITS} et alloue l'en-tête à l'identification FITS spécifiée, qui peut prendre la forme d'un \emph{string} ou d'une séquence de contrôle. Dans ce dernier cas, \tsobj[macro]{\csstring} est utilisée pour retirer le caractère d'échappement pour le stockage interne. De ce fait, si une séquence de contrôle est utilisée, elle ne contient en réalité pas l'en-tête. Ainsi, les deux formats d'argument sont interchangeables. La macro utilise également la bibliothèque \texttt{Kpathsea}, et il n'est donc pas nécessaire que les fichiers se trouvent dans le répertoire courant. Cette documentation utilisera une \href{https://fits.gsfc.nasa.gov/fits_samples.html}{image publique} pour démontrer les macros du paquet (l'image utilisée est la première dans le tableau).
% \end{codedescribe}
% 
% \begin{codestore}[loadfits]
%		\loadheader\HST{WFPC2u5780205r_c0fx.fits}
%		\loadheader{HST}{WFPC2u5780205r_c0fx.fits} % Façon alternative de charger le fichier
% \end{codestore}
% 
% \tscode*[fits]{loadfits}
% \loadheader\HST{../../WFPC2u5780205r_c0fx.fits}
% 
% \begin{codedescribe}[macro]{\showheader}
% \begin{codesyntax}
%		\tsobj[code]{\showheader}\tsargs[marg]{identification FITS}
% \end{codesyntax}
%		Cette macro affiche le contenu de l'en-tête de l'identification FITS dans le fichier log.
% \end{codedescribe}
% 
% \begin{codestore}[showfits]
%		\showheader\HST
%		\showheader{HST} % Produirait le même résultat
% \end{codestore}
% 
% \tscode*[fits]{showfits}
% 
% \begin{codedescribe}[macro]{\headerkey}
% \begin{codesyntax}
%		\tsobj[code]{\headerkey}\tsargs[marg]{identification FITS}\tsargs[marg]{clé}
% \end{codesyntax}
%		Cette macro récupère la valeur associée à la clé désignée dans l'en-tête de l'identification FITS donnée.
% \end{codedescribe}
% 
% \begin{codestore}[keyfits]
%		\headerkey\HST{FITSDATE}\par
%		\headerkey\HST{TELESCOP}
% \end{codestore}
% 
% \tsdemo[fits]{keyfits}
% 
% \begin{codedescribe}[macro]{\setheaderkey, \gsetheaderkey}
% \begin{codesyntax}
%		\tsobj[code]{\setheaderkey}\tsargs[marg]{FITS identification}\tsargs[marg]{key}\tsargs[marg]{control sequence}
% \end{codesyntax}
%		Cette macro récupère la valeur associée à la clé désignée dans l'en-tête de l'identification FITS donnée et l'assigne à la séquence de contrôle spécifiée. L'assignation est soit locale au groupe courant, soit globale.
% \end{codedescribe}
% 
% \begin{codestore}[setfitskey]
%		\ttfamily
%		\begingroup
%		\setheaderkey\HST{RA_TARG}\ra
%		\setheaderkey\HST{DEC_TARG}\dec
%		\gsetheaderkey\HST{EXPTIME}\exptime
%		\meaning\ra\par
%		\meaning\dec\par
%		\meaning\exptime\par
%		\endgroup
%		\meaning\exptime\par
%		\meaning\ra\par
%		\meaning\dec
% \end{codestore}
% 
% \tsdemo[fits]{setfitskey}
% 
% \begin{changelog}[sectioncmd=\section*]
% \begin{version}[v=1.0.1, date=09-07-2026]
% \added
% \item Documentation française.
% \fixed
% \item Amélioration du script Lua et de la documentation.
% \end{version}
% \shortversion{v=1.0.0, date=06-04-2026, changes=Version initiale.}
% \end{changelog}
% 
% \end{documentation}
% 
% \begin{implementation}
% 
% \newpage
% 
% \section{Implémentation}
% 
% \subsection{Package \TeX}
% 
%    \begin{macrocode}
%<*package-tex>
\ifdefined\loadheader\expandafter\endinput\fi

\def\FITSerr#1{\errmessage{Package fits Error: #1}}
\def\FITSwar#1{\message{Package fits Warning: #1}}

\unless\ifdefined\directlua\FITSerr{LuaTeX required}\fi

\directlua{dofile('fits-astro.lua')}

\def\loadheader#1#2{
  \directlua{
    local file = '\csstring#1'
    if FITS_data[file] then
      tex.print('\luaescapestring{\FITSerr{FITS file '#2' already loaded}}')
    else
      FITS_data[file] = load_FITS_header('#2')
    end
  }
}

\def\showheader#1{
  \directlua{
    show_fits(FITS_data['\csstring#1'])
  }
}

\def\headerkey#1#2{
  \directlua{
    if FITS_data['\csstring#1']['#2'] then
      get_header_key(FITS_data['\csstring#1'], '#2')
    else
      tex.print('\luaescapestring{\FITSwar{Header key '#2' not found}}')
    end
  }
}

\def\setheaderkey#1#2#3{
  \directlua{
    if FITS_data['\csstring#1']['#2'] then
      token.set_macro('\csstring#3', FITS_data['\csstring#1']['#2'])
    end
  }
}

\def\gsetheaderkey#1#2#3{
  \directlua{
    if FITS_data['\csstring#1']['#2'] then
      token.set_macro('\csstring#3', FITS_data['\csstring#1']['#2'], 'global')
    end
  }
}

%</package-tex>
%    \end{macrocode}
% \subsection{Package \LaTeX}
% \c@CodelineNo=\z@
%    \begin{macrocode}
%<*package-latex>
\NeedsTeXFormat{LaTeX2e}

\def\fitsastro@module{fits-astro}
\def\fitsastro@version{v1.0.1}
\def\fitsastro@date{2026-07-09}
\def\fitsastro@description{Retrieving FITS file header information in LuaTeX}

\ProvidesPackage{fits-astro}[\fitsastro@date\space\fitsastro@version\space\fitsastro@description]

\input{fits-astro.tex}
%</package-latex>
%    \end{macrocode}
% \subsection{Script Lua}
% \c@CodelineNo=\z@
%    \begin{macrocode}
%<*package-lua>
tex = tex or {}
texio = texio or {}
FITS_data = {}

function load_FITS_header(file)
  local file_path = kpse.find_file(file)
  if not file_path then
    tex.error("FITS file not found: " .. file)
  else
    local FITS_file = io.open(file_path, "rb")
    local header = {}
    repeat
      local header_line = FITS_file:read(80)
      if not header_line or #header_line < 80 then break end

      -- string
      local key, value = header_line:match("([%u%-%d_]+)%s*=%s*'([^']*)'")
      if not key then
        -- number or boolean
        key, value = header_line:match("([%u%-%d_]+)%s*=%s*([^/]+)")
      end

      if key and value then
        value = sanitize_FITS_key(value)
        if value:match("^%s*$") then
          header[key] = nil
        else
          header[key] = parse_FITS_key(key, value)
        end
      end
  until header_line:sub(1, 3) == "END"
  FITS_file:close()
  return header
  end
end

function parse_FITS_key(key, value)
  -- converts FITS standard status to a boolean
  if key == "SIMPLE" then
    if value == "T" then
      value = true
    else
      value = false
    end
    return value
  end
  -- converts numbers into proper integers
  local num = tonumber(value)
  if num then value = num end
  return value
end

function sanitize_FITS_key(value)
  value = value:match("^%s*(.-)%s*$") or value
  return value
end

function get_header_key(header, key)
  local value = header[key]
  if value then
    tex.print(value)
  end
end

function show_fits(header)
  texio.write_nl("FITS header information:")
  for key, value in pairs(header) do
    texio.write_nl(string.format("%s = %s", key, tostring(value)))
  end
end
%</package-lua>
%    \end{macrocode}
% \end{implementation}
% 
% \newpage
% 
% \PrintIndex
