pythonimageimage-processingwandmagickwand

Image creation with Wand in Python


My goal for the project that I'm working on is to take in the content from a word document and then output a single image that contains bordered boxes that contain the segments of text that I've automatically funneled through with my code. Here is an example of what I need:

Wanted Output

I've already managed to get 90% of the footwork done for this project thanks to a little bit of my own knowledge and a whole lot of knowledge from other people. My code already takes the text from word documents and takes what I want automatically. The problem arises when trying to mimic this image. I'm using Wand to create the image but I still can't quite get it to work. I know that I'm so close, but I'm not at all certain what I'm missing. The following is the code strictly for the image creation:

from wand.image import Image
from wand.drawing import Drawing

target_width = 500
target_height = 0
y_offset = 0
y_padding = 4
x_padding = 5

with Image(width=2000, height=2000, pseudo='xc:white') as img:
    for match in find_matches(text=fullText):
        ct += 1
        with Drawing() as ctx:
            ctx.font_size = 20
            ctx.text_alignment = 'center'
            words = match.split(" ")
            words.append("\n" + str(ct))
            word_count = len(words)
            while True:
                temp_text = rebuild_text(words, word_count)
                metrics = ctx.get_font_metrics(img, temp_text, multiline=True)
                if metrics.text_width > target_width:
                    word_count -= 1
                else:
                    text = temp_text
                    target_height = int(metrics.text_height + 0.5)
                    break
            ctx.push()
            ctx.fill_color = 'white'
            ctx.stroke_width = 3
            ctx.stroke_color = 'black'
            ctx.rectangle(2, y_offset + y_padding, width=2*x_padding+target_width,
                          height=6*y_padding+target_height)
            ctx.pop()
            ctx.text(x_padding + (target_width // 2), 16 + 6*y_padding+y_offset, text)
            ctx(img)
            y_offset = target_height + 100*y_padding + 7
    img.trim()
    img.save(filename='patdrawdemoTest.png')

I've tried messing with this code over and over again to no avail. I know that all of the text that I want is being passed through as I added a print statement to make sure of this already, and after messing with the code here and there, sometimes I find that there's lots of text that overlaps, but I can never get more than three bordered boxes. Here are some examples of the outputs that I've been able to get by changing values here and there:

Example1 Example2 Example3

I can't replicate the text-overlap output, however, the above are just some examples. I'm not sure if it helps at all, but the differences between those are the y-axis-related values for the text and rectangles and such.

The lines of text that I need are taken with the docx library in python which is then put into variables; it's not a simple string. Additionally, this code must work for any occasion; Whether it's 5 boxes of text, 2, 8, or 100, it must create an image with that many text boxes. The following is the text that I've parsed and that is passed through the code I have above:

Storing a first data related to an operation style of a transport in a first area 243

Storing a second data related to an operation style of the transport in a second area
244

Modifying functionality of the transport based on the combined energy consumption efficiency
245

Determining the combined energy consumption efficiency, wherein the determining comprises a blockchain consensus between a peer group comprising one or more of the transport, a server, and at least one other transport 246

Executing a smart contract to record the combined energy consumption efficiency on a blockchain, based on the blockchain consensus
247

I'll greatly appreciate any help I can get, as I'm completely stumped. Please let me know if I can be more clear or if there's something I'm missing to help you answer my question. Thanks.


Solution

  • @emconnville gave me the answer that I needed on another post a while before I posted this question, however there was an error that took me ages to see but this the answer to my question:

    def to_chunks(words, size):
        for idx in range(0, len(words), size):
            yield words[idx:idx + size]
    
    
    def rebuild_text(words, size, ct):
        ret = "\n".join([" ".join(w) for w in to_chunks(words, size)])
        ret += "\n" + ct
        return ret
    
    
    target_width = 375
    target_height = 0
    y_offset = 0
    y_padding = 5
    x_padding = 6
    
    with Image(width=2000, height=5000, pseudo='xc:white') as img:
        for match in find_matches(text=fullText):
            ct += 1
            with Drawing() as ctx:
                ctx.font_size = 20
                ctx.text_alignment = 'center'
                words = match.split(" ")
                word_count = len(words)
                while True:
                    temp_text = rebuild_text(words, word_count, str(ct))
                    metrics = ctx.get_font_metrics(img, temp_text, multiline=True)
                    if metrics.text_width > target_width:
                        word_count -= 1
                    else:
                        text = temp_text
                        target_height = int(metrics.text_height + 1)
                        break
                ctx.push()
                ctx.fill_color = 'white'
                ctx.stroke_width = 3
                ctx.stroke_color = 'black'
                ctx.rectangle(2, y_offset + y_padding, width=2*x_padding+target_width,
                              height=2*y_padding+target_height)
                ctx.pop()
                ctx.text(x_padding+2 + (target_width // 2), 10 + 4*y_padding+y_offset, text)
                ctx(img)
                y_offset += target_height + 4*y_padding - 2
        img.trim()
        img.save(filename='patdrawdemoTest.png')
    

    The error was in this specific block of code:

            ctx.text(x_padding+2 + (target_width // 2), 10 + 4*y_padding+y_offset, text)
            ctx(img)
            y_offset += target_height + 4*y_padding - 2
    img.trim()
    img.save(filename='patdrawdemoTest.png')
    

    Where the "y_offset += target_height + 4*y_padding - 2" was originally just "y_offset = target..." So... Basically I was just missing the +=

    I wish I wasn't blind.