#
# This file is licensed under the MIT No Attribution license.
#

. /prelude.bash.tmp

export TZ=UTC
SOURCE_DATE_EPOCH=$(cat source-date-epoch.tmp)
export SOURCE_DATE_EPOCH
export PERL_HASH_SEED=0
export PERL_PERTURB_KEYS=0
export GS_OPTIONS='-dOmitInfoDate=true -dOmitID=true -dOmitXMP=true'

source_dir=$PWD
readonly source_dir

mkdir -p /out.tmp/images
> /out.tmp/files

utf8() {
  # BusyBox iconv silently discards invalid UTF-8 bytes. Validate before
  # converting the older manuals' ISO-8859-1 text instead.
  perl -MEncode=decode,encode,FB_CROAK -0777 -ne '
    $copy = $_;
    eval { decode("UTF-8", $copy, FB_CROAK) };
    print $@ ? encode("UTF-8", decode("ISO-8859-1", $_)) : $_;
  ' "$1" > "$2"
}

html_scripts() {
  sed '
    /^<!-- CreationDate:/ d
    s|</head>|\
<style>* { font-variant-ligatures: none !important; }</style>\
<script defer src="'"$2"'notice.js"></script>\
<script defer src="'"$2"'qref.js"></script>\
&|
  ' "$1" > "$1.tmp"
  mv "$1.tmp" "$1"
}

check_pdf() {
  cat /pdf.log.tmp >&2
  if grep -Eq 'error:|returned status [1-9]' /pdf.log.tmp; then
    exit 1
  fi
  test -s "$1"
}

