Skip to content

Guide

Introduction

ng2web is one in a long line of Norton Guide tools I've written over the latest couple or so decades. It is, in effect, a replacement for w3ng and ng2html.

As for what it does: it will take a Norton Guide file and turns the content into a collection of HTML pages, which you can then incorporate into a web site.

Installing

ng2web is a Python application and is distributed via PyPI. It can be installed with tools such as pipx:

pipx install ng2web

or uv:

uv tool install ng2web

Also, if you do have uv installed, you can simply use uvx:

uvx ng2web

to run ng2web.

ng2web is also installable with Homebrew:

brew install davep/homebrew/ng2web

Command line options

The command is called ng2web and all command line options can be found with:

ng2web --help

giving output like this:

usage: ng2web [-h] [-i] [-o OUTPUT] [-t TEMPLATES] [-v] guide

Convert a Norton Guide database to HTML documents

positional arguments:
  guide                 The guide to convert

options:
  -h, --help            show this help message and exit
  -i, --index           Generate the first entry in the guide as index.html
  -o, --output OUTPUT   Directory where the output files will be created
  -t, --templates TEMPLATES
                        Directory of template overrides
  -v, --version         Show version information

v1.0.0 (ngdb v0.12.0; Jinja2 v3.1.6)

The key options are:

--index

By default ng2web generates all pages with names that are prefixed with the filename of the guide (minus the extension) and, for all pages relating to short and long entries, including the byte offset of the entry in the guide; this means that amongst the generated pages there's no obvious starting location.

Add the --index switch to tell ng2web to always generate the first entry in the guide as the file index.html.

--output

Use this switch to optionally specify the output directory for the generated HTML. By default all HTML files will be generated in the current directory.

--templates

Use this switch to optionally specify a location to look for templates that will override the default templates (see the next section in this document for details on how to use templates to control the output of ng2web).

Templates

The output of ng2web is styled using a collection of templates. The builtin templates are designed to give the output a bit of a classic Norton Guide reader look.

Default look of the output

The template engine used is Jinja. If you want to modify the templates, or create your own from scratch, the Jinja template designer documentation will be worth a read.

There are a number of templates that control each of the major types of content inside a Norton Guide.

The base template (base.html)

The base template is the base for the other templates that output a specific type of page.

