From 5f9685010addd4a0f04c13f889856084381dd1c6 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 20 May 2021 14:57:09 +0300 Subject: Added GmTypesetter --- CMakeLists.txt | 2 ++ src/defs.h | 6 ++++++ src/gmdocument.c | 29 +++++++++++++++-------------- src/gmdocument.h | 9 ++------- src/gmtypesetter.c | 25 +++++++++++++++++++++++++ src/gmtypesetter.h | 41 +++++++++++++++++++++++++++++++++++++++++ src/ui/documentwidget.c | 14 +++++++------- 7 files changed, 98 insertions(+), 28 deletions(-) create mode 100644 src/gmtypesetter.c create mode 100644 src/gmtypesetter.h 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; -- cgit v1.2.3