このモジュールについての説明文ページを モジュール:画像サイズ/doc に作成できます

--[==[
モジュール:画像サイズ

なにをするもの?
  記事に書かれた [[ファイル:○○.jpg]] のように「大きさの指定がない画像」に、
  表示に必要なぶんだけの大きさ(既定360px)を自動でつけます。

なぜ必要?
  大きさを書かないと、MediaWikiは元の画像をそのまま送ります。
  宇宙wikiのインフォボックスは、画像をPCで359px・スマホで334pxの枠に
  収めて表示しているのに、実際には900pxや600pxの元画像が
  そのまま送られていました(実測: 10,231枚・819MBのうち、
  800pxより大きい画像が4,500枚・562MB)。
  大きさを書けばMediaWikiが縮小版を作り、しかも
  画面のこまかさ(1倍/1.5倍/2倍)に合わせて自動で選んでくれます。
  実測では1枚117KB → 22〜67KBになりました。

  記事を1本ずつ直すのは現実的ではないので、
  テンプレート側で一度だけ通せば全記事に効くようにしています。

つかいかた(テンプレートの中で):
  {{#invoke:画像サイズ|fit|{{{画像|}}}}}
  大きさを変えたいとき:
  {{#invoke:画像サイズ|fit|{{{画像|}}}|px=250}}

さわらないもの(そのまま素通しします):
  ・すでに 250px / x200px / upright などの大きさ指定があるもの
  ・thumb や サムネイル が書かれているもの(MediaWikiが既に縮めているため)
  ・ファイル以外のリンク
  ・テンプレート({{...}})やHTMLタグが混ざっているもの
  ・空っぽのもの

注意: この説明文の中に [[ ]] が出てくるため、
      コメントの囲みは --[[ ではなく --[==[ を使っています。
--]==]

local p = {}

-- 既定の幅(px)。インフォボックスの実測表示幅(PC 359px / スマホ 334px)に合わせる。
local DEFAULT_PX = 360

-- 「ファイル」を表す名前空間の書き方(小文字にして比べる)
local FILE_NS = {
	['ファイル'] = true,
	['file'] = true,
	['画像'] = true,
	['image'] = true,
}

-- 画像として扱う拡張子(ファイル名だけが書かれていた場合の判定用)
local IMAGE_EXT = {
	jpg = true, jpeg = true, png = true, gif = true, webp = true, svg = true,
}

-- thumb / frame などが書かれている画像は、MediaWiki が
-- もともと既定の大きさ(220pxなど)に縮めてくれるので、
-- こちらから px を足すとかえって大きくなってしまう。さわらない。
local ALREADY_SIZED = {
	thumb = true,
	thumbnail = true,
	frameless = true,
	frame = true,
	['サムネイル'] = true,
	['枠なし'] = true,
	['枠'] = true,
}

-- その値が「大きさの指定」かどうか
local function isSize( s )
	s = mw.text.trim( s )
	local lower = mw.ustring.lower( s )
	if lower:match( '^%d+%s*px$' ) then return true end
	if lower:match( '^x%d+%s*px$' ) then return true end
	if lower:match( '^%d+%s*x%s*%d+%s*px$' ) then return true end
	if lower:match( '^upright' ) then return true end
	return false
end

-- [[...]] の中身を受け取り、必要なら大きさを足して返す。
-- 足す必要がなければ nil を返す(= 呼び出し元はもとのまま残す)。
local function fixInner( inner, px )
	local parts = mw.text.split( inner, '|', true )
	local target = mw.text.trim( parts[ 1 ] )

	-- 先頭の「ファイル:」を確認する。無ければ画像リンクではない。
	local ns = target:match( '^([^:]+):' )
	if not ns then return nil end
	if not FILE_NS[ mw.ustring.lower( mw.text.trim( ns ) ) ] then return nil end

	-- すでに大きさが書かれている、または thumb などで
	-- MediaWiki が自動で縮めてくれるものは、何もしない
	for i = 2, #parts do
		local v = mw.ustring.lower( mw.text.trim( parts[ i ] ) )
		if isSize( v ) or ALREADY_SIZED[ v ] then return nil end
	end

	table.insert( parts, 2, px .. 'px' )
	return '[[' .. table.concat( parts, '|' ) .. ']]'
end

-- 本体
function p._fit( text, px )
	if text == nil then return '' end
	text = mw.text.trim( text )
	if text == '' then return '' end

	px = tonumber( px ) or DEFAULT_PX
	if px < 40 or px > 2000 then px = DEFAULT_PX end

	-- テンプレートやHTMLが混ざっているものは、こわす危険があるのでさわらない
	if text:find( '{{', 1, true ) or text:find( '<', 1, true ) then
		return text
	end

	-- (1) [[ファイル:...]] の形
	if text:find( '[[', 1, true ) then
		local changed = false
		local out = text:gsub( '%[%[([^%[%]]-)%]%]', function( inner )
			local fixed = fixInner( inner, px )
			if fixed then
				changed = true
				return fixed
			end
			return nil -- そのまま
		end )
		return changed and out or text
	end

	-- (2) ファイル名だけが書かれている形 (例: れじぇくん.jpg)
	if not text:find( '|', 1, true ) and not text:find( ']', 1, true ) then
		local ext = text:match( '%.([%a%d]+)$' )
		if ext and IMAGE_EXT[ mw.ustring.lower( ext ) ] then
			return '[[ファイル:' .. text .. '|' .. px .. 'px]]'
		end
	end

	return text
end

function p.fit( frame )
	local args = frame.args
	return p._fit( args[ 1 ], args.px or args[ 2 ] )
end

return p