Reportlab PDF: нельзя распределять абзацы пропорционально по всему фрейму - PullRequest
0 голосов
/ 30 мая 2018

Я пытаюсь создать скрипт, который извлекает данные о событиях из Календаря Google и для каждого дня отображает события дней в двух столбцах с разрывами между ними, которые пропорциональны времени (чем больше времени между событиями, тем больше места между абзацами).Каждое событие представляет собой абзац Reportlab Platypus, и между ними есть разделители. Полный код здесь (скоро будет комментировано, обещаю).

Моя проблема в том, что я не могу рассчитать точное расстояние, необходимое для того, чтобы точно вписать каждый абзац в кадр: Iвсегда преувеличивать или недооценивать, независимо от формулы, последний абзац никогда не попадает точно в конец кадра.

Основной расчет начинается с add_events_to_frame ().Абзацы создаются для каждого события, и доступное пространство сохраняется, запрашиваются значения высоты разделителя и история, записанная на холст:

def add_events_to_frame(frame, events, canvas):

    story = []
    free_space = frame.height

    for event in events:
        paragraph = get_event_paragraph(event)
        story.append(paragraph)
        free_space -= paragraph.wrap(frame_width, frame_height)[1] - 1

    real_story = []

    if free_space > 0:
        spaces = Collector.get_space_after(events, free_space)
    for i, paragraph in enumerate(story):
        real_story.append(paragraph)
        real_story.append(Spacer(1, spaces[i] + 1))
    frame.addFromList(real_story, canvas)
    return frame

get_event_paragraph () выглядит следующим образом:

def get_event_paragraph(event, spaceAfter=0, borderPadding=2):

    event_style = ParagraphStyle(
        "event",
        fontName="Times-Roman",
        fontSize=5,
        backColor=event['color'],
        textColor='white',
        leading=6,
        borderPadding=borderPadding,
        spaceAfter=spaceAfter + 2 * borderPadding
    )

    beginTime = event['start'].strftime("%H:%M")
    endTime = event['end'].strftime("%H:%M")

    eventStringList = [f"<b>{beginTime}-{endTime}</b>"]

    eventStringList.append(event['summary'])

    return Paragraph(' '.join(eventStringList), event_style)

И калькулятор высоты проставки:

def get_space_after(events, free_space):
    """
    Calculate spaces between events in percentages based on sum of free time and constraints.

    :param events:
    :return:
    """
    timesum = timedelta(seconds=1)
    time_list = []

    for i in range(len(events) - 1):
        time_diff = events[i + 1]['start'] - events[i]['end']
        timesum += time_diff
        time_list.append(time_diff)

    time_list.append(timedelta(hours=0))

    spaces = []

    for diff in time_list:
        spaces.append(round(diff/timesum * free_space, 4))
    return spaces

Я пробовал KeepInFrame (), но когда он работает, он оставляет дизайн неровным и уродливым.Также я попытался исключить отступы фрейма, отступы абзаца и пространство в 1px между абзацами из переменной free_space в различных комбинациях, но, опять же, нет точного соответствия.Что делать?

1 Ответ

0 голосов
/ 31 мая 2018

Я выяснил точную формулу для свободного места.Далее следует формула для точного расчета общего свободного пространства между абзацами, она вытекает из того факта, что borderPadding абзаца является просто предисловием, все вычисления выполняются в контексте отступов фрейма и высоты абзаца.Все переменные соответствуют коду, указанному в вопросе.

Во-первых, нам нужно учитывать заполнение кадра в верхней и нижней части кадра:

free_space = frame.height - frame.topPadding - frame.bottomPadding

Во-вторых, для каждогоДля абзаца в нашем фрейме нам нужно вычесть обернутую высоту в контексте отступов фрейма и добавить к ним потерю высоты при заполнении границ абзаца (ПРИМЕЧАНИЕ: это применимо, когда значения borderPadding одинаковы для всех абзацев) и пробелжелаемый между абзацами (в данном случае 1 в конце):

free_space -= paragraph.wrap(
                frame_width - frame.lefPadding - frame.rightPadding + 2 * paragraph.style.borderPadding, \
                frame_height - frame.topPadding - frame.bottomPadding)[1] \
        + paragraph.style.borderPadding * 2 + 1

Наконец, вам нужно добавить дважды значение borderPadding абзаца к free_space, потому что в противном случае вы потерпите неудачу: borderPadding не беретсяучитывается только края абзаца, поэтому нам нужно заполнить пустоту, созданную первым и последним абзацем, свободным пространством, чтобы выровнять край абзаца с отступами рамки:

    free_space += paragraph.style.borderPadding * 2

Это освободит пространствомежду абзацем и рамкой:

frame.{any padding} - paragraph.style.borderPadding
...