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

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

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

你应该这样读阿拉伯语

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

1-税前折扣
2-总无

但我需要这样

1-总无
2-税前折扣

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

这是阿拉伯语的问题:

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

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

这是我的代码:

  1. from fpdf import FPDF
  2. import arabic_reshaper
  3. def arabic(text):
  4. if text.isascii():
  5. return text
  6. else:
  7. reshaped_text = arabic_reshaper.reshape(text)
  8. return reshaped_text[::-1]
  9. pdf = FPDF()
  10. pdf.add_page()
  11. pdf.add_font(family='DejaVu', style='', fname="DejaVuSans.ttf")
  12. pdf.add_font(family='DejaVu', style='B', fname="DejaVuSans.ttf")
  13. pdf.set_font(family='DejaVu', style='', size=55)
  14. pdf.set_margin(5.0)
  15. with pdf.table(cell_fill_color=(200,200,200), cell_fill_mode="ALL", text_align="CENTER") as table:
  16. names_row = table.row()
  17. names_row.cell(text=arabic("الإجمالي بدون الضريبة قبل الخصم"))
  18. pdf.set_fill_color(r=255,g=255,b=255)
  19. row = table.row()
  20. row.cell(text=str(5000))
  21. pdf.output('final.pdf')

代码的结果:

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

xqkwcwgp

xqkwcwgp1#

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

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

这是FPDF库中的问题。

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

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

那么如何编辑库?

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

这是阿拉伯语的台词

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

