【问题标题】:How can i format Text in python like i would in Illustrator?如何像在 Illustrator 中一样在 python 中格式化文本?
【发布时间】:2012-12-04 14:03:49
【问题描述】:

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

一张图胜过千言万语: 我想转换这个:

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

到这里:

如何使用 python 做到这一点?有没有允许我这样做的框架? 我将其转换为乳胶,但是我无法使上标数字起作用。它们需要在文本之上,但不能充当角色,因为图片的列之间存在关系。

示例:A 位于位置 1。它具有属性 S 和 O,这就是为什么它位于这两个之上的原因,这就是为什么有半个字符的缩进。 S的上标是170,T的上标是185。

Str 部分移动半个字符的部分也特别困难。我想有一个示例将其集成到我的程序中。

【问题讨论】:

  • 输出到 LaTeX 或其他一些排版/格式化工具似乎是要走的路。 Python 不附带排版库。
  • 感谢您的快速回答。我用插画家做了“正确的” grafic。但是我现在即使使用乳胶也无法让这些东西工作:(一旦我更进一步,就会更新。
  • 这个问题是否仅限于单行距字体?

标签: python latex typesetting


【解决方案1】:

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

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])

【讨论】:

  • 太棒了。正是我想要的。如果我想在下面添加更多像 Str 这样的行怎么办?我可以将'for di, data in enumerate([line1, line2]):' 更改为 for di, data in enumerate([line1, line2, linex]): 吗?
  • 是的,可以。但是您还需要在for k, v in line3_1_top.iteritems(): ... 处调整绘图。我将首先添加一个num_lines = x,其中x 是您编写的总行数,在您的情况下为4,在我的示例中为3。并更改用于绘制下标的代码以进行处理。
  • 其实我不需要重复。我只想以与 str 相同的方式在下面添加更多行。你的代码比我的技能高一个级别:-)。一旦我得到它的工作,我将奖励赏金。我还想每隔 10 个字符左右重复 line_3_1 位,例如列号
  • 如果您只想在最初的 3 行下方添加更多行,那么您将不得不为下一行重复此代码。这可以变成一个函数。 dict line3_1_top 表示字符位置为键,下标为值,因此您只需在其上添加 11:"180" 即可。如果我们对您要执行的操作达成一致,我可以尝试更新代码,以便在绘制其他字符串集时更加灵活。
  • 查看更新后的答案,代码更易于管理,应该很容易更改以匹配您的输入。
【解决方案2】:

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

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

+------+--+--+--+--+--+--+--+
| pos. |  |  1  |  2  |  3  |      ... 
+------+--+--+--+--+--+--+--+
| name.|  |  A  |  B  |  C  |      ... 
+------+--+--+--+--+--+--+--+--+
|      | 170 |     |     |     |   ... 
+------+--+--+--+--+--+--+--+--+
| Str. |  S  |  O  |  M  |  E  |   ... 
+------+--+--+--+--+--+--+--+--+

(其中+s 表示实际列的位置,大多数单元格跨越两列。带有“170”的行将使用\tiny 或类似的命令。)

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

【讨论】:

    【解决方案3】:

    虽然 PIL 适用于简单的东西,但如果你使用 SVG,你有更多的选择。您可以使用pySVG(方便的pySVG tutorial)或svgwrite 以编程方式创建您的SVG,或者仅将print 用作文本。然后使用 ImageMagick(在命令行上使用 convert 或从 python 使用 pythonmagicwand)将 SVG 渲染为您想要的任何光栅类型。在微调 SVG 的同时,您可以使用文本编辑器对其进行编辑并在 Web 浏览器中查看它:)

    # 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')
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-05-31
      • 2014-07-21
      • 2012-03-08
      • 1970-01-01
      • 2020-06-22
      • 2015-12-05
      • 2013-04-13
      相关资源
      最近更新 更多