{# -*- engine: jinja2 -*- #}

<!DOCTYPE html>
<html>

  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta charset="utf-8">
    <meta name="generator" content="{{ generator }}">
    <link rel="stylesheet" type="text/css" href="{{ stylesheet }}">
    <title>{% block title %}{{ None|title }}{% endblock %}</title>
    <meta name="decription" content="{{ None|title }}">
    {% if previous_url %}<link rel="prev" href="{{ previous_url }}">{% endif %}
    {% if next_url %}<link rel="next" href="{{ next_url }}">{% endif %}
    {% block extraheaders %}{% endblock %}
  </head>

  <body>

    <header>
      <nav class="box">
        <ul>
          {% with prompt="About", link=about_url %}{% include "inc/nav-link.html" %}{% endwith %}
          {% with prompt="Previous", link=previous_url %}{% include "inc/nav-link.html" %}{% endwith %}
          {% with prompt="Up", link=up_url %}{% include "inc/nav-link.html" %}{% endwith %}
          {% with prompt="Next", link=next_url %}{% include "inc/nav-link.html" %}{% endwith %}
        </ul>
      </nav>
    </header>

    <section>

      <nav class="menu box">
        <ul>
          {%- for menu in guide.menus %}
            <li>{{ menu }}</li>
            <ul>
              {% for option in menu -%}
                <li><a href="{{ option|urlify }}">{{ option.text }}</a></li>
              {% endfor %}
            </ul>
          {%- endfor %}
        </ul>
      </nav>

      <article class="box">
        {% block content %}{% endblock %}
      </article>

    </section>

  </body>

</html>

{# base.html ends here #}

If you wish to change the look and feel of every page in the output, this is probably the template you want to override.

The about page template (about.html)

This is the template for the "about" page of the output; typically this is where the credits for the guide will be shown.

{# -*- engine: jinja2 -*- #}

{% extends "base.html" %}

{% block title %}About: {{ None|title }}{% endblock %}

{% block content %}

  <h1>About {{ None|title }}</h1>
  <hr />
  <pre>
    {% for line in guide.credits %}
      {{ line|toHTML }}
    {%-  endfor %}
  </pre>

{% endblock %}

{# about.html ends here #}

The base entry template (entry.html)

This is the template that both short and long entry templates build upon.

{# -*- engine: jinja2 -*- #}

{% extends "base.html" %}

{% block title %}{{ entry|title }}{% endblock %}

{% block content %}
  {% block prelines %}{% endblock %}
  {% block lines %}
    <pre class="entry">
      {%- for line in entry -%}
        {%- if line is string -%}
          <span class="line">{{ line|toHTML }}</span>
        {%- else -%}
          <a class="line" href="{{ line|urlify }}">{{ line.text|toHTML }}</a>
        {%- endif -%}
        <br />
    {%- endfor -%}
    </pre>
  {% endblock %}
  {% block postlines %}{% endblock %}
{% endblock %}

{# entry.html ends here #}

The short entry template (short.html)

This is the template for creating pages from short entries in the guide. Short entries typically have no "see also" section and also have lines that link elsewhere.

{# -*- engine: jinja2 -*- #}

{% extends "entry.html" %}

{# short.html ends here #}

The long entry template (long.html)

This is the template for creating pages from long entries in the guide. Long entries typically have an optional "see also" section and have lines that don't link anywhere else; the text content of a long entry is simply text, not links that go elsewhere.

{# -*- engine: jinja2 -*- #}

{% extends "entry.html" %}

{% block postlines %}
  {% if entry.has_see_also %}
    <nav class="seeAlso">
      <ul>
        <li>See Also:</li>
        {% for see_also in entry.see_also %}
          <li><a href="{{ see_also|urlify }}">{{ see_also.text }}</a></li>
        {% endfor %}
      </ul>
    </nav>
  {% endif %}
{% endblock %}

{# long.html ends here #}

This is a utility include template that is used by the base template to emit the navigation links that appear at the top of the page: the About, Previous, Up and Next links.

{# -*- engine: jinja2 -*- #}

<li>
  {%- if link -%}
    <a href="{{ link }}">{{ prompt }}</a>
  {%- else -%}
    <span>{{ prompt }}</span>
  {%- endif -%}
</li>

{# nav-link.html ends here #}

The stylesheet template

The stylesheet for the site is generated using this template.

body {
    font-family: sans-serif;
    background: dimgray;
}

footer {
  clear: both;
  font-size: 70%;
  color: silver;
  padding: 1ex;
}

.box {
  border: solid 1px silver;
  box-shadow: 4px 4px 7px 0px rgba( 0, 0, 0, 0.2 );
  background: navy;
  color: silver;
}

a {
  text-decoration: none;
  color: inherit;
}

nav a:hover, nav a:hover *, article a:hover, article a:hover * {
  background: red;
  color: white;
}

header nav {
  padding: 0;
  margin-bottom: 1ex;
}

header nav ul {
  padding-left: 1ex;
  padding-top: 0;
  padding-bottom: 0;
}

header nav ul li {
  display: inline-block;
  color: dimgray;
}

header nav ul li::after {
  content: " |";
  color: silver;
}

header nav a {
  color: white;
  padding-left: 1em;
  padding-right: 1em;
}

header nav span {
  padding-left: 1em;
  padding-right: 1em;
}

section nav.menu {
  float: left;
  padding-right: 1ex;
  margin-right: 1ex;
  margin-bottom: 1ex;
}

section nav.menu ul {
  list-style: none;
  padding-left: 1ex;
}

section nav.menu > ul > li {
  color: white;
}

section nav.menu > ul > li > ul > li {
  color: silver;
}

section nav.menu a {
  display: block;
}

nav.seeAlso {
  border-top: solid 1px silver;
  white-space: normal;
  margin-top: 1ex;
}

nav.seeAlso ul {
  padding: 0;
  padding-left: 0.5em;
  margin: 0;
  margin-top: 0.5em;
}

nav.seeAlso ul li:first-child {
  color: white;
}

nav.seeAlso ul li {
  display: inline-block;
  margin-right: 1em;
}

section article {
  overflow: scroll;
  font-family: 'Roboto Mono', monospace;
  padding: 0.5em;
  margin-bottom: 1ex;
}

section article pre {
  font-family: inherit;
  margin: 0;
  padding: 0;
}

section article h1 {
  color: white;
  margin-bottom: 0;
}

section article ul {
  list-style: none;
  padding-left: 1ex;
  white-space: normal;
}

section article span.ngb {
  color: white;
}

section article span.ngu {
  color: fuchsia;
}

section article a {
  width: 100%;
  display: inline-block;
}

{% for i, c in colours %}
span.bg{{ i }} {
    background: {{ c }};
}

span.fg{{ i }} {
    color: {{ c }};
}
{% endfor %}

@media screen and (max-width: 480px) {

  .box {
    box-shadow: none;
  }

  section nav.menu {
    float: none;
    margin: 0;
  }

  section article {
    clear: both;
  }

  header nav {
    padding: 0;
    margin: 0;
    text-align: center;
  }

  header nav ul, header nav ul a, header nav ul span {
    padding: 0;
  }

  header nav ul li {
    padding-left: 0.5em;
    padding-right: 0.5em;
    display: inline;
  }

  header nav ul li::after {
    content: "";
  }

  header nav ul li:nth-child(2):after {
    content: "\\A";
    white-space: pre;
  }

  section nav.menu li {
    display: inline-block;
  }

  section nav.menu a {
    display: inline-block;
    font-size: 80%;
  }

  section nav.menu ul ul li::after {
    content: " |";
    color: white;
  }

  section article {
    font-size: 80%;
  }
}

/* base.css ends here */

As you'll see: the bulk of this file is just static CSS; the only part making use of templating being the span foreground and background colour utility classes; the styles being .bg0 through .bg15 and .fg0 through .fg15. By default the expansion is a selection of web colours that best match the colours common in text modes on PC/DOS systems.

The default selection is:

  1. black
  2. navy
  3. green
  4. teal
  5. maroon
  6. purple
  7. olive
  8. silver
  9. gray
  10. blue
  11. lime
  12. aqua
  13. red
  14. fuchsia
  15. yellow
  16. white

Overriding templates

Custom template locations

By default, when a template is needed, ng2web will look in the following locations, in the following order. Once a template is found that one is used.

  1. The directory provided with the --templates switch.
  2. ./templates/, below the current working directory, if it exists.
  3. ng2web's own default templates.

Tip

You only need to make a copy of a template you actually want to change. Overriding one template doesn't mean you need to make a copy of all of them. Each tamplet is individually looked for using these rules.

Global values

The following global variables are available in the templates:

generator

The generator name for ng2web, this will include the version number of ng2web and of ngdb (the library that ng2web is built on).

An example looks like:

ng2web v0.1.1 (ngdb v0.12.0)

guide

A reference to the guide object that is being used to read the Norton Guide file. This allows access to any of the properties and methods of ngdb.NortonGuide.

Warning

It's best to restrict use of this to read-only properties; calling anything that may change the state of the underlying guide object could cause unexpected results.

about_url

The URL for the about page that will be generated.

stylesheet

The name of the stylesheet that will be generated.

Available filters

The following filters are made available in the templates:

urlify

Takes a particular linked option within a guide and turns it into a web link. Most often used when generating links in entries; for example:

<a class="line" href="{{ line|urlify }}">{{ line.text|toHTML }}</a>

toHTML

Takes some text and makes it safe to use in a HTML document. This can also be seen being used in the example given above.

title

Takes a guide entry and emits a suitable title for it. For example:

{% block title %}{{ entry|title }}{% endblock %}

The title will be rendered as path of sorts, which will include the title of the guide, the title of the menu the entry relates to, and the prompt from the menu that the entry relates to. For example:

MrDebug for CA-Clipper Ver 1.20.147ß » Reference » Menus

Tip

As a special case, if you pass None as the entry title will simply output the title of the guide.

Getting help

If you need some help using ng2web, or have ideas for improvements, please feel free to drop by the discussions and ask or suggest. If you believe you've found a bug please feel free to raise an issue.