Skip to content

Commit 566900a

Browse files
fix: whitespaces parsing inside normalizer
1 parent 7db3d82 commit 566900a

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

cpp/parser/GumboNormalizer.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,8 +497,23 @@ static void walk_node(GumboNode *node, buffer_t *out);
497497

498498
static void flatten_bq_node(GumboNode *node, buffer_t *ib, buffer_t *out);
499499

500+
/** True if buf is empty or contains only ASCII whitespace. */
501+
static bool is_whitespace_only(const char *data, size_t len) {
502+
for (size_t i = 0; i < len; i++) {
503+
unsigned char c = (unsigned char)data[i];
504+
if (c != ' ' && c != '\t' && c != '\n' && c != '\r' && c != '\f')
505+
return false;
506+
}
507+
return true;
508+
}
509+
510+
/**
511+
* Flush buffered inline content as a <p>. Inter-block whitespace (newlines /
512+
* spaces between block tags in pretty-printed HTML) is discarded so it does
513+
* not become empty paragraphs that later serialize as extra <br>s.
514+
*/
500515
static void flush_inline_p(buffer_t *ib, buffer_t *out) {
501-
if (ib->len > 0) {
516+
if (ib->len > 0 && !is_whitespace_only(ib->data, ib->len)) {
502517
buffer_append_str(out, "<p>");
503518
buffer_append(out, ib->data, ib->len);
504519
buffer_append_str(out, "</p>");
@@ -674,7 +689,8 @@ static void walk_children(GumboNode *node, buffer_t *out) {
674689
!is_blockquote_node(children->data[i])) {
675690
child = children->data[i];
676691
if (is_br_node(child)) {
677-
if (ib.len > 0)
692+
/* Whitespace-only buffer is layout noise; treat like empty → <br> */
693+
if (ib.len > 0 && !is_whitespace_only(ib.data, ib.len))
678694
flush_inline_p(&ib, out);
679695
else
680696
buffer_append_str(out, "<br>");

cpp/tests/GumboParserTest.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,3 +509,30 @@ TEST(GumboParserTest, BrRemappings) {
509509
"<p><b>Asdasdasd</b></p><br><br><p>Sent with <a "
510510
"href=\"https://google.com\">Net</a></p>");
511511
}
512+
513+
TEST(GumboParserTest, InterBlockWhitespace) {
514+
// Pretty-printed consecutive paragraphs must not gain empty <p>s from the
515+
// newlines between them (those would later serialize as extra <br>s).
516+
EXPECT_EQ(GumboParser::normalizeHtml(
517+
"<p>Asdasd</p>\n<p>Asdasd</p>\n<p>Asdasda</p>"),
518+
"<p>Asdasd</p><p>Asdasd</p><p>Asdasda</p>");
519+
EXPECT_EQ(GumboParser::normalizeHtml(
520+
"<p>Asdasd</p>\n\n<p>Asdasd</p>\n\n<p>Asdasda</p>"),
521+
"<p>Asdasd</p><p>Asdasd</p><p>Asdasda</p>");
522+
EXPECT_EQ(GumboParser::normalizeHtml(
523+
"<html>\n<p>Asdasd</p>\n<p>Asdasd</p>\n<p>Asdasda</p>\n</html>"),
524+
"<p>Asdasd</p><p>Asdasd</p><p>Asdasda</p>");
525+
EXPECT_EQ(GumboParser::normalizeHtml("<p>Asdasd</p> <p>Asdasd</p>"),
526+
"<p>Asdasd</p><p>Asdasd</p>");
527+
528+
// Significant inline content between blocks is still wrapped in <p>.
529+
EXPECT_EQ(GumboParser::normalizeHtml("<p>a</p> hello <p>b</p>"),
530+
"<p>a</p><p> hello </p><p>b</p>");
531+
532+
// Spaces inside text / between inlines must be preserved.
533+
EXPECT_EQ(GumboParser::normalizeHtml("hello world"), "hello world");
534+
EXPECT_EQ(GumboParser::normalizeHtml("<p>hello world</p>"),
535+
"<p>hello world</p>");
536+
EXPECT_EQ(GumboParser::normalizeHtml("<b>hello</b> <i>world</i>"),
537+
"<b>hello</b> <i>world</i>");
538+
}

0 commit comments

Comments
 (0)