2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу 1, 2, 3  След.
 
 Как обойти \detokenize?
Сообщение21.06.2022, 20:57 
Заслуженный участник
Аватара пользователя


28/09/06
10847
Предыстория вопроса:

Я решил сделать в файле pdf всплывающие подсказки с текстом сносок. Типа, наводишь мышь на символ сноски и появляется окошко с текстом сноски. Гораздо удобнее, чем скакать каждый раз вниз страницы по ссылке, которую предлагает пакет hyperref. Поскольку я убедился, что существующие пакеты - типа cooltooltip - делают это весьма криво (как минимум, они пытаются встроить в pdf JavaScript, каковой многие ридеры отказываются исполнять по соображениям безопасности), я провёл некоторое собственное исследование, покопавшись в PDF reference и в мануале к pdfTeX, которым я пользуюсь для компиляции TeX-овских исходников. В итоге я пришёл к выводу, что более или менее нормально на роль всплывающих подсказок подходят только т.н. "аннотации" типа /Text (вообще-то в pdf предусмотрена куча разных типов аннотаций, но всё остальное - не то).

Неприятность заключается в том, что выводимый в аннотацию текст должен иметь кодировку UTF-16 big endian, ничего иного формат pdf для отображения в этом месте русских букв не предлагает. Я нашёл пакет stringenc, который предлагает средства перекодировки текста. В итоге вот такой код:
Используется синтаксис LaTeX
\usepackage{stringenc}
...
\StringEncodingConvert\myfootip{\detokenize{#1}}{utf8}{utf16be}%
...

делает из исходной кодировки моего текста UTF-8 то, что мне нужно. А именно, на основе текста, переданного макросу в параметре #1, создаётся команда \myfootip, которую я могу поставить в команду, генерирующую pdf-аннотацию типа /Text со вполне читаемым русским содержанием. Сначала я не понял, зачем нужна команда \detokenize{} внутри макроса и пытался выполнять перекодировку без неё. Но в результате на выходе макроса перекодировки получается (и, соответственно, отображается в тексте аннотации) вместо русских букв всякая ерунда типа \T2A\CYRP или \T2A\cyro. Почитав документацию к пакету stringenc, я осознал, что pdfTeX воспринимает каждую русскую букву как токен (таков механизм русификации в латех), а эти коды - результат "раскрытия" этих токенов. Там я и нашёл подсказку про то, что нужно использовать \detokenize{}. После этого всё стало замечательно работать.

Но (и тут от предыстории мы переходим собственно к сути вопроса) у меня в сносках полно ссылок на литературу! Типа: "Подробнее об этом можно почитать в [1]". В исходном тексте сноски, который я передаю макросу на перекодировку, ссылку на библиографию в виде \cite{source_name} или чего-то подобного передавать бесполезно, потому что \detokenize{} препятствует раскрытию этого кода и на выходе мы получим ерунду. Можно везде по тексту руками заменить эти коды на [1] и т.п., но это очень кривое решение, потому что в библиографию может что-то вставиться и вся нумерация источников съедет.

Вопрос заключается в том, как обойти этот \detokenize{} для ссылок на библиографию (так, чтобы TeX нормально сгенерировал номер источника), но при этом сохранить его эффект воздействия на русские буквы?

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение22.06.2022, 12:52 
Заслуженный участник


14/10/14
1220
Может быть, проще набирать исходный файл в UTF-16, или перекодировать непосредственно перед компиляцией?

-- 22.06.2022, 13:58 --

Покажите компилирующийся пример, я попробую с ним поиграть, может быть.

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение22.06.2022, 18:12 
Заслуженный участник
Аватара пользователя


28/09/06
10847
Slav-27 в сообщении #1558185 писал(а):
Может быть, проще набирать исходный файл в UTF-16, или перекодировать непосредственно перед компиляцией?
Не хочется прогибаться под изменчивый мир. UTF-16 - это что-то специфически виндузовское, к моей рабочей среде отношения не имеющее.

Slav-27 в сообщении #1558185 писал(а):
Покажите компилирующийся пример, я попробую с ним поиграть, может быть.

код: [ скачать ] [ спрятать ]
Используется синтаксис LaTeX
\documentclass[
    a4paper,% размеры листа
    11pt,% размеры нормального шрифта
    toc=bibliography% включить в оглавление литературу
    ]{scrbook} % книга KOMAscript, см. scrguien.pdf

\usepackage[T2A]{fontenc}% Выбор внутренней TeX−кодировки
\usepackage[utf8]{inputenc}% Выбор кодовой страницы документа.
\usepackage[english,russian]{babel}% Выбор языка документа.
\usepackage{indentfirst}% Начинать первый параграф с красной строки.
\usepackage{xcolor}% Для работы с цветами, включая гиперссылки
\usepackage{stringenc}% Конвертер кодировок, позволяет из utf8 делать utf16be
\usepackage[hyperfootnotes=false,pagebackref=true,pdftex]{hyperref}% Для гиперссылок, включая из оглавления, на библиографию и обратные
\hypersetup{
    colorlinks=true% ссылки будут выделяться цветом, а не рамкой
}

\title{Пробный заголовок}
\subtitle{пробной книги}
\author{epros}
\date{\today}

% Макрос создаёт сноску с всплывающей подсказкой (первый аргумент); второй агрумент - содержимое нормальной сноски внизу страницы
\newcommand*{\myfootnote}[2]{%
 \StringEncodingConvert\myfootip{\detokenize{#1}}{utf8}{utf16be}%
 \pdfstartlink user{%
  /Subtype /Text% создаётся аннотация типа Text
  /T (epros)% автор аннотации
  /Contents (\string\376\string\377\myfootip)% текст аннотации; префикс перед \myfootip согласно формату pdf идентифициует строку unicode
  /AP <<
   /N \emptyicon\space 0 R% ссылка на создаваемый в начале документа объект pdf - основной вид значка аннотации
   /R \emptyicon\space 0 R% вид значка при наведённом указателе мыши
   /D \emptyicon\space 0 R% вид значка при нажатой кнопке мыши
  >>
 }\footnote{#2}\pdfendlink%
}%

\begin{document}
   
    \maketitle
   
        \tableofcontents
       
        % Этот код создаёт объект pdf - пустой бокс для использования вместо стандартного значка текстовой аннотации
        \newbox\tempboxa
        \setbox\tempboxa=\hbox{}
        \immediate\pdfxform\tempboxa
        \edef\emptyicon{\the\pdflastxform}% сохраняем номер объекта для использования при вызове макроса \myfootnote
 
        \part{Пробная часть}
        \chapter{Пробная глава}
        \section{Пробный раздел}
    Что же позволяет говорить не просто об эквивалентности, а о равенстве объектов? Для этого они должны быть ещё и неразличимы. Принцип, отождествляющий неразличимость и равенство объектов, известен как <<закон Лейбница>>\myfootnote{См. переписку Лейбница с Кларком в [1].}{См. переписку Лейбница с Кларком в~\cite{leibniz_tom1}.}.

{\raggedright
\begin{thebibliography}{99}
       
        \bibitem{leibniz_tom1}Г.\,В.~Лейбниц. Сочинения в четырёх томах (под редакцией В.\,В.~Соколова, 1982). Том 1. Упоминается на с.\!\!
       

\end{thebibliography}
}
   
\end{document}
 


Компилировать нужно с помощью pdfTeX.

Если убрать из макроса \detokenize, то коды TA2 вместо русских букв появляются уже в строке \myfootip. Я проверял с помощью \write16{\myfootip}, т.е. это не эффект генерации pdf, а результат работы непосредственно макроса \StringEncodingConvert.

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение23.06.2022, 15:43 
Заслуженный участник
Аватара пользователя


28/09/06
10847
Я также попробовал на тестовом примере последовать и вот этому совету:

Slav-27 в сообщении #1558185 писал(а):
Может быть, проще набирать исходный файл в UTF-16, или перекодировать непосредственно перед компиляцией?

Сразу хочу заметить, что это оказалось не проще.
Во-первых, мой комп оказался little endian, так что текстовые редакторы (в основном я пользуюсь Kate) понимать UTF-16BE отказались. К счастью, оказалось, что ридеры PDF нормально воспринимают и текст формата UTF-16LE, нужно только в начале текстовой строки ставить \377\376 вместо \376\377.
Во-вторых, у текстовых редакторов оказалась настройка "кодировки по умолчанию", которая, конечно, стоит на UTF-8 и менять её - значит создавать неудобства при работе с другими файлами. Почему-то кодировку UTF-16LE они по умолчанию распознавать не хотят, так что приходится при каждом открытии файла им указывать кодировку явно.
В третьих, мой pdfTeX почему-то ждёт на входе файл именно в кодировке UTF-8. Вот что я вижу в логе:
Код:
! LaTeX Error: Invalid UTF-8 byte "FF.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
...                                             
                                                 
l.1 �
     �\^^@U^^@s^^@e^^@R^^@a^^@w^^@I^^@n^^@p^^@u^^@t^^@E^^@n^^@c^^@o^^@d^^@i^...

The document does not appear to be in UTF-8 encoding.
Try adding \UseRawInputEncoding as the first line of the file
or specify an encoding such as \usepackage [latin1]{inputenc}
in the document preamble.
Alternatively, save the file in UTF-8 using your editor or another tool

Попытка, как советуют, указать в первой сроке \UseRawInputEncoding, а также указать \usepackage[utf16]{inputenc} (вместо \usepackage[utf8]{inputenc}) ничего не изменила.

Пока мне так и не удалось решить эту проблему. Почему-то не нахожу в документации на pdfTeX, где ему можно указать, что кодировка исходного файла - UTF-16LE.

-- Чт июн 23, 2022 17:40:13 --

Ха, ответ (на вопрос о возможности использования исходного файла в кодировке UTF-16) здесь. Как видите, он отрицательный. В мануал на пакет inputenc я тоже заглянул, там в списке допустимых кодировок никаких UTF-16 не значится. Стало быть, такой вариант отпадает.

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение24.06.2022, 15:43 
Заслуженный участник
Аватара пользователя


28/09/06
10847
Похоже, что такие макросы (я имею в виду перекодировщик) пишутся для использования исключительно гуру чистого TeX. Во всяком случае, до моего понимания столь тонкие нюансы работы TeX пока не доходят. Вот, простая задача: Хочу перекодировать в UTF-16 имя автора, которое находится в переменной \@author. И не могу! потому что такой вариант:
Используется синтаксис LaTeX
\StringEncodingConvert\recoded{\@author}{utf8}{utf16le}

раскрывает каждую русскую букву имени автора в T2A коды, а такой вариант:
Используется синтаксис LaTeX
\StringEncodingConvert\recoded{\detokenize{\@author}}{utf8}{utf16le}

вместо имени автора выводит имя команды \@author.

Получается, что переменную, содержащую имя автора, я использовать никак не могу и мне придётся здесь вбивать имя автора вручную. А это значит, что нужно помнить, где что вбито.

-- Пт июн 24, 2022 17:09:50 --

Очевидно, нужно обладать какими-то глубинными познаниями о том, когда и каким образом какие именно макросы раскрывает TeX. Например, можно непосредственно передать в сгенерированный объект PDF (в ту же аннотацию) код ссылки на библиографию - \cite{source_name}, но при этом этот код не будет раскрыт, т.е. в файле PDF окажется именно код, а не номер источника. При этом можно определить промежуточную переменную, содержащую эту ссылку:
Используется синтаксис LaTeX
\def\reference_on_this_source{\cite{source_name}}

но попытка её передачи в объект PDF тоже приведёт к передаче только кода, а не результата его выполнения. Вы скажете, что для этих случаев есть \edef, который должен раскрывать все макросы в момент его вызова. Но если определить переменную вот так:
Используется синтаксис LaTeX
\edef\reference_on_this_source{\cite{source_name}}

а потом попытаться её передать в объект PDF, то возникает ошибка. Оно и понятно, код ссылки на библиографию, очевидно, может быть раскрыт только после того, как TeX сформирует библиографию, т.е. ему станет известен номер этого источника. Но мне-то как определить этот момент? Я могу подать команду на формирование объекта PDF и позже. Вот только когда?

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение24.06.2022, 18:10 
Заслуженный участник


14/10/14
1220
Надо перед \detokenize делать \expandafter.

В общем, похоже, с этим Юникодом без извращений нельзя.
Я повозился, окончательно не сделал, но кое-что получилось, подробнее скорее всего напишу попозже, сейчас некогда просто.

Но вообще-то я совершенно не эксперт, может быть, имеет смысл спросить на tex.stackexchange.com или дождаться Red_Herring.

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение24.06.2022, 20:37 
Заслуженный участник
Аватара пользователя


28/09/06
10847
Slav-27 в сообщении #1558386 писал(а):
Надо перед \detokenize делать \expandafter.

\expandafter как-то не так срабатывает. В PDF попадает имя переменной \@author вместо её значения, причём в исходной кодировке UTF-8.

Вообще, эти теховские правила формирования и раскрытия токенов - это явно не для средних умов. Вот я решил попробовать ещё один вариант перекодировки - с помощью вызова системной утилиты iconv. Правда для этого мне пришлось искать, как в конфигах texlive включить shell_escape, который по умолчанию ограничен коротким списком команд. Но теперь вот такой код выводит у меня нормальную строку:
Используется синтаксис LaTeX
\def\recodedtext#1{%
 \input{|echo "#1"|iconv -f UTF8 -t UTF8 /dev/stdin}%
}
...
\recodedtext{См. переписку Лейбница с Кларком в [1].}

Здесь выходной кодировкой я поставил тоже UTF-8, иначе получившиеся кракозябры tex не воспринимает. Но сути это не меняет - перекодировщик работает, при необходимости можно добавить параметр -o outfile.txt, который вместо используемого по умолчанию стандартного выхода указывает файл, и убедиться в том, что файл создаётся и с правильным содержимым. Но если я укажу этот \recodedtext в качестве параметра команды \write16 или параметра своего макроса, генерирующего аннотацию, то возникают ошибки. Почему?

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение25.06.2022, 23:07 
Заслуженный участник


14/10/14
1220
Вот что получилось. Прямо с \cite не сработает, но дальше уже проще (?).
код: [ скачать ] [ спрятать ]
Используется синтаксис LaTeX
\documentclass[
    a4paper,% размеры листа
    11pt,% размеры нормального шрифта
    toc=bibliography% включить в оглавление литературу
    ]{book} % книга KOMAscript, см. scrguien.pdf (у меня нету такого документкласса)

\usepackage[T2A]{fontenc}% Выбор внутренней TeX−кодировки
\usepackage[utf8]{inputenc}% Выбор кодовой страницы документа.
\usepackage[english,russian]{babel}% Выбор языка документа.
\usepackage{indentfirst}% Начинать первый параграф с красной строки.
\usepackage{xcolor}% Для работы с цветами, включая гиперссылки
\usepackage{stringenc}% Конвертер кодировок, позволяет из utf8 делать utf16be
\usepackage[hyperfootnotes=false,pagebackref=true,pdftex]{hyperref}% Для гиперссылок, включая из оглавления, на библиографию и обратные
\hypersetup{
    colorlinks=true% ссылки будут выделяться цветом, а не рамкой
}


\def\makeother#1#2\relax{\ifx\relax#1\relax\else\catcode`#1=12\makeother#2\relax\relax\fi}% берёт последовательность токенов-символов, заканчивающуюся токеном "контрольная последовательность relax" (буква кириллицы -- 2 токена-символа), и меняет для таких символов правило назначения каткодов: им будет назначаться other
\def\azbuka{АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя}



\title{Пробный заголовок}
%\subtitle{пробной книги}
\author{epros}
\date{\today}


% Макрос создаёт сноску с всплывающей подсказкой (ВТОРОЙ аргумент); ПЕРВЫЙ агрумент - содержимое нормальной сноски внизу страницы
%
% Каждая буква кириллицы в UTF-8 кодируется 2 байтами. TeX читает каждый из этих 2 байтов как отдельный токен-символ с каткодом 13 (active). Я меняю каткоды символов, встречающихся в буквах кириллицы, на 12 (other), чтобы они не раскрывались (expand), когда мы будем раскрывать всё остальное.
% Каткоды замораживаются при передаче в качестве аргумента макроса (потому что каткод назначается при токенизации, которая происходит только один раз и до передачи аргумента макросу). Поэтому я вместо 1 макроса пишу 2, первый берёт в качестве аргумента текст нормальной сноски и далее меняет правила назначения каткодов, а второй потом забирает оставшийся аргумент -- текст всплывающей подсказки.
\def\myfootnote#1{%
\begingroup\expandafter\makeother\azbuka\relax\myfootnoteaux{#1}}
\def\myfootnoteaux#1#2{%
\edef\myfootipUTFeight{#2}% здесь происходит раскрытие контрольных последовательностей, содержащихся в тексте всплывающей подсказки
\StringEncodingConvert\myfootip{\myfootipUTFeight}{utf8}{utf16be}%
\pdfstartlink user{%
  /Subtype /Text% создаётся аннотация типа Text
  /T (epros)% автор аннотации
  /Contents (\string\376\string\377\myfootip)% текст аннотации; префикс перед \myfootip согласно формату pdf идентифициует строку unicode
  /AP <<
   /N \emptyicon\space 0 R% ссылка на создаваемый в начале документа объект pdf - основной вид значка аннотации
   /R \emptyicon\space 0 R% вид значка при наведённом указателе мыши
   /D \emptyicon\space 0 R% вид значка при нажатой кнопке мыши
  >>
}%
\footnote{#1}%
\pdfendlink%
\endgroup% изменённые правила назначения каткодов действуют досюда
}

\begin{document}
   
    \maketitle
   
   \tableofcontents
   
   % Этот код создаёт объект pdf - пустой бокс для использования вместо стандартного значка текстовой аннотации
   \newbox\tempboxa
   \setbox\tempboxa=\hbox{}
   \immediate\pdfxform\tempboxa
   \edef\emptyicon{\the\pdflastxform}% сохраняем номер объекта для использования при вызове макроса \myfootnote
 
   \part{Пробная часть}
   \chapter{Пробная глава}
   \section{Пробный раздел}

\def\mycs{kontrol'naya posledovatel'nost' raskrylas'}

    Что же позволяет говорить не просто об эквивалентности, а о равенстве объектов? Для этого они должны быть ещё и неразличимы. Принцип, отождествляющий неразличимость и равенство объектов, известен как <<закон Лейбница>>%
\myfootnote{См. переписку Лейбница с Кларком в \cite{leibniz_tom1}.}{См. переписку Лейбница с Кларком в \mycs.}.

{\raggedright
\begin{thebibliography}{99}
   
   \bibitem{leibniz_tom1}Г.\,В.~Лейбниц. Сочинения в четырёх томах (под редакцией В.\,В.~Соколова, 1982). Том 1. Упоминается на с.%\!\!

\end{thebibliography}
}
   
\end{document}


Наверно, можно менять каткоды непосредственно у токенов аргумента, но для этого придётся парсить аргумент.

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение26.06.2022, 11:48 
Заслуженный участник


14/10/14
1220
epros в сообщении #1558402 писал(а):
\expandafter как-то не так срабатывает. В PDF попадает имя переменной \@author вместо её значения, причём в исходной кодировке UTF-8.
Используется синтаксис LaTeX
\def\myauthorUTFeight{Пушкин}
\StringEncodingConvert\myauthor{\expandafter\detokenize\expandafter{\myauthorUTFeight}}{utf8}{utf16be}

В выражении \detokenize{\myauthorUTFeight} между \detokenize и \myauthorUTFeight стоит токен {, поэтому надо 2 \expandafter. Первый \expandafter раскрывает второй \expandafter (не трогая \detokenize), который раскрывает \myauthorUTFeight, не трогая {.

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение27.06.2022, 21:07 
Заслуженный участник
Аватара пользователя


28/09/06
10847
Slav-27 в сообщении #1558513 писал(а):
Используется синтаксис LaTeX
\def\myauthorUTFeight{Пушкин}
\StringEncodingConvert\myauthor{\expandafter\detokenize\expandafter{\myauthorUTFeight}}{utf8}{utf16be}

В выражении \detokenize{\myauthorUTFeight} между \detokenize и \myauthorUTFeight стоит токен {, поэтому надо 2 \expandafter. Первый \expandafter раскрывает второй \expandafter (не трогая \detokenize), который раскрывает \myauthorUTFeight, не трогая {.

Да, спасибо, это понятно.

-- Пн июн 27, 2022 22:47:31 --

Slav-27 в сообщении #1558491 писал(а):
код: [ скачать ] [ спрятать ]
Используется синтаксис LaTeX
\documentclass[
    a4paper,% размеры листа
    11pt,% размеры нормального шрифта
    toc=bibliography% включить в оглавление литературу
    ]{book} % книга KOMAscript, см. scrguien.pdf (у меня нету такого документкласса)

\usepackage[T2A]{fontenc}% Выбор внутренней TeX−кодировки
\usepackage[utf8]{inputenc}% Выбор кодовой страницы документа.
\usepackage[english,russian]{babel}% Выбор языка документа.
\usepackage{indentfirst}% Начинать первый параграф с красной строки.
\usepackage{xcolor}% Для работы с цветами, включая гиперссылки
\usepackage{stringenc}% Конвертер кодировок, позволяет из utf8 делать utf16be
\usepackage[hyperfootnotes=false,pagebackref=true,pdftex]{hyperref}% Для гиперссылок, включая из оглавления, на библиографию и обратные
\hypersetup{
    colorlinks=true% ссылки будут выделяться цветом, а не рамкой
}


\def\makeother#1#2\relax{\ifx\relax#1\relax\else\catcode`#1=12\makeother#2\relax\relax\fi}% берёт последовательность токенов-символов, заканчивающуюся токеном "контрольная последовательность relax" (буква кириллицы -- 2 токена-символа), и меняет для таких символов правило назначения каткодов: им будет назначаться other
\def\azbuka{АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя}



\title{Пробный заголовок}
%\subtitle{пробной книги}
\author{epros}
\date{\today}


% Макрос создаёт сноску с всплывающей подсказкой (ВТОРОЙ аргумент); ПЕРВЫЙ агрумент - содержимое нормальной сноски внизу страницы
%
% Каждая буква кириллицы в UTF-8 кодируется 2 байтами. TeX читает каждый из этих 2 байтов как отдельный токен-символ с каткодом 13 (active). Я меняю каткоды символов, встречающихся в буквах кириллицы, на 12 (other), чтобы они не раскрывались (expand), когда мы будем раскрывать всё остальное.
% Каткоды замораживаются при передаче в качестве аргумента макроса (потому что каткод назначается при токенизации, которая происходит только один раз и до передачи аргумента макросу). Поэтому я вместо 1 макроса пишу 2, первый берёт в качестве аргумента текст нормальной сноски и далее меняет правила назначения каткодов, а второй потом забирает оставшийся аргумент -- текст всплывающей подсказки.
\def\myfootnote#1{%
\begingroup\expandafter\makeother\azbuka\relax\myfootnoteaux{#1}}
\def\myfootnoteaux#1#2{%
\edef\myfootipUTFeight{#2}% здесь происходит раскрытие контрольных последовательностей, содержащихся в тексте всплывающей подсказки
\StringEncodingConvert\myfootip{\myfootipUTFeight}{utf8}{utf16be}%
\pdfstartlink user{%
  /Subtype /Text% создаётся аннотация типа Text
  /T (epros)% автор аннотации
  /Contents (\string\376\string\377\myfootip)% текст аннотации; префикс перед \myfootip согласно формату pdf идентифициует строку unicode
  /AP <<
   /N \emptyicon\space 0 R% ссылка на создаваемый в начале документа объект pdf - основной вид значка аннотации
   /R \emptyicon\space 0 R% вид значка при наведённом указателе мыши
   /D \emptyicon\space 0 R% вид значка при нажатой кнопке мыши
  >>
}%
\footnote{#1}%
\pdfendlink%
\endgroup% изменённые правила назначения каткодов действуют досюда
}

\begin{document}
   
    \maketitle
   
   \tableofcontents
   
   % Этот код создаёт объект pdf - пустой бокс для использования вместо стандартного значка текстовой аннотации
   \newbox\tempboxa
   \setbox\tempboxa=\hbox{}
   \immediate\pdfxform\tempboxa
   \edef\emptyicon{\the\pdflastxform}% сохраняем номер объекта для использования при вызове макроса \myfootnote
 
   \part{Пробная часть}
   \chapter{Пробная глава}
   \section{Пробный раздел}

\def\mycs{kontrol'naya posledovatel'nost' raskrylas'}

    Что же позволяет говорить не просто об эквивалентности, а о равенстве объектов? Для этого они должны быть ещё и неразличимы. Принцип, отождествляющий неразличимость и равенство объектов, известен как <<закон Лейбница>>%
\myfootnote{См. переписку Лейбница с Кларком в \cite{leibniz_tom1}.}{См. переписку Лейбница с Кларком в \mycs.}.

{\raggedright
\begin{thebibliography}{99}
   
   \bibitem{leibniz_tom1}Г.\,В.~Лейбниц. Сочинения в четырёх томах (под редакцией В.\,В.~Соколова, 1982). Том 1. Упоминается на с.%\!\!

\end{thebibliography}
}
   
\end{document}

Да, вижу, что \mycs в тексте аннотации раскрылся. Но в коде пока я многого не понял.
1) Я правильно понимаю, что использование \makeother внутри определения \makeother - это рекурсия? Так можно?
2) Я правильно понимаю, что \makeother#2\relax\relax раскрывается в то же самое, что стоит внутри фигурных скобок после \makeother#1#2\relax, только в аргументе на один байт в начале строки меньше? А зачем нужен последний \relax в обоих выражениях? Почитав описание синтаксиса \def, я так понял, что #1#2\relax в перечислении аргументов означает, что второй аргумент определяемого макроса должен заканчиваться на \relax. Вот только я не понял, как TeX, встретив этот макрос, определит, где первый аргумент, а где второй. У Вас там записано \makeother\azbuka\relax\myfootnoteaux{#1}. Это значит, что первый байт строки, в которую раскроется \azbuka, воспримется как первый аргумент, а остальная строка до \relax - как второй? Почему, например, не воспринять как второй аргумент второй байт строки и не выдать ошибку, потому что после него не обнаружен \relax?
3) И что такое \myfootnoteaux{#1}? Дальше, вроде, идёт нечто, похожее на его определение с двумя аргументами, но:
(а) разве может быть определение макроса позже первого вызова?
(б) в вызове этого макроса я не вижу двух аргументов; или второй аргумент - это закрывающая фигурная скобка? так можно?
(в) в определении этого макроса я вижу открывающую фигурную скобку, к которой не вижу парную закрывающую.
4) Я не понял использования \begingroup и \endgroup. Разве это не то же самое, что фигурные скобки? А если так, то разве следующая за \begingroup закрывающая фигурная скобка не должна закрыть группу?

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение28.06.2022, 01:41 
Заслуженный участник


14/10/14
1220
epros (UPD: я сделал существенные исправления)
Стандартное место, где искать ответы на такие вопросы, -- The TeXBook, by Don Knuth. Я, как уже говорил, не эксперт: отвечаю в меру своего понимания, относитесь ко мне с недоверием.

1) Да. Да.

2) Да (кроме последней итерации).
The TeXBook, chapter 20 писал(а):
How does TeX determine where an argument stops, you ask. Answer: There are two cases. A delimited parameter is followed in the parameter text by one or more non-parameter tokens, before reaching the end of the parameter text or the next parameter token; in this case the corresponding argument is the shortest (possibly empty) sequence of tokens with properly nested {...} groups that is followed in the input by this particular list of non-parameter tokens. (Category codes and character codes must both match, and control sequence names must be the same.) An undelimited parameter is followed immediately in the parameter text by a parameter token, or it occurs at the very end of the parameter text; in this case the corresponding argument is the next nonblank token, unless that token is ‘ { ’, when the argument will be the entire {...} group that follows. In both cases, if the argument found in this way has the form ‘ { <nested tokens> } ’, where <nested tokens> stands for any sequence of tokens that is properly nested with respect to braces, the outermost braces enclosing the argument are removed and the <nested tokens> will remain.
То есть если определить \def\mymacro#1#2\relax{...}
и вызвать \mymacro abcd\relax, то 1-м аргументом будет a, а 2-м bcd;
если вызвать \mymacro{ab}cd\relax или \mymacro{ab}{cd}\relax, то 1-м будет ab, а 2-м cd;
если вызвать \mymacro{ab}{c}d\relax, то 1-м будет ab, а 2-м {c}d;
если вызвать \mymacro{}abcd\relax, то 1-м будет пустое слово, а 2-м abcd,
если вызвать \mymacro{abcd}\relax, то 1-м будет abcd, а 2-м пустое слово,
если вызвать \mymacro\relax\relax, то 1-м будет \relax, а 2-м -- пустое слово.
(Проверьте!)
Можно написать, например, \def\mymacro[#1][#2]{...}, тогда вызывать надо будет в формате \mymacro[ab][cd].

\relax\relax нужно для корректного прохождения последней итерации. В начале предпоследней от "азбуки" останется 1 байт, который подставится в качестве #1, в качестве #2 будет пустое слово, а 1-й из этих 2 \relax'ов будет маркером конца аргументов. В результате предпоследней итерации будет вызвано \makeother\relax\relax, поэтому в качестве #1 подставится 1-й \relax, в качестве #2 пустое слово, а 2-й \relax будет маркером конца.

3) Да, \myfootnoteaux -- это макрос с 2 аргументами.

3а) Нет; но тут как раз определение РАНЬШЕ 1-го вызова, потому что 1-й вызов происходит в самом низу, уже после \begin{document}.

3б) Нет. Нет.
Предположим, написано \myfootnote{AAAA}{BBBB}. Сначала выполняется макрос \myfootnote с аргументом AAAA; после этого шага \myfootnote{AAAA}{BBBB} превращается в \begingroup\expandafter\makeother\azbuka\relax\myfootnoteaux{AAAA}{BBBB} (с BBBB пока вообще ничего не происходит). Далее будет раскрыта \azbuka, потом выполнится \makeother, а потом \myfootnoteaux, в который подставятся 2 аргумента: AAAA и BBBB.

3в) Закрывающая есть, она сразу перед \begin{document}.

4) Это не совсем одно и то же. \begingroup спаривается только с \endgroup, но не с }, а { только с }, но не с \endgroup. При определении макроса с помощью \def "замещающий текст" должен быть между {}, а \begingroup и \endgroup не учитываются как ограничители замещающего текста.

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение28.06.2022, 10:55 
Заслуженный участник


14/10/14
1220
Один \relax в определении \makeother у меня в принципе избыточен: 1-й аргумент не оказывается пустым, поэтому можно так:
Используется синтаксис LaTeX
\def\makeother#1#2\relax{\ifx\relax#1\else\catcode`#1=12\makeother#2\relax\relax\fi}

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение28.06.2022, 13:25 
Заслуженный участник
Аватара пользователя


28/09/06
10847
Slav-27 в сообщении #1558680 писал(а):
Стандартное место, где искать ответы на такие вопросы, -- The TeXBook, by Don Knuth. Я, как уже говорил, не эксперт: отвечаю в меру своего понимания, относитесь ко мне с недоверием.

1) Да. Да.

2) Да (кроме последней итерации).
The TeXBook, chapter 20 писал(а):
How does TeX determine where an argument stops, you ask. Answer: There are two cases. A delimited parameter is followed in the parameter text by one or more non-parameter tokens, before reaching the end of the parameter text or the next parameter token; in this case the corresponding argument is the shortest (possibly empty) sequence of tokens with properly nested {...} groups that is followed in the input by this particular list of non-parameter tokens. (Category codes and character codes must both match, and control sequence names must be the same.) An undelimited parameter is followed immediately in the parameter text by a parameter token, or it occurs at the very end of the parameter text; in this case the corresponding argument is the next nonblank token, unless that token is ‘ { ’, when the argument will be the entire {...} group that follows. In both cases, if the argument found in this way has the form ‘ { <nested tokens> } ’, where <nested tokens> stands for any sequence of tokens that is properly nested with respect to braces, the outermost braces enclosing the argument are removed and the <nested tokens> will remain.
То есть если определить \def\mymacro#1#2\relax{...}
и вызвать \mymacro abcd\relax, то 1-м аргументом будет a, а 2-м bcd;
если вызвать \mymacro{ab}cd\relax или \mymacro{ab}{cd}\relax, то 1-м будет ab, а 2-м cd;
если вызвать \mymacro{ab}{c}d\relax, то 1-м будет ab, а 2-м {c}d;
если вызвать \mymacro{}abcd\relax, то 1-м будет пустое слово, а 2-м abcd,
если вызвать \mymacro{abcd}\relax, то 1-м будет abcd, а 2-м пустое слово,
если вызвать \mymacro\relax\relax, то 1-м будет \relax, а 2-м -- пустое слово.
(Проверьте!)

Кто бы мог подумать, что для решения такой, казалось бы, простой задачи, как добавление текстовой аннотации к сноске, потребуется столь глубокое копание в столь объёмных и сложных источниках, как Кнутовское описание plain TeX. Процитированный Вами абзац, кстати, у Кнута помечен двойным знаком Z "опасная дорога", что согласно предисловию означает: "Не стоит читать этот абзац, если в этом нет необходимости. Храбрые и опытные исследователи системы TeX будут погружаться в эти области, но для большинства такие подробности излишни". А двойной знак обозначает: "Слишком экзотичные абзацы", для которых: "Всё, что говорилось об одном знаке опасного поворота, удваивается".

Slav-27 в сообщении #1558680 писал(а):
Предположим, написано \myfootnote{AAAA}{BBBB}. Сначала выполняется макрос \myfootnote с аргументом AAAA; после этого шага \myfootnote{AAAA}{BBBB} превращается в \begingroup\expandafter\makeother\azbuka\relax\myfootnoteaux{AAAA}{BBBB} (с BBBB пока вообще ничего не происходит).

Какой хитрый способ добавления второго аргумента! Надо сказать, что я видел нечто похожее вот здесь при обсуждении того, зачем в TeX нужна группировка. В пункте 3 ответа Yiannis Lazarides сказано, что группировка может применяться при необходимости разделения команды на две части, например:

Используется синтаксис LaTeX
 \def\index{\@bsphack\begingroup \@sanitize\@index}
 \def\@index#1{\endgroup\@esphack}

Но без Ваших пояснений я бы ещё нескоро осознал смысл этого кода.

-- Вт июн 28, 2022 14:29:42 --

Вообще, plain TeX на первый взгляд здорово непохож на нормальный язык программирования, поэтому сходу в нём мало что понятно. К сожалению, похоже, что без копания в нём эту задачу не решить.

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение28.06.2022, 13:51 
Заслуженный участник


14/10/14
1220
Я слышал, что в LuaTeX и XeTeX с Юникодом проще, чем в pdfTeX, но никогда не пользовался и ничего про них не знаю.

epros в сообщении #1558699 писал(а):
К сожалению, похоже, что без копания в нём эту задачу не решить.
Мне кажется, что дальше будет проще. \cite{...} не работает, потому что при его раскрытии получается не только текст [1], но и ещё много чего, в частности, гиперссылка. Осталось получить макрос, который будет после раскрытия выдавать текст и ничего более. Вероятно, такой макрос где-то там уже есть и \cite его вызывает.

-- 28.06.2022, 15:11 --

С \def\mycs{\arabic{section}} всё работает, например.

 Профиль  
                  
 
 Re: Как обойти \detokenize?
Сообщение28.06.2022, 15:11 
Заслуженный участник
Аватара пользователя


28/09/06
10847
Slav-27 в сообщении #1558702 писал(а):
Я слышал, что в LuaTeX и XeTeX с Юникодом проще, чем в pdfTeX, но никогда не пользовался и ничего про них не знаю.

Не знаю, может быть это позволит обходиться без \detokenize, зато заново нужно изучать, какими командами создавать аннотации в PDF. К тому же я слышал, что эти компиляторы ориентированы на использование общесистемных шрифтов, а мне это совсем не нужно, я хочу, чтобы мой PDF создавался строго с включёнными в него шрифтами, т.е. чтобы от системы читателя никак не зависеть.

Slav-27 в сообщении #1558702 писал(а):
Мне кажется, что дальше будет проще. \cite{...} не работает, потому что при его раскрытии получается не только текст [1], но и ещё много чего, в частности, гиперссылка.

Думаю, если бы дело было только в гиперрсылке, то можно было бы обойтись таким кодом:

Используется синтаксис LaTeX
\edef\tmpcite{\begin{NoHyper}\cite{leibniz_tom1}\end{NoHyper}}

Окружение NoHyper выключает гиперссылки, которые создаёт пакет hyperref. Но этот \edef почему-то выдаёт ошибки, так что сразу раскрыть макрос, который создаёт ссылку на литературу с указанием номера источника, не удаётся.

-- Вт июн 28, 2022 16:32:37 --

Похоже, что \cite раскрывается за два прохода. При первом проходе у меня печатается вопросительный знак в квадратных скобках и в сообщениях пишет:
[BibTeX] finished with exit code 2

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 37 ]  На страницу 1, 2, 3  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group