To write the Figures element (Fi) in the index.


§1. Not only figures but also sounds and external files, a little questionably.

void FiguresElement::render(OUTPUT_STREAM, index_session *session) {
    localisation_dictionary *LD = Indexing::get_localisation(session);
    inter_tree *I = Indexing::get_tree(session);
    tree_inventory *inv = Indexing::get_inventory(session);
    InterNodeList::array_sort(inv->figure_nodes, MakeSynopticModuleStage::module_order);
    InterNodeList::array_sort(inv->sound_nodes, MakeSynopticModuleStage::module_order);
    InterNodeList::array_sort(inv->file_nodes, MakeSynopticModuleStage::module_order);
    Index the figures1.1;
    Index the sounds1.2;
    Index the internal files1.3;
    Index the files1.4;
}

§1.1. Figures. Index the figures1.1 =

    if (InterNodeList::array_len(inv->figure_nodes) <= 1) {  cover art always creates 1
        HTML_OPEN("p");
        Localisation::roman(OUT, LD, I"Index.Elements.Fi.NoFigures");
        HTML_CLOSE("p");
    } else {
        HTML_OPEN("p");
        Localisation::bold(OUT, LD, I"Index.Elements.Fi.ListOfFigures");
        HTML_CLOSE("p");
        Tabulate the figures1.1.1;
    }

§1.1.1. The table is presented with thumbnails of a given pixel width, which the HTML renderer automatically scales to fit. Height is adjusted so as to match this width, preserving the aspect ratio.

define THUMBNAIL_WIDTH 80

Tabulate the figures1.1.1 =

    inter_package *settings = InterPackage::from_URL(I, I"/main/completion/basics");
    int MAX_INDEXED_FIGURES =
        (int) Metadata::read_optional_numeric(settings, I"^max_indexed_figures");
    HTML::begin_html_table(OUT, NULL, TRUE, 0, 0, 0, 0, 0);
    int count_of_displayed_figures = 0;
    inter_package *pack;
    LOOP_OVER_INVENTORY_PACKAGES(pack, i, inv->figure_nodes) {
        inter_ti id = Metadata::read_numeric(pack, I"^resource_id");
        if (id > 1) {
            text_stream *filename_as_text = Metadata::required_textual(pack, I"^filename");
            filename *F = Filenames::from_text(filename_as_text);
            TEMPORARY_TEXT(description)
            unsigned int width = 0, height = 0;
            int format_found = 0;
            Find image format and dimensions1.1.1.1;
            Render a table row for the image1.1.1.2;
            DISCARD_TEXT(description)
        }
    }
    HTML::end_html_table(OUT);
    if (count_of_displayed_figures > MAX_INDEXED_FIGURES) {
        HTML_OPEN("p");
        WRITE("(");
        Localisation::roman_ii(OUT, LD, I"Index.Elements.Fi.ThumbnailLimit",
            MAX_INDEXED_FIGURES, 10*MAX_INDEXED_FIGURES);
        WRITE(")");
        HTML_CLOSE("p");
    }

§1.1.1.1. Find image format and dimensions1.1.1.1 =

    FILE *FIGURE_FILE = Filenames::fopen(F, "rb");
    if (FIGURE_FILE) {
        text_stream *real_format = I"JPEG";
        format_found = ImageFiles::get_JPEG_dimensions(FIGURE_FILE, &width, &height);
        fclose(FIGURE_FILE);
        if (format_found == 0) {
            FIGURE_FILE = Filenames::fopen(F, "rb");
            if (FIGURE_FILE) {
                real_format = I"PNG";
                format_found = ImageFiles::get_PNG_dimensions(FIGURE_FILE, &width, &height);
                fclose(FIGURE_FILE);
            }
        }
        if (format_found == 0) {
            Localisation::italic(description, LD, I"Index.Elements.Fi.UnknownFormat");
        } else {
            Localisation::roman_t(description, LD, I"Index.Elements.Fi.Format", real_format);
            WRITE_TO(description, ": ");
            Localisation::roman_ii(description, LD, I"Index.Elements.Fi.Dimensions",
                (int) width, (int) height);
        }
    } else {
        Localisation::italic(description, LD, I"Index.Elements.Fi.Missing");
    }

