From 0ab57fb277726dd56f59b60b04e04595aab01fb9 Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Sun, 14 Nov 2021 03:58:20 +0900 Subject: [PATCH 1/2] Remove recognized keywords from image alternative text --- src/markdown/background_image/parse.js | 28 ++++++++--- src/markdown/image/parse.js | 68 ++++++++++++++++++++++---- test/markdown/image.js | 9 ++++ 3 files changed, 90 insertions(+), 15 deletions(-) diff --git a/src/markdown/background_image/parse.js b/src/markdown/background_image/parse.js index 6dbc2ac3..91a69d7e 100644 --- a/src/markdown/background_image/parse.js +++ b/src/markdown/background_image/parse.js @@ -30,27 +30,43 @@ function backgroundImageParse(md) { if (t.type === 'image') { const { marpitImage } = t.meta - if (t.meta.marpitImage.options.includes('bg')) { + if ( + marpitImage.options.some((v) => !v.consumed && v.content === 'bg') + ) { marpitImage.background = true t.hidden = true for (const opt of marpitImage.options) { + if (opt.consumed) continue + let consumed = false + + // bg keyword + if (opt.content === 'bg') consumed = true + // Background size keyword - if (bgSizeKeywords[opt]) - marpitImage.backgroundSize = bgSizeKeywords[opt] + if (bgSizeKeywords[opt.content]) { + marpitImage.backgroundSize = bgSizeKeywords[opt.content] + consumed = true + } // Split background keyword - const matched = opt.match(splitSizeMatcher) + const matched = opt.content.match(splitSizeMatcher) if (matched) { const [, splitSide, splitSize] = matched marpitImage.backgroundSplit = splitSide marpitImage.backgroundSplitSize = splitSize + + consumed = true } // Background aligned direction - if (opt === 'vertical' || opt === 'horizontal') - marpitImage.backgroundDirection = opt + if (opt.content === 'vertical' || opt.content === 'horizontal') { + marpitImage.backgroundDirection = opt.content + consumed = true + } + + if (consumed) opt.consumed = true } } } diff --git a/src/markdown/image/parse.js b/src/markdown/image/parse.js index 37f4b483..0f9ff9fc 100644 --- a/src/markdown/image/parse.js +++ b/src/markdown/image/parse.js @@ -43,7 +43,9 @@ optionMatchers.set( for (const arg of matches.slice(1)) { if (arg) { - const colorFunc = arg.match(/^(rgba?|hsla?)\((.*)\)$/) + const colorFunc = arg.match( + /^(rgba?|hsla?|hwb|(?:ok)?(?:lab|lch)|color)\((.*)\)$/ + ) args.push( colorFunc ? `${colorFunc[1]}(${escape(colorFunc[2])})` : escape(arg) @@ -96,14 +98,36 @@ function parseImage(md) { let originalURLMap let refCount = 0 - const finalizeTokenAttr = (token) => { + const finalizeTokenAttr = (token, state) => { // Convert imprimitive attribute value into primitive string - if (token.attrs && Array.isArray(token.attrs)) + if (token.attrs && Array.isArray(token.attrs)) { token.attrs = token.attrs.map(([name, value]) => [name, value.toString()]) + } + // Apply finalization recursively to inline tokens if (token.type === 'inline') { - // Apply finalization recursively to inline tokens - for (const t of token.children) finalizeTokenAttr(t) + for (const t of token.children) finalizeTokenAttr(t, state) + } + + // Re-generate the alt text of image token to remove Marpit specific options + if (token.type === 'image' && token.meta && token.meta.marpitImage) { + let updatedAlt = '' + let hasConsumed = false + + for (const opt of token.meta.marpitImage.options) { + if (opt.consumed) { + hasConsumed = true + } else { + updatedAlt += opt.leading + opt.content + } + } + + if (hasConsumed) { + let newTokens = [] + md.inline.parse(updatedAlt.trimLeft(), state.md, state.env, newTokens) + + token.children = newTokens + } } } @@ -129,7 +153,7 @@ function parseImage(md) { if (refCount === 0) { // Apply finalization for every tokens - for (const token of state.tokens) finalizeTokenAttr(token) + for (const token of state.tokens) finalizeTokenAttr(token, state) } } } @@ -137,7 +161,30 @@ function parseImage(md) { md.inline.ruler2.push('marpit_parse_image', ({ tokens }) => { for (const token of tokens) { if (token.type === 'image') { - const options = token.content.split(/\s+/).filter((s) => s.length > 0) + // Parse alt text as options + const optsBase = token.content.split(/(\s+)/) + + let currentIdx = 0 + let leading = '' + + const options = optsBase.reduce((acc, opt, i) => { + if (i % 2 === 0 && opt.length > 0) { + currentIdx += leading.length + acc.push({ + content: opt, + index: currentIdx, + leading, + consumed: false, + }) + + leading = '' + currentIdx += opt.length + } else { + leading += opt + } + return acc + }, []) + const url = token.attrGet('src') const originalUrl = originalURLMap.has(url) ? originalURLMap.get(url) @@ -162,9 +209,11 @@ function parseImage(md) { // Parse keyword through matchers for (const opt of options) { for (const [regexp, mergeFunc] of optionMatchers) { - const matched = opt.match(regexp) + if (opt.consumed) continue + const matched = opt.content.match(regexp) - if (matched) + if (matched) { + opt.consumed = true token.meta.marpitImage = { ...token.meta.marpitImage, ...mergeFunc(matched, { @@ -172,6 +221,7 @@ function parseImage(md) { ...token.meta.marpitImage, }), } + } } } } diff --git a/test/markdown/image.js b/test/markdown/image.js index 69da3c45..7314d48e 100644 --- a/test/markdown/image.js +++ b/test/markdown/image.js @@ -100,6 +100,15 @@ describe('Marpit image plugin', () => { expect(style('h:.678%')).not.toBe('height:.678%;') expect(style('h:unexpected')).not.toBe('height:unexpected;') }) + + it('removes recognized Marpit keywords from alt attribute of the output image', () => { + const output = md().render( + `![w:100px \t This is example\timage \t h:200px](https://example.com/test.jpg)` + ) + const $ = cheerio.load(output) + + expect($('img').attr('alt')).toBe('This is example\timage') + }) }) describe('Shorthand for text color', () => { From 8c1ca8b45530183d5f9658f49ed677e714b69e6d Mon Sep 17 00:00:00 2001 From: Yuki Hattori Date: Fri, 19 Nov 2021 20:59:05 +0900 Subject: [PATCH 2/2] [ci skip] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e65b0fdf..9bdaaf41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Remove recognized image keywords from alt text ([#316](https://github.com/marp-team/marpit/issues/316), [#318](https://github.com/marp-team/marpit/pull/318)) + ## v2.1.2 - 2021-10-26 ### Changed