# Some handbooks depend on PDF-only layout or macros. Preserve their pages
# as selectable SVG objects, including the existing links and contents.
publish_pdf_pages() {
  declare pdf=$1
  declare file=${pdf%.pdf}.html
  pushd /out.tmp >/dev/null
  DVISVGM_PDF_PROC=mutool dvisvgm --pdf --page=1- --font-format=woff2 \
    --embed-bitmaps --verbosity=3 --output="images/${pdf%.pdf}-%p.svg" "$pdf"
  perl -0777 -i -pe \
    's~(?:\@font-face\{[^}]*\}\n?)+~join("\n", sort split /\n/, $&) . "\n"~ge;
     s~(<style[^>]*>\n)~$1text { white-space: pre; text-rendering: geometricPrecision; }\n~' \
    images/"${pdf%.pdf}"-*.svg
  groff-svg-text "$pdf" images/"${pdf%.pdf}"-*.svg
  cat > /pdf-html.js.tmp <<'JS'
var pdf = scriptArgs[0];
var name = pdf.replace(/\.pdf$/, '');
var file = name + '.html';
var doc = Document.openDocument(pdf);
var titles = {
  automake: 'Using Automake in the Groff project',
  msboxes: 'Using PDF boxes with groff and the ms macros',
  'groff-pdf-features': 'Groff PDF features'
};
var title = doc.getMetaData('info:Title') || titles[name] || name;
function escape(s) {
  return s.replace(/&/g, '&amp;').replace(/</g, '&lt;')
    .replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
function href(uri, fromSVG) {
  if (uri.charAt(0) === '#') {
    var page = doc.resolveLink(uri);
    if (page < 0) throw new Error('Unresolved PDF link: ' + uri);
    return (fromSVG ? '../' + file : '') + '#page-' + (page + 1);
  }
  var man = /^man:([^()]+)\((\d+)\)$/.exec(uri);
  if (man) return (fromSVG ? '../' : '') + man[1] + '.' + man[2] + '.html';
  return uri;
}
function contents(items) {
  if (!items || !items.length) return;
  print('<ul>');
  items.forEach(function (item) {
    print('<li><a href="' + escape(href(item.uri, false)) + '">' +
      escape(item.title) + '</a>');
    contents(item.down);
    print('</li>');
  });
  print('</ul>');
}
print('<!DOCTYPE html>\n<html lang="en">\n<head>');
print('<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">');
print('<meta name="viewport" content="width=device-width, initial-scale=1">');
print('<title>' + escape(title) + '</title>');
print('<style>\nbody { margin: 1em; }\n' +
  'main { width: fit-content; max-width: 100%; margin: auto; }\n' +
  'svg.inline-svg { display: block; max-width: 100%; height: auto; margin: 1em auto; }\n' +
  'aside { white-space: pre-wrap; }\n</style>\n</head>\n<body>\n<main>');
var outline = doc.loadOutline();
if (outline && outline.length) {
  print('<nav aria-label="Contents">');
  contents(outline);
  print('</nav>');
}
var pages = doc.countPages();
for (var n = 0; n < pages; n++) {
  var page = doc.loadPage(n);
  var bounds = page.getBounds();
  var number = String(n + 1);
  while (number.length < String(pages).length) number = '0' + number;
  var image = 'images/' + name + '-' + number + '.svg';
  var svg = read(image);
  var links = '';
  page.getLinks().forEach(function (link) {
    var r = link.getBounds();
    links += '<a xlink:href="' + escape(href(link.getURI(), true)) +
      '" target="_top"><rect x="' + (r[0] - bounds[0]) +
      '" y="' + (r[1] - bounds[1]) + '" width="' + (r[2] - r[0]) +
      '" height="' + (r[3] - r[1]) + '" fill="transparent"/></a>\n';
  });
  svg = svg.replace('</svg>', links + '</svg>');
  var buffer = new Buffer();
  buffer.write(svg);
  buffer.save(image);
  print('<div id="page-' + (n + 1) + '"><object type="image/svg+xml" data="' +
    image + '" width="' + ((bounds[2] - bounds[0]) * 4 / 3) +
    '" height="' + ((bounds[3] - bounds[1]) * 4 / 3) +
    '">Page ' + (n + 1) + '</object>');
  page.getAnnotations().forEach(function (annotation) {
    var text = annotation.getContents();
    if (text) print('<aside>Note: ' + escape(text) + '</aside>');
  });
  print('</div>');
}
print('</main>\n</body>\n</html>');
JS
  mutool run /pdf-html.js.tmp "$pdf" > "$file"
  html_scripts "$file" ''
  rm "$pdf"
  echo "$file" >> files
  popd >/dev/null
}

publish_pdf_html() {
  declare file=$1
  shift
  groff -Kutf8 -Tpdf -P-e "$@" > "/out.tmp/$file" 2> /pdf.log.tmp
  check_pdf "/out.tmp/$file"
  publish_pdf_pages "$file"
}

publish_html() {
  declare file=$1
  shift
  pushd /out.tmp >/dev/null
  groff -Kutf8 -Thtml -I"$source_dir" -P-Iimages/"${file%.html}" \
    "$@" > "$file" 2> /html.log.tmp
  cat /html.log.tmp >&2
  if grep -Eq 'error:|returned status [1-9]' /html.log.tmp; then
    exit 1
  fi
  utf8 "$file" "$file.tmp"
  mv "$file.tmp" "$file"
  html_scripts "$file" ''
  echo "$file" >> files
  popd >/dev/null
}

# The upstream lists include generated pages and aliases that a filename
# search would miss. Numbered files elsewhere can instead be test fixtures.
for x in /manuals.tmp/*.[157]; do
  d=${x##*.}
  mkdir -p /manuals.tmp/man$d
  ln -s ../"${x##*/}" /manuals.tmp/man$d/"${x##*/}"
done

for x in /manuals.tmp/*.[157]; do
  utf8 "$x" "$x.tmp"
  mv "$x.tmp" "$x"
  name=${x##*/}
  # Historical markup typos otherwise abort a page or lose its closing
  # quotation mark. Keep these corrections in the build, not the sources.
  sed -i \
    -e 's/\\\[\]cq\]/\\[cq]/g' \
    -e 's/\\\[\]cq/\\[cq]/g' \
    -e '/^\.  if (\\n\[\.\$\] < 1)$/s/$/ \\/' \
    -e 's@^\.UR dl\.dropbox\.com/@.UR https://dl.dropbox.com/@' \
    "$x"
  if [[ $name == gdiffmk.1 ]]; then
    sed -i 's/^\.MT \.$/.ME ./' "$x"
  fi
  if [[ $name == groff_mdoc.7 ]]; then
    # Modern mdoc requires numeric widths for this five-column table.
    sed -i '/^\.Bl -column "quote" "close" "open"/c\
.Bl -column 6n 6n 6n 30n 20n' "$x"
  fi
  if [[ $name == groffer.1 ]]; then
    # One release has a soft hyphen in place of the roff hyphen escape.
    sed -i 's/x\\\xC2\xADviewer/x\\-viewer/g' "$x"
  fi
  if [[ $name == groff_me.7 ]]; then
    sed -i '/^\\&\.ce n\t\tcenter next n lines$/s/\t\t/\t/' "$x"
  fi
  if [[ $name == groff_markup.7 || $name == groff_mwww.7 ]]; then
    # These manuals demonstrate the older argument order of URL/FTP.
    # Adapt executable calls to the formatter, leaving examples intact.
    perl -0777 -i -pe \
      's/^(\.(?:URL|FTP)) +("[^"\n]*"|\S+) +(?:\\\n)?("[^"\n]*"|\S+)([^\n]*)$/$1 $3 $2$4/mg' \
      "$x"
  fi
  pushd /out.tmp >/dev/null
  if ! groff -Kutf8 -Thtml -s -t -mandoc \
      -I/manuals.tmp -P-Iimages/"$name" "$x" \
      > "$name.html" 2> "$name.log.tmp"; then
    cat "$name.log.tmp" >&2
    exit 1
  fi
  cat "$name.log.tmp" >&2
  if grep -Eq 'error:|returned status [1-9]' "$name.log.tmp"; then
    exit 1
  fi
  # Every extracted image must be present and contain vector artwork.
  images=$(perl -ne 'while (/<object type="image\/svg\+xml" data="([^"]+)"/g) { print "$1\n" }' "$name.html")
  for image in $images; do
    test -s "$image"
    [[ $image == *.svg ]]
    grep -q '<svg ' "$image"
    grep -Eq '<(path|text|use|image)[ >]' "$image"
  done
  if [[ $name == groff_mom.7 ]]; then
    sed -i 's@href="[^"]*/\(momdoc\|mom\)/toc.html"@href="mom/toc.html"@g' "$name.html"
  fi
  html_scripts "$name.html" ''
  echo "$name.html" >> files
  rm "$name.log.tmp"
  popd >/dev/null
