コンテンツにスキップ

モジュール:File clip

モジュールの解説[表示] [編集] [履歴] [キャッシュを破棄]

PDF{{File clip}}

{{File clip/sandbox}}Template:File clip/testcases

使[]

{{#invoke:File clip|呼び出す関数名|ファイル名|width=表示サイズ(横幅)|その他のオプション ... }}

thumbnothumb



:File:

|width=



|t=, |r=, |b=, |l= - 0100%0

|w=, |h= - Scribunto使|w=|h=

|align= - [[:|...]]thumbHelp:#thumbnothumbHelp:#

|caption=|c= - 使

|alt= - imgalt

|page= - DjVuPDF1

|link= - CC BY使Help:#

使[]



{{#invoke:File clip|thumb|Wikipedia Logo 1.0.png|width=200|align=left|t=0|r=25|b=70|l=35|caption=2003年から2010年まで使われていたウィキペディアのロゴの一部分}}


拡大
2003年から2010年まで使われていたウィキペディアのロゴの一部分

require('strict')

local function showImage(filename, caption, options)
 --[=[
  画像を表示するウィキテキスト構文を生成して返す
  filename: ファイル名(文字列型、必須)
  caption(): 画像のキャプション(文字列型、省略可)
  options: その他のオプション(テーブル型、省略可)
 ]=]--
 if type(filename) ~= 'string' or filename == '' then
  error('ファイル名が正しく指定されていません。', 2)
 end
 local t = { filename }
 if options then
  for k, v in pairs(options) do
   table.insert(
    t,
    (type(k) == 'string') and mw.ustring.format('%s=%s', k, v) or v
   )
  end
 end
 if caption then table.insert(t, caption) end
 return mw.ustring.format('[[File:%s]]', table.concat(t, '|'))
end

local function isValign(value)
 for _, v in ipairs({'baseline', 'sub', 'super', 'top', 'text-top',
 'middle', 'bottom', 'text-bottom'}) do
  if v == value then return true end
 end
 return false
end

--------------------------------------------------------------------------------
-- fileClipクラス
--  
-- fileClipをテーブルのメタテーブルに設定することで、
-- そのテーブルはfileClipクラスのインスタンスであるかのように振る舞う
--------------------------------------------------------------------------------
local fileClip = {}
fileClip.__index = fileClip

function fileClip:_setError(message)
 self._error = mw.html.create('strong')
  :addClass('error')
  :wikitext('エラー:' .. message)
 return self
end

function fileClip._new(args)
 local obj = {}
 setmetatable(obj, fileClip)
 
 -- 配置指定
 obj._align = args.align
 
 if not args[1] then
  return obj:_setError('ファイル名が指定されていません。')
 end
 
 -- 表示サイズ(横幅)
 obj._width = tonumber(args.width)
 if not obj._width then
  obj._width = 200
  return obj:_setError('表示サイズが指定されていません。')
 end
 
 -- 切り抜き範囲(百分率)
 -- 指定省略時は0%
 local crop_top = tonumber(args[2] or args.t) or 0
 local crop_right = tonumber(args[3] or args.r) or 0
 local crop_bottom = tonumber(args[4] or args.b) or 0
 local crop_left = tonumber(args[5] or args.l) or 0
 
 -- 画像の表示範囲(百分率)を計算
 -- 縦方向・横方向のどちらか一方でも0%以下ならエラー
 local v_rest = 100 - (crop_top + crop_bottom)
 local h_rest = 100 - (crop_right + crop_left)
 if v_rest <= 0 then
  return (h_rest <= 0)
   and obj:_setError('縦方向・横方向ともに100%以上切り取っています。')
   or obj:_setError('縦方向に100%以上切り取っています。')
 end
 if h_rest <= 0 then
  return obj:_setError('横方向に100%以上切り取っています。')
 end
 
 -- ファイルページのタイトルオブジェクト
 local filepage = mw.title.new(args[1], 'ファイル')
 
 -- ファイル名(名前空間プレフィックスを含まない)
 obj._filename = filepage.text
 
 -- 元の画像の横幅・縦幅
 -- 画像の横幅・縦幅はファイルメタデータを参照すれば取得可能だが、
 -- ファイルメタデータを取得する処理は高負荷である。
 -- 横幅・縦幅の両方が手動で指定されている場合は、それを使用し、
 -- ファイルメタデータの取得を避ける。
 local orig_width, orig_height
 if args.w and args.h then
  orig_width = tonumber(args.w)
  orig_height = tonumber(args.h)
 else
  local filemetadata = filepage.file -- 高負荷
  if not filemetadata.exists then
   return obj:_setError(
    mw.ustring.format('[[:File:%s]]は存在しません。', obj._filename)
   )
  end
  if string.find(filemetadata.mimeType, 'image/', 1, true) ~= 1
  and filemetadata.mimeType ~= 'application/pdf' then
   return obj:_setError(
    mw.ustring.format(
     '[[:File:%s]]([[MIMEタイプ]]: %s)は切り抜き表示に対応していません。',
     obj._filename,
     filemetadata.mimeType
    )
   )
  end
  local page = tonumber(args.page)
  if page and page > 1 and filemetadata.pages then
   filemetadata = filemetadata.pages[page]
  end
  orig_width = filemetadata.width
  orig_height = filemetadata.height
 end
 
 -- 切り抜き画像の表示サイズ(縦幅)を計算
 obj._height = math.floor(
  (obj._width * orig_height * v_rest) /
  (orig_width * h_rest)
 )
 
 -- 画像を拡大後、上方向および左方向に何pxずらせばよいか計算
 obj._shift_up = math.floor(
  (obj._width * orig_height * crop_top) / 
  (orig_width * h_rest)
 )
 obj._shift_left = math.floor((obj._width * crop_left) / h_rest)
 
 -- 画像のキャプション
 -- 指定省略時は指定されたファイル名(名前空間プレフィックスを含まない)
 obj._caption = args[6] or args.c or args.caption or obj._filename
 
 -- 拡大後の画像の横幅、その他のオプション
 obj._image_options = {
  math.floor((obj._width * 100) / h_rest) .. 'px',
  alt = args.alt,
  link = args.link,
  page = args.page,
  class = args.class,
  lang = args.lang
 }
 
 return obj
end

function fileClip:thumb()
 local center = mw.html.create('div'):addClass('center')
 local thumb = mw.html.create('div')
 if self._align == 'center' or self._align == 'none' then
  thumb:addClass('thumb fileclip-thumb tnone')
 elseif self._align == 'left' then
  thumb:addClass('thumb fileclip-thumb tleft')
 else
  thumb:addClass('thumb fileclip-thumb tright')
 end
 local thumbinner = mw.html.create('div')
  :addClass('thumbinner fileclip-thumbinner')
  :css('width', (self._width + 2) .. 'px')
 if self._error then
  thumb:node(thumbinner:node(self._error))
  return (self._align == 'center') and center:node(thumb) or thumb
 end
 local clipper = mw.html.create('div')
  :addClass('fileclip-clipper thumbimage')
  :css({ width = self._width .. 'px', height = self._height .. 'px' })
  :cssText('position: relative; overflow: hidden;')
 local shifting = mw.html.create('div')
  :addClass('fileclip-shifting')
  :css({ top = -self._shift_up .. 'px', left = -self._shift_left .. 'px' })
  :cssText('position: absolute;')
  :wikitext(showImage(self._filename, nil, self._image_options))
 clipper:node(shifting)
 local thumbcaption = mw.html.create('div'):addClass('thumbcaption')
 local magnify = mw.html.create('div')
  :addClass('magnify fileclip-magnify')
  :wikitext(
   showImage(
    'Scissors icon black.svg',
    '拡大',
    { 'text-top|16px', link = 'File:' .. self._filename }
   )
  )
 thumbcaption:node(magnify):wikitext(self._caption)
 thumb:node(thumbinner:node(clipper):node(thumbcaption))
 return (self._align == 'center') and center:node(thumb) or thumb
  
end

function fileClip:nothumb()
 if self._error then return self._error end
 local center = mw.html.create('div'):addClass('center')
 local aligndiv
 local clipper = mw.html.create('span')
  :addClass('fileclip-clipper')
  :css({ width = (self._width .. 'px'), height = (self._height .. 'px') })
  :cssText('position: relative; overflow: hidden; display: inline-block;')
 
 if self._align == 'center' or self._align == 'none' then
  aligndiv = mw.html.create('div'):addClass('floatnone')
 elseif self._align == 'right' or self._align == 'left' then
  aligndiv = mw.html.create('div'):addClass('float' .. self._align)
 elseif self._align and isValign(self._align) then
  clipper:css({ ['vertical-align'] = self._align })
 else
  clipper:css('vertical-align', 'middle')
 end
 
 local shifting = mw.html.create('span')
  :addClass('fileclip-shifting')
  :css({ top = -self._shift_up .. 'px', left = -self._shift_left .. 'px' })
  :cssText('position: absolute;')
  :wikitext(showImage(self._filename, self._caption, self._image_options))
 clipper:node(shifting)
 if not aligndiv then return clipper end
 aligndiv:node(clipper)
 return (self._align == 'center') and center:node(aligndiv) or aligndiv
end

local getArgs

local function _main(mode, frame)
 if not getArgs then getArgs = require('モジュール:Arguments').getArgs end
 local args = getArgs(frame, {
  valueFunc = function (key, value)
   -- alt引数とlink引数については、省略した場合と
   -- 空文字列を指定した場合を区別する
   
   if value then
    value = mw.text.trim(value)
   end
   if value == '' and key ~= 'alt' and key ~= 'link' then
    return nil
   end
   return value
  end,
  wrappers = { 'Template:File clip', 'Template:File clip2' }
 })
 local thumb = fileClip._new(args)
 thumb._export = thumb[mode]
 return frame:extensionTag{
   name = 'templatestyles',
   args = { src = 'File clip/styles.css' }
  }
  .. tostring(thumb:_export())
  .. '[[Category:切り抜き画像]]'
end

local p = {
 thumb = function(frame) return _main('thumb', frame) end,
 nothumb = function(frame) return _main('nothumb', frame) end
}

return p