% \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} % % \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 } } % % \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} % % \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 % % \end{macrocode} % \end{implementation} % % \newpage % % \PrintIndex