Replies: 4 comments 1 reply
-
Hi @gmischler. Thanks for opening this discussion!
Agreed, seems like the way to go forward. I opened #340 in order to track this.
What is the need here exactly?
FYI there is currently a tutorial covering this: I'm not against making this an internal feature, but could you please give details on that
Sounds great!
I disagree here. In order to follow the principle of least astonishment, I think the text rendering should continue onto the next page, or an exception should be raised to inform the user of the issue.
FYI, issue #210 is already open about this. You ask very interesting open questions at the end 😄 |
Beta Was this translation helpful? Give feedback.
-
Current status: Phase 1 has been implemented in PRs #308 and #346. Phase 2a is also done. PRs #511 and #519 provide the foundation, making it technically possible to horizontally align text that varies in almost any properties (font, size, style, stretching, spacing, etc.). More to follow... |
Beta Was this translation helpful? Give feedback.
-
Nearly a year later, we have some more progress: The PR #897 (still a work in progress) implements the following:
|
Beta Was this translation helpful? Give feedback.
-
With release 2.7.6, #897 has been merged (still declared "experimental"). It implements the basic mechanisms and a new method The next step will be to add a special type of "paragraph" for images to be rendered within columns. |
Beta Was this translation helpful? Give feedback.
-
Now that the
.multicell()
refactoring #308 by @oleksii-shymann has landed (congrats, and thanks for implementing my feature request!), it opens the path to several other possible changes.Here's what I imagine could be done, in relatively small steps to keep it manageable. I'm presenting this for discussion, to discuss feasability and maybe others will come up with possibilities that I've missed. It's probably best to take this as a proposal for a road map, which may eventually result in a series of feature request issues.
1. Refactor
.write()
to use the new line wrapping codeThis will eliminate redundant code and enable
.write()
to use soft-hyphens.2. Enable text alignment for
.write()
.write()
currently only supports left alignment, but we also want right, center, and justify. This will require to keep a cache of the text already added, at least until the current line is full. The challenge is what to do with the last line. The simplest solution might be to trigger it's addition through a manual newline (eg..nl()
). But that would be a confusing change of semantics, and also a dead end in terms of functionality.The more elegant and pythonic way is to create a class
TextArea
, which acts as a context manager, and handles the alignment of the text added within its scope, using the page margins as.write()
already does on its own.Of course, only one such context can be active at any time.
For backwards compatibility, any
.write()
call outside of such a context should keep working as it does now.3. Flexible text column width
Having to manipulate the page margins in order to create multiple columns of text is somewhat annoying. A simple solution is a subclass
TextColumn
, which accepts its own left and right boundaries.In fact, it should probably accept arguments to specify the boundaries for several columns, in order to fully automate multi-column layouts.
4. Other text outline shapes
But why limit ourselfes to just straight columns? All we need to extend on that are a few more subclasses:
This will require a slight modification to the line wrapping code, so that in the presence of a text area context, it won't just use a fixed width, but asks the context for each line where it should start and end.
The idea is that for any line of text (a vertical space between
pdf.y
andpdf.y + lineheight
), the context manager instance will determine the maximum horizontal space for text it could hold within.Using an outline that is defined in all directions (not just on the sides as with a column), these are likely to be restricted to a single page. Consequently we need to think about what happens when the user supplies more text than fits in the given area. Most likely the surplus text will simply be dropped with a warning. But we could also give it a callback, in order for the client code to create a new shape, possibly on another page.
Any new call to
.write()
simply continues on the same line as the previous one has ended, but possibly with a radically different font size. This raises the issue of vertical alignment, which can probably be solved with avertical_align="top|bottom|middle"
argument.Given that our lateral boundaries are not always vertical anymore, it may also be tricky to figure out which line height actually applies to a given line, which then in turn could make the horizontal extent ambiguous (and vice versa, in the worst case).
5. Combine shapes
This one is a possible solution to issue #245.
While using one shape at a time is straightforward enough, there's also the possibility to combine several of them by means of modification. You start with one of the shapes serving as context, and then extend or reduce its area by adding and/or subtracting other shapes from it:
After the context has determined its own width for a line of text, it will ask each removed shape which range it will exclude (by its outer bounds), and each added shape which range it will enable (by its inner bounds). It will do so in the sequence in which they have been applied, and change its writeable width accordingly.
I don't know what the best algorithm is to do that, but the result should be one or several stretches to place text on that line (several if we created a gap in the middle).
6. Use image data to shape text
TextImageMap
is probably the most ambitious one, so it might be regarded as a bonus round. It will need aPIL.Image.Image
instance (maybe.image()
should be made to return one?).Then it can be configured with one or several methods like:
The
combine="any"
(out of "any"|"all") means that either condition will block the text (too dark or not transparent enough), with "all" requiring them all to be true for the same pixel. Many other criteria could be implemented. Most likely it will be helpful to scale the image down first, to make it more efficient and to blur any extreme single pixels away.Questions to consider
A few of the issues that need to be considered before implementation may be:
.write()
, is there any useful purpose for.cell()
,.multicell()
, and/or.text()
within a text area? I don't currently see one, but you never know....write()
method ofFPDF()
be adapted for use within those context managers, or is it more practical for them to have a.write()
method (wrapping the former with the necessary modifications)?.write_html()
as well?Any thoughts on how to further improve those concepts?
Anyone wants to have fun with this?
Beta Was this translation helpful? Give feedback.
All reactions