§1.1.1.2. Render a table row for the image1.1.1.2 =

    HTML::first_html_column(OUT, THUMBNAIL_WIDTH+10);
    if (format_found == 0) {
        HTML_TAG_WITH("img",
            "border=\"0\" src=\"inform:/doc_images/image_problem.png\"");
        WRITE("&nbsp;");
    } else if (count_of_displayed_figures++ < MAX_INDEXED_FIGURES) {
        HTML_TAG_WITH("img",
            "border=\"1\" src=\"file:%f\" width=\"%d\" height=\"%d\"",
            F, THUMBNAIL_WIDTH, THUMBNAIL_WIDTH*height/width);
        WRITE("&nbsp;");
    } else {
        HTML_OPEN_WITH("div",
            "class=\"figureindexbox\" style=\"width:%dpx; height:%dpx; border:1px solid;\"",
            THUMBNAIL_WIDTH, THUMBNAIL_WIDTH*height/width);
        WRITE("&nbsp;");
        HTML_CLOSE("div");
    }

    HTML::next_html_column(OUT, 0);
    WRITE("%S", Metadata::required_textual(pack, I"^name"));
    IndexUtilities::link_package(OUT, pack);
    HTML_TAG("br");
    if (Str::len(description) > 0) {
        WRITE("%S", description);
        HTML_TAG("br");
    }
    Localisation::roman_ti(description, LD, I"Index.Elements.Fi.Resource",
        Filenames::get_leafname(F), (int) id);
    HTML::end_html_row(OUT);

§1.2. Sounds. Index the sounds1.2 =

    HTML_OPEN("p");
    Localisation::bold(OUT, LD, I"Index.Elements.Fi.ListOfSounds");
    HTML_CLOSE("p");
    if (InterNodeList::array_len(inv->sound_nodes) == 0) {
        HTML_OPEN("p");
        Localisation::roman(OUT, LD, I"Index.Elements.Fi.NoSounds");
        HTML_CLOSE("p");
    } else {
        Tabulate the sounds1.2.1;
    }

§1.2.1. Tabulate the sounds1.2.1 =

    HTML::begin_html_table(OUT, NULL, TRUE, 0, 0, 0, 0, 0);
    inter_package *pack;
    LOOP_OVER_INVENTORY_PACKAGES(pack, i, inv->sound_nodes) {
        inter_ti id = Metadata::read_numeric(pack, I"^resource_id");
        text_stream *filename_as_text = Metadata::required_textual(pack, I"^filename");
        filename *F = Filenames::from_text(filename_as_text);
        unsigned int duration, pBitsPerSecond, pChannels, pSampleRate, fsize,
            midi_version = 0, no_tracks = 0;
        int preview = TRUE, waveform_style = TRUE;
        TEMPORARY_TEXT(description)
        int format_found = 0;
        Find sound format and duration1.2.1.1
        Render a table row for the sound1.2.1.2;
        DISCARD_TEXT(description)
    }
    HTML::end_html_table(OUT);

§1.2.1.1. Find sound format and duration1.2.1.1 =

    FILE *SOUND_FILE = Filenames::fopen(F, "rb");
    if (SOUND_FILE) {
        text_stream *real_format = I"AIFF";
        format_found = SoundFiles::get_AIFF_duration(SOUND_FILE, &duration, &pBitsPerSecond,
            &pChannels, &pSampleRate);
        fseek(SOUND_FILE, 0, SEEK_END);
        fsize = (unsigned int) (ftell(SOUND_FILE));
        fclose(SOUND_FILE);
        if (format_found == 0) {
            SOUND_FILE = Filenames::fopen(F, "rb");
            if (SOUND_FILE) {
                real_format = I"Ogg Vorbis";
                preview = FALSE;
                format_found = SoundFiles::get_OggVorbis_duration(SOUND_FILE, &duration,
                    &pBitsPerSecond, &pChannels, &pSampleRate);
                fclose(SOUND_FILE);
            }
        }
        if (format_found == 0) {
            SOUND_FILE = Filenames::fopen(F, "rb");
            if (SOUND_FILE) {
                waveform_style = FALSE;
                real_format = I"MIDI";
                preview = TRUE;
                format_found = SoundFiles::get_MIDI_information(SOUND_FILE,
                    &midi_version, &no_tracks);
                fclose(SOUND_FILE);
            }
        }
        if (format_found == 0) {
            Localisation::italic(description, LD, I"Index.Elements.Fi.UnknownSoundFormat");
        } else {
            if (waveform_style == FALSE) Describe sound in MIDI format1.2.1.1.1
            else Describe sound in waveform format1.2.1.1.2;
        }
    } else {
        Localisation::italic(description, LD, I"Index.Elements.Fi.MissingSound");
    }

