diff --git a/CHANGES.md b/CHANGES.md
index 6cd9e2d7..000ebb37 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -16,6 +16,10 @@ This file describes changes in the AutoDoc package.
`...` for GAP keywords (as returned by
`ALL_KEYWORDS()`), otherwise as before `...`
- Allow XML-style comments in `.autodoc` files
+ - Ignore trailing blank lines after single-line worksheet title-page
+ commands such as `@Title`, `@Subtitle`, `@Version`, `@Author`, and
+ `@Date`, and trim trailing blank lines from generated title-page
+ content so they no longer leak into filenames or empty author entries
- Greatly improve the package manual.
- Convert the hand-written manual chapters from XML to `.autodoc`
- Fix an unexpected and confusing error when mixing explicit
diff --git a/gap/AutoDocMainFunction.gi b/gap/AutoDocMainFunction.gi
index 62eedc2b..f6c3392b 100644
--- a/gap/AutoDocMainFunction.gi
+++ b/gap/AutoDocMainFunction.gi
@@ -224,7 +224,7 @@ end );
InstallGlobalFunction( CreateTitlePage,
function( dir, argument_rec )
local indent, tag, names, filestream, entity_list, OutWithTag, Out, i,
- parsed_date;
+ parsed_date, NormalizeTitlePageContent;
filestream := AUTODOC_OutputTextFile( dir, "title.xml" );
indent := 0;
@@ -255,19 +255,42 @@ InstallGlobalFunction( CreateTitlePage,
AppendTo( filestream, s, "", tag, ">\n" );
end;
+ # Parser state can leave title-page fields as a list of lines. Normalize
+ # them here so trailing blank lines do not leak into output or filenames,
+ # while still preserving intentional internal line breaks for multiline fields.
+ NormalizeTitlePageContent := function( content )
+ local normalized;
+
+ if IsString( content ) then
+ content := [ content ];
+ fi;
+ normalized := List( content, line -> StripBeginEnd( line, "\n\r" ) );
+ while Length( normalized ) > 0 and
+ IsString( normalized[ Length( normalized ) ] ) and
+ StripBeginEnd( normalized[ Length( normalized ) ], " \t\r\n" ) = "" do
+ Remove( normalized );
+ od;
+ if Length( normalized ) = 1 then
+ return normalized[ 1 ];
+ fi;
+ return normalized;
+ end;
+
Out( AUTODOC_XML_HEADER );
Out( "\n" );
indent := indent + 1;
for i in [ "Title", "Subtitle", "Version", "TitleComment" ] do
if IsBound( argument_rec.( i ) ) then
- OutWithTag( i, argument_rec.( i ) );
+ OutWithTag( i, NormalizeTitlePageContent( argument_rec.( i ) ) );
fi;
od;
if IsBound( argument_rec.Author ) then
- for i in argument_rec.Author do
- OutWithTag( "Author", i );
+ for i in List( argument_rec.Author, NormalizeTitlePageContent ) do
+ if not IsString( i ) or StripBeginEnd( i, " \t\r\n" ) <> "" then
+ OutWithTag( "Author", i );
+ fi;
od;
fi;
@@ -282,12 +305,12 @@ InstallGlobalFunction( CreateTitlePage,
argument_rec.Date := AUTODOC_FormatDate( parsed_date );
fi;
fi;
- OutWithTag( "Date", argument_rec.Date );
+ OutWithTag( "Date", NormalizeTitlePageContent( argument_rec.Date ) );
fi;
for i in [ "Address", "Abstract", "Copyright", "Acknowledgements", "Colophon" ] do
if IsBound( argument_rec.( i ) ) then
- OutWithTag( i, StripBeginEnd( argument_rec.( i ), "\n\r" ) );
+ OutWithTag( i, NormalizeTitlePageContent( argument_rec.( i ) ) );
fi;
od;
diff --git a/gap/Parser.gi b/gap/Parser.gi
index 74defcce..e387b537 100644
--- a/gap/Parser.gi
+++ b/gap/Parser.gi
@@ -218,6 +218,7 @@ InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
scope_group, read_example, command_function_record, autodoc_read_line,
current_command, filename, groupnumber, rest_of_file_skipped,
context_stack, new_man_item, add_man_item, Reset, read_code, title_item, title_item_list, plain_text_mode,
+ single_line_title_item_list, active_title_item_name, active_title_item_is_multiline,
current_line_unedited, current_line_info, NormalizeInputLine,
ReadLineWithLineCount, Normalized_ReadLine, line_number, ErrorWithPos, create_title_item_function,
current_line_positition_for_filter, read_session_example, DeclarationDelimiterPosition,
@@ -231,6 +232,8 @@ InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
context_stack := [ ];
chapter_info := [ ];
line_number := 0;
+ active_title_item_name := fail;
+ active_title_item_is_multiline := false;
ReadLineWithLineCount := function( stream )
line_number := line_number + 1;
@@ -964,6 +967,10 @@ InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
if not IsBound( current_item ) then
return;
fi;
+ if active_title_item_name <> fail and
+ active_title_item_is_multiline = false then
+ return;
+ fi;
Add( current_item, current_command[ 2 ] );
end,
@BeginLatexOnly := function()
@@ -1022,13 +1029,17 @@ InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
## information directly into the document.
title_item_list := [ "Title", "Subtitle", "Version", "TitleComment", "Author",
"Date", "Address", "Abstract", "Copyright", "Acknowledgements", "Colophon" ];
-
+ single_line_title_item_list := [ "Title", "Subtitle", "Version", "Author", "Date" ];
+
create_title_item_function := function( name )
return function()
if not IsBound( tree!.TitlePage.( name ) ) then
tree!.TitlePage.( name ) := [ ];
fi;
current_item := tree!.TitlePage.( name );
+ active_title_item_name := name;
+ active_title_item_is_multiline :=
+ Position( single_line_title_item_list, name ) = fail;
Add( current_item, current_command[ 2 ] );
end;
end;
@@ -1116,6 +1127,11 @@ InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
fi;
if current_command[ 1 ] <> false then
autodoc_read_line := current_line_info.allows_declaration_scan;
+ if Position( title_item_list, current_command[ 1 ]{ [ 2 .. Length( current_command[ 1 ] ) ] } ) = fail and
+ current_command[ 1 ] <> "STRING" then
+ active_title_item_name := fail;
+ active_title_item_is_multiline := false;
+ fi;
if not IsBound( command_function_record.(current_command[ 1 ]) ) then
ErrorWithPos("unknown AutoDoc command ", current_command[ 1 ]);
fi;
diff --git a/tst/worksheets/paired-titlepage-autoplain.sheet/plain.autodoc b/tst/worksheets/paired-titlepage-autoplain.sheet/plain.autodoc
index 50c1b7f7..e4ac1691 100644
--- a/tst/worksheets/paired-titlepage-autoplain.sheet/plain.autodoc
+++ b/tst/worksheets/paired-titlepage-autoplain.sheet/plain.autodoc
@@ -1,13 +1,23 @@
@Title Paired Titlepage Test
+
@Subtitle Comment mode title coverage
+
@Version 2.0
+
@TitleComment Internal worksheet fixture
+
@Author Ada Example
+
@Date 2026-03-07
+
@Address Example Street 7
+
@Abstract This worksheet exercises title-page commands in plain-text mode.
+
@Copyright 2026 Example Authors
+
@Acknowledgements Thanks to the worksheet harness.
+
@Colophon Generated for AutoDoc regression coverage.
@Chapter Titlepage Chapter
@Section Titlepage Section