将文字包装成一定比例的矩形(不是大小)。

问题描述 投票:3回答:3

有谁知道有一种算法可以将文字在字的边界处打碎,使之适合某个近似的矩形。比率 - 如:60:40(width:height)?

请注意,这不仅仅是宽度(如80个字符或600px等)和一个任意的高度,这排除了我能找到的所有文字包装算法。

奖励javascript,但这更多的是关于算法而不是实现。

c# java php javascript algorithm
3个回答
3
投票

这个可以做到。

int lineHeight := getHeightOfTextLine()
int lines := 0
do {
  lines += 1
  int width = lines * lineHeight * ratio
  String wrappedText := break(input, width)
} while(getNumberOfLines(wrappedText) != lines)

从一行开始,我简单地测试每个高度(lineHeight的倍数)是否有一个给定比例的矩形可以容纳文本。如果在计算出的宽度上打破文本导致一个字符串的行数超过了允许的行数(对于运行),继续,否则我有一个解决方案。


1
投票

好吧,如果你从每个字的高度&宽度的数组开始,那么你需要运行几种可能性,直到你找到给定的宽度:高度的最小浪费(字与字之间的空间和)。

通常你会从

ratio := 6 / 4
noOfLines := totalWidth / ( ratio * lineHeight )
targetLineWidth := totalWidth / noOfLines

然后尝试确定在哪些单词后面放上换行符以减少单词之间的空间。

如果你试图将每一行的空间最小化,你可能最终会在最后一行留下额外的空间。如果你首先确保连最后一行都是均匀分布的,那么你只需要研究几个变化就应该没问题了。

编辑 如果你想搞精确的字体指标。这个 q&a看起来很有用。


1
投票

这里有一个Python的实现,使用 textwrapPillow,也就是保留现有的换行符。

from PIL import Image, ImageDraw
import textwrap

def get_text_with_linebreaks_to_fit_ratio(input_text, target_ratio):
    width_in_nchar = 1 
    placeholder_img = Image.new('RGB', (1, 1), (255, 255, 255))
    placeholder_img_D = ImageDraw.Draw(placeholder_img)
    intermediary_text = input_text.split("\n")   # splits on newlines

    while True:
        intermediary_text2 = [textwrap.wrap(element, width_in_nchar) for element in intermediary_text] # for each paragraph, cut it with a width of width_in_nchar
        wrapped_text = [item for sublist in intermediary_text2 for item in sublist]   # flattening the output list
        wrapped_text_as_string = "".join([el+"\n" for el in wrapped_text])

        a, b = placeholder_img.multiline_textsize(wrapped_text_as_string)
        if a/b > target_ratio:
            newest_ratio = a/b
            break
        old_ratio = a/b
        width_in_nchar +=1

    if newest_ratio - target_ratio> old_ratio - target_ratio: # if the last ratio we got is farther from target ratio than the previous ratio
        width_in_nchar -=1 # then we go one step back
        intermediary_text2 = [textwrap.wrap(element, width_in_nchar) for element in intermediary_text] 
        wrapped_text = [item for sublist in intermediary_text2 for item in sublist]  
        wrapped_text_as_string = "".join([el+"\n" for el in wrapped_text])

    return {"as_string" : wrapped_text_as_string, "as_list" : wrapped_text}

然后看输出。

input_text = """Lorem ipsum dolor sit amet, consectetur adipiscing elit. In pellentesque pharetra ex, at varius sem suscipit ac. Suspendisse luctus condimentum velit a laoreet. Donec dolor urna, tempus sed nulla vitae, dignissim varius neque. Etiam non vulputate diam. Nullam luctus nisi mauris, sit amet feugiat nisi dapibus in. Fusce in interdum nisi. Nullam mattis a odio non interdum.

Sed accumsan laoreet pretium. Nulla facilisi. Morbi in eros suscipit, commodo turpis id, dignissim lorem. Maecenas quis urna auctor, rutrum velit vel, efficitur sem. Donec vulputate viverra justo a accumsan. Phasellus posuere est consectetur, tincidunt lorem volutpat, porttitor erat. Sed at ipsum euismod eros blandit vestibulum.

Integer a auctor quam. Mauris scelerisque sapien quis elementum euismod. Curabitur sed est tortor. Nullam eget tristique purus, eget venenatis enim. Etiam sem quam, lacinia at quam sed, laoreet ultrices mauris. Nunc aliquam dui iaculis pretium fringilla. Maecenas in ante vel libero eleifend condimentum. Vivamus at venenatis libero. Pellentesque sagittis tristique risus a molestie. Fusce vitae leo sed mauris ultricies tincidunt venenatis in lacus. Integer finibus arcu porttitor, viverra massa in, bibendum lacus.

Donec gravida nisi in facilisis sollicitudin. In aliquam vulputate velit. Pellentesque semper vitae justo efficitur tincidunt. Maecenas sit amet arcu eget arcu congue lobortis quis quis massa. Sed fringilla iaculis augue sit amet sodales. Ut at diam id lorem dapibus dignissim non eu tellus. Morbi accumsan, massa cursus eleifend facilisis, sapien ligula fringilla augue, quis bibendum neque lorem tristique est. Aenean sed augue at elit condimentum lacinia quis eu lacus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.

Nullam quis nisl lacinia nulla congue eleifend vitae id neque. Quisque lacinia nulla in dui fermentum, non ullamcorper massa rutrum. Vestibulum varius blandit facilisis. Aenean bibendum lorem ac sem aliquet ultrices. Nam nunc metus, auctor vel metus ac, interdum vestibulum magna. Vivamus facilisis vulputate ligula. Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris volutpat tristique libero eu auctor. Ut ac vestibulum eros.
"""

wrapped_text_as_string = get_text_with_linebreaks_to_fit_ratio(input_text, 16/9)["as_string"]

img_width = 1000
img_height = 1000
img = Image.new('RGB', (img_width, img_height), (255, 255, 255))
img_D = ImageDraw.Draw(img)
img_D.multiline_text((10, 10), wrapped_text_as_string, fill=(0,0,0))
img.save("test_img.jpeg", 'jpeg', optimize=True, quality = 200)
© www.soinside.com 2019 - 2024. All rights reserved.