123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901 |
- /* ManPages.cpp
- *
- * Copyright (C) 1996-2018 Paul Boersma
- *
- * This code is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This code is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this work. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <ctype.h>
- #include "ManPages.h"
- #include "../kar/longchar.h"
- #include "Interpreter.h"
- #include "praat.h"
- Thing_implement (ManPages, Daata, 0);
- #define LONGEST_FILE_NAME 55
- static bool isAllowedFileNameCharacter (char32 c) {
- return isalnum ((int) c) || c == U'_' || c == U'-' || c == U'+';
- }
- static bool isSingleWordCharacter (char32 c) {
- return isalnum ((int) c) || c == U'_';
- }
- static integer lookUp_unsorted (ManPages me, conststring32 title);
- void structManPages :: v_destroy () noexcept {
- /*
- A ManPages object is the ambiguous owner of its paragraphs.
- */
- if (our dynamic) {
- for (integer ipage = 1; ipage <= our pages.size; ipage ++) {
- ManPage page = our pages.at [ipage];
- if (page -> paragraphs) {
- ManPage_Paragraph par;
- for (par = page -> paragraphs; (int) par -> type != 0; par ++)
- Melder_free (par -> text); // not an autostring32, because it can be a string literal (if not dynamic)
- NUMvector_free <struct structManPage_Paragraph> (page -> paragraphs, 0);
- }
- if (ipage == 1) {
- NUMvector_free <integer> (page -> linksHither, 1);
- NUMvector_free <integer> (page -> linksThither, 1);
- }
- }
- }
- ManPages_Parent :: v_destroy ();
- }
- static conststring32 extractLink (conststring32 text, const char32 *p, char32 *link) {
- char32 *to = link, *max = link + 300;
- if (! p) p = text;
- /*
- * Search for next '@' that is not in a backslash sequence.
- */
- for (;;) {
- p = str32chr (p, U'@');
- if (! p) return nullptr; // no more '@'
- if (p - text <= 0 || (p [-1] != U'\\' && (p - text <= 1 || p [-2] != U'\\'))) break;
- p ++;
- }
- Melder_assert (*p == U'@');
- if (p [1] == U'@') {
- const char32 *from = p + 2;
- while (*from != U'@' && *from != U'|' && *from != U'\0') {
- if (to >= max) {
- Melder_throw (U"(ManPages::grind:) Link starting with \"@@\" is too long:\n", text);
- }
- *to ++ = *from ++;
- }
- if (*from == U'|') { from ++; while (*from != U'@' && *from != U'\0') from ++; }
- if (*from) p = from + 1; else p = from; /* Skip '@' but not '\0'. */
- } else {
- const char32 *from = p + 1;
- while (isSingleWordCharacter (*from)) {
- if (to >= max) {
- Melder_throw (U"(ManPages::grind:) Link starting with \"@@\" is too long:\n", text);
- }
- *to ++ = *from ++;
- }
- p = from;
- }
- *to = U'\0';
- return p;
- }
- static void readOnePage (ManPages me, MelderReadText text) {
- autostring32 title;
- try {
- title = texgetw16 (text);
- } catch (MelderError) {
- Melder_throw (U"Cannot find page title.");
- }
- /*
- Check whether a page with this title is already present.
- */
- if (lookUp_unsorted (me, title.get())) {
- return;
- }
- autoManPage autopage = Thing_new (ManPage);
- autopage -> title = title.move();
- /*
- Add the page early, so that lookUp can find it.
- */
- ManPage page = my pages. addItem_move (autopage.move());
- try {
- page -> author = texgetw16 (text);
- } catch (MelderError) {
- Melder_throw (U"Cannot find author.");
- }
- try {
- page -> date = texgetu32 (text);
- } catch (MelderError) {
- Melder_throw (U"Cannot find date.");
- }
- try {
- page -> recordingTime = texgetr64 (text);
- } catch (MelderError) {
- Melder_throw (U"Cannot find recording time.");
- }
- page -> paragraphs = NUMvector <struct structManPage_Paragraph> (0, 500);
- ManPage_Paragraph par = & page -> paragraphs [0];
- for (;; par ++) {
- char32 link [501], fileName [256];
- try {
- par -> type = (kManPage_type) texgete8 (text, (enum_generic_getValue) kManPage_type_getValue);
- } catch (MelderError) {
- if (Melder_hasError (U"end of text")) {
- Melder_clearError ();
- break;
- } else {
- throw;
- }
- }
- if (par -> type == kManPage_type::SCRIPT) {
- par -> width = texgetr64 (text);
- par -> height = texgetr64 (text);
- }
- try {
- par -> text = texgetw16 (text).transfer();
- } catch (MelderError) {
- Melder_throw (U"Cannot find text.");
- }
- for (const char32 *plink = extractLink (par -> text, nullptr, link); plink != nullptr; plink = extractLink (par -> text, plink, link)) {
- /*
- * Now, `link' contains the link text, with spaces and all.
- * Transform it into a file name.
- */
- structMelderFile file2 { };
- if (link [0] == U'\\' && link [1] == U'F' && link [2] == U'I') {
- /*
- * A link to a sound file: see if it exists.
- */
- MelderDir_relativePathToFile (& my rootDirectory, link + 3, & file2);
- if (! MelderFile_exists (& file2)) {
- Melder_warning (U"Cannot find sound file ", MelderFile_messageName (& file2), U".");
- }
- } else if (link [0] == U'\\' && link [1] == U'S' && link [2] == U'C') {
- /*
- * A link to a script: see if it exists.
- */
- char32 *p = link + 3;
- if (*p == '\"') {
- char32 *q = fileName;
- p ++;
- while (*p != U'\"' && *p != U'\0') * q ++ = * p ++;
- *q = '\0';
- } else {
- char32 *q = fileName;
- while (*p != U' ' && *p != U'\0') * q ++ = * p ++; // one word, up to the next space
- *q = '\0';
- }
- MelderDir_relativePathToFile (& my rootDirectory, fileName, & file2);
- if (! MelderFile_exists (& file2)) {
- Melder_warning (U"Cannot find script ", MelderFile_messageName (& file2), U".");
- }
- my executable = true;
- } else {
- char32 *q;
- /*
- * A link to another page: follow it.
- */
- for (q = link; *q; q ++) if (! isAllowedFileNameCharacter (*q)) *q = U'_';
- Melder_sprint (fileName,256, link, U".man");
- MelderDir_getFile (& my rootDirectory, fileName, & file2);
- try {
- autoMelderReadText text2 = MelderReadText_createFromFile (& file2);
- try {
- readOnePage (me, text2.get());
- } catch (MelderError) {
- Melder_throw (U"File ", & file2, U".");
- }
- } catch (MelderError) {
- /*
- * Second try: with upper case.
- */
- Melder_clearError ();
- link [0] = Melder_toUpperCase (link [0]);
- Melder_sprint (fileName,256, link, U".man");
- MelderDir_getFile (& my rootDirectory, fileName, & file2);
- autoMelderReadText text2 = MelderReadText_createFromFile (& file2);
- try {
- readOnePage (me, text2.get());
- } catch (MelderError) {
- Melder_throw (U"File ", & file2, U".");
- }
- }
- }
- }
- }
- ++ par; // room for the last paragraph (because counting starts at 0)
- ++ par; // room for the final zero-type paragraph
- page -> paragraphs = (ManPage_Paragraph) Melder_realloc (page -> paragraphs, (int64) sizeof (struct structManPage_Paragraph) * (par - page -> paragraphs));
- }
- void structManPages :: v_readText (MelderReadText text, int /*formatVersion*/) {
- our dynamic = true;
- MelderDir_copy (& Data_directoryBeingRead, & rootDirectory);
- readOnePage (this, text);
- }
- autoManPages ManPages_create () {
- autoManPages me = Thing_new (ManPages);
- return me;
- }
- void ManPages_addPage (ManPages me, conststring32 title, conststring32 author, integer date,
- struct structManPage_Paragraph paragraphs [])
- {
- autoManPage page = Thing_new (ManPage);
- page -> title = Melder_dup (title);
- page -> paragraphs = & paragraphs [0];
- page -> author = Melder_dup (author);
- page -> date = date;
- my pages. addItem_move (page.move());
- }
- static int pageCompare (const void *first, const void *second) {
- ManPage me = * (ManPage *) first, thee = * (ManPage *) second;
- const char32 *p = & my title [0], *q = & thy title [0];
- for (;;) {
- const char32 plower = Melder_toLowerCase (*p), qlower = Melder_toLowerCase (*q);
- if (plower < qlower) return -1;
- if (plower > qlower) return 1;
- if (plower == U'\0') return str32cmp (my title.get(), thy title.get());
- p ++, q ++;
- }
- return 0; // should not occur
- }
- static integer lookUp_unsorted (ManPages me, conststring32 title) {
- /*
- First try to match an unaltered 'title' with the titles of the man pages.
- */
- for (integer i = 1; i <= my pages.size; i ++) {
- ManPage page = my pages.at [i];
- if (str32equ (page -> title.get(), title)) return i;
- }
- /*
- If that fails, try to find the upper-case variant.
- */
- if (Melder_isLowerCaseLetter (title [0])) {
- char32 upperTitle [300];
- Melder_sprint (upperTitle,300, title);
- upperTitle [0] = Melder_toUpperCase (upperTitle [0]);
- for (integer i = 1; i <= my pages.size; i ++) {
- ManPage page = my pages.at [i];
- if (str32equ (page -> title.get(), upperTitle)) return i;
- }
- }
- return 0;
- }
- static integer lookUp_sorted (ManPages me, conststring32 title) {
- static autoManPage dummy;
- ManPage *page;
- if (! dummy) dummy = Thing_new (ManPage);
- dummy -> title = Melder_dup (title);
- page = (ManPage *) bsearch (& dummy, & my pages.at [1], integer_to_uinteger (my pages.size), sizeof (ManPage), pageCompare); // noexcept
- if (page) return (page - & my pages.at [1]) + 1;
- if (Melder_isLowerCaseLetter (title [0]) || Melder_isUpperCaseLetter (title [0])) {
- char32 caseSwitchedTitle [300];
- Melder_sprint (caseSwitchedTitle,300, title);
- caseSwitchedTitle [0] = Melder_isLowerCaseLetter (title [0]) ? Melder_toUpperCase (caseSwitchedTitle [0]) : Melder_toLowerCase (caseSwitchedTitle [0]);
- dummy -> title = Melder_dup (caseSwitchedTitle);
- page = (ManPage *) bsearch (& dummy, & my pages.at [1], integer_to_uinteger (my pages.size), sizeof (ManPage), pageCompare); // noexcept
- if (page) return (page - & my pages.at [1]) + 1;
- }
- return 0;
- }
- static void grind (ManPages me) {
- integer ndangle = 0, jpage, grandNlinks, ilinkHither, ilinkThither;
- integer *grandLinksHither, *grandLinksThither;
- qsort (& my pages.at [1], integer_to_uinteger (my pages.size), sizeof (ManPage), pageCompare);
- /*
- * First pass: count and check links: fill in nlinksHither and nlinksThither.
- */
- grandNlinks = 0;
- for (integer ipage = 1; ipage <= my pages.size; ipage ++) {
- ManPage page = my pages.at [ipage];
- for (integer ipar = 0; (int) page -> paragraphs [ipar]. type != 0; ipar ++) {
- conststring32 text = page -> paragraphs [ipar]. text;
- const char32 *p;
- char32 link [301];
- if (text) for (p = extractLink (text, nullptr, link); p != nullptr; p = extractLink (text, p, link)) {
- if (link [0] == U'\\' && ((link [1] == U'F' && link [2] == U'I') || (link [1] == U'S' && link [2] == U'C')))
- continue; // ignore "FILE" links
- if ((jpage = lookUp_sorted (me, link)) != 0) {
- page -> nlinksThither ++;
- my pages.at [jpage] -> nlinksHither ++;
- grandNlinks ++;
- } else {
- MelderInfo_writeLine (U"Page \"", page -> title.get(), U"\" contains a dangling link to \"", link, U"\".");
- ndangle ++;
- }
- }
- }
- }
- if (ndangle) {
- MelderInfo_close ();
- Melder_warning (U"(ManPages::grind:) ", ndangle, U" dangling links encountered. See console window.");
- }
- /*
- * Second pass: allocate memory: fill in linksHither and linksThither.
- * Some optimization required: use only two mallocs.
- * Forget nlinksHither and nlinksThither.
- */
- if (grandNlinks == 0) { my ground = true; return; }
- if (! (grandLinksHither = NUMvector <integer> (1, grandNlinks)) || ! (grandLinksThither = NUMvector <integer> (1, grandNlinks))) {
- Melder_flushError ();
- return;
- }
- ilinkHither = ilinkThither = 0;
- for (integer ipage = 1; ipage <= my pages.size; ipage ++) {
- ManPage page = my pages.at [ipage];
- page -> linksHither = grandLinksHither + ilinkHither;
- page -> linksThither = grandLinksThither + ilinkThither;
- ilinkHither += page -> nlinksHither;
- ilinkThither += page -> nlinksThither;
- page -> nlinksHither = 0;
- page -> nlinksThither = 0;
- }
- Melder_assert (ilinkHither == grandNlinks && ilinkThither == grandNlinks);
- /*
- * Third pass: remember the links: fill in linksThither [1..nlinksThither] and linksHither [1..nlinksHither].
- * Rebuild nlinksHither and nlinksThither.
- */
- for (integer ipage = 1; ipage <= my pages.size; ipage ++) {
- ManPage page = my pages.at [ipage];
- for (int ipar = 0; (int) page -> paragraphs [ipar]. type != 0; ipar ++) {
- conststring32 text = page -> paragraphs [ipar]. text;
- const char32 *p;
- char32 link [301];
- if (text) for (p = extractLink (text, nullptr, link); p != nullptr; p = extractLink (text, p, link)) {
- if (link [0] == U'\\' && ((link [1] == U'F' && link [2] == U'I') || (link [1] == U'S' && link [2] == U'C')))
- continue; // ignore "FILE" links
- if ((jpage = lookUp_sorted (me, link)) != 0) {
- bool alreadyPresent = false;
- for (int ilink = 1; ilink <= page -> nlinksThither; ilink ++) {
- if (page -> linksThither [ilink] == jpage) {
- alreadyPresent = true;
- break;
- }
- }
- if (! alreadyPresent) {
- ManPage otherPage = my pages.at [jpage];
- page -> linksThither [++ page -> nlinksThither] = jpage;
- otherPage -> linksHither [++ otherPage -> nlinksHither] = ipage;
- }
- }
- }
- }
- }
- my ground = true;
- }
- integer ManPages_uniqueLinksHither (ManPages me, integer ipage) {
- ManPage page = my pages.at [ipage];
- integer result = page -> nlinksHither;
- for (integer ilinkHither = 1; ilinkHither <= page -> nlinksHither; ilinkHither ++) {
- integer link = page -> linksHither [ilinkHither];
- for (integer ilinkThither = 1; ilinkThither <= page -> nlinksThither; ilinkThither ++)
- if (page -> linksThither [ilinkThither] == link) { result --; break; }
- }
- return result;
- }
- integer ManPages_lookUp (ManPages me, conststring32 title) {
- if (! my ground) grind (me);
- return lookUp_sorted (me, title);
- }
- static integer ManPages_lookUp_caseSensitive (ManPages me, conststring32 title) {
- if (! my ground) grind (me);
- for (integer i = 1; i <= my pages.size; i ++) {
- ManPage page = my pages.at [i];
- if (str32equ (page -> title.get(), title)) return i;
- }
- return 0;
- }
- conststring32vector ManPages_getTitles (ManPages me) {
- if (! my ground) grind (me);
- if (! my titles) {
- my titles = autostring32vector (my pages.size);
- for (integer i = 1; i <= my pages.size; i ++) {
- ManPage page = my pages.at [i];
- my titles [i] = Melder_dup (page -> title.get());
- }
- }
- return my titles.get();
- }
- static const struct stylesInfo {
- conststring32 htmlIn, htmlOut;
- } stylesInfo [] = {
- { nullptr, nullptr },
- /* INTRO: */ { U"<p>", U"</p>" },
- /* ENTRY: */ { U"<h3>", U"</h3>" },
- /* NORMAL: */ { U"<p>", U"</p>" },
- /* LIST_ITEM: */ { U"<dd>", U"" },
- /* TAG: */ { U"<dt>", U"" },
- /* DEFINITION: */ { U"<dd>", U"" },
- /* CODE: */ { U"<code>", U"<br></code>" },
- /* PROTOTYPE: */ { U"<p>", U"</p>" },
- /* FORMULA: */ { U"<table width=\"100%\"><tr><td align=middle>", U"</table>" },
- /* PICTURE: */ { U"<p>", U"</p>" },
- /* SCRIPT: */ { U"<p>", U"</p>" },
- /* LIST_ITEM1: */ { U"<dd> ", U"" },
- /* LIST_ITEM2: */ { U"<dd> ", U"" },
- /* LIST_ITEM3: */ { U"<dd> ", U"" },
- /* TAG1: */ { U"<dt> ", U"" },
- /* TAG2: */ { U"<dt> ", U"" },
- /* TAG3: */ { U"<dt> ", U"" },
- /* DEFINITION1: */ { U"<dd> ", U"" },
- /* DEFINITION2: */ { U"<dd> ", U"" },
- /* DEFINITION3: */ { U"<dd> ", U"" },
- /* CODE1: */ { U"<code> ", U"<br></code>" },
- /* CODE2: */ { U"<code> ", U"<br></code>" },
- /* CODE3: */ { U"<code> ", U"<br></code>" },
- /* CODE4: */ { U"<code> ", U"<br></code>" },
- /* CODE5: */ { U"<code> ", U"<br></code>" }
- };
- static void writeParagraphsAsHtml (ManPages me, MelderFile file, ManPage_Paragraph paragraphs, MelderString *buffer) {
- integer numberOfPictures = 0;
- bool inList = false, inItalic = false, inBold = false;
- bool inSub = false, inCode = false, inSuper = false, ul = false, inSmall = false;
- bool wordItalic = false, wordBold = false, wordCode = false, letterSuper = false;
- for (ManPage_Paragraph paragraph = paragraphs; (int) paragraph -> type != 0; paragraph ++) {
- const char32 *p = & paragraph -> text [0];
- bool inTable, inPromptedTable;
- bool isListItem = paragraph -> type == kManPage_type::LIST_ITEM ||
- (paragraph -> type >= kManPage_type::LIST_ITEM1 && paragraph -> type <= kManPage_type::LIST_ITEM3);
- bool isTag = paragraph -> type == kManPage_type::TAG ||
- (paragraph -> type >= kManPage_type::TAG1 && paragraph -> type <= kManPage_type::TAG3);
- bool isDefinition = paragraph -> type == kManPage_type::DEFINITION ||
- (paragraph -> type >= kManPage_type::DEFINITION1 && paragraph -> type <= kManPage_type::DEFINITION3);
- /*bool isCode = paragraph -> type == kManPage_type::CODE ||
- (paragraph -> type >= kManPage_type::CODE1 && paragraph -> type <= kManPage_type::CODE5);*/
- if (paragraph -> type == kManPage_type::PICTURE) {
- numberOfPictures ++;
- structMelderFile pngFile;
- MelderFile_copy (file, & pngFile);
- pngFile. path [str32len (pngFile. path) - 5] = U'\0'; // delete extension ".html"
- str32cat (pngFile. path, Melder_cat (U"_", numberOfPictures, U".png"));
- {// scope
- autoGraphics graphics = Graphics_create_pngfile (& pngFile, 300, 0.0, paragraph -> width, 0.0, paragraph -> height);
- Graphics_setFont (graphics.get(), kGraphics_font::TIMES);
- Graphics_setFontStyle (graphics.get(), 0);
- Graphics_setFontSize (graphics.get(), 12);
- Graphics_setWrapWidth (graphics.get(), 0);
- Graphics_setViewport (graphics.get(), 0.0, paragraph -> width, 0.0, paragraph -> height);
- paragraph -> draw (graphics.get());
- Graphics_setViewport (graphics.get(), 0, 1, 0, 1);
- Graphics_setWindow (graphics.get(), 0, 1, 0, 1);
- Graphics_setTextAlignment (graphics.get(), Graphics_LEFT, Graphics_BOTTOM);
- }
- MelderString_append (buffer, Melder_cat (U"<p align=middle><img height=", paragraph -> height * 100,
- U" width=", paragraph -> width * 100, U" src=", MelderFile_name (& pngFile), U"></p>"));
- continue;
- }
- if (paragraph -> type == kManPage_type::SCRIPT) {
- autoInterpreter interpreter = Interpreter_createFromEnvironment (nullptr);
- numberOfPictures ++;
- structMelderFile pdfFile;
- MelderFile_copy (file, & pdfFile);
- pdfFile. path [str32len (pdfFile. path) - 5] = U'\0'; // delete extension ".html"
- str32cat (pdfFile. path, Melder_cat (U"_", numberOfPictures, U".pdf"));
- {// scope
- autoGraphics graphics = Graphics_create_pdffile (& pdfFile, 100, 0.0, paragraph -> width, 0.0, paragraph -> height);
- Graphics_setFont (graphics.get(), kGraphics_font::TIMES);
- Graphics_setFontStyle (graphics.get(), 0);
- Graphics_setFontSize (graphics.get(), 12);
- Graphics_setWrapWidth (graphics.get(), 0);
- static structPraatApplication praatApplication;
- static structPraatObjects praatObjects;
- static structPraatPicture praatPicture;
- theCurrentPraatApplication = & praatApplication;
- theCurrentPraatApplication -> batch = true;
- theCurrentPraatApplication -> topShell = theForegroundPraatApplication. topShell; // needed for UiForm_create () in dialogs
- theCurrentPraatObjects = (PraatObjects) & praatObjects;
- theCurrentPraatPicture = (PraatPicture) & praatPicture;
- theCurrentPraatPicture -> graphics = graphics.get(); // FIXME: should be move()?
- theCurrentPraatPicture -> font = (int) kGraphics_font::TIMES;
- theCurrentPraatPicture -> fontSize = 12;
- theCurrentPraatPicture -> lineType = Graphics_DRAWN;
- theCurrentPraatPicture -> colour = Graphics_BLACK;
- theCurrentPraatPicture -> lineWidth = 1.0;
- theCurrentPraatPicture -> arrowSize = 1.0;
- theCurrentPraatPicture -> speckleSize = 1.0;
- theCurrentPraatPicture -> x1NDC = 0.0;
- theCurrentPraatPicture -> x2NDC = paragraph -> width;
- theCurrentPraatPicture -> y1NDC = 0.0;
- theCurrentPraatPicture -> y2NDC = paragraph -> height;
- Graphics_setViewport (graphics.get(), theCurrentPraatPicture -> x1NDC, theCurrentPraatPicture -> x2NDC, theCurrentPraatPicture -> y1NDC, theCurrentPraatPicture -> y2NDC);
- Graphics_setWindow (graphics.get(), 0.0, 1.0, 0.0, 1.0);
- integer x1DC, y1DC, x2DC, y2DC;
- Graphics_WCtoDC (graphics.get(), 0.0, 0.0, & x1DC, & y2DC);
- Graphics_WCtoDC (graphics.get(), 1.0, 1.0, & x2DC, & y1DC);
- Graphics_resetWsViewport (graphics.get(), x1DC, x2DC, y1DC, y2DC);
- Graphics_setWsWindow (graphics.get(), 0.0, paragraph -> width, 0.0, paragraph -> height);
- theCurrentPraatPicture -> x1NDC = 0.0;
- theCurrentPraatPicture -> x2NDC = paragraph -> width;
- theCurrentPraatPicture -> y1NDC = 0.0;
- theCurrentPraatPicture -> y2NDC = paragraph -> height;
- Graphics_setViewport (graphics.get(), theCurrentPraatPicture -> x1NDC, theCurrentPraatPicture -> x2NDC, theCurrentPraatPicture -> y1NDC, theCurrentPraatPicture -> y2NDC);
- {// scope
- autoMelderProgressOff progress;
- autoMelderWarningOff warning;
- autoMelderSaveDefaultDir saveDir;
- if (! MelderDir_isNull (& my rootDirectory)) {
- Melder_setDefaultDir (& my rootDirectory);
- }
- try {
- autostring32 text = Melder_dup (p);
- Interpreter_run (interpreter.get(), text.get());
- } catch (MelderError) {
- trace (U"interpreter fails on ", pdfFile. path);
- Melder_flushError ();
- }
- }
- Graphics_setViewport (graphics.get(), 0.0, 1.0, 0.0, 1.0);
- Graphics_setWindow (graphics.get(), 0.0, 1.0, 0.0, 1.0);
- Graphics_setTextAlignment (graphics.get(), Graphics_LEFT, Graphics_BOTTOM);
- }
- structMelderFile tiffFile;
- MelderFile_copy (file, & tiffFile);
- tiffFile. path [str32len (tiffFile. path) - 5] = U'\0'; // delete extension ".html"
- str32cat (tiffFile. path, Melder_cat (U"_", numberOfPictures, U".png"));
- system (Melder_peek32to8 (Melder_cat (U"/usr/local/bin/gs -q -dNOPAUSE "
- "-r200x200 -sDEVICE=png16m -sOutputFile=", tiffFile.path,
- U" ", pdfFile. path, U" quit.ps")));
- MelderFile_delete (& pdfFile);
- MelderString_append (buffer, U"<p align=middle><img height=", paragraph -> height * 100,
- U" width=", paragraph -> width * 100, U" src=", MelderFile_name (& tiffFile), U"></p>");
- theCurrentPraatApplication = & theForegroundPraatApplication;
- theCurrentPraatObjects = & theForegroundPraatObjects;
- theCurrentPraatPicture = & theForegroundPraatPicture;
- continue;
- }
- if (isListItem || isTag || isDefinition) {
- if (! inList) {
- ul = isListItem && (p [0] == U'•' || (p [0] == U'\\' && p [1] == U'b' && p [2] == U'u'));
- MelderString_append (buffer, ul ? U"<ul>\n" : U"<dl>\n");
- inList = true;
- }
- if (ul) {
- if (p [0] == U'•' && p [1] == U' ') p += 1;
- if (p [0] == U'\\' && p [1] == U'b' && p [2] == U'u' && p [3] == U' ') p += 3;
- }
- MelderString_append (buffer, ul ? U"<li>" : stylesInfo [(int) paragraph -> type]. htmlIn, U"\n");
- } else {
- if (inList) {
- MelderString_append (buffer, ul ? U"</ul>\n" : U"</dl>\n");
- inList = ul = false;
- }
- MelderString_append (buffer, stylesInfo [(int) paragraph -> type]. htmlIn, U"\n");
- }
- inTable = !! str32chr (p, U'\t');
- if (inTable) {
- if (*p == U'\t') {
- MelderString_append (buffer, U"<table border=0 cellpadding=0 cellspacing=0><tr><td width=100 align=middle>");
- p ++;
- inPromptedTable = false;
- } else {
- MelderString_append (buffer, U"<table border=0 cellpadding=0 cellspacing=0><tr><td width=100 align=left>");
- inPromptedTable = true;
- }
- }
- /*
- * Leading spaces should be visible (mainly used in code fragments).
- */
- while (*p == U' ') {
- MelderString_append (buffer, U" ");
- p ++;
- }
- while (*p) {
- if (wordItalic && ! isSingleWordCharacter (*p)) { MelderString_append (buffer, U"</i>"); wordItalic = false; }
- if (wordBold && ! isSingleWordCharacter (*p)) { MelderString_append (buffer, U"</b>"); wordBold = false; }
- if (wordCode && ! isSingleWordCharacter (*p)) { MelderString_append (buffer, U"</code>"); wordCode = false; }
- if (*p == U'@') {
- static MelderString link, linkText;
- MelderString_empty (& link);
- MelderString_empty (& linkText);
- if (p [1] == U'@') {
- p += 2;
- while (*p != U'@' && *p != U'|' && *p != U'\0') MelderString_append (& link, *p++);
- if (*p == U'|') {
- p ++; // skip '|'
- while (*p != U'@' && *p != U'\0') {
- if (*p == U'^') {
- if (inSuper) {
- MelderString_append (& linkText, U"</sup>"); inSuper = false; p ++;
- } else if (p [1] == U'^') {
- MelderString_append (& linkText, U"<sup>"); inSuper = true; p += 2;
- } else {
- MelderString_append (& linkText, U"<sup>"); letterSuper = true; p ++;
- }
- } else {
- if (*p == U'\\') {
- char32 kar1 = *++p, kar2 = *++p;
- Longchar_Info info = Longchar_getInfo (kar1, kar2);
- if (info -> unicode < 127) {
- MelderString_appendCharacter (& linkText, info -> unicode ? info -> unicode : U'?');
- } else {
- MelderString_append (& linkText, U"&#", (int) info -> unicode, U";");
- }
- p ++;
- } else {
- if (*p < 127) {
- MelderString_appendCharacter (& linkText, *p);
- } else {
- MelderString_append (& linkText, U"&#", (int) *p, U";");
- }
- p ++;
- }
- if (letterSuper) {
- //if (wordItalic) { MelderString_append (buffer, U"</i>"); wordItalic = false; }
- //if (wordBold) { MelderString_append (buffer, U"</b>"); wordBold = false; }
- MelderString_append (& linkText, U"</sup>"); letterSuper = false;
- }
- }
- }
- } else {
- MelderString_copy (& linkText, link.string);
- }
- if (*p) p ++;
- } else {
- p ++;
- while (isSingleWordCharacter (*p) && *p != U'\0') MelderString_append (& link, *p++);
- MelderString_copy (& linkText, link.string);
- }
- /*
- * We write the link in the following format:
- * <a href="link.html">linkText</a>
- * If "link" (initial lower case) is not in the manual, we write "Link.html" instead.
- * All spaces and strange symbols in "link" are replaced by underscores,
- * because it will be a file name (see ManPages_writeAllToHtmlDir).
- * The file name will have no more than 30 or 60 characters, and no less than 1.
- */
- MelderString_append (buffer, U"<a href=\"");
- if (str32nequ (link.string, U"\\FI", 3)) {
- MelderString_append (buffer, link.string + 3); // file link
- } else {
- char32 *q = link.string;
- if (! ManPages_lookUp_caseSensitive (me, link.string)) {
- MelderString_appendCharacter (buffer, Melder_toUpperCase (link.string [0]));
- if (*q) q ++; // first letter already written
- }
- while (*q && q - link.string < LONGEST_FILE_NAME) {
- if (! isAllowedFileNameCharacter (*q)) MelderString_appendCharacter (buffer, U'_');
- else MelderString_appendCharacter (buffer, *q);
- q ++;
- }
- if (link.string [0] == U'\0') MelderString_appendCharacter (buffer, U'_'); /* Otherwise Mac problems or Unix invisibility. */
- MelderString_append (buffer, U".html");
- }
- MelderString_append (buffer, U"\">", linkText.string, U"</a>");
- } else if (*p == U'%') {
- if (inItalic) { MelderString_append (buffer, U"</i>"); inItalic = false; p ++; }
- else if (p [1] == U'%') { MelderString_append (buffer, U"<i>"); inItalic = true; p += 2; }
- else if (p [1] == U'#') { MelderString_append (buffer, U"<i><b>"); wordItalic = true; wordBold = true; p += 2; }
- else { MelderString_append (buffer, U"<i>"); wordItalic = true; p ++; }
- } else if (*p == U'_') {
- if (inSub) {
- /*if (wordItalic) { MelderString_append (buffer, U"</i>"); wordItalic = false; }
- if (wordBold) { MelderString_append (buffer, U"</b>"); wordBold = false; }*/
- MelderString_append (buffer, U"</sub>"); inSub = false; p ++;
- } else if (p [1] == U'_') {
- if (wordItalic) { MelderString_append (buffer, U"</i>"); wordItalic = false; }
- if (wordBold) { MelderString_append (buffer, U"</b>"); wordBold = false; }
- MelderString_append (buffer, U"<sub>"); inSub = true; p += 2;
- } else { MelderString_append (buffer, U"_"); p ++; }
- } else if (*p == U'#') {
- if (inBold) { MelderString_append (buffer, U"</b>"); inBold = false; p ++; }
- else if (p [1] == U'#') { MelderString_append (buffer, U"<b>"); inBold = true; p += 2; }
- else if (p [1] == U'%') { MelderString_append (buffer, U"<b><i>"); wordBold = true; wordItalic = true; p += 2; }
- else { MelderString_append (buffer, U"<b>"); wordBold = true; p ++; }
- } else if (*p == U'$') {
- if (inCode) { MelderString_append (buffer, U"</code>"); inCode = false; p ++; }
- else if (p [1] == U'$') { MelderString_append (buffer, U"<code>"); inCode = true; p += 2; }
- else { MelderString_append (buffer, U"<code>"); wordCode = true; p ++; }
- } else if (*p == U'^') {
- if (inSuper) {
- /*if (wordItalic) { MelderString_append (buffer, U"</i>"); wordItalic = false; }
- if (wordBold) { MelderString_append (buffer, U"</b>"); wordBold = false; }*/
- MelderString_append (buffer, U"</sup>"); inSuper = false; p ++;
- } else if (p [1] == U'^') {
- /*if (wordItalic) { MelderString_append (buffer, U"</i>"); wordItalic = false; }
- if (wordBold) { MelderString_append (buffer, U"</b>"); wordBold = false; }*/
- MelderString_append (buffer, U"<sup>"); inSuper = true; p += 2;
- } else {
- /*if (wordItalic) { MelderString_append (buffer, U"</i>"); wordItalic = false; }
- if (wordBold) { MelderString_append (buffer, U"</b>"); wordBold = false; }*/
- MelderString_append (buffer, U"<sup>"); letterSuper = true; p ++;
- }
- } else if (*p == U'}') {
- if (inSmall) { MelderString_append (buffer, U"</font>"); inSmall = false; p ++; }
- else { MelderString_append (buffer, U"}"); p ++; }
- } else if (*p == U'\\' && p [1] == U's' && p [2] == U'{') {
- MelderString_append (buffer, U"<font size=-1>"); inSmall = true; p += 3;
- } else if (*p == U'\t' && inTable) {
- if (inPromptedTable) {
- inPromptedTable = false;
- p ++; // skip one tab
- } else {
- MelderString_append (buffer, U"<td width=100 align=middle>"); p ++;
- }
- } else if (*p == U'<') {
- MelderString_append (buffer, U"<"); p ++;
- } else if (*p == U'>') {
- MelderString_append (buffer, U">"); p ++;
- } else if (*p == U'&') {
- MelderString_append (buffer, U"&"); p ++;
- } else {
- /*if (wordItalic && ! isSingleWordCharacter (*p)) { MelderString_append (buffer, U"</i>"); wordItalic = false; }
- if (wordBold && ! isSingleWordCharacter (*p)) { MelderString_append (buffer, U"</b>"); wordBold = false; }
- if (wordCode && ! isSingleWordCharacter (*p)) { MelderString_append (buffer, U"</code>"); wordCode = false; }*/
- if (*p == U'\\') {
- char32 kar1 = *++p, kar2 = *++p;
- Longchar_Info info = Longchar_getInfo (kar1, kar2);
- if (info -> unicode < 127) {
- MelderString_appendCharacter (buffer, info -> unicode ? info -> unicode : U'?');
- } else {
- MelderString_append (buffer, U"&#", (int) info -> unicode, U";");
- }
- p ++;
- } else {
- if (*p < 127) {
- MelderString_appendCharacter (buffer, *p);
- } else {
- MelderString_append (buffer, U"&#", (int) *p, U";");
- }
- p ++;
- }
- if (letterSuper) {
- if (wordItalic) { MelderString_append (buffer, U"</i>"); wordItalic = false; }
- if (wordBold) { MelderString_append (buffer, U"</b>"); wordBold = false; }
- MelderString_append (buffer, U"</sup>"); letterSuper = false;
- }
- }
- }
- if (inItalic || wordItalic) { MelderString_append (buffer, U"</i>"); inItalic = wordItalic = false; }
- if (inBold || wordBold) { MelderString_append (buffer, U"</b>"); inBold = wordBold = false; }
- if (inCode || wordCode) { MelderString_append (buffer, U"</code>"); inCode = wordCode = false; }
- if (inSub) { MelderString_append (buffer, U"</sub>"); inSub = false; }
- if (inSuper || letterSuper) { MelderString_append (buffer, U"</sup>"); inSuper = letterSuper = false; }
- if (inTable) { MelderString_append (buffer, U"</table>"); inTable = false; }
- MelderString_append (buffer, stylesInfo [(int) paragraph -> type]. htmlOut, U"\n");
- }
- if (inList) { MelderString_append (buffer, ul ? U"</ul>\n" : U"</dl>\n"); inList = false; }
- }
- static const conststring32 month [] =
- { U"", U"January", U"February", U"March", U"April", U"May", U"June",
- U"July", U"August", U"September", U"October", U"November", U"December" };
- static void writePageAsHtml (ManPages me, MelderFile file, integer ipage, MelderString *buffer) {
- ManPage page = my pages.at [ipage];
- ManPage_Paragraph paragraphs = page -> paragraphs;
- MelderString_append (buffer, U"<html><head><meta name=\"robots\" content=\"index,follow\">"
- U"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"
- U"<title>", page -> title.get(), U"</title></head><body bgcolor=\"#FFFFFF\">\n\n");
- MelderString_append (buffer, U"<table border=0 cellpadding=0 cellspacing=0><tr><td bgcolor=\"#CCCC00\">"
- U"<table border=4 cellpadding=9><tr><td align=middle bgcolor=\"#000000\">"
- U"<font face=\"Palatino,Times\" size=6 color=\"#999900\"><b>\n",
- page -> title.get(), U"\n</b></font></table></table>\n");
- writeParagraphsAsHtml (me, file, paragraphs, buffer);
- if (ManPages_uniqueLinksHither (me, ipage)) {
- integer ilink, jlink, lastParagraph = 0;
- while ((int) page -> paragraphs [lastParagraph]. type != 0) lastParagraph ++;
- if (lastParagraph > 0) {
- conststring32 text = page -> paragraphs [lastParagraph - 1]. text;
- if (text && text [0] != U'\0' && text [str32len (text) - 1] != U':')
- MelderString_append (buffer, U"<h3>Links to this page</h3>\n");
- }
- MelderString_append (buffer, U"<ul>\n");
- for (ilink = 1; ilink <= page -> nlinksHither; ilink ++) {
- integer link = page -> linksHither [ilink];
- bool alreadyShown = false;
- for (jlink = 1; jlink <= page -> nlinksThither; jlink ++)
- if (page -> linksThither [jlink] == link)
- alreadyShown = true;
- if (! alreadyShown) {
- ManPage linkingPage = my pages.at [page -> linksHither [ilink]];
- conststring32 title = linkingPage -> title.get();
- const char32 *p;
- MelderString_append (buffer, U"<li><a href=\"");
- for (p = & title [0]; *p; p ++) {
- if (p - title >= LONGEST_FILE_NAME) break;
- if (! isAllowedFileNameCharacter (*p)) MelderString_append (buffer, U"_");
- else MelderString_appendCharacter (buffer, *p);
- }
- if (title [0] == U'\0') MelderString_append (buffer, U"_");
- MelderString_append (buffer, U".html\">", title, U"</a>\n");
- }
- }
- MelderString_append (buffer, U"</ul>\n");
- }
- MelderString_append (buffer, U"<hr>\n<address>\n\t<p>© ", page -> author.get());
- if (page -> date) {
- integer date = page -> date;
- int imonth = date % 10000 / 100;
- if (imonth < 0 || imonth > 12) imonth = 0;
- MelderString_append (buffer, U", ", month [imonth], U" ", date % 100);
- MelderString_append (buffer, U", ", date / 10000);
- }
- MelderString_append (buffer, U"</p>\n</address>\n</body>\n</html>\n");
- }
- void ManPages_writeOneToHtmlFile (ManPages me, integer ipage, MelderFile file) {
- static MelderString buffer { };
- MelderString_empty (& buffer);
- writePageAsHtml (me, file, ipage, & buffer);
- MelderFile_writeText (file, buffer.string, kMelder_textOutputEncoding::UTF8);
- }
- void ManPages_writeAllToHtmlDir (ManPages me, conststring32 dirPath) {
- structMelderDir dir { };
- Melder_pathToDir (dirPath, & dir);
- for (integer ipage = 1; ipage <= my pages.size; ipage ++) {
- ManPage page = my pages.at [ipage];
- char32 fileName [256];
- Melder_assert (str32len (page -> title.get()) < 256 - 100);
- trace (U"page ", ipage, U": ", page -> title.get());
- Melder_sprint (fileName,256, page -> title.get());
- for (char32 *p = fileName; *p; p ++)
- if (! isAllowedFileNameCharacter (*p))
- *p = U'_';
- if (fileName [0] == U'\0')
- str32cpy (fileName, U"_"); // no empty file names please
- fileName [LONGEST_FILE_NAME] = U'\0';
- str32cat (fileName, U".html");
- static MelderString buffer { };
- MelderString_empty (& buffer);
- structMelderFile file { };
- MelderDir_getFile (& dir, fileName, & file);
- writePageAsHtml (me, & file, ipage, & buffer);
- /*
- * An optimization because reading is much faster than writing:
- * we write the file only if the old file is different or doesn't exist.
- */
- autostring32 oldText;
- try {
- oldText = MelderFile_readText (& file);
- } catch (MelderError) {
- Melder_clearError ();
- }
- if (! oldText // doesn't the file exist yet?
- || str32cmp (buffer.string, oldText.get())) // isn't the old file identical to the new text?
- {
- MelderFile_writeText (& file, buffer.string, kMelder_textOutputEncoding::UTF8); // then write the new text
- }
- }
- }
- /* End of file ManPages.cpp */
|