Skip to content

Commit

Permalink
Merge pull request #318 from marp-team/remove-keywords-from-image-alt
Browse files Browse the repository at this point in the history
Remove recognized keywords from image alternative text
  • Loading branch information
yhatt authored Nov 19, 2021
2 parents f9f99d9 + 8c1ca8b commit d62392f
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 15 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 22 additions & 6 deletions src/markdown/background_image/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
Expand Down
68 changes: 59 additions & 9 deletions src/markdown/image/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
}
}

Expand All @@ -129,15 +153,38 @@ 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)
}
}
}

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)
Expand All @@ -162,16 +209,19 @@ 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, {
filters: [],
...token.meta.marpitImage,
}),
}
}
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions test/markdown/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down

0 comments on commit d62392f

Please sign in to comment.