§1.2.1.1.1. Describe sound in MIDI format1.2.1.1.1 =

    if (no_tracks == 1) {
        Localisation::roman_it(description, LD, I"Index.Elements.Fi.SoundFormatOneTrack",
            (int) midi_version, real_format);
    } else {
        Localisation::write_iti(description, LD, I"Index.Elements.Fi.SoundFormatMultiTrack",
            (int) midi_version, real_format, (int) no_tracks);
    }
    WRITE_TO(description, " - ");
    Localisation::italic(description, LD, I"Index.Elements.Fi.SoundUnsupported");

§1.2.1.1.2. Describe sound in waveform format1.2.1.1.2 =

    TEMPORARY_TEXT(size)
    WRITE_TO(size, "%d.%01dKB", fsize/1024, (fsize%1024)/102);
    Localisation::roman_tt(description, LD, I"Index.Elements.Fi.SoundFile", size, real_format);
    DISCARD_TEXT(size)
    int min = (duration/6000), sec = (duration%6000)/100, centisec = (duration%100);
    WRITE_TO(description, ": ");
    TEMPORARY_TEXT(seconds)
    if (centisec == 0) WRITE_TO(seconds, "%d", sec);
    else WRITE_TO(seconds, "%d.%02d", sec, centisec);
    if (min > 0) {
        if ((sec > 0) || (centisec > 0)) {
            Localisation::roman_it(description, LD, I"Index.Elements.Fi.DurationMS",
                min, seconds);
        } else {
            Localisation::roman_i(description, LD, I"Index.Elements.Fi.DurationM", min);
        }
    } else {
        Localisation::roman_t(description, LD, I"Index.Elements.Fi.DurationS", seconds);
    }
    DISCARD_TEXT(seconds)
    WRITE_TO(description, "<br>");
    TEMPORARY_TEXT(sample)
    WRITE_TO(sample, "%d.%01dkHz", pSampleRate/1000, (pSampleRate%1000)/100);
    Localisation::roman_t(description, LD, I"Index.Elements.Fi.Sampled", sample);
    DISCARD_TEXT(sample)
    WRITE_TO(description, " ");
    if (pChannels == 1) Localisation::roman(OUT, LD, I"Index.Elements.Fi.Mono");
    else Localisation::roman(description, LD, I"Index.Elements.Fi.Stereo");
    WRITE_TO(description, " (");
    TEMPORARY_TEXT(bitrate)
    WRITE_TO(bitrate, "%d.%01d", pBitsPerSecond/1000, (pSampleRate%1000)/100);
    Localisation::roman_t(description, LD, I"Index.Elements.Fi.BitRate", bitrate);
    DISCARD_TEXT(bitrate)
    WRITE_TO(description, ")");

§1.2.1.2. Render a table row for the sound1.2.1.2 =

    HTML::first_html_column(OUT, THUMBNAIL_WIDTH+10);
    if (format_found == 0) {
        HTML_TAG_WITH("img", "border=\"0\" src=\"inform:/doc_images/image_problem.png\"");
    } else if (preview) {
        HTML_OPEN_WITH("embed",
            "src=\"file:%f\" width=\"%d\" height=\"64\" "
            "autostart=\"false\" volume=\"50%%\" mastersound",
            F, THUMBNAIL_WIDTH);
        HTML_CLOSE("embed");
    } else {
        HTML_TAG_WITH("img", "border=\"0\" src=\"inform:/doc_images/sound_okay.png\"");
    }
    WRITE("&nbsp;");
    HTML::next_html_column(OUT, 0);
    WRITE("%S", Metadata::required_textual(pack, I"^name"));
    IndexUtilities::link_package(OUT, pack);
    HTML_TAG("br");
    if (Str::len(description) > 0) {
        WRITE("%S", description);
        HTML_TAG("br");
    }
    Localisation::roman_ti(description, LD, I"Index.Elements.Fi.Resource",
        Filenames::get_leafname(F), (int) id);
    HTML::end_html_row(OUT);

