apex-ruby 1.0.34 → 1.0.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +9 -1
- data/ext/apex_ext/apex_src/include/apex/apex.h +2 -2
- data/ext/apex_ext/apex_src/src/html_renderer.c +109 -162
- data/lib/apex/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b213f78349fcb6d156e9cae279f5f161f145001e4aebe8b1441813cdd2408e7d
|
|
4
|
+
data.tar.gz: 0255dcf57b9a66d64c5b264b786e388b305097e6461d34c7e33423ee88095510
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 554d9a71943f8e42901269a03c7ba3f20e3d1b3ff5a9122e70d1ecc0c23544b0d47472829be3bce9fb31d39e589547d4f0cd5a4f684805830f0366c5debd81a6
|
|
7
|
+
data.tar.gz: 127e6d7d384cca60b47331f0a2034be2a00ac37ffb78c0772802bc9f548f2622ee02cb6ddade0a8a5dc2a8b9d073a9e6bc607dc64623957ab367f27181f4dd90
|
data/README.md
CHANGED
|
@@ -6,7 +6,15 @@ Apex is a C library that supports CommonMark, GFM, MultiMarkdown, Kramdown, and
|
|
|
6
6
|
Marked-specific extensions. This gem vendors the Apex sources (currently the **1.1.x** library)
|
|
7
7
|
and exposes a small, kramdown-style Ruby API for converting Markdown to HTML.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The gem has its own version number, separate from the Apex library it bundles. Each gem
|
|
10
|
+
release ships the latest Apex release, and `Apex::Native.version` reports which one you have:
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
require "apex"
|
|
14
|
+
|
|
15
|
+
Apex::VERSION # => "1.0.34" (the gem)
|
|
16
|
+
Apex::Native.version # => "1.1.36" (the bundled Apex library)
|
|
17
|
+
```
|
|
10
18
|
|
|
11
19
|
## Requirements
|
|
12
20
|
|
|
@@ -21,8 +21,8 @@ typedef struct cmark_node cmark_node;
|
|
|
21
21
|
|
|
22
22
|
#define APEX_VERSION_MAJOR 1
|
|
23
23
|
#define APEX_VERSION_MINOR 1
|
|
24
|
-
#define APEX_VERSION_PATCH
|
|
25
|
-
#define APEX_VERSION_STRING "1.1.
|
|
24
|
+
#define APEX_VERSION_PATCH 37
|
|
25
|
+
#define APEX_VERSION_STRING "1.1.37"
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Processor compatibility modes
|
|
@@ -2473,6 +2473,41 @@ char *apex_adjust_quote_language(const char *html, const char *quotes_language)
|
|
|
2473
2473
|
return output;
|
|
2474
2474
|
}
|
|
2475
2475
|
|
|
2476
|
+
typedef struct {
|
|
2477
|
+
char *data;
|
|
2478
|
+
size_t len;
|
|
2479
|
+
size_t cap;
|
|
2480
|
+
} aria_buf;
|
|
2481
|
+
|
|
2482
|
+
static bool aria_buf_append(aria_buf *b, const char *s, size_t n) {
|
|
2483
|
+
if (b->len + n + 1 > b->cap) {
|
|
2484
|
+
size_t new_cap = b->cap ? b->cap : 64;
|
|
2485
|
+
while (b->len + n + 1 > new_cap) new_cap *= 2;
|
|
2486
|
+
char *grown = realloc(b->data, new_cap);
|
|
2487
|
+
if (!grown) return false;
|
|
2488
|
+
b->data = grown;
|
|
2489
|
+
b->cap = new_cap;
|
|
2490
|
+
}
|
|
2491
|
+
memcpy(b->data + b->len, s, n);
|
|
2492
|
+
b->len += n;
|
|
2493
|
+
b->data[b->len] = '\0';
|
|
2494
|
+
return true;
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2497
|
+
/* strstr limited to [start, end) */
|
|
2498
|
+
static const char *aria_find_in(const char *start, const char *end, const char *needle) {
|
|
2499
|
+
size_t n = strlen(needle);
|
|
2500
|
+
for (const char *p = start; p && p + n <= end; p++) {
|
|
2501
|
+
if (*p == *needle && memcmp(p, needle, n) == 0) return p;
|
|
2502
|
+
}
|
|
2503
|
+
return NULL;
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
static bool aria_has_table_figure_class(const char *start, const char *end) {
|
|
2507
|
+
return aria_find_in(start, end, "class=\"table-figure\"") ||
|
|
2508
|
+
aria_find_in(start, end, "class='table-figure'");
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2476
2511
|
/**
|
|
2477
2512
|
* Apply ARIA labels and accessibility attributes to HTML output
|
|
2478
2513
|
* @param html The HTML output
|
|
@@ -2512,11 +2547,7 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
|
|
|
2512
2547
|
const char *figure_start_pos = NULL;
|
|
2513
2548
|
while (before_cap >= html && before_cap > search - 200) {
|
|
2514
2549
|
if (*before_cap == '<' && strncmp(before_cap, "<figure", 7) == 0) {
|
|
2515
|
-
|
|
2516
|
-
if (!class_check) {
|
|
2517
|
-
class_check = strstr(before_cap, "class='table-figure'");
|
|
2518
|
-
}
|
|
2519
|
-
if (class_check && class_check < cap_tag_start) {
|
|
2550
|
+
if (aria_has_table_figure_class(before_cap, cap_tag_start)) {
|
|
2520
2551
|
in_table_figure = true;
|
|
2521
2552
|
figure_start_pos = before_cap;
|
|
2522
2553
|
break;
|
|
@@ -2527,13 +2558,13 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
|
|
|
2527
2558
|
|
|
2528
2559
|
if (in_table_figure) {
|
|
2529
2560
|
/* Check if ID already exists */
|
|
2530
|
-
const char *id_attr =
|
|
2561
|
+
const char *id_attr = aria_find_in(cap_tag_start, cap_tag_end, "id=\"");
|
|
2531
2562
|
if (!id_attr) {
|
|
2532
|
-
id_attr =
|
|
2563
|
+
id_attr = aria_find_in(cap_tag_start, cap_tag_end, "id='");
|
|
2533
2564
|
}
|
|
2534
2565
|
|
|
2535
2566
|
char *caption_id = NULL;
|
|
2536
|
-
if (id_attr
|
|
2567
|
+
if (id_attr) {
|
|
2537
2568
|
/* Extract existing ID */
|
|
2538
2569
|
const char *id_start = id_attr + 4;
|
|
2539
2570
|
const char *id_end = strchr(id_start, '"');
|
|
@@ -2573,41 +2604,26 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
|
|
|
2573
2604
|
search++;
|
|
2574
2605
|
}
|
|
2575
2606
|
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
if (!
|
|
2580
|
-
|
|
2581
|
-
while (caption_list) {
|
|
2582
|
-
caption_info *next = caption_list->next;
|
|
2583
|
-
free(caption_list->caption_id);
|
|
2584
|
-
free(caption_list);
|
|
2585
|
-
caption_list = next;
|
|
2586
|
-
}
|
|
2587
|
-
return strdup(html);
|
|
2588
|
-
}
|
|
2607
|
+
aria_buf out = {0};
|
|
2608
|
+
out.cap = html_len + html_len / 8 + 256;
|
|
2609
|
+
out.data = malloc(out.cap);
|
|
2610
|
+
if (!out.data) goto oom;
|
|
2611
|
+
out.data[0] = '\0';
|
|
2589
2612
|
|
|
2590
2613
|
const char *read = html;
|
|
2591
|
-
char *write = output;
|
|
2592
|
-
size_t remaining = capacity;
|
|
2593
2614
|
|
|
2594
|
-
/*
|
|
2615
|
+
/* Arguments are evaluated before appending so the read position always advances */
|
|
2616
|
+
#define APPEND_N(s, n) do { \
|
|
2617
|
+
if (!aria_buf_append(&out, (s), (n))) goto oom; \
|
|
2618
|
+
} while (0)
|
|
2595
2619
|
#define APPEND_SAFE(str) do { \
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
write += len; \
|
|
2600
|
-
remaining -= len; \
|
|
2601
|
-
} \
|
|
2602
|
-
} while(0)
|
|
2603
|
-
|
|
2604
|
-
/* Helper macro to copy characters safely */
|
|
2620
|
+
const char *str_ = (str); \
|
|
2621
|
+
APPEND_N(str_, strlen(str_)); \
|
|
2622
|
+
} while (0)
|
|
2605
2623
|
#define COPY_CHAR(c) do { \
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
} \
|
|
2610
|
-
} while(0)
|
|
2624
|
+
char c_ = (c); \
|
|
2625
|
+
APPEND_N(&c_, 1); \
|
|
2626
|
+
} while (0)
|
|
2611
2627
|
|
|
2612
2628
|
while (*read) {
|
|
2613
2629
|
/* Check for <nav class="toc"> */
|
|
@@ -2620,29 +2636,16 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
|
|
|
2620
2636
|
}
|
|
2621
2637
|
|
|
2622
2638
|
/* Check if this is a TOC nav element */
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
class_attr = strstr(tag_start, "class='toc'");
|
|
2626
|
-
}
|
|
2627
|
-
|
|
2628
|
-
if (class_attr && class_attr < tag_end) {
|
|
2629
|
-
/* Check if aria-label already exists */
|
|
2630
|
-
const char *aria_label = strstr(tag_start, "aria-label=");
|
|
2631
|
-
if (!aria_label || aria_label > tag_end) {
|
|
2632
|
-
/* Copy up to just before closing >, add aria-label, then close */
|
|
2633
|
-
size_t prefix_len = tag_end - tag_start;
|
|
2634
|
-
if (prefix_len <= remaining) {
|
|
2635
|
-
memcpy(write, tag_start, prefix_len);
|
|
2636
|
-
write += prefix_len;
|
|
2637
|
-
remaining -= prefix_len;
|
|
2638
|
-
}
|
|
2639
|
+
bool is_toc = aria_find_in(tag_start, tag_end, "class=\"toc\"") ||
|
|
2640
|
+
aria_find_in(tag_start, tag_end, "class='toc'");
|
|
2639
2641
|
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2642
|
+
if (is_toc && !aria_find_in(tag_start, tag_end, "aria-label=")) {
|
|
2643
|
+
/* Copy up to just before closing >, add aria-label, then close */
|
|
2644
|
+
APPEND_N(tag_start, (size_t)(tag_end - tag_start));
|
|
2645
|
+
APPEND_SAFE(" aria-label=\"Table of contents\"");
|
|
2646
|
+
COPY_CHAR('>');
|
|
2647
|
+
read = tag_end + 1;
|
|
2648
|
+
continue;
|
|
2646
2649
|
}
|
|
2647
2650
|
}
|
|
2648
2651
|
|
|
@@ -2655,18 +2658,9 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
|
|
|
2655
2658
|
continue;
|
|
2656
2659
|
}
|
|
2657
2660
|
|
|
2658
|
-
|
|
2659
|
-
const char *role_attr = strstr(tag_start, "role=");
|
|
2660
|
-
if (!role_attr || role_attr > tag_end) {
|
|
2661
|
+
if (!aria_find_in(tag_start, tag_end, "role=")) {
|
|
2661
2662
|
/* Copy up to just before closing >, add role, then close */
|
|
2662
|
-
size_t
|
|
2663
|
-
if (prefix_len <= remaining) {
|
|
2664
|
-
memcpy(write, tag_start, prefix_len);
|
|
2665
|
-
write += prefix_len;
|
|
2666
|
-
remaining -= prefix_len;
|
|
2667
|
-
}
|
|
2668
|
-
|
|
2669
|
-
/* Add role="figure" before closing > */
|
|
2663
|
+
APPEND_N(tag_start, (size_t)(tag_end - tag_start));
|
|
2670
2664
|
APPEND_SAFE(" role=\"figure\"");
|
|
2671
2665
|
COPY_CHAR('>');
|
|
2672
2666
|
read = tag_end + 1;
|
|
@@ -2683,85 +2677,44 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
|
|
|
2683
2677
|
continue;
|
|
2684
2678
|
}
|
|
2685
2679
|
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
const char *
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
const char *before_table = read - 1;
|
|
2697
|
-
while (before_table >= html && before_table > read - 500) {
|
|
2698
|
-
if (*before_table == '<' && strncmp(before_table, "<figure", 7) == 0) {
|
|
2699
|
-
const char *class_check = strstr(before_table, "class=\"table-figure\"");
|
|
2700
|
-
if (!class_check) {
|
|
2701
|
-
class_check = strstr(before_table, "class='table-figure'");
|
|
2702
|
-
}
|
|
2703
|
-
if (class_check && class_check < tag_start) {
|
|
2704
|
-
in_table_figure = true;
|
|
2680
|
+
bool needs_role = !aria_find_in(tag_start, tag_end, "role=");
|
|
2681
|
+
bool has_aria_desc = aria_find_in(tag_start, tag_end, "aria-describedby=") != NULL;
|
|
2682
|
+
|
|
2683
|
+
/* Find the enclosing table-figure, if any */
|
|
2684
|
+
const char *this_figure_start = NULL;
|
|
2685
|
+
const char *find_fig = read - 1;
|
|
2686
|
+
while (find_fig >= html && find_fig > read - 500) {
|
|
2687
|
+
if (*find_fig == '<' && strncmp(find_fig, "<figure", 7) == 0) {
|
|
2688
|
+
if (aria_has_table_figure_class(find_fig, tag_start)) {
|
|
2689
|
+
this_figure_start = find_fig;
|
|
2705
2690
|
break;
|
|
2706
2691
|
}
|
|
2707
2692
|
}
|
|
2708
|
-
|
|
2693
|
+
find_fig--;
|
|
2709
2694
|
}
|
|
2710
2695
|
|
|
2711
|
-
/*
|
|
2712
|
-
char *caption_id = NULL;
|
|
2713
|
-
if (
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
if (*find_fig == '<' && strncmp(find_fig, "<figure", 7) == 0) {
|
|
2719
|
-
const char *class_check = strstr(find_fig, "class=\"table-figure\"");
|
|
2720
|
-
if (!class_check) {
|
|
2721
|
-
class_check = strstr(find_fig, "class='table-figure'");
|
|
2722
|
-
}
|
|
2723
|
-
if (class_check && class_check < tag_start) {
|
|
2724
|
-
this_figure_start = find_fig;
|
|
2725
|
-
break;
|
|
2726
|
-
}
|
|
2727
|
-
}
|
|
2728
|
-
find_fig--;
|
|
2729
|
-
}
|
|
2730
|
-
|
|
2731
|
-
/* Look for a caption in this figure (either before or after table) */
|
|
2732
|
-
if (this_figure_start) {
|
|
2733
|
-
for (caption_info *cap = caption_list; cap; cap = cap->next) {
|
|
2734
|
-
if (cap->figure_start == this_figure_start) {
|
|
2735
|
-
/* Found a caption in the same figure - use it regardless of position */
|
|
2736
|
-
caption_id = strdup(cap->caption_id);
|
|
2737
|
-
break;
|
|
2738
|
-
}
|
|
2696
|
+
/* Look for a caption in this figure (either before or after table) */
|
|
2697
|
+
const char *caption_id = NULL;
|
|
2698
|
+
if (this_figure_start && !has_aria_desc) {
|
|
2699
|
+
for (caption_info *cap = caption_list; cap; cap = cap->next) {
|
|
2700
|
+
if (cap->figure_start == this_figure_start) {
|
|
2701
|
+
caption_id = cap->caption_id;
|
|
2702
|
+
break;
|
|
2739
2703
|
}
|
|
2740
2704
|
}
|
|
2741
2705
|
}
|
|
2742
2706
|
|
|
2743
2707
|
if (needs_role || caption_id) {
|
|
2744
2708
|
/* Copy up to just before closing >, add attributes, then close */
|
|
2745
|
-
size_t
|
|
2746
|
-
if (prefix_len <= remaining) {
|
|
2747
|
-
memcpy(write, tag_start, prefix_len);
|
|
2748
|
-
write += prefix_len;
|
|
2749
|
-
remaining -= prefix_len;
|
|
2750
|
-
}
|
|
2751
|
-
|
|
2752
|
-
/* Add role="table" if needed */
|
|
2709
|
+
APPEND_N(tag_start, (size_t)(tag_end - tag_start));
|
|
2753
2710
|
if (needs_role) {
|
|
2754
2711
|
APPEND_SAFE(" role=\"table\"");
|
|
2755
2712
|
}
|
|
2756
|
-
|
|
2757
|
-
/* Add aria-describedby if we found a caption ID */
|
|
2758
2713
|
if (caption_id) {
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
free(caption_id);
|
|
2714
|
+
APPEND_SAFE(" aria-describedby=\"");
|
|
2715
|
+
APPEND_SAFE(caption_id);
|
|
2716
|
+
COPY_CHAR('"');
|
|
2763
2717
|
}
|
|
2764
|
-
|
|
2765
2718
|
COPY_CHAR('>');
|
|
2766
2719
|
read = tag_end + 1;
|
|
2767
2720
|
continue;
|
|
@@ -2786,30 +2739,17 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
|
|
|
2786
2739
|
}
|
|
2787
2740
|
}
|
|
2788
2741
|
|
|
2789
|
-
if (this_caption
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
memcpy(write, tag_start, prefix_len);
|
|
2801
|
-
write += prefix_len;
|
|
2802
|
-
remaining -= prefix_len;
|
|
2803
|
-
}
|
|
2804
|
-
|
|
2805
|
-
/* Add id attribute */
|
|
2806
|
-
char id_attr_str[128];
|
|
2807
|
-
snprintf(id_attr_str, sizeof(id_attr_str), " id=\"%s\"", this_caption->caption_id);
|
|
2808
|
-
APPEND_SAFE(id_attr_str);
|
|
2809
|
-
COPY_CHAR('>');
|
|
2810
|
-
read = tag_end + 1;
|
|
2811
|
-
continue;
|
|
2812
|
-
}
|
|
2742
|
+
if (this_caption &&
|
|
2743
|
+
!aria_find_in(tag_start, tag_end, "id=\"") &&
|
|
2744
|
+
!aria_find_in(tag_start, tag_end, "id='")) {
|
|
2745
|
+
/* No ID in original, add the one we generated/collected */
|
|
2746
|
+
APPEND_N(tag_start, (size_t)(tag_end - tag_start));
|
|
2747
|
+
APPEND_SAFE(" id=\"");
|
|
2748
|
+
APPEND_SAFE(this_caption->caption_id);
|
|
2749
|
+
COPY_CHAR('"');
|
|
2750
|
+
COPY_CHAR('>');
|
|
2751
|
+
read = tag_end + 1;
|
|
2752
|
+
continue;
|
|
2813
2753
|
}
|
|
2814
2754
|
}
|
|
2815
2755
|
|
|
@@ -2817,20 +2757,27 @@ char *apex_apply_aria_labels(const char *html, cmark_node *document) {
|
|
|
2817
2757
|
COPY_CHAR(*read++);
|
|
2818
2758
|
}
|
|
2819
2759
|
|
|
2760
|
+
#undef APPEND_N
|
|
2820
2761
|
#undef APPEND_SAFE
|
|
2821
2762
|
#undef COPY_CHAR
|
|
2822
2763
|
|
|
2823
|
-
*write = '\0';
|
|
2824
|
-
|
|
2825
|
-
/* Free caption list */
|
|
2826
2764
|
while (caption_list) {
|
|
2827
2765
|
caption_info *next = caption_list->next;
|
|
2828
2766
|
free(caption_list->caption_id);
|
|
2829
2767
|
free(caption_list);
|
|
2830
2768
|
caption_list = next;
|
|
2831
2769
|
}
|
|
2770
|
+
return out.data;
|
|
2832
2771
|
|
|
2833
|
-
|
|
2772
|
+
oom:
|
|
2773
|
+
free(out.data);
|
|
2774
|
+
while (caption_list) {
|
|
2775
|
+
caption_info *next = caption_list->next;
|
|
2776
|
+
free(caption_list->caption_id);
|
|
2777
|
+
free(caption_list);
|
|
2778
|
+
caption_list = next;
|
|
2779
|
+
}
|
|
2780
|
+
return strdup(html);
|
|
2834
2781
|
}
|
|
2835
2782
|
|
|
2836
2783
|
/* Helper: trim leading/trailing ASCII whitespace from an attribute value */
|
data/lib/apex/version.rb
CHANGED