\documentclass{ltjtarticle}
%\documentclass{ltjsarticle}
\usepackage{luacode}
\begin{luacode}
local metrics = luatexja.jfont.metrics

-- ******** STEP 1: メトリックの改造 ********
local max = math.max
local copy = table.fastcopy
local function add_two_em_dash(ji,_) -- 2倍角，3倍角ダーシ用の文字クラスを作成
  local char_type = ji.char_type
  local m, d = 0, (ji.chars[0x2015] or 0) -- d: class of U+2015
  for i,_ in pairs(char_type) do m=max(m,i) end
  m = m+1
  for i=1,2 do
    char_type[m*i+d] = copy(char_type[d])
    char_type[m*i+d].width=char_type[d].width*(i+1)
    if ji.dir=='tate' then char_type[m*i+d].left=-0.5*i end
  end
  ji.extra={}; ji.extra['dash_offset']=m
  return ji
end
luatexbase.add_to_callback('luatexja.load_jfm',add_two_em_dash, 'hoge', 1)
--  既に読み込まれているメトリックに対しても，同じことをやらないといけない
for i,v in pairs(metrics) do
  metrics[i] = add_two_em_dash(metrics[i], nil)
  local cache_index = {}
  for j,w in pairs(metrics[i].size_cache) do
    table.insert(cache_index, j)
  end
  for _,j in ipairs(cache_index) do
    metrics[i].size_cache[j]=nil;
    luatexja.jfont.update_jfm_cache(i,j)
  end
end

-- ******** STEP 2: フォントから倍角ダーシの番号を抽出 ********
-- U+2E3A: Two-Em Dash, U+2E3B: Three-Em Dash
local abs = math.abs
local function add_table_for_dash(fmtable, fn)
   local fc = fonts.hashes.identifiers[fn].characters
   local met, zw = metrics[fmtable.jfm], fmtable.zw
   local d,doff =  met.chars[0x2015] or 0, met.extra['dash_offset'] or 0
   if met.dir=='yoko' then
     for i,v in pairs(fc) do -- 幅が 2, 3 全角のグリフは全部ダーシとして扱う
       if abs(v.width/zw-2)<0.01 then
         fmtable.cid_char_type = fmtable.cid_char_type  or {}
         fmtable.cid_char_type[i] = doff+d
       elseif abs(v.width/zw-3)<0.01 then
         fmtable.cid_char_type = fmtable.cid_char_type  or {}
         fmtable.cid_char_type[i] = 2*doff+d
       end
     end
   elseif fc[0x2015] then
     local ohd = 0.88 - 0.12 -- 決め打ち（全角仮想ボディの高さ - 深さ）
     for i,v in pairs(fc) do
       if v.height and v.depth and v.height+v.depth > 1.8*zw
         and abs((v.height-v.depth)/zw-ohd)<0.01 then
         -- 倍角ダーシは全角の仮想ボディに対して上下対称に伸びていると仮定
         fmtable.cid_char_type = fmtable.cid_char_type  or {}
         fmtable.cid_char_type[i] = ((v.height+v.depth > 2.5*zw) and 2 or 1)*doff+d
         -- 高さと深さの和が 2.5 全角を超えたら 3 倍ダーシとして扱う
       end
     end
   end
   return fmtable
end
luatexbase.add_to_callback("luatexja.define_jfont",
			   add_table_for_dash, "hoge", 1)
--  既に読み込まれているフォントに対しても，同じことをやらないといけない
for fn, v in pairs(luatexja.jfont.font_metric_table) do
   luatexja.jfont.font_metric_table[fn] = add_table_for_dash(v, fn)
end

-- ******** STEP 3: STEP 2 のデータを用いて文字クラスの決定 ********
local function cid_set_char_class(arg, fmtable, char)
   if arg~=0 then return arg
   elseif fmtable.cid_char_type then
      return fmtable.cid_char_type[char] or 0
   else return 0
   end
end
luatexbase.add_to_callback("luatexja.find_char_class",
			   cid_set_char_class, "ltj.otf.find_char_class", 1)
\end{luacode}
\usepackage[sourcehan]{luatexja-preset}

\begin{document}

\def\TEST#1{\chardef\D="#1
\medskip
\texttt{U+#1}\par

\parbox<y>{20\zw}{%
しかし\D\D\D そして\D\D なぜなら\D\D ！\D\D まさか\D\D ？\D\D 『聞こえますか……\D\D 』\D\D 終

\textgt{しかし\D\D\D そして\D\D なぜなら\D\D ！\D\D まさか\D\D ？\D\D 『聞こえますか……\D\D 』\D\D 終}%
}%
\parbox<t>{25\zw}{%
しかし\D\D\D そして\D\D なぜなら\D\D ！\D\D まさか\D\D ？\D\D 『聞こえますか……\D\D 』\D\D 終

\textgt{しかし\D\D\D そして\D\D なぜなら\D\D ！\D\D まさか\D\D ？\D\D 『聞こえますか……\D\D 』\D\D 終}%
}}

\TEST{2014}

\TEST{2015}


\end{document}