§1.3. Internal files.

define EXTERNAL_TEXT_FILE_NFSMF 0
define EXTERNAL_BINARY_FILE_NFSMF 1
define INTERNAL_TEXT_FILE_NFSMF 2
define INTERNAL_BINARY_FILE_NFSMF 3

Index the internal files1.3 =

    HTML_OPEN("p");
    Localisation::bold(OUT, LD, I"Index.Elements.Fi.ListOfInternalFiles");
    HTML_CLOSE("p");
    if (InterNodeList::array_len(inv->internal_file_nodes) == 0) {
        HTML_OPEN("p");
        Localisation::roman(OUT, LD, I"Index.Elements.Fi.NoInternalFiles");
        HTML_CLOSE("p");
    } else {
        Tabulate the internal files1.3.1;
    }

§1.3.1. Tabulate the internal files1.3.1 =

    HTML::begin_html_table(OUT, NULL, TRUE, 0, 0, 0, 0, 0);
    inter_package *pack;
    LOOP_OVER_INVENTORY_PACKAGES(pack, i, inv->internal_file_nodes) {
        inter_ti id = Metadata::read_numeric(pack, I"^resource_id");
        text_stream *filename_as_text = Metadata::required_textual(pack, I"^filename");
        filename *F = Filenames::from_text(filename_as_text);
        inter_ti format = Metadata::read_numeric(pack, I"^internal_file_format");
        TEMPORARY_TEXT(description)
        TEMPORARY_TEXT(preview)
        Find internal file preview1.3.1.1;
        Render a table row for the internal file1.3.1.2;
        DISCARD_TEXT(description)
        DISCARD_TEXT(preview)
    }
    HTML::end_html_table(OUT);

§1.3.1.1. Find internal file preview1.3.1.1 =

    FILE *INTERNAL_FILE = Filenames::fopen(F, "rb");
    if (INTERNAL_FILE) {
        switch (format) {
            case INTERNAL_TEXT_FILE_NFSMF:
                WRITE_TO(description, "%S", filename_as_text);
                Offer a textual preview1.3.1.1.1;
                break;
            case INTERNAL_BINARY_FILE_NFSMF:
                WRITE_TO(description, "%S", filename_as_text);
                Offer a binary preview1.3.1.1.2;
                break;
        }
    } else {
        Localisation::italic(description, LD, I"Index.Elements.Fi.MissingInternalFile");
    }

§1.3.1.1.1. Offer a textual preview1.3.1.1.1 =

    for (int row = 0; row < 6; ) {
        int col = 0;
        unsigned int B, line_ended = FALSE;
        while (BinaryFiles::read_int8(INTERNAL_FILE, &B)) {
            if ((B == 10) || (B == 13)) { line_ended = TRUE; break; }
            if (col++ >= 80) { WRITE_TO(preview, "[...]"); line_ended = TRUE; break; }
            if ((B >= 0x20) && (B <= 0x7E)) WRITE_TO(preview, "%c", B);
            else WRITE_TO(preview, "?");
        }
        if (line_ended == FALSE) break;
        if (col > 0) row++;
        WRITE_TO(preview, "\n");
    }

§1.3.1.1.2. Offer a binary preview1.3.1.1.2 =

    for (int row = 0; row < 6; row++) {
        unsigned int bytes[16];
        for (int col = 0; col < 16; col++) {
            if (!BinaryFiles::read_int8(INTERNAL_FILE, &(bytes[col]))) {
                for (; col < 16; col++) bytes[col] = 256;
                row = 6; break;
            }
        }
        for (int col = 0; col < 16; col++) {
            if (bytes[col] < 256) WRITE_TO(preview, "%02x ", bytes[col]);
            else WRITE_TO(preview, ".. ");
        }
        for (int col = 0; col < 16; col++) {
            if ((bytes[col] >= 0x20) && (bytes[col] <= 0x7E)) WRITE_TO(preview, "%c", bytes[col]);
            else WRITE_TO(preview, ".");
        }
        WRITE_TO(preview, "\n");
    }

