@@ -421,12 +421,47 @@ static void emit_attributes(GumboElement *el, const char *tag_name,
421421 emit_one_attr (out , el , "width" );
422422 emit_one_attr (out , el , "height" );
423423 } else if (strcmp (tag_name , "ul" ) == 0 ) {
424+
424425 const char * val = get_attr (el , "data-type" );
425- if (val && strcmp (val , "checkbox" ) == 0 )
426+ bool is_checkbox = (val && (strcmp (val , "checkbox" ) == 0 || strcmp (val , "checkboxList" ) == 0 ));
427+
428+ // In Google Docs and MS Word the <li> elements define if it is a checkbox list
429+ if (!is_checkbox ) {
430+ GumboVector * children = & el -> children ;
431+ for (unsigned int i = 0 ; i < children -> length ; i ++ ) {
432+ GumboNode * child = children -> data [i ];
433+ if (is_element (child )) {
434+ char child_tag [64 ];
435+ if (get_tag_name (child , child_tag , sizeof (child_tag )) && strcmp (child_tag , "li" ) == 0 ) {
436+ GumboElement * child_el = & child -> v .element ;
437+ const char * role = get_attr (child_el , "role" );
438+ const char * cls = get_attr (child_el , "class" );
439+
440+ if ((role && strcmp (role , "checkbox" ) == 0 ) ||
441+ (cls && strstr (cls , "checklist" ) != NULL )) {
442+ is_checkbox = true;
443+ }
444+ break ; // We only need to check the first <li>
445+ }
446+ }
447+ }
448+ }
449+
450+ if (is_checkbox ) {
426451 buffer_append_str (out , " data-type=\"checkbox\"" );
452+ }
427453 } else if (strcmp (tag_name , "li" ) == 0 ) {
428- if (gumbo_get_attribute (& el -> attributes , "checked" ) != NULL )
454+ const char * data_checked = get_attr (el , "data-checked" );
455+ const char * aria_checked = get_attr (el , "aria-checked" );
456+ const char * level_text = get_attr (el , "data-leveltext" );
457+
458+ // "\xEF\x83\xBE" is the UTF-8 hex encoding for U+F0FE (MS Word Checked Box)
459+ if (gumbo_get_attribute (& el -> attributes , "checked" ) != NULL ||
460+ (data_checked && strcmp (data_checked , "true" ) == 0 ) ||
461+ (aria_checked && strcmp (aria_checked , "true" ) == 0 ) ||
462+ (level_text && strcmp (level_text , "\xEF\x83\xBE" ) == 0 )) {
429463 buffer_append_str (out , " checked" );
464+ }
430465 } else if (strcmp (tag_name , "mention" ) == 0 ) {
431466 emit_one_attr (out , el , "id" );
432467 emit_one_attr (out , el , "text" );
@@ -551,6 +586,19 @@ static void flatten_li_node(GumboNode *node, buffer_t *ib, buffer_t *out,
551586 flatten_li_children (node , ib , out , ctx );
552587 return ;
553588 }
589+
590+ char buf [64 ];
591+ const char * tag = get_tag_name (node , buf , sizeof (buf ));
592+ if (tag && strcmp (tag , "img" ) == 0 ) {
593+ const char * role = get_attr (ctx -> el , "role" );
594+ const char * cls = get_attr (ctx -> el , "class" );
595+ // strip the <img> that Google Docs uses for the display of a checkbox icon
596+ if ((role && strcmp (role , "checkbox" ) == 0 ) ||
597+ (cls && strstr (cls , "checklist" ) != NULL )) {
598+ return ;
599+ }
600+ }
601+
554602 if (is_list_node (node )) {
555603 if (* ctx -> nested_count < ctx -> max_nested ) {
556604 ctx -> nested_lists [* ctx -> nested_count ] = node ;
0 commit comments