python-3.x 如何在FPDF2中解决语言方向的问题?

iyfjxgzm  于 2023-10-21  发布在  Python
关注(0)|答案(1)|浏览(131)

我正在进行一个项目,使用Python中的FPDF2在PDF文件上打印阿拉伯语单词。
但我在展示这种语言的原始方向时遇到了很多问题。
FPDF2以英语方向显示所有语言。
这就是你读英语的方法:

你应该这样读阿拉伯语

但是当我把上面的单词用阿拉伯语写出来时,我面临一个问题>
问题是FPDF像这样显示阿拉伯语:

1-税前折扣
2-总无

但我需要这样

1-总无
2-税前折扣

我用英语写的问题可以理解。

这是阿拉伯语的问题:

但为什么会出现这个问题?

因为FPDF2显示阿拉伯语就像它显示英语一样,它以相同的方向显示。

这是我的代码:

from fpdf import FPDF
import arabic_reshaper


def arabic(text):
    if text.isascii():
        return text
    else:
        reshaped_text = arabic_reshaper.reshape(text)
        return reshaped_text[::-1]

pdf = FPDF()
pdf.add_page()
pdf.add_font(family='DejaVu', style='', fname="DejaVuSans.ttf")
pdf.add_font(family='DejaVu', style='B', fname="DejaVuSans.ttf")
pdf.set_font(family='DejaVu', style='', size=55)
pdf.set_margin(5.0)


with pdf.table(cell_fill_color=(200,200,200), cell_fill_mode="ALL", text_align="CENTER") as table:
    names_row = table.row()
    names_row.cell(text=arabic("الإجمالي بدون الضريبة قبل الخصم"))

    pdf.set_fill_color(r=255,g=255,b=255)
    row = table.row()
    row.cell(text=str(5000))



pdf.output('final.pdf')

代码的结果:

当你必须将文本分隔成多行而不是一行时,问题就会出现。
我试过很多方法,但没有一个能让我接近。
谢谢.

xqkwcwgp

xqkwcwgp1#

为什么会发生这种情况的原因是FPDF当它想打破文本ot行它从左到右打破它像这样

但是在阿拉伯语中,你应该从右到左将文本分成行,但是FPDF并没有这样做,它处理它就像它是一个英语文本一样,它从左到右像这样分割它

这是FPDF库中的问题。

我是怎么解决这个问题的?

您应该编辑库本身,而不是如何将文本输入库。

那么如何编辑库?

当你把一个文本放到一个单元格内的表格中时,你使用了multi_cell来使这个文本自动被库打破。
然后你必须去的功能,它处理的multi_cell,并编辑它,当这个函数采取阿拉伯语它必须扭转行,并添加到单元格从下到上。
将它们添加到单元格之前的行将是这样的:

这是阿拉伯语的台词

现在,我们将fuces对林斯 * 名单林**,以解决我们的问题。

林斯列表

就像我们说的,你应该在将它们添加到最后一个单元格之前反转行列表,当你反转它们时,它们会像这样:

这就是理论上的解决方案。
但是我怎么能编辑功能multi_cellFPDF当输入文本是英文的列表将像它是什么,但当输入文本是阿拉伯文的列表将被逆转,我们怎么能做到这一点?
为了解释一切,这将是困难的,但我会告诉你的代码,我添加在功能multi_cll做我想要的。
这是我在函数multi_cell中添加的代码:

if not txt.isascii():
    text_lines.reverse()

这是我把它放在函数内部的地方,以便工作良好:
这是编辑的函数:

def multi_cell(
        self,
        w,
        h=None,
        txt="",
        border=0,
        align=Align.J,
        fill=False,
        split_only=False,  # DEPRECATED
        link="",
        ln="DEPRECATED",
        max_line_height=None,
        markdown=False,
        print_sh=False,
        new_x=XPos.RIGHT,
        new_y=YPos.NEXT,
        wrapmode: WrapMode = WrapMode.WORD,
        dry_run=False,
        output=MethodReturnValue.PAGE_BREAK,
        center=False,
    ):
        """
        This method allows printing text with line breaks. They can be automatic
        (breaking at the most recent space or soft-hyphen character) as soon as the text
        reaches the right border of the cell, or explicit (via the `\\n` character).
        As many cells as necessary are stacked, one below the other.
        Text can be aligned, centered or justified. The cell block can be framed and
        the background painted.

        Args:
            w (float): cell width. If 0, they extend up to the right margin of the page.
            h (float): cell height. Default value: None, meaning to use the current font size.
            txt (str): string to print.
            border: Indicates if borders must be drawn around the cell.
                The value can be either a number (`0`: no border ; `1`: frame)
                or a string containing some or all of the following characters
                (in any order):
                `L`: left ; `T`: top ; `R`: right ; `B`: bottom. Default value: 0.
            align (fpdf.enums.Align, str): Set text alignment inside the cell.
                Possible values are:
                `J`: justify (default value); `L` or empty string: left align;
                `C`: center; `X`: center around current x position; `R`: right align
            fill (bool): Indicates if the cell background must be painted (`True`)
                or transparent (`False`). Default value: False.
            split_only (bool): **DEPRECATED since 2.7.4**:
                Use `dry_run=True` and `output=("LINES",)` instead.
            link (str): optional link to add on the cell, internal
                (identifier returned by `add_link`) or external URL.
            new_x (fpdf.enums.XPos, str): New current position in x after the call. Default: RIGHT
            new_y (fpdf.enums.YPos, str): New current position in y after the call. Default: NEXT
            ln (int): **DEPRECATED since 2.5.1**: Use `new_x` and `new_y` instead.
            max_line_height (float): optional maximum height of each sub-cell generated
            markdown (bool): enable minimal markdown-like markup to render part
                of text as bold / italics / underlined. Default to False.
            print_sh (bool): Treat a soft-hyphen (\\u00ad) as a normal printable
                character, instead of a line breaking opportunity. Default value: False
            wrapmode (fpdf.enums.WrapMode): "WORD" for word based line wrapping (default),
                "CHAR" for character based line wrapping.
            dry_run (bool): if `True`, does not output anything in the document.
                Can be useful when combined with `output`.
            output (fpdf.enums.MethodReturnValue): defines what this method returns.
                If several enum values are joined, the result will be a tuple.
            center (bool): center the cell horizontally on the page.

        Using `new_x=XPos.RIGHT, new_y=XPos.TOP, maximum height=pdf.font_size` is
        useful to build tables with multiline text in cells.

        Returns: a single value or a tuple, depending on the `output` parameter value
        """
        if split_only:
            warnings.warn(
                # pylint: disable=implicit-str-concat
                'The parameter "split_only" is deprecated.'
                ' Use instead dry_run=True and output="LINES".',
                DeprecationWarning,
                stacklevel=get_stack_level(),
            )
        if dry_run or split_only:
            with self._disable_writing():
                return self.multi_cell(
                    w=w,
                    h=h,
                    txt=txt,
                    border=border,
                    align=align,
                    fill=fill,
                    link=link,
                    ln=ln,
                    max_line_height=max_line_height,
                    markdown=markdown,
                    print_sh=print_sh,
                    new_x=new_x,
                    new_y=new_y,
                    wrapmode=wrapmode,
                    dry_run=False,
                    split_only=False,
                    output=MethodReturnValue.LINES if split_only else output,
                    center=center,
                )
        if not self.font_family:
            raise FPDFException("No font set, you need to call set_font() beforehand")
        wrapmode = WrapMode.coerce(wrapmode)
        if isinstance(w, str) or isinstance(h, str):
            raise ValueError(
                # pylint: disable=implicit-str-concat
                "Parameter 'w' and 'h' must be numbers, not strings."
                " You can omit them by passing string content with txt="
            )
        new_x = XPos.coerce(new_x)
        new_y = YPos.coerce(new_y)
        if ln != "DEPRECATED":
            # For backwards compatibility, if "ln" is used we overwrite "new_[xy]".
            if ln == 0:
                new_x = XPos.RIGHT
                new_y = YPos.NEXT
            elif ln == 1:
                new_x = XPos.LMARGIN
                new_y = YPos.NEXT
            elif ln == 2:
                new_x = XPos.LEFT
                new_y = YPos.NEXT
            elif ln == 3:
                new_x = XPos.RIGHT
                new_y = YPos.TOP
            else:
                raise ValueError(
                    f'Invalid value for parameter "ln" ({ln}),'
                    " must be an int between 0 and 3."
                )
            warnings.warn(
                (
                    'The parameter "ln" is deprecated.'
                    f" Instead of ln={ln} use new_x=XPos.{new_x.name}, new_y=YPos.{new_y.name}."
                ),
                DeprecationWarning,
                stacklevel=get_stack_level(),
            )
        align = Align.coerce(align)

        page_break_triggered = False

        if h is None:
            h = self.font_size
        # If width is 0, set width to available width between margins
        if w == 0:
            w = self.w - self.r_margin - self.x
        if center:
            self.x = (
                self.w / 2 if align == Align.X else self.l_margin + (self.epw - w) / 2
            )
        maximum_allowed_width = w - 2 * self.c_margin

        # Calculate text length
        txt = self.normalize_text(txt)
        normalized_string = txt.replace("\r", "")
        styled_text_fragments = self._preload_font_styles(normalized_string, markdown)

        prev_font_style, prev_underline = self.font_style, self.underline
        prev_x, prev_y = self.x, self.y
        total_height = 0

        if not border:
            border = ""
        elif border == 1:
            border = "LTRB"

        text_lines = []
        multi_line_break = MultiLineBreak(
            styled_text_fragments,
            justify=(align == Align.J),
            print_sh=print_sh,
            wrapmode=wrapmode,
        )
        txt_line = multi_line_break.get_line_of_given_width(maximum_allowed_width)
        while (txt_line) is not None:
            text_lines.append(txt_line)
            txt_line = multi_line_break.get_line_of_given_width(maximum_allowed_width)

        if not text_lines:  # ensure we display at least one cell - cf. issue #349
            text_lines = [
                TextLine(
                    "",
                    text_width=0,
                    number_of_spaces=0,
                    justify=False,
                    trailing_nl=False,
                )
            ]
        should_render_bottom_blank_cell = False