done

# The GNU troff book is a single Texinfo manual, including its appendices.
for x in doc/groff.@(texi|texinfo); do
  # Some older books only expose their Top node in Info output.
  top_nodes=$(sed -n '/^@node Top/p' "$x" | wc -l)
  if [[ $top_nodes == 1 ]]; then
    sed '/^@titlepage/,$ { /^@ifinfo$/d; /^@end ifinfo$/d; }' \
      "$x" > "$x.tmp"
    mv "$x.tmp" "$x"
  fi
  # The 1.19 books put a duplicate HTML menu before the Top node.
  # The identical menu following that node already serves HTML output.
  perl -0777 -i -pe \
    's/\@ifhtml\n\@menu\n.*?\@end menu\n\n(?=\@node Top)/\@ifhtml\n/s' \
    "$x"
  # Repair historical Texinfo syntax errors without changing prose.
  sed \
    -e 's/^@node Top,.*/@node Top/' \
    -e 's/@copyright@/@copyright{}@/g' \
    -e 's/^@end _Def/@end_Def/' \
    -e 's/end_Def/endDef/g' \
    -e '/^@itemx \\D.P /s/^@itemx /@item /' \
    -e '/^@Defesc {\\\\z, , g, , }/s/, , }$/, }/' \
    "$x" > "$x.tmp"
  mv "$x.tmp" "$x"
  pushd doc >/dev/null
  texi2any --html --no-split -o /out.tmp/groff.html "${x##*/}"
  images=$(perl -ne 'while (/<img src="([^"]+)"/g) { print "$1\n" }' /out.tmp/groff.html)
  for image in $images; do
    cp "$image" /out.tmp/
  done
  html_scripts /out.tmp/groff.html ''
  echo groff.html >> /out.tmp/files
  popd >/dev/null
done

if [[ -f .version ]]; then
  version=$(cat .version)
elif [[ -f .tarball-version ]]; then
  version=$(cat .tarball-version)
else
  version=$(cat VERSION)
  if [[ -f REVISION ]]; then
    revision=$(cat REVISION)
    version+=$revision
  fi
fi

