我正在记录我维护的系统。本文档包含我在 TeX/TikZ 中创建的图表,该图表被渲染为 PDF 文件。然后,我将 PDF 文件转换为图像文件(通过 imagemagick 的 PNG),并将其包含在我的 HTML 文档中。效果很好。
现在我想为图像创建一个图像映射,以便我可以添加超链接/鼠标悬停/等。我希望根据系统中的更改定期更新此图像,因此如果可能的话,我希望自动化此过程。
当 PDF 文件渲染为 PNG 时,有没有办法使用软件库或工具自动创建 PDF 文件中各种文本内容的图像映射?
这是我创建的要点的一个示例:
在这种情况下,我想通过在 PDF 中定位它们的边界框,将一些不同的文本字符串转换为超链接:
controlleractuatorsensorABCDuyF(s)G(s)H(s)(它们都是PDF文件中的文本内容;我可以在Acrobat Reader中选择其中任何文本,然后复制+粘贴到我的文本编辑器中。)
有没有办法做到这一点?
我能够整理出以下可以作为起点的 Python 解决方案。它将 pdf 转换为 png 并输出相应的图像映射标记。
\n\n它将输出 dpi 作为可选参数(默认 200),以便将边界框从默认 pdf dpi 72 正确缩放到 png 上:
\n\nfrom pdf2image import convert_from_path\nfrom pdfminer.converter import PDFPageAggregator\nfrom pdfminer.layout import LAParams, LTTextBox\nfrom pdfminer.pdfinterp import PDFPageInterpreter\nfrom pdfminer.pdfinterp import PDFResourceManager\nfrom pdfminer.pdfpage import PDFPage\n\nfrom yattag import Doc, indent\n\nimport argparse\nimport os\n\n\ndef transform_coords(lobj, mb):\n\n # Transform LTTextBox bounding box to image map area bounding box.\n #\n # The bounding box of each LTTextBox is specified as:\n #\n # x0: the distance from the left of the page to the left edge of the box\n # y0: the distance from the bottom of the page to the lower edge of the box\n # x1: the distance from the left of the page to the right edge of the box\n # y1: the distance from the bottom of the page to the upper edge of the box\n #\n # So the y coordinates start from the bottom of the image. But with image map\n # areas, y coordinates start from the top of the image, so here we subtract\n # the bounding box\'s y-axis values from the total height.\n\n return [lobj.x0, mb[3] - lobj.y1, lobj.x1, mb[3] - lobj.y0]\n\n\ndef get_imagemap(d):\n doc, tag, text = Doc().tagtext()\n with tag("map", name="map"):\n for k, v in d.items():\n doc.stag("area", shape="rect", coords=",".join(v), href="", alt=k)\n return indent(doc.getvalue())\n\n\ndef get_bboxes(pdf, dpi):\n fp = open(pdf, "rb")\n rsrcmgr = PDFResourceManager()\n device = PDFPageAggregator(rsrcmgr, laparams=LAParams())\n interpreter = PDFPageInterpreter(rsrcmgr, device)\n page = list(PDFPage.get_pages(fp))[0]\n\n interpreter.process_page(page)\n layout = device.get_result()\n\n # PDFminer reports bounding boxes based on a dpi of 72. I could not find a way\n # to change this, so instead I scale each coordinate by multiplying by dpi/72\n scale = dpi / 72.0\n\n return {\n lobj.get_text().strip(): [\n str(int(x * scale)) for x in transform_coords(lobj, page.mediabox)\n ]\n for lobj in layout\n if isinstance(lobj, LTTextBox)\n }\n\n\ndef main():\n parser = argparse.ArgumentParser()\n parser.add_argument("pdf")\n parser.add_argument("--dpi", type=int, default=200)\n\n args = parser.parse_args()\n\n page = list(convert_from_path(args.pdf, args.dpi))[0]\n page.save(f"{os.path.splitext(args.pdf)[0]}.png", "PNG")\n\n print(get_imagemap(get_bboxes(args.pdf, args.dpi)))\n\n\nif __name__ == "__main__":\n main()\nRun Code Online (Sandbox Code Playgroud)\n\n结果示例:
\n\n<img src="https://i.stack.imgur.com/aXWMc.png" usemap="#map">\r\n<map name="map">\r\n <area shape="rect" coords="361,8,380,43" href="#" alt="B" />\r\n <area shape="rect" coords="434,31,500,64" href="#" alt="G(s)" />\r\n <area shape="rect" coords="432,93,502,117" href="#" alt="actuator" />\r\n <area shape="rect" coords="552,8,572,42" href="#" alt="C" />\r\n <area shape="rect" coords="596,58,609,86" href="#" alt="y" />\r\n <area shape="rect" coords="105,26,119,40" href="#" alt="+" />\r\n <area shape="rect" coords="107,54,122,78" href="#" alt="\xe2\x88\x92" />\r\n <area shape="rect" coords="35,58,51,86" href="#" alt="u" />\r\n <area shape="rect" coords="164,8,182,43" href="#" alt="A" />\r\n <area shape="rect" coords="163,152,183,187" href="#" alt="D" />\r\n <area shape="rect" coords="241,31,311,64" href="#" alt="H(s)" />\r\n <area shape="rect" coords="236,94,316,118" href="#" alt="controller" />\r\n <area shape="rect" coords="243,175,309,208" href="#" alt="F (s)" />\r\n <area shape="rect" coords="247,234,305,258" href="#" alt="sensor" />\r\n</map>Run Code Online (Sandbox Code Playgroud)\r\n