林斯列表

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

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

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

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

  1. def multi_cell(
  2. self,
  3. w,
  4. h=None,
  5. txt="",
  6. border=0,
  7. align=Align.J,
  8. fill=False,
  9. split_only=False, # DEPRECATED
  10. link="",
  11. ln="DEPRECATED",
  12. max_line_height=None,
  13. markdown=False,
  14. print_sh=False,
  15. new_x=XPos.RIGHT,
  16. new_y=YPos.NEXT,
  17. wrapmode: WrapMode = WrapMode.WORD,
  18. dry_run=False,
  19. output=MethodReturnValue.PAGE_BREAK,
  20. center=False,
  21. ):
  22. """
  23. This method allows printing text with line breaks. They can be automatic
  24. (breaking at the most recent space or soft-hyphen character) as soon as the text
  25. reaches the right border of the cell, or explicit (via the `\\n` character).
  26. As many cells as necessary are stacked, one below the other.
  27. Text can be aligned, centered or justified. The cell block can be framed and
  28. the background painted.
  29. Args:
  30. w (float): cell width. If 0, they extend up to the right margin of the page.
  31. h (float): cell height. Default value: None, meaning to use the current font size.
  32. txt (str): string to print.
  33. border: Indicates if borders must be drawn around the cell.
  34. The value can be either a number (`0`: no border ; `1`: frame)
  35. or a string containing some or all of the following characters
  36. (in any order):
  37. `L`: left ; `T`: top ; `R`: right ; `B`: bottom. Default value: 0.
  38. align (fpdf.enums.Align, str): Set text alignment inside the cell.
  39. Possible values are:
  40. `J`: justify (default value); `L` or empty string: left align;
  41. `C`: center; `X`: center around current x position; `R`: right align
  42. fill (bool): Indicates if the cell background must be painted (`True`)
  43. or transparent (`False`). Default value: False.
  44. split_only (bool): **DEPRECATED since 2.7.4**:
  45. Use `dry_run=True` and `output=("LINES",)` instead.
  46. link (str): optional link to add on the cell, internal
  47. (identifier returned by `add_link`) or external URL.
  48. new_x (fpdf.enums.XPos, str): New current position in x after the call. Default: RIGHT
  49. new_y (fpdf.enums.YPos, str): New current position in y after the call. Default: NEXT
  50. ln (int): **DEPRECATED since 2.5.1**: Use `new_x` and `new_y` instead.
  51. max_line_height (float): optional maximum height of each sub-cell generated
  52. markdown (bool): enable minimal markdown-like markup to render part
  53. of text as bold / italics / underlined. Default to False.
  54. print_sh (bool): Treat a soft-hyphen (\\u00ad) as a normal printable
  55. character, instead of a line breaking opportunity. Default value: False
  56. wrapmode (fpdf.enums.WrapMode): "WORD" for word based line wrapping (default),
  57. "CHAR" for character based line wrapping.
  58. dry_run (bool): if `True`, does not output anything in the document.
  59. Can be useful when combined with `output`.
  60. output (fpdf.enums.MethodReturnValue): defines what this method returns.
  61. If several enum values are joined, the result will be a tuple.
  62. center (bool): center the cell horizontally on the page.
  63. Using `new_x=XPos.RIGHT, new_y=XPos.TOP, maximum height=pdf.font_size` is
  64. useful to build tables with multiline text in cells.
  65. Returns: a single value or a tuple, depending on the `output` parameter value
  66. """
  67. if split_only:
  68. warnings.warn(
  69. # pylint: disable=implicit-str-concat
  70. 'The parameter "split_only" is deprecated.'
  71. ' Use instead dry_run=True and output="LINES".',
  72. DeprecationWarning,
  73. stacklevel=get_stack_level(),
  74. )
  75. if dry_run or split_only:
  76. with self._disable_writing():
  77. return self.multi_cell(
  78. w=w,
  79. h=h,
  80. txt=txt,
  81. border=border,
  82. align=align,
  83. fill=fill,
  84. link=link,
  85. ln=ln,
  86. max_line_height=max_line_height,
  87. markdown=markdown,
  88. print_sh=print_sh,
  89. new_x=new_x,
  90. new_y=new_y,
  91. wrapmode=wrapmode,
  92. dry_run=False,
  93. split_only=False,
  94. output=MethodReturnValue.LINES if split_only else output,
  95. center=center,
  96. )
  97. if not self.font_family:
  98. raise FPDFException("No font set, you need to call set_font() beforehand")
  99. wrapmode = WrapMode.coerce(wrapmode)
  100. if isinstance(w, str) or isinstance(h, str):
  101. raise ValueError(
  102. # pylint: disable=implicit-str-concat
  103. "Parameter 'w' and 'h' must be numbers, not strings."
  104. " You can omit them by passing string content with txt="
  105. )
  106. new_x = XPos.coerce(new_x)
  107. new_y = YPos.coerce(new_y)
  108. if ln != "DEPRECATED":
  109. # For backwards compatibility, if "ln" is used we overwrite "new_[xy]".
  110. if ln == 0:
  111. new_x = XPos.RIGHT
  112. new_y = YPos.NEXT
  113. elif ln == 1:
  114. new_x = XPos.LMARGIN
  115. new_y = YPos.NEXT
  116. elif ln == 2:
  117. new_x = XPos.LEFT
  118. new_y = YPos.NEXT
  119. elif ln == 3:
  120. new_x = XPos.RIGHT
  121. new_y = YPos.TOP
  122. else:
  123. raise ValueError(
  124. f'Invalid value for parameter "ln" ({ln}),'
  125. " must be an int between 0 and 3."
  126. )
  127. warnings.warn(
  128. (
  129. 'The parameter "ln" is deprecated.'
  130. f" Instead of ln={ln} use new_x=XPos.{new_x.name}, new_y=YPos.{new_y.name}."
  131. ),
  132. DeprecationWarning,
  133. stacklevel=get_stack_level(),
  134. )
  135. align = Align.coerce(align)
  136. page_break_triggered = False
  137. if h is None:
  138. h = self.font_size
  139. # If width is 0, set width to available width between margins
  140. if w == 0:
  141. w = self.w - self.r_margin - self.x
  142. if center:
  143. self.x = (
  144. self.w / 2 if align == Align.X else self.l_margin + (self.epw - w) / 2
  145. )
  146. maximum_allowed_width = w - 2 * self.c_margin
  147. # Calculate text length
  148. txt = self.normalize_text(txt)
  149. normalized_string = txt.replace("\r", "")
  150. styled_text_fragments = self._preload_font_styles(normalized_string, markdown)
  151. prev_font_style, prev_underline = self.font_style, self.underline
  152. prev_x, prev_y = self.x, self.y
  153. total_height = 0
  154. if not border:
  155. border = ""
  156. elif border == 1:
  157. border = "LTRB"
  158. text_lines = []
  159. multi_line_break = MultiLineBreak(
  160. styled_text_fragments,
  161. justify=(align == Align.J),
  162. print_sh=print_sh,
  163. wrapmode=wrapmode,
  164. )
  165. txt_line = multi_line_break.get_line_of_given_width(maximum_allowed_width)
  166. while (txt_line) is not None:
  167. text_lines.append(txt_line)
  168. txt_line = multi_line_break.get_line_of_given_width(maximum_allowed_width)
  169. if not text_lines: # ensure we display at least one cell - cf. issue #349
  170. text_lines = [
  171. TextLine(
  172. "",
  173. text_width=0,
  174. number_of_spaces=0,
  175. justify=False,
  176. trailing_nl=False,
  177. )
  178. ]
  179. should_render_bottom_blank_cell = False
  1. if not txt.isascii():
  2. text_lines.reverse()
  1. for text_line_index, text_line in enumerate(text_lines):
  2. is_last_line = text_line_index == len(text_lines) - 1
  3. should_render_bottom_blank_cell = False
  4. if max_line_height is not None and h > max_line_height:
  5. current_cell_height = max_line_height
  6. h -= current_cell_height
  7. if is_last_line:
  8. if h > 0 and len(text_lines) > 1:
  9. should_render_bottom_blank_cell = True
  10. else:
  11. h += current_cell_height
  12. current_cell_height = h
  13. else:
  14. current_cell_height = h
  15. has_line_after = not is_last_line or should_render_bottom_blank_cell
  16. new_page = self._render_styled_text_line(
  17. text_line,
  18. w,
  19. h=current_cell_height,
  20. border="".join(
  21. (
  22. "T" if "T" in border and text_line_index == 0 else "",
  23. "L" if "L" in border else "",
  24. "R" if "R" in border else "",
  25. "B" if "B" in border and not has_line_after else "",
  26. )
  27. ),
  28. new_x=new_x if not has_line_after else XPos.LEFT,
  29. new_y=new_y if not has_line_after else YPos.NEXT,
  30. align=Align.L if (align == Align.J and is_last_line) else align,
  31. fill=fill,
  32. link=link,
  33. )
  34. page_break_triggered = page_break_triggered or new_page
  35. total_height += current_cell_height
  36. if not is_last_line and align == Align.X:
  37. # prevent cumulative shift to the left
  38. self.x = prev_x
  39. if should_render_bottom_blank_cell:
  40. new_page = self._render_styled_text_line(
  41. TextLine(
  42. "",
  43. text_width=0,
  44. number_of_spaces=0,
  45. justify=False,
  46. trailing_nl=False,
  47. ),
  48. w,
  49. h=h,
  50. border="".join(
  51. (
  52. "L" if "L" in border else "",
  53. "R" if "R" in border else "",
  54. "B" if "B" in border else "",
  55. )
  56. ),
  57. new_x=new_x,
  58. new_y=new_y,
  59. fill=fill,
  60. link=link,
  61. )
  62. page_break_triggered = page_break_triggered or new_page
  63. if new_page and new_y == YPos.TOP:
  64. # When a page jump is performed and the requested y is TOP,
  65. # pretend we started at the top of the text block on the new page.
  66. # cf. test_multi_cell_table_with_automatic_page_break
  67. prev_y = self.y
  68. # pylint: disable=undefined-loop-variable
  69. if text_line and text_line.trailing_nl and new_y in (YPos.LAST, YPos.NEXT):
  70. # The line renderer can't handle trailing newlines in the text.
  71. self.ln()
  72. if new_y == YPos.TOP: # We may have jumped a few lines -> reset
  73. self.y = prev_y
  74. if markdown:
  75. if self.font_style != prev_font_style:
  76. self.font_style = prev_font_style
  77. self.current_font = self.fonts[self.font_family + self.font_style]
  78. self.underline = prev_underline
  79. output = MethodReturnValue.coerce(output)
  80. return_value = ()
  81. if output & MethodReturnValue.PAGE_BREAK:
  82. return_value += (page_break_triggered,)
  83. if output & MethodReturnValue.LINES:
  84. output_lines = []
  85. for text_line in text_lines:
  86. characters = []
  87. for frag in text_line.fragments:
  88. characters.extend(frag.characters)
  89. output_lines.append("".join(characters))
  90. return_value += (output_lines,)
  91. if output & MethodReturnValue.HEIGHT:
  92. return_value += (total_height,)
  93. if len(return_value) == 1:
  94. return return_value[0]
  95. return return_value

这就是一切谢谢

展开查看全部

相关问题