Search code examples
pythonpython-docxpymupdf

How do I merge items from a list avoiding repeated content inside the items?


Edit 4:

Simpler example of what I want to do:

I have a list like this:

sentences = ['Hello, how are','how are you','you doing?']

And I want to turn it into a string like this:

sentence = 'Hello, how are you doing?'

Any help is appreciated!

Original post:

I'm trying to get highlighted text out of a .pdf file and put it inside a .docx file with that same name.

Here's the code for it:

    from typing import List, Tuple

import fitz  # install with 'pip install pymupdf'
import os
from docx import Document


def _parse_highlight(annot: fitz.Annot, wordlist: List[Tuple[float, float, float, float, str, int, int, int]]) -> str:
    points = annot.vertices
    quad_count = int(len(points) / 4)
    sentences = []
    for i in range(quad_count):
        # where the highlighted part is
        r = fitz.Quad(points[i * 4: i * 4 + 4]).rect

        words = [w for w in wordlist if fitz.Rect(w[:4]).intersects(r)]
        sentences.append(" ".join(w[4] for w in words))
    sentence = " ".join(sentences)
    return sentence


def handle_page(page):
    wordlist = page.getText("words")  # list of words on page
    wordlist.sort(key=lambda w: (w[3], w[0]))  # ascending y, then x
    separator = "PÁGINA NÚMERO " + str(page.number) + ": "

    highlights = []
    annot = page.firstAnnot
    while annot:
        if annot.type[0] == 8:
            highlights.append(separator)
            highlights.append(_parse_highlight(annot, wordlist))
            document.add_paragraph(highlights)
        annot = annot.next

    return highlights


def main(filepath: str) -> List:
    doc = fitz.open(filepath)

    highlights = []
    for page in doc:
        highlights += handle_page(page)

    return highlights

dir_files = [f for f in os.listdir(".") if os.path.isfile(os.path.join(".", f))]
print(dir_files)
document = Document()
for file in dir_files:  # look at every file in the current directory
    if file.endswith('.pdf'):  # if it is a PDF, use it
        print('Working on converting: ' + file)

        main(file)
        document.save(file.replace(".pdf",".docx"))

I have to say I didn't write the part for getting the text out of the highlights. I got it here.

The problem is the list it creates gets repeated items. Here's a sample of the output to the .docx file:

PÁGINA NÚMERO 0: En los primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras que se extiende a través del citoplasma (fig. 6-20). El citoesqueleto, revelado la presencia del citoesqueleto, una red de fibras que se extiende a través del citoplasma (fig. 6-20). El citoesqueleto, que desempena un papel importante en la organizacion a través del citoplasma (fig. 6-20). que desempena un papel importante en la organizacion estructuras y las actividades de la célula, PÁGINA NÚMERO 0: En los primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras que se extiende a través del citoplasma (fig. 6-20). El citoesqueleto, revelado la presencia del citoesqueleto, una red de

As you can see it repeats the same thing more than once.

I think it's because my pdf is split in two like this:

enter image description here