§1.3.1.2. Render a table row for the internal file1.3.1.2 =

    HTML::first_html_column(OUT, THUMBNAIL_WIDTH+10);
    if (Str::len(description) == 0) {
        HTML_TAG_WITH("img", "border=\"0\" src=\"inform:/doc_images/image_problem.png\"");
    } else {
        HTML_TAG_WITH("img", "border=\"0\" src=\"inform:/doc_images/data.png\"");
    }
    WRITE("&nbsp;");
    HTML::next_html_column(OUT, 0);
    HTML_OPEN_WITH("p", "class=\"hang\"");
    switch (format) {
        case INTERNAL_TEXT_FILE_NFSMF:   WRITE("(text) "); break;
        case INTERNAL_BINARY_FILE_NFSMF: WRITE("(binary) "); break;
    }
    WRITE("%S", Metadata::required_textual(pack, I"^name"));
    IndexUtilities::link_package(OUT, pack);
    if (Str::len(description) > 0) {
        HTML_TAG("br");
        WRITE("%S", description);
    }
    HTML_CLOSE("p");
    if (Str::len(preview) > 0) {
        HTML_OPEN("pre");
        int N = Streams::get_indentation(OUT);
        Streams::set_indentation(OUT, 0);
        WRITE("\n%S", preview);
        Streams::set_indentation(OUT, N);
        HTML_CLOSE("pre");
        WRITE("\n");
    }
    HTML_OPEN("p");
    Localisation::roman_ti(description, LD, I"Index.Elements.Fi.Resource",
        Filenames::get_leafname(F), (int) id);
    HTML_CLOSE("p");
    HTML::end_html_row(OUT);

§1.4. Files. This is more or less perfunctory, but still of some use, if only as a list.

Index the files1.4 =

    HTML_OPEN("p");
    Localisation::bold(OUT, LD, I"Index.Elements.Fi.ListOfFiles");
    HTML_CLOSE("p");
    if (InterNodeList::array_len(inv->file_nodes) == 0) {
        HTML_OPEN("p");
        Localisation::roman(OUT, LD, I"Index.Elements.Fi.NoFiles");
        HTML_CLOSE("p");
    } else {
        Tabulate the files1.4.1;
    }

§1.4.1. Tabulate the files1.4.1 =

    HTML::begin_html_table(OUT, NULL, TRUE, 0, 0, 0, 0, 0);
    inter_package *pack;
    LOOP_OVER_INVENTORY_PACKAGES(pack, i, inv->file_nodes) {
        HTML::first_html_column(OUT, THUMBNAIL_WIDTH+10);
        int is_binary = (int) Metadata::read_optional_numeric(pack, I"^is_binary");
        if (is_binary) {
            HTML_TAG_WITH("img", "border=\"0\" src=\"inform:/doc_images/exf_binary.png\"");
        } else {
            HTML_TAG_WITH("img", "border=\"0\" src=\"inform:/doc_images/exf_text.png\"");
        }
        WRITE("&nbsp;");
        HTML::next_html_column(OUT, 0);
        WRITE("%S", Metadata::required_textual(pack, I"^name"));
        IndexUtilities::link_package(OUT, pack);
        HTML_TAG("br");
        if (is_binary)
            Localisation::roman_t(OUT, LD, I"Index.Elements.Fi.BinaryFile",
                Metadata::required_textual(pack, I"^leafname"));
        else
            Localisation::roman_t(OUT, LD, I"Index.Elements.Fi.TextFile",
                Metadata::required_textual(pack, I"^leafname"));
        WRITE(" - ");
        if (Metadata::read_optional_numeric(pack, I"^file_owned")) {
            Localisation::roman(OUT, LD, I"Index.Elements.Fi.FileOwnedByThis");
        } else if (Metadata::read_optional_numeric(pack, I"^file_owned_by_other")) {
            Localisation::roman(OUT, LD, I"Index.Elements.Fi.FileOwnedByOther");
        } else {
            Localisation::roman_t(OUT, LD, I"Index.Elements.Fi.FileOwnedBy",
                Metadata::required_textual(pack, I"^file_owner"));
        }
        HTML::end_html_row(OUT);
    }
    HTML::end_html_table(OUT);