使用直接 lua 将二进制字节读取为纯文本?

使用直接 lua 将二进制字节读取为纯文本?

此主题,@David Carlisle 告诉我们如何使用 luatex 读取 png 图像的一些属性。唉,图像库似乎无法获取该colorspace属性。总是报告nil

如果我可以从文件中读取两个字节(在已知位置),并将这些字节作为纯文本 00 到 FF 分配给 LaTeX 变量,那么我知道从那里该做什么。

我看过此主题,这似乎相关,但它并没有提供我需要的东西。

因此,这是我的一般问题:Lua 代码如何做到这一点:

1) 打开任意二进制文件(部分)。虽然我考虑的是 PNG,但我假设可以使用任何文件类型。该方法必须是跨平台的。

2) 仅选择从偏移量 P 到偏移量 Q 的字节,其中 P 和 Q 通过 LaTeX 宏作为变量提供。可能多达几百个字节,但可能要少得多。

3) 使用代码 0 到 F 将字节表示为纯文本字符串。因此,如果要求十个字节,则字符串可能看起来A9003405BC没有空格或返回。

4)将字符串分配给 LaTeX 宏,以便通过普通 LaTeX 进行后续处理。

我为什么想知道:在我的 LuaLaTeX 应用程序中,图像的使用受到限制。我了解图像规范,但(不是程序员)对诸如此类的事情感到困惑LuaTeX 手册

为什么其他人应该关心:在我看来,Lua 的功能在 LuaLaTeX 中没有得到充分利用,可能是因为大多数用户都在做一次性项目,不想学习另一种编程语言。我的要求是模块化的,具有普遍适用性。

请注意,当所需信息不在特定字节偏移处时,也可以使用 1-4 的解决方案,只要它位于文件开头附近的某个位置即可。然后,可以使用普通的 LaTeX 方法解析纯文本字符串以获取值。

编辑:看了下面大卫的回答,我有一些问题:

所需代码如下(伪代码,这不起作用):

