如何像Illustrator中那样在python中格式化文本?

如何像Illustrator中那样在python中格式化文本?,python,latex,typesetting,Python,Latex,Typesetting,我希望实现几个功能,以提高我的程序的输出。 我想将文本列的背景设置为某种颜色,以便 更清楚的是,列属于一个整体 一幅画胜过千言万语: 我想将其转换为: Pos. :123456789012345 Name. :ABCDEFGHIJKLMNO Str. :SOMESTRINGSOMET <---- indented by half a row, Column number superscripted Str. :SOM SOMET Str. :SOMESTRIN

我希望实现几个功能,以提高我的程序的输出。 我想将文本列的背景设置为某种颜色,以便 更清楚的是,列属于一个整体

一幅画胜过千言万语: 我想将其转换为:

Pos.  :123456789012345
Name. :ABCDEFGHIJKLMNO  
Str.  :SOMESTRINGSOMET  <---- indented by half a row, Column number superscripted
Str.  :SOM       SOMET
Str.  :SOMESTRIN    ET
位置:123456789012345
姓名:ABCDEFGHIJKLMNO

Str.:SOMESTRINGSOMET这实际上更像是一个LaTeX问题,而不是Python问题——也就是说,如果您确定希望Python脚本生成的标记,那么实际生成它是相当容易的

在这种情况下,我认为您可以使用一个相当复杂的表(
\begin{table}…\end{table}
)获得所需的效果,其中每个单元格跨越两列,一列用作偏移量以获得所需的“半字符”。如果有一种更简单的方法可以在LaTeX中实现这一点,我也不会感到惊讶(LaTeX中的库比我能记住的要多得多),但这就是我想到的方法。所以你的桌子看起来像这样:

+------+--+--+--+--+--+--+--+
| pos. |  |  1  |  2  |  3  |      ... 
+------+--+--+--+--+--+--+--+
| name.|  |  A  |  B  |  C  |      ... 
+------+--+--+--+--+--+--+--+--+
|      | 170 |     |     |     |   ... 
+------+--+--+--+--+--+--+--+--+
| Str. |  S  |  O  |  M  |  E  |   ... 
+------+--+--+--+--+--+--+--+--+
(其中,
+
s表示实际列的位置,大多数单元格跨越两列。带有“170”的行将使用
\tiny
或类似命令。)


我的建议是先手动生成一个LaTeX文件,它可以满足您的需要(或者您选择的其他格式化语言);然后再担心编写一些python代码来生成它。

考虑到单间距字体,可能会想出一些棘手的解决方案来解决您的问题。但他们只是在等待失败。您的帖子中的灰色列可以添加到与您的图中完全相同的位置,但我选择不让它更改字母中的颜色(只是因为我觉得这样更可读)

以下是一些可能产生PIL的方法:

稍微更改字体:

下一步是更“严重”的字体更改,所有更改都是按原样代码生成的

接下来是生成这些数字的代码。我并没有把它做得很好,它可以在很多方面得到改进。把这当作你需要做的一个例子,也许,为你的问题找到一个解决方案。要使用任何字体,您都需要实际的排版系统,如LaTeX

import sys
import Image, ImageDraw, ImageFont


# Assumption: some other piece of code separates the data in the following
# format.
line1 = [("Pos.  :", 0), ("123456789012345", 0)]
line2 = [("Name. :", 0), ("ABCDEFGHIJKLMNO", 0)]
line3 = [("Str.  ", 0), (":", -0.5), ("SOMESTRINGSOMEST", -0.5)]
line4 = [("Wave 1:", 0), ("_XXXX_X____X_X_", 0)]
line5 = [("Wave 2:", 0), ("__XX_XXX_X__X_X", 0)]
line_data = [line1, line2, line3, line4, line5]
# Texts to draw over the last element, in specific positions,
# of lines present in line_data.
subscript = {
        2: { # Meaning: third item in line_data
            0: "170",  # Meaning: draw "170" over the first char
            len(line3[-1][0]) - 1: "185", # Draw "185" over the last char
            7: "180",   # Meaning: draw "180" over the eight char
           },
        4: {5: "hi"},
        3: {6: "ops"}
        }

# If the following fonts are not mono spaced, you are going to suffer.
#
# Normal font.
font = ImageFont.truetype('FreeMono.ttf', 40)
# Font for subscript.
font_tiny = ImageFont.truetype('FreeMono.ttf', 20)


im = Image.new("RGBA", (1000, 1000), 'white')
draw = ImageDraw.Draw(im)
line_offset = 4
start_y = 6

