diff options
-rw-r--r-- | src/gmdocument.c | 30 |
1 files changed, 28 insertions, 2 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c index 42bc7515..6c66451c 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -67,6 +67,21 @@ static int lastVisibleRunBottom_GmDocument_(const iGmDocument *d) { | |||
67 | return 0; | 67 | return 0; |
68 | } | 68 | } |
69 | 69 | ||
70 | iInt2 measurePreformattedBlock_GmDocument_(const iGmDocument *d, const char *start, int font) { | ||
71 | const iRangecc content = { start, constEnd_String(&d->source) }; | ||
72 | iRangecc line = iNullRange; | ||
73 | nextSplit_Rangecc(&content, "\n", &line); | ||
74 | iAssert(startsWith_Rangecc(&line, "```")); | ||
75 | iRangecc preBlock = { line.end + 1, line.end + 1 }; | ||
76 | while (nextSplit_Rangecc(&content, "\n", &line)) { | ||
77 | if (startsWith_Rangecc(&line, "```")) { | ||
78 | break; | ||
79 | } | ||
80 | preBlock.end = line.end; | ||
81 | } | ||
82 | return measureRange_Text(font, preBlock); | ||
83 | } | ||
84 | |||
70 | static void doLayout_GmDocument_(iGmDocument *d) { | 85 | static void doLayout_GmDocument_(iGmDocument *d) { |
71 | if (d->size.x <= 0 || isEmpty_String(&d->source)) { | 86 | if (d->size.x <= 0 || isEmpty_String(&d->source)) { |
72 | return; | 87 | return; |
@@ -98,6 +113,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
98 | iRangecc preAltText = iNullRange; | 113 | iRangecc preAltText = iNullRange; |
99 | enum iGmLineType prevType = text_GmLineType; | 114 | enum iGmLineType prevType = text_GmLineType; |
100 | iBool isFirstText = iTrue; | 115 | iBool isFirstText = iTrue; |
116 | int preFont = preformatted_FontId; | ||
101 | while (nextSplit_Rangecc(&content, "\n", &line)) { | 117 | while (nextSplit_Rangecc(&content, "\n", &line)) { |
102 | iGmRun run; | 118 | iGmRun run; |
103 | run.color = white_ColorId; | 119 | run.color = white_ColorId; |
@@ -109,6 +125,12 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
109 | indent = indents[type]; | 125 | indent = indents[type]; |
110 | if (type == preformatted_GmLineType) { | 126 | if (type == preformatted_GmLineType) { |
111 | isPreformat = iTrue; | 127 | isPreformat = iTrue; |
128 | preFont = preformatted_FontId; | ||
129 | /* Use a smaller font if the block contents are wide. */ | ||
130 | if (measurePreformattedBlock_GmDocument_(d, line.start, preFont).x > | ||
131 | d->size.x - indents[preformatted_GmLineType]) { | ||
132 | preFont = preformattedSmall_FontId; | ||
133 | } | ||
112 | trimLine_Rangecc_(&line, type); | 134 | trimLine_Rangecc_(&line, type); |
113 | preAltText = line; | 135 | preAltText = line; |
114 | /* TODO: store and link the alt text to this run */ | 136 | /* TODO: store and link the alt text to this run */ |
@@ -118,21 +140,23 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
118 | run.font = fonts[type]; | 140 | run.font = fonts[type]; |
119 | } | 141 | } |
120 | else { | 142 | else { |
143 | /* Preformatted line. */ | ||
121 | type = preformatted_GmLineType; | 144 | type = preformatted_GmLineType; |
122 | if (startsWithSc_Rangecc(&line, "```", &iCaseSensitive)) { | 145 | if (startsWithSc_Rangecc(&line, "```", &iCaseSensitive)) { |
123 | isPreformat = iFalse; | 146 | isPreformat = iFalse; |
124 | preAltText = iNullRange; | 147 | preAltText = iNullRange; |
125 | continue; | 148 | continue; |
126 | } | 149 | } |
127 | run.font = preformatted_FontId; | 150 | run.font = preFont; |
128 | indent = indents[type]; | 151 | indent = indents[type]; |
129 | } | 152 | } |
130 | /* Check the margin. */ | 153 | /* Empty lines don't produce text runs. */ |
131 | if (isEmpty_Range(&line)) { | 154 | if (isEmpty_Range(&line)) { |
132 | pos.y += lineHeight_Text(run.font); | 155 | pos.y += lineHeight_Text(run.font); |
133 | prevType = text_GmLineType; | 156 | prevType = text_GmLineType; |
134 | continue; | 157 | continue; |
135 | } | 158 | } |
159 | /* Check the margin vs. previous run. */ | ||
136 | if (!isPreformat || (prevType != preformatted_GmLineType)) { | 160 | if (!isPreformat || (prevType != preformatted_GmLineType)) { |
137 | int required = | 161 | int required = |
138 | iMax(topMargin[type], bottomMargin[prevType]) * lineHeight_Text(paragraph_FontId); | 162 | iMax(topMargin[type], bottomMargin[prevType]) * lineHeight_Text(paragraph_FontId); |
@@ -144,6 +168,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
144 | pos.y += required - delta; | 168 | pos.y += required - delta; |
145 | } | 169 | } |
146 | } | 170 | } |
171 | /* List bullet. */ | ||
147 | if (type == bullet_GmLineType) { | 172 | if (type == bullet_GmLineType) { |
148 | run.bounds.pos = addX_I2(pos, indent * gap_UI); | 173 | run.bounds.pos = addX_I2(pos, indent * gap_UI); |
149 | run.bounds.size = advance_Text(run.font, bullet); | 174 | run.bounds.size = advance_Text(run.font, bullet); |
@@ -151,6 +176,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
151 | run.text = (iRangecc){ bullet, bullet + strlen(bullet) }; | 176 | run.text = (iRangecc){ bullet, bullet + strlen(bullet) }; |
152 | pushBack_Array(&d->layout, &run); | 177 | pushBack_Array(&d->layout, &run); |
153 | } | 178 | } |
179 | /* Special formatting for the first paragraph (e.g., subtitle, introduction, or lede). */ | ||
154 | if (type == text_GmLineType && isFirstText) { | 180 | if (type == text_GmLineType && isFirstText) { |
155 | run.font = firstParagraph_FontId; | 181 | run.font = firstParagraph_FontId; |
156 | isFirstText = iFalse; | 182 | isFirstText = iFalse; |