1

I am currently writing a kind of requirement analysis and want to enumerate my sections and subsections in a certain style:

My sections, should look like normal so e.g. "1.1 Import and Export of Files", but the should define a macro "\startLetter" to be the first letter in the title (here "I").

The following subsection's enumeration should start with the current \startLetter and have an increasing three digit identifier, which resets at the start of the next section, e.g. "I001 Text Files Import". When referencing these, I want only the Letter + ID to be printed, so "I001" and it also still referencing there via hyperref.

My current setup is in the MWE below. The problems are

  1. I don't know how to shorten the reference to only the ID without breaking the hyperref link and
  2. The ID counter still counts up when referencing. I pretty sure that is because the macro becomes part of the section name and therefore gets reevaluate every time. How do I stop that?

I had a look into titlesec, xparse and etoolbox's pretocmd and apptocmd, but this would require to patch the @ssect command (?) and I am to inexperienced to just edit in the underlying commands of Latex sections. Is there a "noob-understandable" way to solve this problem?

Thanks in advance :)

MWE:

\documentclass[10pt,a4paper]{book}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\usepackage{xstring}
\usepackage{nameref}
\usepackage{hyperref}
\newcommand{\startLetter}{}
\newcounter{reqID}


\newcommand{\reqSection}[1]{%
    \section{#1}
    \renewcommand{\startLetter}{\StrMid{#1}{0}{1}}
    \setcounter{reqID}{1}
}

\newcommand{\leadingZerosID}{%
    \ifnum\value{reqID}<100 0\fi\ifnum\value{reqID}<10 0\fi\arabic{reqID}
}   

\newcommand{\reqSubsection}[1]{%
    \subsection*{\startLetter\leadingZerosID{} #1}
    \stepcounter{reqID}
}

\parindent0mm

\begin{document}
    \chapter{Any chapter}
    \reqSection{Import Export}
    \reqSubsection{Text Files Import}
    \label{lbl:import-text}
    Some useless blindtext. Some useless blindtext. Some useless blindtext. Some useless blindtext.
    \reqSubsection{Text Files Export}
    \label{lbl:export-text}
    Some useless blindtext. Some useless blindtext. Some useless blindtext. Some useless blindtext.
    \reqSubsection{Video Files Import}
    \label{lbl:import-video}
    Some useless blindtext. Some useless blindtext. Some useless blindtext. Some useless blindtext.
    \vspace{1cm}

    I am refering to requirement \nameref{lbl:export-text}.
\end{document}

1 Answer 1

0

Assuming I understand the question properly, I think this does what you need. The basic idea is to use ordinary subsections, changing their headings to meet your requirements. This eliminates the issue with your counter, and allows you to use the ordinary \ref command to reference by ID number.

\documentclass{book}

\makeatletter
\def\firstchar#1{\firstchar@internal#1\relax}
\def\firstchar@internal#1#2\relax{#1}
\makeatother

\newcommand{\reqSection}[1]{%
\section{#1}
\xdef\startLetter{\firstchar{#1}}
}

\newcommand{\leadingZerosSS}{%
    \ifnum\value{subsection}<100 0\fi\ifnum\value{subsection}<10 0\fi\arabic{subsection}
}   

\renewcommand\thesubsection{\startLetter\leadingZerosSS{}}

\usepackage{hyperref}

\begin{document}
    \chapter{Any chapter}
    \reqSection{Import Export}
    \subsection{Text Files Import}
    \label{lbl:import-text}
    Some useless blindtext. Some useless blindtext. Some useless blindtext. Some useless blindtext.
    \subsection{Text Files Export}
    \label{lbl:export-text}
    Some useless blindtext. Some useless blindtext. Some useless blindtext. Some useless blindtext.
    \subsection{Video Files Import}
    \label{lbl:import-video}
    Some useless blindtext. Some useless blindtext. Some useless blindtext. Some useless blindtext.
    \vspace{1cm}

    I am refering to requirement \nameref{lbl:export-text}.

    I can refer to the ID using \verb+\ref+ rather than \verb+\nameref+, for example \ref{lbl:export-text}
\end{document}

EDIT

For reasons I have not investigated, your \startLetter macro didn't work for me, so I wrote my own. The idea (which I picked up from an answer by David Carlisle) is that \firstchar@internal expects two arguments; #1 is the first token it finds (*), but #2 must be terminated by \relax. #1 is returned as the result, and #2 is ignored. \firstchar appends \relax (**) to the end of its argument and sends this on to \firstchar@internal. So...

\firschar{abc}
--> \firstchar@internal{abc\relax} 
#1 = a
#2 = bc
--> a

(*) It could also be a group enclosed by braces, but something like \subsection{{abc} def} seems unlikely to me).

(**) Originally I used a \relax to ensure there are two things for TeX to find if you do \firstchar{a} but in fact this was unnecessary because in that case #1 = a and #2 ends up empty. The new implementation causes an error if you do \firstchar{}, which makes sense; the old implementation would return a!

2
  • Hi Ian, this solution in fact works quite nicely. Unfortunately I misstated my problem slightly. I wanted to only have a certain part of subsections to be enumerated in this special way. I fixed it by adding another \renewcommand\thesubsection{\arabic{subsection}} at the end of my requirement analysis. Thanks for your fast and simple solution. Edit: Could you tell me what exactly is the purpose of the \firstchar@internal macro? May 22, 2020 at 14:55
  • @RobertBock --- Please see the edit; I have added an explanation. May 23, 2020 at 10:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.