width_A, height_A = font.getsize('A')
_, height_tiny_A = font_tiny.getsize('A')

# Collect even columns from the last item of list line1.
even_columns = []
x = 0
for i, (text, _) in enumerate(line1):
    for j, letter in enumerate(text):
        if i == len(line1) - 1 and not j % 2:
            even_columns.append(x)
        x += width_A

# Write all lines.
width = 0
l_start_y = start_y
for di, data in enumerate(line_data):
    x = 0
    for i, (text, xoff) in enumerate(data):
        for j, letter in enumerate(text):
            # Apply x offset.
            extra = width_A * xoff
            draw.text((x + extra, l_start_y), letter, font=font, fill='black')
            x += width_A
    width = max(x, width)
    l_start_y += height_A + line_offset

# Collect letter positions from the lines that will have subscripts.
letter_pos = {}
for k in subscript:
    letter_pos[k] = {}
    x = sum(len(text) for text, _ in line_data[k][:-1]) * width_A
    text, xoff = line_data[k][-1]
    for i in range(len(text)):
        extra = width_A * xoff
        letter_pos[k][i] = x + extra
        x += width_A
# Write all subscripts.
for k, v in subscript.items():
    line = line_data[k]
    for pos, text in v.items():
        x = letter_pos[k][pos]
        y = start_y + (line_offset + height_A) * k
        y -= height_tiny_A * 0.4 # XXX A poor heuristic that worked here.
        draw.text((x, y), text, font=font_tiny, fill='black')
        width = max(width, int(x + font_tiny.getsize(text)[0]))

# Draw grey columns.
columns = Image.new(im.mode, im.size, 'white')
mask = Image.new("L", im.size, 'white')
for x in even_columns:
    columns.paste((128, 128, 128), (x, line_offset, x + width_A, l_start_y))
    mask.paste(164, (x, line_offset, x + width_A, l_start_y), )
im = Image.composite(im, columns, mask)

# Crop and save the resulting image.
im.crop((0, 0, width, l_start_y + 2)).save(sys.argv[1])

虽然PIL可以用于简单的东西,但如果使用SVG,您有更多的选项。您可以使用(handy)或svgwrite以编程方式创建SVG,也可以将其打印为文本。然后使用ImageMagick(在命令行上作为
convert
或从python使用)将SVG渲染为所需的任何光栅类型。微调SVG时,您可以使用文本编辑器对其进行编辑,并在web浏览器中查看:)


输出到LaTeX或其他排版/格式化工具似乎是一条可行之路。Python没有附带排版库。谢谢您的快速回答。我用illustrator画了“右”字。然而,即使使用latex,我现在也无法让这些东西发挥作用:(一旦我进一步了解,就会更新。这个问题是否仅限于单间距字体?令人惊叹。这正是我所寻找的。如果我想在下面添加更多类似Str的行,我会在enumerate([line1,line2])中更改“di,数据”吗:'对于di,枚举中的数据([line1,line2,linex]):?是的,可以。但是您还需要调整第3行中k,v的
处的图形。iteritems():…
。我会先添加一个
num_lines=x
,其中
x
是你写的行的总数,比如在你的例子中是4行,在我的例子中是3行。然后更改用于绘制下标的代码以处理该问题。实际上,我不需要重复该操作。我只想以与str相同的方式在下面添加更多行。你的代码比我的技能高一级:-)。一旦我得到了赏金,我会奖励的。我还想每10个字符左右重复一行,比如列号,如果你想在首字母3下面再加上几行,那么你必须在下一行重复这段代码。这可以转化为一个函数。
dict
line3_1_top表示字符位置作为键,下标作为值,因此您只需向其添加11:“180”,在这种情况下它就可以工作。如果我们对您希望执行的操作达成一致,我可以尝试更新代码,以便在彼此下方绘制其他字符串集时更加灵活。请参阅更新的答案,代码更易于管理,并且应该易于更改以匹配您的输入。
# make some text in any font, any size, any effects
import pysvg
s = svg()
myStyle = StyleBuilder()
myStyle.setFontFamily(fontfamily="Verdana")
myStyle.setFontSize("5em")
myStyle.setFilling("blue")
t1 = text("Hello World", 0, 100)
t1.set_style(myStyle.getStyle())
s.addElement(t1)
s.save('test.svg')

from pythonmagickwand.image import Image
img = Image('test.svg')
img.format = 'PNG'
img.save('test.png')