Text is positioned incorrectly (Tesseract.js)

I’m creating a image to text converter where the text in the image can be selected (like Live Text on iOS). I would like the generated text to be the same size and position as the original text in an image.

The idea is that the generated text will be transparent (It’s solid in the image for demonstration purposes) because I think it’ll be too complicated to match the text in the image exactly but at least the user will be able to select the text.

Result

Original Photo for Testing

Here’s my code

document.getElementById('imageUpload').addEventListener('change', function(event) {
    const file = event.target.files[0];
    if (file) {
        const reader = new FileReader();
        reader.onload = function(e) {
            const image = document.getElementById('uploadedImage');
            image.src = e.target.result;

            const progressBar = document.getElementById('progressBar');
            progressBar.style.display = 'block';

            Tesseract.recognize(
                image.src,
                'eng',
                {
                    logger: function(m) {
                        if (m.status === 'recognizing text') {
                            progressBar.value = m.progress;
                        }
                    }
                }
            ).then(({ data: { text, lines } }) => {
                progressBar.style.display = 'none';
                const textOverlay = document.getElementById('textOverlay');
                textOverlay.innerHTML = '';

                lines.forEach(line => {
                    const div = document.createElement('div');
                    div.textContent = line.text;
                    div.style.top = `${line.bbox.y0}px`;
                    div.style.left = `${line.bbox.x0}px`;
                    div.style.width = `${line.bbox.x1 - line.bbox.x0}px`;
                    div.style.height = `${line.bbox.y1 - line.bbox.y0}px`;
                    div.style.fontSize = `${line.bbox.y1 - line.bbox.y0}px`;
                    div.classList.add('text-line');
                    textOverlay.appendChild(div);
                });
            });
        };
        reader.readAsDataURL(file);
    }
});
body {
    font-family: 'Arial', sans-serif;
    background-color: #f4f4f9;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    color: #333;
}

header {
    background-color: #007BFF; /* Change to blue */
    color: white;
    padding: 20px 0;
    width: 100%;
    text-align: center;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    position: relative;
}

header h1 {
    margin: 0;
    font-size: 2.5em;
    font-weight: bold;
}

header::after {
    content: '';
    display: block;
    width: 50px;
    height: 4px;
    background-color: white;
    margin: 10px auto 0;
    border-radius: 2px;
}
main {
    margin: 20px;
    width: 90%;
    max-width: 800px;
    text-align: center;
    background: white;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

footer {
    background-color: #4CAF50;
    color: white;
    padding: 10px 0;
    width: 100%;
    text-align: center;
    position: fixed;
    bottom: 0;
}

#image-container {
    position: relative;
    display: inline-block;
    margin-top: 20px;
    border: 2px solid #ddd;
    border-radius: 5px;
    overflow: hidden;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

#image-container img {
    max-width: 100%;
    height: auto;
    display: block;
}

.text-overlay {
    position: absolute;
    top: 0;
    left: 0;
    color: transparent;
    white-space: pre;
    font-family: monospace;
    pointer-events: none;
    overflow: hidden;
    width: 100%;
    height: 100%;
}

.text-line {
    position: absolute;
    background: rgba(255, 255, 255, 0.7);
    color: black; /* Change to black to make text visible */
    pointer-events: auto;
    white-space: pre;
    border-radius: 3px;
    padding: 2px;
    font-size: 16px; /* Adjust font size as needed */
    line-height: 1.2; /* Adjust line height as needed */
}

#uploadedImage {
    user-select: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
}

#progressBar {
    width: 100%;
    height: 20px;
    border-radius: 10px;
    overflow: hidden;
    background-color: #ddd;
    margin-top: 20px;
}

#progressBar::-webkit-progress-bar {
    background-color: #ddd;
    border-radius: 10px;
}

#progressBar::-webkit-progress-value {
    background-color: #4CAF50;
    border-radius: 10px;
}

#progressBar::-moz-progress-bar {
    background-color: #4CAF50;
    border-radius: 10px;
}

.custom-file-upload {
    display: inline-block;
    padding: 10px 20px;
    cursor: pointer;
    background-color: #007BFF;
    color: white;
    border: none;
    border-radius: 5px;
    font-size: 16px;
    font-weight: bold;
    text-align: center;
    transition: background-color 0.3s ease;
}

.custom-file-upload:hover {
    background-color: #0056b3;
}

.custom-file-upload:active {
    background-color: #004494;
}

#imageUpload {
    display: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Selectable Text Overlay</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>Selectable Text Overlay</h1>
    </header>
    <main>
        <h2>Upload an Image</h2>
        <label for="imageUpload" class="custom-file-upload">
            Choose File
        </label>
        <input type="file" id="imageUpload" accept="image/*">
        <div id="image-container">
            <img id="uploadedImage" src="">
            <div id="textOverlay" class="text-overlay"></div>
        </div>
        <progress id="progressBar" value="0" max="1" style="width: 100%; display: none;"></progress>
    </main>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/tesseract.min.js"></script>
    <script src="script.js"></script>
</body>
</html>