=> 5f9685010addd4a0f04c13f889856084381dd1c6
[1mdiff --git a/CMakeLists.txt b/CMakeLists.txt[m [1mindex a1037b0c..edee3d85 100644[m [1m--- a/CMakeLists.txt[m [1m+++ b/CMakeLists.txt[m [36m@@ -124,6 +124,8 @@[m [mset (SOURCES[m src/gmdocument.h[m src/gmrequest.c[m src/gmrequest.h[m [32m+[m[32m src/gmtypesetter.c[m [32m+[m[32m src/gmtypesetter.h[m src/gmutil.c[m src/gmutil.h[m src/gopher.c[m [1mdiff --git a/src/defs.h b/src/defs.h[m [1mindex 71719f7a..0da404bf 100644[m [1m--- a/src/defs.h[m [1m+++ b/src/defs.h[m [36m@@ -24,6 +24,12 @@[m [mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m [m #include "lang.h"[m [m [32m+[m[32menum iSourceFormat {[m [32m+[m[32m undefined_SourceFormat = -1,[m [32m+[m[32m gemini_SourceFormat = 0,[m [32m+[m[32m plainText_SourceFormat,[m [32m+[m[32m};[m [32m+[m enum iFileVersion {[m initial_FileVersion = 0,[m addedResponseTimestamps_FileVersion = 1,[m [1mdiff --git a/src/gmdocument.c b/src/gmdocument.c[m [1mindex 67adb9cc..f8d41172 100644[m [1m--- a/src/gmdocument.c[m [1m+++ b/src/gmdocument.c[m [36m@@ -21,6 +21,7 @@[m [mANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT[m SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m [m #include "gmdocument.h"[m [32m+[m[32m#include "gmtypesetter.h"[m #include "gmutil.h"[m #include "lang.h"[m #include "ui/color.h"[m [36m@@ -73,7 +74,7 @@[m [miDefineTypeConstruction(GmLink)[m [m struct Impl_GmDocument {[m iObject object;[m [31m- enum iGmDocumentFormat format;[m [32m+[m[32m enum iSourceFormat format;[m iString unormSource; /* unnormalized source */[m iString source; /* normalized source */[m iString url; /* for resolving relative links */[m [36m@@ -95,7 +96,7 @@[m [mstruct Impl_GmDocument {[m iDefineObjectConstruction(GmDocument)[m [m static enum iGmLineType lineType_GmDocument_(const iGmDocument *d, const iRangecc line) {[m [31m- if (d->format == plainText_GmDocumentFormat) {[m [32m+[m[32m if (d->format == plainText_SourceFormat) {[m return text_GmLineType;[m }[m return lineType_Rangecc(line);[m [36m@@ -305,7 +306,7 @@[m [mstatic void linkContentWasLaidOut_GmDocument_(iGmDocument *d, const iGmMediaInfo[m [m static iBool isNormalized_GmDocument_(const iGmDocument *d) {[m const iPrefs *prefs = prefs_App();[m [31m- if (d->format == plainText_GmDocumentFormat) {[m [32m+[m[32m if (d->format == plainText_SourceFormat) {[m return iTrue; /* tabs are always normalized in plain text */[m }[m if (startsWithCase_String(&d->url, "gemini:") && prefs->monospaceGemini) {[m [36m@@ -433,7 +434,7 @@[m [mstatic void doLayout_GmDocument_(iGmDocument *d) {[m enum iGmLineType prevType = text_GmLineType;[m enum iGmLineType prevNonBlankType = text_GmLineType;[m iBool followsBlank = iFalse;[m [31m- if (d->format == plainText_GmDocumentFormat) {[m [32m+[m[32m if (d->format == plainText_SourceFormat) {[m isPreformat = iTrue;[m isFirstText = iFalse;[m }[m [36m@@ -502,14 +503,14 @@[m [mstatic void doLayout_GmDocument_(iGmDocument *d) {[m if (contentLine.start == content.start) {[m prevType = type;[m }[m [31m- if (d->format == gemini_GmDocumentFormat &&[m [32m+[m[32m if (d->format == gemini_SourceFormat &&[m startsWithSc_Rangecc(line, "```", &iCaseSensitive)) {[m isPreformat = iFalse;[m addSiteBanner = iFalse; /* overrides the banner */[m continue;[m }[m run.preId = preId;[m [31m- run.font = (d->format == plainText_GmDocumentFormat ? regularMonospace_FontId : preFont);[m [32m+[m[32m run.font = (d->format == plainText_SourceFormat ? regularMonospace_FontId : preFont);[m indent = indents[type];[m }[m if (addSiteBanner) {[m [36m@@ -582,7 +583,7 @@[m [mstatic void doLayout_GmDocument_(iGmDocument *d) {[m }[m }[m /* Folded blocks are represented by a single run with the alt text. */[m [31m- if (isPreformat && d->format != plainText_GmDocumentFormat) {[m [32m+[m[32m if (isPreformat && d->format != plainText_SourceFormat) {[m const iGmPreMeta *meta = constAt_Array(&d->preMeta, preId - 1);[m if (meta->flags & folded_GmPreMetaFlag) {[m const iBool isBlank = isEmpty_Range(&meta->altText);[m [36m@@ -678,7 +679,7 @@[m [mstatic void doLayout_GmDocument_(iGmDocument *d) {[m pushBack_Array(&d->layout, &icon);[m }[m run.color = colors[type];[m [31m- if (d->format == plainText_GmDocumentFormat) {[m [32m+[m[32m if (d->format == plainText_SourceFormat) {[m run.color = colors[text_GmLineType];[m }[m /* Special formatting for the first paragraph (e.g., subtitle, introduction, or lede). */[m [36m@@ -707,8 +708,8 @@[m [mstatic void doLayout_GmDocument_(iGmDocument *d) {[m type == quote_GmLineType ? 4 : 0);[m }[m const iBool isWordWrapped =[m [31m- (d->format == plainText_GmDocumentFormat ? prefs->plainTextWrap : !isPreformat);[m [31m- if (isPreformat && d->format != plainText_GmDocumentFormat) {[m [32m+[m[32m (d->format == plainText_SourceFormat ? prefs->plainTextWrap : !isPreformat);[m [32m+[m[32m if (isPreformat && d->format != plainText_SourceFormat) {[m /* Remember the top left coordinates of the block (first line of block). */[m iGmPreMeta *meta = at_Array(&d->preMeta, preId - 1);[m if (~meta->flags & topLeft_GmPreMetaFlag) {[m [36m@@ -866,7 +867,7 @@[m [mstatic void doLayout_GmDocument_(iGmDocument *d) {[m }[m [m void init_GmDocument(iGmDocument *d) {[m [31m- d->format = gemini_GmDocumentFormat;[m [32m+[m[32m d->format = gemini_SourceFormat;[m init_String(&d->unormSource);[m init_String(&d->source);[m init_String(&d->url);[m [36m@@ -1397,7 +1398,7 @@[m [mvoid setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) {[m #endif[m }[m [m [31m-void setFormat_GmDocument(iGmDocument *d, enum iGmDocumentFormat format) {[m [32m+[m[32mvoid setFormat_GmDocument(iGmDocument *d, enum iSourceFormat format) {[m d->format = format;[m }[m [m [36m@@ -1439,7 +1440,7 @@[m [mstatic void normalize_GmDocument(iGmDocument *d) {[m iRangecc src = range_String(&d->source);[m iRangecc line = iNullRange;[m iBool isPreformat = iFalse;[m [31m- if (d->format == plainText_GmDocumentFormat) {[m [32m+[m[32m if (d->format == plainText_SourceFormat) {[m isPreformat = iTrue; /* Cannot be turned off. */[m }[m const int preTabWidth = 4; /* TODO: user-configurable parameter */[m [36m@@ -1467,7 +1468,7 @@[m [mstatic void normalize_GmDocument(iGmDocument *d) {[m }[m }[m appendCStr_String(normalized, "\n");[m [31m- if (d->format == gemini_GmDocumentFormat &&[m [32m+[m[32m if (d->format == gemini_SourceFormat &&[m lineType_GmDocument_(d, line) == preformatted_GmLineType) {[m isPreformat = iFalse;[m }[m [1mdiff --git a/src/gmdocument.h b/src/gmdocument.h[m [1mindex 1e54a16a..5137bb28 100644[m [1m--- a/src/gmdocument.h[m [1m+++ b/src/gmdocument.h[m [36m@@ -22,6 +22,7 @@[m [mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m [m #pragma once[m [m [32m+[m[32m#include "defs.h"[m #include "gmutil.h"[m #include "media.h"[m [m [36m@@ -148,12 +149,6 @@[m [miRangecc findLoc_GmRun (const iGmRun *, iInt2 pos);[m iDeclareClass(GmDocument)[m iDeclareObjectConstruction(GmDocument)[m [m [31m-enum iGmDocumentFormat {[m [31m- undefined_GmDocumentFormat = -1,[m [31m- gemini_GmDocumentFormat = 0,[m [31m- plainText_GmDocumentFormat,[m [31m-};[m [31m-[m enum iGmDocumentBanner {[m none_GmDocumentBanner,[m siteDomain_GmDocumentBanner,[m [36m@@ -166,7 +161,7 @@[m [menum iGmDocumentUpdate {[m };[m [m void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed);[m [31m-void setFormat_GmDocument (iGmDocument *, enum iGmDocumentFormat format);[m [32m+[m[32mvoid setFormat_GmDocument (iGmDocument *, enum iSourceFormat format);[m void setBanner_GmDocument (iGmDocument *, enum iGmDocumentBanner type);[m void setWidth_GmDocument (iGmDocument *, int width);[m void redoLayout_GmDocument (iGmDocument *);[m [1mdiff --git a/src/gmtypesetter.c b/src/gmtypesetter.c[m [1mnew file mode 100644[m [1mindex 00000000..29a1bd93[m [1m--- /dev/null[m [1m+++ b/src/gmtypesetter.c[m [36m@@ -0,0 +1,25 @@[m [32m+[m[32m/* Copyright 2021 Jaakko Keränen[m [32m+[m [32m+[m[32mRedistribution and use in source and binary forms, with or without[m [32m+[m[32mmodification, are permitted provided that the following conditions are met:[m [32m+[m [32m+[m[32m1. Redistributions of source code must retain the above copyright notice, this[m [32m+[m[32m list of conditions and the following disclaimer.[m [32m+[m[32m2. Redistributions in binary form must reproduce the above copyright notice,[m [32m+[m[32m this list of conditions and the following disclaimer in the documentation[m [32m+[m[32m and/or other materials provided with the distribution.[m [32m+[m [32m+[m[32mTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND[m [32m+[m[32mANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED[m [32m+[m[32mWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE[m [32m+[m[32mDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR[m [32m+[m[32mANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES[m [32m+[m[32m(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;[m [32m+[m[32mLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON[m [32m+[m[32mANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT[m [32m+[m[32m(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS[m [32m+[m[32mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m [32m+[m [32m+[m[32m#include "gmtypesetter.h"[m [32m+[m[32m#include "gmdocument.h"[m [32m+[m [1mdiff --git a/src/gmtypesetter.h b/src/gmtypesetter.h[m [1mnew file mode 100644[m [1mindex 00000000..aba351dd[m [1m--- /dev/null[m [1m+++ b/src/gmtypesetter.h[m [36m@@ -0,0 +1,41 @@[m [32m+[m[32m/* Copyright 2021 Jaakko Keränen [m [32m+[m [32m+[m[32mRedistribution and use in source and binary forms, with or without[m [32m+[m[32mmodification, are permitted provided that the following conditions are met:[m [32m+[m [32m+[m[32m1. Redistributions of source code must retain the above copyright notice, this[m [32m+[m[32m list of conditions and the following disclaimer.[m [32m+[m[32m2. Redistributions in binary form must reproduce the above copyright notice,[m [32m+[m[32m this list of conditions and the following disclaimer in the documentation[m [32m+[m[32m and/or other materials provided with the distribution.[m [32m+[m [32m+[m[32mTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND[m [32m+[m[32mANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED[m [32m+[m[32mWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE[m [32m+[m[32mDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR[m [32m+[m[32mANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES[m [32m+[m[32m(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;[m [32m+[m[32mLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON[m [32m+[m[32mANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT[m [32m+[m[32m(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS[m [32m+[m[32mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m [32m+[m [32m+[m[32m#pragma once[m [32m+[m [32m+[m[32m#include "defs.h"[m [32m+[m [32m+[m[32m#include [m [32m+[m[32m#include [m [32m+[m[32m#include [m [32m+[m [32m+[m[32m/* GmTypesetter has two jobs: it normalizes incoming source text, and typesets it as a[m [32m+[m[32m sequence of GmRuns. New data can be appended progressively. */[m [32m+[m [32m+[m[32miDeclareType(GmTypesetter)[m [32m+[m[32miDeclareTypeConstruction(GmTypesetter)[m [32m+[m[41m [m [32m+[m[32mvoid reset_GmTypesetter (iGmTypesetter *, enum iSourceFormat format);[m [32m+[m[32mvoid setWidth_GmTypesetter (iGmTypesetter *, int width);[m [32m+[m[32mvoid addInput_GmTypesetter (iGmTypesetter *, const iString *source);[m [32m+[m[32miBool getRuns_GmTypesetter (iGmTypesetter *, iArray *runs_out); /* returns false when no output generated */[m [32m+[m[32mvoid skip_GmTypesetter (iGmTypesetter *, int ySkip);[m [1mdiff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c[m [1mindex 9169ea06..94337f8d 100644[m [1m--- a/src/ui/documentwidget.c[m [1m+++ b/src/ui/documentwidget.c[m [36m@@ -1076,7 +1076,7 @@[m [mstatic void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode[m }[m }[m setBanner_GmDocument(d->doc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner);[m [31m- setFormat_GmDocument(d->doc, gemini_GmDocumentFormat);[m [32m+[m[32m setFormat_GmDocument(d->doc, gemini_SourceFormat);[m translate_Lang(src);[m d->state = ready_RequestState;[m setSource_DocumentWidget(d, src);[m [36m@@ -1214,7 +1214,7 @@[m [mstatic void updateDocument_DocumentWidget_(iDocumentWidget *d,[m if (isSuccess_GmStatusCode(statusCode)) {[m /* Check the MIME type. */[m iRangecc charset = range_CStr("utf-8");[m [31m- enum iGmDocumentFormat docFormat = undefined_GmDocumentFormat;[m [32m+[m[32m enum iSourceFormat docFormat = undefined_SourceFormat;[m const iString *mimeStr = collect_String(lower_String(&response->meta)); /* for convenience */[m set_String(&d->sourceMime, mimeStr);[m iRangecc mime = range_String(mimeStr);[m [36m@@ -1223,18 +1223,18 @@[m [mstatic void updateDocument_DocumentWidget_(iDocumentWidget *d,[m iRangecc param = seg;[m trim_Rangecc(¶m);[m if (equal_Rangecc(param, "text/gemini")) {[m [31m- docFormat = gemini_GmDocumentFormat;[m [32m+[m[32m docFormat = gemini_SourceFormat;[m setRange_String(&d->sourceMime, param);[m }[m else if (startsWith_Rangecc(param, "text/") ||[m equal_Rangecc(param, "application/json")) {[m [31m- docFormat = plainText_GmDocumentFormat;[m [32m+[m[32m docFormat = plainText_SourceFormat;[m setRange_String(&d->sourceMime, param);[m }[m else if (equal_Rangecc(param, "application/zip") ||[m (startsWith_Rangecc(param, "application/") &&[m endsWithCase_Rangecc(param, "+zip"))) {[m [31m- docFormat = gemini_GmDocumentFormat;[m [32m+[m[32m docFormat = gemini_SourceFormat;[m setRange_String(&d->sourceMime, param);[m iString *key = collectNew_String();[m toString_Sym(SDLK_s, KMOD_PRIMARY, key);[m [36m@@ -1261,7 +1261,7 @@[m [mstatic void updateDocument_DocumentWidget_(iDocumentWidget *d,[m startsWith_Rangecc(param, "audio/")) {[m const iBool isAudio = startsWith_Rangecc(param, "audio/");[m /* Make a simple document with an image or audio player. */[m [31m- docFormat = gemini_GmDocumentFormat;[m [32m+[m[32m docFormat = gemini_SourceFormat;[m setRange_String(&d->sourceMime, param);[m const iGmLinkId imgLinkId = 1; /* there's only the one link */[m /* TODO: Do the image loading in `postProcessRequestContent_DocumentWidget_()` */[m [36m@@ -1306,7 +1306,7 @@[m [mstatic void updateDocument_DocumentWidget_(iDocumentWidget *d,[m }[m }[m }[m [31m- if (docFormat == undefined_GmDocumentFormat) {[m [32m+[m[32m if (docFormat == undefined_SourceFormat) {[m showErrorPage_DocumentWidget_(d, unsupportedMimeType_GmStatusCode, &response->meta);[m deinit_String(&str);[m return;[m
text/gemini; charset=utf-8
This content has been proxied by September (3851b).