# These are distinct handbooks, not the sample documents in doc/.
for x in doc/meintro.me doc/meintro_fr.me doc/meref.me doc/ms.ms; do
  if [[ -f $x.in ]]; then
    make "$x"
  fi
done
if [[ -f contrib/sboxes/msboxes.ms.in ]]; then
  make contrib/sboxes/msboxes.ms
fi

for x in \
  doc/meintro.me \
  doc/meintro_fr.me \
  doc/meref.me \
  doc/pic.ms \
  doc/ms.ms \
  doc/automake.mom \
  contrib/mom/examples/mom-pdf.mom \
  contrib/sboxes/msboxes.ms \
  doc/groff-pdf-features.ms \
  grohtml/design.ms \
  src/devices/grohtml/design.ms \
; do
  if [[ ! -f $x ]]; then
    continue
  fi
  utf8 "$x" "$x.utf8.tmp"
  sed -e "s|@VERSION@|$version|g" "$x.utf8.tmp" > "$x.render.tmp"
  name=${x##*/}
  name=${name%.*}.pdf
  case $x in
    */meintro_fr.me) flags=(-me -mfr) ;;
    *.me) flags=(-me) ;;
    *.mom) flags=(-mom) ;;
    */msboxes.ms) flags=(-ms -Mcontrib/sboxes -msboxes) ;;
    */groff-pdf-features.ms)
      flags=(-ms -Mcontrib/sboxes -msboxes -rPDFFEAT=1 -Idoc)
    ;;
    */design.ms) name=grohtml-design.pdf; flags=(-ms) ;;
    *) flags=(-ms -rPDFFEAT=1) ;;
  esac
  if [[ $x == */design.ms ]]; then
    # These early notes use the old one-letter name for Courier.
    sed -i '1i.ftr C CR' "$x.render.tmp"
  fi
  if [[ $x == */design.ms || $x == doc/pic.ms ]]; then
    publish_html "${name%.pdf}.html" -t -e -p \
      "${flags[@]}" "$source_dir/$x.render.tmp"
  elif [[ $x == *.me ]]; then
    # Keep floating examples beside their explanations in continuous HTML.
    # Extract tab-aligned tables and annotated examples as SVG, and keep
    # the reference summary in one column during the image pass.
    perl -0777 -i -pe '
      s{^\.([()])z\b}{.$1b}mg;
      s{(^\.nf\n.*?^\.fi\n)}{
        index($1,"\t") >= 0
          ? ".do HTML-IMAGE\n".$1.".br\n.do HTML-IMAGE-END\n" : $1
      }mseg;
      s{(^\.\(([bl])[^\n]*\n.*?^\.\)\2[^\n]*\n)}{
        index($1,"\t") >= 0
          ? ".do HTML-IMAGE\n".$1.".br\n.do HTML-IMAGE-END\n" : $1
      }mseg;
      s{^\.if t \.2c$}{.if !r ps4html .if t .2c}mg;
    ' "$x.render.tmp"
    publish_html "${name%.pdf}.html" -t -e -p \
      "${flags[@]}" "$source_dir/$x.render.tmp"
  elif [[ $x == doc/ms.ms ]]; then
    # HTML needs no separate report cover. Use the same typesetting
    # branches in the HTML and PostScript image passes.
    sed -i '/^\.RP$/d' "$x.render.tmp"
    mkdir -p /html-macros.tmp
    printf '.troff\n.ftr TI I\n' > /html-macros.tmp/typeset.tmac
    publish_html "${name%.pdf}.html" -t -e -p \
      -M/html-macros.tmp -mtypeset -ms "$source_dir/$x.render.tmp"
  elif [[ $x == *.mom || $x == */groff-pdf-features.ms ]]; then
    pdfmom -Kutf8 -Tpdf -P-e --roff -t -e -p \
      "${flags[@]}" "$x.render.tmp" > "/out.tmp/$name" 2> /pdf.log.tmp
    check_pdf "/out.tmp/$name"
    publish_pdf_pages "$name"
  else
    publish_pdf_html "$name" -t -e -p "${flags[@]}" "$x.render.tmp"
  fi
done

