Lagrange [work/v1.6]

Added GmTypesetter

=> 5f9685010addd4a0f04c13f889856084381dd1c6

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

This content has been proxied by September (3851b).