%#!lualatex

%% 行末の句読点の位置によって，全角取りか半角取りかを自動的に調整する
%% 仕様：全調整量が二分以上なら，全角取りにする
\documentclass{article}
\usepackage{luatexja,luacode}
\begin{luacode}
local id_hlist = node.id('hlist')
local id_penalty = node.id('penalty')
local id_glue  = node.id('glue')
local id_glue_spec = node.id('glue_spec')
local attr_icflag = luatexbase.attributes['ltj@icflag']
local PACKED = luatexja.icflag_table.PACKED
local PROCESSED_BEGIN_FLAG = luatexja.icflag_table.PROCESSED_BEGIN_FLAG

local function get_attr_icflag(p)
   return (node.has_attribute(p, attr_icflag) or 0) % PROCESSED_BEGIN_FLAG
end

-- box 内で伸縮された glue の合計値を計算
local function compute_total_stretched(p)
  local go, gf, gs = p.glue_order, p.glue_set, p.glue_sign
  if gs ~= 1 and gs ~= 2 then return 0 end
  local a = 0
  for q in node.traverse_id(id_glue, p.head) do
    local qs = q
    if gs == 1 then -- stretching
      if qs.stretch_order == go then a = a + qs.stretch end
    else -- shrinking
      if qs.shrink_order == go then a = a - qs.shrink end
    end
  end
  return tex.round(a * gf)
end

function adjust_width_toten(head) 
  if not head then return head end
  for p in node.traverse_id(id_hlist, head) do repeat
    local t = node.tail(p.head) -- \rightskip glue
    local x = node.prev(t)      -- 本当の行末の node を格納
    if not x then break end
    if x.id == id_glue and x.subtype == 15 then
      -- 段落最終行のときの補正
      x = node.prev(node.prev(x))
    end
    while x do
      if x.id == id_penalty then x = node.prev(x) else break end
    end
    if x.id == id_hlist and get_attr_icflag(x) == PACKED then
       local xc = x.head
       local xcs = tex.round(font.fonts[xc.font].size*0.5)
       -- 句読点の最大補正幅
       local xcc = unicode.utf8.char(xc.char)
       local cts = compute_total_stretched(p)
       if (xcc == '，'  or xcc == '、' or xcc == '．'  or xcc == '。') and cts>=0 then
         local ag  = node.new(id_glue) -- additional glue
         ag.width = math.min(xcs, cts)
         ag.shrink, ag.stretch, ag.shrink_order, ag.stretch_order = 0, 0, 0, 0
         node.insert_after(p.head, x, ag)
         local f = node.hpack(p.head, p.width, 'exactly')
         f.head, p.glue_set, p.glue_sign, p.glue_order 
           = nil, f.glue_set, f.glue_sign, f.glue_order
         node.free(f)
       end
    end
  until true end
  return head
end

luatexbase.add_to_callback('post_linebreak_filter', 
  adjust_width_toten, 'Adjust width of toten', 100)
\end{luacode}

\begin{document}
\parindent0pt
\leavevmode
\vrule\setbox0=\vbox{\hsize=20\zw 
０１２３４５６７８\hskip-0.25\zw ９あいうえおかきくけ，\break
０１２３４５６７８\hskip0\zw ９あいうえおかきくけ，\break
０１２３４５６７８\hskip0.25\zw ９あいうえおかきくけ，\break
０１２３４５６７８\hskip0.5\zw ９あいうえおかきくけ，\break
０１２３４５６７８\hskip0.75\zw ９あいうえおかきくけ，\break
０１２３４５６７８\hskip1\zw ９あいうえおかきくけ，\break

０１２３４５６７８９あいうえおかきくけこ%
}\copy0\vrule

\end{document}