if [[ -f contrib/hdtbl/examples/short_reference.roff ]]; then
  # This short reference is itself a table-macro demonstration. Keep its
  # colored tables together as one SVG, without a printed page break.
  perl -0777 -pe '
    s{^\.(?:t\*)?pv 1\.2\b}{.do HTML-IMAGE\n$&}m;
    s{^\.bp\n}{}mg;
    $_ .= "\n.br\n.do HTML-IMAGE-END\n";
  ' contrib/hdtbl/examples/short_reference.roff > /hdtbl.render.tmp
  publish_html hdtbl-short-reference.html \
    -U -t -e -p -M"$source_dir/contrib/hdtbl" -mhdtbl -mwww \
    -dsopath="$source_dir/contrib/hdtbl/" /hdtbl.render.tmp
  sed -i 's|<title></title>|<title>Short Reference for the HDtbl Macros</title>|' \
    /out.tmp/hdtbl-short-reference.html
fi

if [[ -f contrib/pdfmark/pdfmark.ms ]]; then
  if [[ -f Makefile.am ]]; then
    make pdfroff
    pdfroff=$PWD/pdfroff
  else
    make do=all arch/misc
    make -C contrib/pdfmark \
      -f "$PWD/Makefile.comm" -f "$PWD/Makefile.cfg" \
      -f Makefile.sub -f "$PWD/Makefile.man" \
      srcdir=. top_srcdir="$PWD" top_builddir="$PWD" pdfroff gnu.eps
    pdfroff=$PWD/contrib/pdfmark/pdfroff
  fi
  pushd contrib/pdfmark >/dev/null
  if [[ ! -f gnu.eps ]]; then
    cp "$source_dir/doc/gnu.eps" .
  fi
  "$pdfroff" -M. -mspdf --stylesheet=cover.ms pdfmark.ms \
    > /out.tmp/pdfmark.pdf 2> /pdf.log.tmp
  check_pdf /out.tmp/pdfmark.pdf
  publish_pdf_pages pdfmark.pdf
  popd >/dev/null
fi

# Mom ships its manual as HTML. Keep the page and stylesheet links intact.
if [[ -d contrib/mom/momdoc ]]; then
  cp -R contrib/mom/momdoc /out.tmp/mom
  for x in /out.tmp/mom/*.html; do
    utf8 "$x" "$x.tmp"
    mv "$x.tmp" "$x"
    # Repair misspelled local destinations in the distributed HTML.
    sed -i \
      -e 's/charset=iso-8859-1/charset=utf-8/Ig' \
      -e 's/href="\(autolabel\|toc-appends-author\|oldstyle-headings\|quote\|signature-space\|number-blockquote-lines\|number-quote-lines\|prefix-chapter-number\|STRING_TABS_TUT\|HDRFTR_PLAIN\|BIBLIOGRAPHY\)"/href="#\1"/g' \
      -e 's/href="pdfmom"/href="using.html#pdfmom"/g' \
      -e 's/columns.html#COLUMNS/docprocessing.html#COLUMNS/g' \
      -e 's/docprocessing.html.#PRINTSTYLE/docprocessing.html#PRINTSTYLE/g' \
      -e 's/typesetting.html.#autoloead/typesetting.html#autolead/g' \
      -e 's/typsetting.html/typesetting.html/g' \
      -e 's/typedocmac.html/typemacdoc.html/g' \
      -e 's/color.htmlXCOLOR/color.html#XCOLOR/g' \
      -e 's/color.htmlxcolor/color.html#xcolor/g' \
      -e 's/docprocessin.html/docprocessing.html/g' \
      -e 's/docprecessing.html/docprocessing.html/g' \
      -e 's/tables-of-contest.html/tables-of-contents.html/g' \
      -e 's/letter.html#TOP/letters.html#TOP/g' \
      "$x"
    html_scripts "$x" ../
  done
  echo mom/toc.html >> /out.tmp/files
fi

python3 /inline-svg.py.tmp /out.tmp
# The image pass can also emit SVGs from print-only source branches.
rm -f /out.tmp/images/*.svg
image_file=$(find /out.tmp/images -type f -print -quit)
if [[ -z $image_file ]]; then
  rmdir /out.tmp/images
fi
pushd /out.tmp >/dev/null
make_ignore_file */ > ignore
sort -o files files
chmod -R a+rX .
popd >/dev/null
