@@ -87,7 +87,9 @@ static Rc<Karm::Text::Fontface> _lookupFontface(Text::FontBook& fontBook, Style:
87
87
return Text::Fontface::fallback ();
88
88
}
89
89
90
- auto RE_SEGMENT_BREAK = Re::single(' \n ' , ' \r ' , ' \f ' , ' \v ' );
90
+ bool isSegmentBreak (Rune rune) {
91
+ return rune == ' \n ' or rune == ' \r ' or rune == ' \f ' or rune == ' \v ' ;
92
+ }
91
93
92
94
Text::ProseStyle _buildProseStyle (Style ::Computer& c, Rc<Style ::Computed> parentStyle) {
93
95
auto fontFace = _lookupFontface (c.fontBook , *parentStyle);
@@ -129,48 +131,75 @@ Text::ProseStyle _buildProseStyle(Style::Computer& c, Rc<Style::Computed> parent
129
131
return proseStyle;
130
132
}
131
133
134
+ void _transformAndAppendRuneToProse (Rc<Text::Prose> prose, Rune rune, TextTransform transform) {
135
+ switch (transform) {
136
+ case TextTransform::UPPERCASE:
137
+ prose->append (toAsciiUpper (rune));
138
+ break ;
139
+
140
+ case TextTransform::LOWERCASE:
141
+ prose->append (toAsciiLower (rune));
142
+ break ;
143
+
144
+ case TextTransform::NONE:
145
+ default :
146
+ prose->append (rune);
147
+ break ;
148
+ }
149
+ }
150
+
151
+ // https://www.w3.org/TR/css-text-3/#white-space-phase-1
152
+ // https://www.w3.org/TR/css-text-3/#white-space-phase-2
132
153
void _appendTextToInlineBox (Io::SScan scan, Rc<Style ::Computed> parentStyle, Rc<Text::Prose> prose) {
133
154
auto whitespace = parentStyle->text ->whiteSpace ;
134
- while (not scan.ended ()) {
135
- switch (parentStyle->text ->transform ) {
136
- case TextTransform::UPPERCASE:
137
- prose->append (toAsciiUpper (scan.next ()));
138
- break ;
155
+ bool whiteSpacesAreCollapsible =
156
+ whitespace == WhiteSpace::NORMAL or
157
+ whitespace == WhiteSpace::NOWRAP or
158
+ whitespace == WhiteSpace::PRE_LINE;
139
159
140
- case TextTransform::LOWERCASE:
141
- prose->append ( toAsciiLower (scan. next ()));
142
- break ;
160
+ // A sequence of collapsible spaces at the beginning of a line is removed.
161
+ if ( not prose->_runes . len ())
162
+ scan. eat ( Re::space ()) ;
143
163
144
- case TextTransform::NONE:
145
- prose->append (scan.next ());
146
- break ;
164
+ while (not scan.ended ()) {
165
+ auto rune = scan.next ();
147
166
148
- default :
149
- break ;
167
+ if (not isAsciiSpace (rune)) {
168
+ _transformAndAppendRuneToProse (prose, rune, parentStyle->text ->transform );
169
+ continue ;
150
170
}
151
171
152
- if (whitespace == WhiteSpace::PRE) {
153
- auto tok = scan.token (Re::space ());
154
- if (tok)
155
- prose->append (tok);
156
- } else if (whitespace == WhiteSpace::PRE_LINE) {
157
- bool hasBlank = false ;
158
- if (scan.eat (Re::blank ())) {
159
- hasBlank = true ;
160
- }
172
+ // https://www.w3.org/TR/css-text-3/#collapse
173
+ if (whiteSpacesAreCollapsible) {
174
+ // Any sequence of collapsible spaces and tabs immediately preceding or following a segment break is removed.
175
+ bool visitedSegmentBreak = false ;
176
+ while (true ) {
177
+ if (isSegmentBreak (rune))
178
+ visitedSegmentBreak = true ;
161
179
162
- if (scan.eat (RE_SEGMENT_BREAK)) {
163
- prose-> append ( ' \n ' ) ;
164
- scan. eat ( Re::blank ());
165
- hasBlank = false ;
180
+ if (scan.ended () or not isAsciiSpace (scan. peek ()))
181
+ break ;
182
+
183
+ rune = scan. next () ;
166
184
}
167
185
168
- if (hasBlank)
186
+ // Any collapsible space immediately following another collapsible space—even one outside the boundary
187
+ // of the inline containing that space, provided both spaces are within the same inline formatting
188
+ // context—is collapsed to have zero advance width. (It is invisible, but retains its soft wrap
189
+ // opportunity, if any.)
190
+ // TODO: not compliant regarding wrap opportunity
191
+
192
+ // https://www.w3.org/TR/css-text-3/#valdef-white-space-pre-line
193
+ // Collapsible segment breaks are transformed for rendering according to the segment
194
+ // break transformation rules.
195
+ if (whitespace == WhiteSpace::PRE_LINE and visitedSegmentBreak)
196
+ prose->append (' \n ' );
197
+ else
169
198
prose->append (' ' );
199
+ } else if (whitespace == WhiteSpace::PRE) {
200
+ prose->append (rune);
170
201
} else {
171
- // NORMAL
172
- if (scan.eat (Re::space ()))
173
- prose->append (' ' );
202
+ panic (" unimplemented whitespace case" );
174
203
}
175
204
}
176
205
}
@@ -181,7 +210,7 @@ void _buildText(Gc::Ref<Dom::Text> node, Rc<Style::Computed> parentStyle, Inline
181
210
if (scan.ended ())
182
211
return ;
183
212
184
- _appendTextToInlineBox (scan , parentStyle, rootInlineBox.prose );
213
+ _appendTextToInlineBox (node-> data () , parentStyle, rootInlineBox.prose );
185
214
}
186
215
187
216
// https://www.w3.org/TR/css-inline-3/#model
0 commit comments