\phantom と \obeylines

\phantom と \obeylines

私はPlain TeXを使用しています(フォントの関係で実際にはXeTeXですが、基本的には慣れているPlainのように使用しています)。誰かのために詩をタイプしていて、次のような効果を作りたいと思っています。

it's magic
     tragic

\phantomおそらくこの目的に使えるだろうと思ったのですが、

\begingroup\obeylines
it's magic
\hphantom{it's} tragic
\endgroup%obeylines

水平方向のスペースが必要なので を使用しました\hphantomが、違いがよくわかりません。 どちらを使用した場合\hphantomも出力は\phantom同じです。

ここに画像の説明を入力してください

また、スペースを\phantomボックスの内側に置くか外側に置くかは問題ではないようです。

ファントムが独自のラインに置かれている理由はよくわかりませんが、\obeylines私が理解していない何かと関係があるのではないかと思います。ご協力に感謝いたします。また、これが愚かな間違いであった場合は申し訳ありません。

答え1

ここに画像の説明を入力してください

LaTeX を使用すると、表示するマークアップは意図したとおりの効果が得られます。

\documentclass{article}

\begin{document}

\begingroup\obeylines
it's magic
\hphantom{it's} tragic
\endgroup%obeylines

\end{document}

プレーンTeXでは\hphantom段落は開始されないので、次のようになります。


\begingroup\obeylines
it's magic
\leavevmode\hphantom{it's} tragic
\endgroup%obeylines


\bye

しかし、私は実際のドキュメントではなく、小さなテスト例にのみプレーン TeX を使用します。

答え2

使用方法\halign:

\halign{#&#\cr
  it's &magic\cr
       &tragic\cr}
\bye

ここに画像の説明を入力してください

答え3

LuaTeX (通常は XeTeX と互換性があるはずです) を使用する場合は、追加のマークアップを必要とせずに、最初のインデントを前の行に自動的に揃える環境を定義できます。

%%%%%%%%%%%%%%%%%%
%%% Formatting %%%
%%%%%%%%%%%%%%%%%%

% Make LuaTeX load fonts like XeTeX
\input luaotfload.sty
\directlua{
    config.luaotfload.default_features.global.tlig = true
    config.luaotfload.default_features.global.trep = true
}

% Load the fonts
\font\seventeenrm="[lmroman17-regular]" at 17pt
\font\adventorbold="TeX Gyre Adventor/B" at 12pt

% Set the main font
\seventeenrm
\baselineskip=1.2em

\nopagenumbers


%%%%%%%%%%%%%%%%
%%% TeX Code %%%
%%%%%%%%%%%%%%%%

\newattribute\alignspacesattr

\def\beginalignspaces{%
    \bgroup%
        \alignspacesattr=1% Mark all nodes with this attribute
        \catcode`\ =12% Make the space into a regular character
        \obeylines% Process line-by-line
        \toksapp\everypar{\setbox0=\lastbox}% Remove the indent from every line
}
\def\endalignspaces{%
        \par% Make sure that the last line is processed
    \egroup%
}


%%%%%%%%%%%%%%%%
%%% Lua Code %%%
%%%%%%%%%%%%%%%%

\directlua{
    % Helper function to traverse through any marked nodes
    local n_attr
    local function get_next_attr(n)
        local next = n.next
        if next then
            n_attr = select(2, node.find_attribute(next, "alignspacesattr"))
            return n_attr
        end
    end

    % Replaces a node with another node
    local function replace_node(head, n1, n2)
        local head, current = node.remove(head, n1)
        head, n2 = node.insert_before(head, current, n2)
        return head, n2
    end

    local prev_widths = {}

    % Define the callback, which runs for every line
    luatexbase.add_to_callback("pre_linebreak_filter", function (head)
        % Remove any discretionaries
        local head = head
        if get_next_attr(head) then
            head = node.flatten_discretionaries(head)
        end

        % Initialize the line-local variables
        local this_widths = {}
        local before_chars = true
        n_attr = head

        % Iterate over every character in a marked line
        while get_next_attr(n_attr) do
            local char = n_attr.char

            % Replace space glyphs with some sort of glue
            if char == string.byte(" ") then
                local hskip = node.new("glue")

                if before_chars then
                    % If we're at the start of the line, then make the space the
                    % same width as the character immediately above it.
                    hskip.width = prev_widths[rawlen(this_widths) + 1]
                else
                    % If we're in the middle of the line, set it to the regular
                    % space width.
                    local params = font.getfont(n_attr.font).parameters
                    node.setglue(
                        hskip,
                        params.space, params.space_stretch, params.space_shrink,
                        0, 0
                    )
                end
                head, n_attr = replace_node(head, n_attr, hskip)
            else
                % If we're not at a space, then set that we're not at the start
                % of the line.
                before_chars = false
            end

            % Recurse through any ligatures
            local chars = {}
            local function check_components(n)
                if n.components then
                    for m in node.traverse(n.components) do
                        check_components(m)
                    end
                elseif char and
                       (n.id == node.id("glyph") or n.id == node.id("glue"))
                then
                    chars[rawlen(chars) + 1] = n
                end
            end
            check_components(n_attr)

            % Save the widths of the characters on this line
            for _, n in ipairs(chars) do
                this_widths[rawlen(this_widths) + 1] = (n.width or 0)
            end
        end
        prev_widths = this_widths

        return head
    end, "align_spaces")
}


%%%%%%%%%%%%
%%% Demo %%%
%%%%%%%%%%%%

\beginalignspaces
% Works with different character widths
abc def ghi jkl mno pqr
    def ghi
         hi jkl m
                 no pqr
                      r stu
      f ghi jkl mno pqr stu
% And with em-dashes
  M l M --- M
    l M --- M
      M --- M
        --- M
            M
% And with ligatures
            waffles
                les
% And with different fonts
            ABCDEF G {\adventorbold H I} J K
                     {\adventorbold   I} J
                EF G {\adventorbold H I}
            ABCDEF G {\adventorbold H  }
abM l M --- ABC
        ---
\endalignspaces

\bye

出力

関連情報