PDF-Make

 view release on metacpan or  search on metacpan

src/pdfmake_markup.c  view on Meta::CPAN


    s.p     = src;
    s.end   = src + len;
    s.line  = 1;
    s.col   = 1;
    s.doc   = doc;
    s.depth = 0;

    if (!validate_utf8(&s, s.p, s.end)) return doc;

    /* Skip a UTF-8 BOM: editors add it, and it is not the author's fault. */
    if (len >= 3 && (unsigned char)src[0] == 0xEF &&
        (unsigned char)src[1] == 0xBB && (unsigned char)src[2] == 0xBF) {
        s.p += 3;
    }

    skip_space(&s);
    while (!at_end(&s) && s.doc->ok && peek(&s) == '<' &&
           peek_at(&s, 1) == '!' && peek_at(&s, 2) == '-') {
        skip_comment(&s);
        skip_space(&s);

src/pdfmake_textract.c  view on Meta::CPAN

        }
    }
    return 0;
}

/*============================================================================
 * Phase 13 — Annotation + form field text extraction
 *==========================================================================*/

/* Decode a PDF "text string" (§7.9.2) to UTF-8, arena-allocated.
 * Handles UTF-16BE (with BOM FE FF) and UTF-8-with-BOM (EF BB BF); everything
 * else is treated as PDFDocEncoding, which matches ISO-8859-1 for the ASCII
 * subset used by every real-world annotation we've seen. */
static const char *decode_pdf_text(pdfmake_arena_t *arena,
                                    const uint8_t *b, size_t n)
{
    size_t i;
    pdfmake_buf_t out;
    char *s;
    uint32_t cp;
    uint32_t lo;
    uint8_t c;
    if (!arena || !b) return NULL;

    /* UTF-16BE BOM */
    if (n >= 2 && b[0] == 0xFE && b[1] == 0xFF) {
        pdfmake_buf_init(&out);
        s = NULL;
        for (i = 2; i + 1 < n; i += 2) {
            cp = ((uint32_t)b[i] << 8) | b[i + 1];
            if (cp >= 0xD800 && cp <= 0xDBFF && i + 3 < n) {
                lo = ((uint32_t)b[i + 2] << 8) | b[i + 3];
                if (lo >= 0xDC00 && lo <= 0xDFFF) {
                    cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00);
                    i += 2;

src/pdfmake_textract.c  view on Meta::CPAN

                pdfmake_buf_append_byte(&out, 0x80 | ((cp >> 6) & 0x3F));
                pdfmake_buf_append_byte(&out, 0x80 | (cp & 0x3F));
            }
        }
        s = pdfmake_arena_alloc(arena, out.len + 1);
        if (s) { memcpy(s, out.data, out.len); s[out.len] = 0; }
        pdfmake_buf_free(&out);
        return s;
    }

    /* UTF-8 with BOM (PDF 2.0) */
    if (n >= 3 && b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF) {
        b += 3; n -= 3;
    }

    /* PDFDocEncoding → promote high-bit bytes to 2-byte UTF-8. */
    pdfmake_buf_init(&out);
    for (i = 0; i < n; i++) {
        c = b[i];
        if (c < 0x80) {
            pdfmake_buf_append_byte(&out, c);

t/50-markup-parse.t  view on Meta::CPAN

};

subtest 'depth is bounded' => sub {
    my $deep = '<doc>' . ('<box>' x 70) . 'x' . ('</box>' x 70) . '</doc>';
    parse_err($deep, qr/nested deeper than 64/, 'deep nesting is refused');

    my $ok = '<doc>' . ('<box>' x 40) . 'x' . ('</box>' x 40) . '</doc>';
    parse_ok($ok, 'nesting within the limit is fine');
};

subtest 'a UTF-8 BOM is tolerated' => sub {
    parse_ok("\xEF\xBB\xBF<doc><h1>x</h1></doc>", 'BOM skipped (bytes)');
    parse_ok("\x{FEFF}<doc><h1>x</h1></doc>",     'BOM skipped (characters)');
};

subtest 'characters in, characters out, whichever way they arrive' => sub {
    my $want = "caf\x{e9} \x{20ac}1,240 \x{4e2d}\x{6587}";
    my $chars = "<doc><text>$want</text></doc>";
    my $bytes = $chars;
    utf8::encode($bytes);

    for my $case (['character string', $chars], ['UTF-8 bytes', $bytes]) {
        my ($name, $src) = @$case;



( run in 1.250 second using v1.01-cache-2.11-cpan-8dfa8b56332 )