But it would be an inconvinience to split every page in half and create a longer pdf. Another problem could be that I OCR'd this pdf and maybe it's causing issues (this is why the text output is slightly different from the pdf but I'm fine with that).

So I'm looking for a way to check if the "highlights" list has repeated items and delete them. Or maybe check before they get added to the list and not add them. But I'm not that experienced in programming so I'm asking for your help!

Any help is appreciated!

And sorry for any bad english!

Edit 1:

I've now tried doing this:

...
def main(filepath: str) -> List:
    doc = fitz.open(filepath)

    highlights = []
    for page in doc:
        highlights += handle_page(page)
        highlights = set(highlights)
        highlights = list(highlights)
        document.add_paragraph(highlights)

    return highlights
...

But it doesn't work. It even changes the order of the items because it deletes stuff that was added first and I don't want that.

Edit 2:

I think I found what's giving me trouble.

I did print(sentences) before they get joined into "sentence" and this is what I get:

['En los primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte', 'primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados', 'pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia', 'en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras', 'microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras que se extiende a través del citoplasma (fig. 6-20). El citoesqueleto,', 'revelado la presencia del citoesqueleto, una red de fibras que se extiende a través del citoplasma (fig. 6-20). El citoesqueleto, que desempena un papel importante en la organizacion', 'a través del citoplasma (fig. 6-20). que desempena un papel importante en la organizacion estructuras y las actividades de la célula,']

As you can see the items inside "sentences" contain one another so even if I used set(sentences) it wouldn't work. The OCR had something to do with that I'm pretty sure.

So now I think I need to shift my focus into crossreferencing each item inside "sentences".

Like when doing A+B-(A∩B)=C. This means C wouldn't have duplicates and, if the order is correct, would make a comprihensible sentence. But I'm completely blank in if there's even a way to accomplish this.

I also learned this to eliminate dupes and still keep the order of a list: list(dict.fromkeys())

Edit 3:

Only ruunning this code:

def _parse_highlight(annot: fitz.Annot, wordlist: List[Tuple[float, float, float, float, str, int, int, int]]) -> str:
    points = annot.vertices
    quad_count = int(len(points) / 4)
    sentences = []
    for i in range(quad_count):
        # where the highlighted part is
        r = fitz.Quad(points[i * 4: i * 4 + 4]).rect

        words = [w for w in wordlist if fitz.Rect(w[:4]).intersects(r)]
        sentences.append(" ".join(w[4] for w in words))
    print(sentences)
    sentence = " ".join(sentences)
    print(sentence)
    return sentence


def handle_page(page):
    wordlist = page.getText("words")  # list of words on page
    wordlist.sort(key=lambda w: (w[3], w[0])) 
    separator = "PÁGINA NÚMERO " + str(page.number) + ": "

    highlights = []
    annot = page.firstAnnot
    while annot:
        if annot.type[0] == 8:
            highlights.append(separator)
            highlights.append(_parse_highlight(annot, wordlist))
        annot = annot.next
    return highlights


def main(filepath: str) -> List:
    doc = fitz.open(filepath)

    highlights = []
    for page in doc:
        highlights += handle_page(page)
    print(highlights)
    return highlights

main(example.pdf)

This is the only thing I higlighted:

enter image description here

Here's what the terminal says:

['El citoesqueleto es una red de fibras que organiza las estructuras', 'citoesqueleto es una red de que organiza las estructuras y las actividades', 'las estructuras y las actividades de la célula', 'En los primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte', 'primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados', 'pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia', 'en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras', 'microscopia 6ptica como en la microscopia revelado la presencia del citoesqueleto, extiende a través del citoplasma']

El citoesqueleto es una red de fibras que organiza las estructuras citoesqueleto es una red de que organiza las estructuras y las actividades las estructuras y las actividades de la célula En los primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras microscopia 6ptica como en la microscopia revelado la presencia del citoesqueleto, extiende a través del citoplasma

['PÁGINA NÚMERO 0: ', 'El citoesqueleto es una red de fibras que organiza las estructuras citoesqueleto es una red de que organiza las estructuras y las actividades las estructuras y las actividades de la célula En los primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras microscopia 6ptica como en la microscopia revelado la presencia del citoesqueleto, extiende a través del citoplasma']

Now I see that some text I didn't highlight is also being turned into a "sentences" item. Again, I believe this to be the OCR at fault or maybe I could tune the pymupdf better idk.


Solution

  • I've implemented another method of getting the 'sentences' list out of the pdf and now it works as intended.

    This is the code:

    from typing import List, Tuple
    
    import fitz
    import os
    from docx import Document
    
    _threshold_intersection = 0.9
    
    def _check_contain(r_word, points):
        r = fitz.Quad(points).rect
        r.intersect(r_word)
    
        if r.getArea() >= r_word.getArea() * _threshold_intersection:
            contain = True
        else:
            contain = False
        return contain
    
    
    def _parse_highlight(annot: fitz.Annot, wordlist: List[Tuple[float, float, float, float, str, int, int, int]]) -> str:
        quad_points = annot.vertices
        quad_count = int(len(quad_points) / 4)
        sentences = ['' for i in range(quad_count)]
    
        for i in range(quad_count):
            points = quad_points[i * 4: i * 4 + 4]
            words = [
                w for w in wordlist if
                _check_contain(fitz.Rect(w[:4]), points)
            ]
            sentences[i] = ' '.join(w[4] for w in words)
        sentence = ' '.join(sentences)
        document.add_paragraph(sentence)
        return sentence
    
    def handle_page(page):
        wordlist = page.getText("words")
        wordlist.sort(key=lambda w: (w[3], w[0]))
    
        highlights = []
        annot = page.firstAnnot
        while annot:
            if annot.type[0] == 8:
                highlights.append(_parse_highlight(annot, wordlist))
            annot = annot.next
    
        return highlights
    
    def main(filepath: str) -> List:
        doc = fitz.open(filepath)
    
        highlights = []
        for page in doc:
            document.add_paragraph("Page Number " + str(page.number+1) + ": ")
            highlights += handle_page(page)
    
        return highlights
    
    dir_files = [f for f in os.listdir(".") if os.path.isfile(os.path.join(".", f))]
    document = Document()
    for file in dir_files:
        if file.endswith('.pdf'):
            print('Working on converting: ' + file)
            main(file)
            document.save(file.replace(".pdf",".docx"))
    

    Got this new method here.