if not txt.isascii():
            text_lines.reverse()
for text_line_index, text_line in enumerate(text_lines):
            is_last_line = text_line_index == len(text_lines) - 1
            should_render_bottom_blank_cell = False
            if max_line_height is not None and h > max_line_height:
                current_cell_height = max_line_height
                h -= current_cell_height
                if is_last_line:
                    if h > 0 and len(text_lines) > 1:
                        should_render_bottom_blank_cell = True
                    else:
                        h += current_cell_height
                        current_cell_height = h
            else:
                current_cell_height = h
            has_line_after = not is_last_line or should_render_bottom_blank_cell
            new_page = self._render_styled_text_line(
                text_line,
                w,
                h=current_cell_height,
                border="".join(
                    (
                        "T" if "T" in border and text_line_index == 0 else "",
                        "L" if "L" in border else "",
                        "R" if "R" in border else "",
                        "B" if "B" in border and not has_line_after else "",
                    )
                ),
                new_x=new_x if not has_line_after else XPos.LEFT,
                new_y=new_y if not has_line_after else YPos.NEXT,
                align=Align.L if (align == Align.J and is_last_line) else align,
                fill=fill,
                link=link,
            )
            page_break_triggered = page_break_triggered or new_page
            total_height += current_cell_height
            if not is_last_line and align == Align.X:
                # prevent cumulative shift to the left
                self.x = prev_x
        if should_render_bottom_blank_cell:
            new_page = self._render_styled_text_line(
                TextLine(
                    "",
                    text_width=0,
                    number_of_spaces=0,
                    justify=False,
                    trailing_nl=False,
                ),
                w,
                h=h,
                border="".join(
                    (
                        "L" if "L" in border else "",
                        "R" if "R" in border else "",
                        "B" if "B" in border else "",
                    )
                ),
                new_x=new_x,
                new_y=new_y,
                fill=fill,
                link=link,
            )
            page_break_triggered = page_break_triggered or new_page
        if new_page and new_y == YPos.TOP:
            # When a page jump is performed and the requested y is TOP,
            # pretend we started at the top of the text block on the new page.
            # cf. test_multi_cell_table_with_automatic_page_break
            prev_y = self.y
        # pylint: disable=undefined-loop-variable
        if text_line and text_line.trailing_nl and new_y in (YPos.LAST, YPos.NEXT):
            # The line renderer can't handle trailing newlines in the text.
            self.ln()

        if new_y == YPos.TOP:  # We may have jumped a few lines -> reset
            self.y = prev_y

        if markdown:
            if self.font_style != prev_font_style:
                self.font_style = prev_font_style
                self.current_font = self.fonts[self.font_family + self.font_style]
            self.underline = prev_underline

        output = MethodReturnValue.coerce(output)
        return_value = ()
        if output & MethodReturnValue.PAGE_BREAK:
            return_value += (page_break_triggered,)
        if output & MethodReturnValue.LINES:
            output_lines = []
            for text_line in text_lines:
                characters = []
                for frag in text_line.fragments:
                    characters.extend(frag.characters)
                output_lines.append("".join(characters))
            return_value += (output_lines,)
        if output & MethodReturnValue.HEIGHT:
            return_value += (total_height,)
        if len(return_value) == 1:
            return return_value[0]
        return return_value

这就是一切谢谢

相关问题