\newcommand\getsomebytes[3]{ % filename, start, end
  \directlua{
    local inp = assert(io.open("#1", "rb"))
    local data = inp:read("*all")
    \gdef\heretheyare{string.byte(data,#2,#3)}
  }
}

然后,在文档主体中,我将使用:

\getsomebytes{filename.png}{4}{56} % bytes from 4 to 56 here

这不会打印任何内容,但会将结果以\heretheyare纯文本形式存储在宏中(数字而不是字母也可以,只要字符串可以通过 LaTeX 字符串方法(例如来自包)进行解析即可xstring)。处理后,我会对另一个文件重新使用这些命令。

我想知道是否有必要打开“全部”文件,因为在某些情况下文件可能非常大(20Mb 或更大)并且我只需要检查其中的一部分。

答案1

下面显示前 5 个字节为 137 80 78 71 13

这与我的测试 png 的十六进制转储相匹配

$ hexdump.exe man2.png | head
0000000 5089 474e 0a0d 0a1a 0000 0d00 4849 5244
0000010 0000 0001 0000 5801 0208 0000 3300 b5cd
0000020 00ab 0000 7301 4752 0042 ceae e91c 0000


\documentclass{article}

\usepackage{graphicx}
\begin{document}

\includegraphics{man2.png}

\directlua{
local inp = assert(io.open("man2.png", "rb"))
local data = inp:read("*all")
local b1,b2,b3,b4,b5 = string.byte(data,1,5)
print(b1 .. ' ' .. b2 .. ' ' ..b3 .. ' ' .. b4 .. ' ' .. b5)
}
\end{document}

只需将 1,5 更改为所需字节序列的开始和长度


使用 seek 的版本(因此不会读取整个文件)并将结果留在 tex 宏中

\documentclass{article}

\usepackage{graphicx}
\begin{document}

\includegraphics{man2.png}

\directlua{
inp = assert(io.open("man2.png", "rb"))
}

\def\zz#1#2{%
\edef\zzhere{\directlua{
local p=inp:seek("cur",#1)
local r=inp:read(#2)
sep=""
for i,_ in string.bytes(r)
do
tex.sprint(sep)
sep=","
tex.sprint(i)
end
}}}

\zz{1}{5}

bytes 1 to 5 are :[\zzhere]
\end{document}

答案2

在考虑了 David 发布的答案(上文)后,我将其扩展为以下 TeX 代码。放在这里以防其他人使用它。

输入文件可以是任何 TeX 可读的内容。输出字符串是纯文本(逗号分隔的数字,范围从 0 到 255),因此可以轻松解析。

在此示例中,输入文件是 PNG 图像。我想知道它的位深度和颜色类型。虽然现有的 Lua 库提供了位深度,但它似乎无法提供颜色类型。因此,我直接获取信息。只需知道在文件中查找的位置以及预期的值。如果所需信息不在特定的字节偏移量处,那么我可以抓取足够大的字符串,解析某种标头,然后从那里偏移。

这也可以查看文件的末尾,某些文件类型在此处存储添加的信息(例如注释)。

% compile only with lualatex
\documentclass{article}
\usepackage{mwe}
\usepackage{xifthen}
\usepackage{xparse}
% Thanks to David Carlisle, via tex.stackexchange.com:
% Output is comma-separated list of byte codes, decimal 0-255.
% Returns -1 if requested start is more than file size.
% Returns all bytes if requested number exceeds file size.
% Does not test if file exists; error if not found.
\DeclareDocumentCommand\getsomebytes { m m m } {%
  % filename, start byte (0=beginning, e=end), number of bytes
  \ifthenelse{\equal{#2}{e}}{% package xifthen
    \long\edef\herearebytes{%
      \directlua{
        inp = assert(io.open("#1", "rb"))
        local e=inp:seek("end")
        if #3>e+1 then
          inp:seek("set")
          local r=inp:read(e)
          sep=""
          for i,_ in string.bytes(r)
          do
          tex.sprint(sep)
          sep=","
          tex.sprint(i)
          end
        else
          local b=e-2-math.min(e,#3)
          local w=1+math.min(e,#3)
          inp:seek("set",b)
          local r=inp:read(w)
          sep=""
          for i,_ in string.bytes(r)
          do
          tex.sprint(sep)
          sep=","
          tex.sprint(i)
          end
        end
      }%
    }%
  }{%
    \long\edef\herearebytes{%
      \directlua{
        inp = assert(io.open("#1", "rb"))
        local e=inp:seek("end")
        if #2>e then tex.sprint(-1) else
          local w=math.min(#3,e-#2)
          inp:seek("set",#2)
          local r=inp:read(w)
           sep=""
          for i,_ in string.bytes(r)
          do
          tex.sprint(sep)
          sep=","
          tex.sprint(i)
          end
        end
      }%
    }%
  }%
} % end getsomebytes
\def\pngbitdepth#1{\getsomebytes{#1}{24}{1}\herearebytes}
\def\pngcolortype#1{\getsomebytes{#1}{25}{1}\herearebytes}
\begin{document}
This `example-image.png' is part of the `mwe' package:\par
PNG bit depth: \pngbitdepth{example-image.png} (expected 8)\par
PNG color type: \pngcolortype{example-image.png} (expected 0)\par
\getsomebytes{example-image.png}{0}{8}
First 8 bytes are: \herearebytes ~(expected 137,80,78,71,13,10,26,10)\par
\getsomebytes{example-image.png}{e}{6}
Last 7 bytes are: \herearebytes ~(expected 73,69,78,68,174,42,60,82)\par
\getsomebytes{example-image.png}{45}{3}
Bytes 45, 46, 47 are: \herearebytes ~(expected 11,252,97)\par
\getsomebytes{example-image.png}{12345}{2}
Bytes 12345 and 12346 are: \herearebytes ~(expected -1 because file is not big enough)\par
\end{document}

编辑:添加案例,如果请求的起始字节大于 EOF,则返回字符串 -1。

编辑 2:我刚刚把 David 的回复用到了极致,现在已包含在“小说”包的 1.52 版中。背景:

这是 LuaLaTeX 特有的,仅指 png 和 jpg 灰度图像。其他一些情况(例如彩色封面图)也可以处理,但代码有所不同。

问题:某些 PDF 文件(例如 PDF/X-1a)限制了可以包含的图像类型。但 TeX 不会检测图像是否符合要求的规格。可以使用 shell-escape 调用外部图形程序,但这会产生两个问题:首先,这种安排不可移植,因为网络或在线用户无法依赖 shell-escape 和外部程序。其次,在 TeX 中处理图像会占用大量时间,每次编译时都会重复此过程。

解决方案:使用 shell/批处理脚本对图像进行预处理。这可以方便地完成,并且不使用 TeX。处理后的图像将包含注释,声明它们是好的。然后,使用 Lua 代码的强大功能,TeX 文档将检查每个图像的注释。这可以快速完成。如果检测到任何未处理的图像,将发出警告。这不是错误,因为未处理的图像可能仍然是好的。由于这部分不需要支持图形或 shell 转义,因此它也是可移植的,可以通过网络或在线完成。此外,TeX 会跟踪图像,因此无需重新检查重复的图像。

有效的 Lua 代码基于 David 的回答。由于所有支持 LuaLaTeX 的代码都太长(而且太具体),因此无法在此处发布。但可以在“novel”软件包版本 1.52 中看到它,位于“novel-Images.sty”文件底部附近。预处理脚本位于文档“extras”文件夹中。如果您愿意,可以查看一下。

为什么这很重要:LuaLaTeX 可以创建 PDF/X-1a 文件,这是一些主要的美国按需印刷服务所要求的。Adobe Acrobat Pro 等程序具有内置图形支持,可以检测和纠正不合规的图像。TeX 没有。也就是说,TeX 直到现在才拥有它!

相关内容