<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Design Tool with Fix for Text Selection and Precision</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            display: flex;
            height: 100vh;
            overflow: hidden;
        }

        #sidebar {
            width: 300px;
            background-color: #f0f0f0;
            padding: 15px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            display: flex;
            flex-direction: column;
        }

        #sidebar h2 {
            margin-bottom: 10px;
        }

        .toolbar-section {
            margin-bottom: 20px;
        }

        #canvas-container {
            flex: 1;
            display: flex;
            justify-content: center;
            align-items: center;
            background-color: #f9f9f9;
            position: relative;
            overflow: auto;
        }

        canvas {
            border: 1px solid black;
            background-color: white;
        }

        button, input, select {
            margin-bottom: 10px;
            padding: 5px;
        }

        select, input[type="number"], input[type="color"], input[type="range"] {
            width: 100%;
        }

        .shape-btn {
            cursor: pointer;
            margin-right: 10px;
        }

    </style>
</head>
<body>

    <div id="sidebar">
        <h2>Chọn kích thước khổ giấy</h2>
        <div class="toolbar-section">
            <select id="paperSize">
                <option value="A5">A5 (148 x 210 mm)</option>
                <option value="A4" selected>A4 (210 x 297 mm)</option>
                <option value="A3">A3 (297 x 420 mm)</option>
                <option value="custom">Tùy chọn</option>
            </select>

            <div id="customSizeInputs" style="display: none;">
                <input type="number" id="customWidth" placeholder="Chiều ngang (mm)">
                <input type="number" id="customHeight" placeholder="Chiều cao (mm)">
            </div>
        </div>

        <h2>Thành phần</h2>
        <div class="toolbar-section">
            <button id="addRect" class="shape-btn">Hình chữ nhật</button>
            <button id="addCircle" class="shape-btn">Hình tròn</button>
            <button id="addText" class="shape-btn">Văn bản</button>
            <input type="text" id="textInput" placeholder="Nhập văn bản">
        </div>

        <div class="toolbar-section">
            <h2>Chọn màu</h2>
            <input type="color" id="colorPicker" value="#000000">
        </div>

        <div class="toolbar-section">
            <h2>Zoom khổ giấy</h2>
            <input type="range" id="zoomSlider" min="0.5" max="2" step="0.1" value="1">
        </div>

        <div class="toolbar-section">
            <button id="undo">Hoàn tác</button>
            <button id="redo">Làm lại</button>
        </div>

        <div class="toolbar-section">
            <button id="exportSVG">Export SVG</button>
            <button id="exportPDF">Export PDF</button>
        </div>
    </div>

    <div id="canvas-container">
        <canvas id="designCanvas"></canvas>
    </div>

    <script>
        const canvas = document.getElementById('designCanvas');
        const ctx = canvas.getContext('2d');
        let shapes = [];
        let currentShape = null;
        let isDragging = false;
        let isResizing = false;
        let resizingCorner = null;
        let zoomLevel = 1;
        let selectedColor = '#000000';
        let canvasWidth = 210;
        let canvasHeight = 297;
        let mmToPxRatio = 3.7795275591;

        // Cập nhật kích thước canvas khi zoom và thay đổi khổ giấy
        function updateCanvasSize(width, height) {
            canvas.width = width * mmToPxRatio * zoomLevel;
            canvas.height = height * mmToPxRatio * zoomLevel;
            drawCanvas();
        }

        // Cập nhật khổ giấy
        document.getElementById('paperSize').addEventListener('change', function() {
            const size = this.value;
            if (size === 'A5') {
                canvasWidth = 148;
                canvasHeight = 210;
            } else if (size === 'A4') {
                canvasWidth = 210;
                canvasHeight = 297;
            } else if (size === 'A3') {
                canvasWidth = 297;
                canvasHeight = 420;
            } else {
                document.getElementById('customSizeInputs').style.display = 'block';
                return;
            }
            updateCanvasSize(canvasWidth, canvasHeight);
        });

        document.getElementById('customWidth').addEventListener('input', function() {
            canvasWidth = parseInt(this.value);
            updateCanvasSize(canvasWidth, canvasHeight);
        });

        document.getElementById('customHeight').addEventListener('input', function() {
            canvasHeight = parseInt(this.value);
            updateCanvasSize(canvasWidth, canvasHeight);
        });

        document.getElementById('colorPicker').addEventListener('input', function() {
            selectedColor = this.value;
        });

        document.getElementById('zoomSlider').addEventListener('input', function() {
            zoomLevel = parseFloat(this.value);
            updateCanvasSize(canvasWidth, canvasHeight);
        });

        class Shape {
            constructor(x, y, width, height, color = 'black', rotation = 0) {
                this.x = x;
                this.y = y;
                this.width = width;
                this.height = height;
                this.color = color;
                this.rotation = rotation; // Độ xoay
                this.selected = false;
                this.type = 'shape'; // Loại đối tượng: shape, text
            }

            draw() {
                ctx.save();
                ctx.translate(this.x + this.width / 2, this.y + this.height / 2);
                ctx.rotate((this.rotation * Math.PI) / 180); // Chuyển đổi độ sang radian
                ctx.fillStyle = this.color;

                if (this.type === 'shape') {
                    ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);
                } else if (this.type === 'text') {
                    ctx.font = `${this.height}px Arial`;
                    ctx.fillText(this.text, -this.width / 2, this.height / 4);
                }

                ctx.restore();

                if (this.selected) {
                    this.drawResizeHandles();
                }
            }

            drawResizeHandles() {
                const size = 8;
                ctx.fillStyle = 'blue';

                ctx.fillRect(this.x - size / 2, this.y - size / 2, size, size); // Top-left
                ctx.fillRect(this.x + this.width - size / 2, this.y - size / 2, size, size); // Top-right
                ctx.fillRect(this.x - size / 2, this.y + this.height - size / 2, size, size); // Bottom-left
                ctx.fillRect(this.x + this.width - size / 2, this.y + this.height - size / 2, size, size); // Bottom-right
            }

            contains(mx, my) {
                if (this.type === 'shape') {
                    return mx >= this.x && mx <= this.x + this.width && my >= this.y && my <= this.y + this.height;
                } else if (this.type === 'text') {
                    const textWidth = ctx.measureText(this.text).width;
                    return mx >= this.x && mx <= this.x + textWidth && my >= this.y && my <= this.y + this.height;
                }
                return false;
            }

            getResizeHandle(mx, my) {
                const size = 8;
                const halfSize = size / 2;
                if (mx >= this.x - halfSize && mx <= this.x + halfSize && my >= this.y - halfSize && my <= this.y + halfSize) {
                    return 'top-left';
                }
                if (mx >= this.x + this.width - halfSize && mx <= this.x + this.width + halfSize && my >= this.y - halfSize && my <= this.y + halfSize) {
                    return 'top-right';
                }
                if (mx >= this.x - halfSize && mx <= this.x + halfSize && my >= this.y + this.height - halfSize && my <= this.y + this.height + halfSize) {
                    return 'bottom-left';
                }
                if (mx >= this.x + this.width - halfSize && mx <= this.x + this.width + halfSize && my >= this.y + this.height - halfSize && my <= this.y + this.height + halfSize) {
                    return 'bottom-right';
                }
                return null;
            }

            resize(mx, my, corner) {
                switch (corner) {
                    case 'top-left':
                        this.width += this.x - mx;
                        this.height += this.y - my;
                        this.x = mx;
                        this.y = my;
                        break;
                    case 'top-right':
                        this.width = mx - this.x;
                        this.height += this.y - my;
                        this.y = my;
                        break;
                    case 'bottom-left':
                        this.width += this.x - mx;
                        this.height = my - this.y;
                        this.x = mx;
                        break;
                    case 'bottom-right':
                        this.width = mx - this.x;
                        this.height = my - this.y;
                        break;
                }
            }
        }

        // Vẽ lại canvas
        function drawCanvas() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            shapes.forEach(shape => shape.draw());
        }

        canvas.addEventListener('mousedown', function(e) {
            const mousePos = getMousePos(canvas, e);
            isDragging = false;
            isResizing = false;
            resizingCorner = null;

            for (let i = shapes.length - 1; i >= 0; i--) {
                if (shapes[i].contains(mousePos.x, mousePos.y)) {
                    currentShape = shapes[i];
                    const corner = currentShape.getResizeHandle(mousePos.x, mousePos.y);
                    if (corner) {
                        isResizing = true;
                        resizingCorner = corner;
                    } else {
                        isDragging = true;
                    }
                    currentShape.selected = true;
                    break;
                }
            }

            shapes.forEach(shape => {
                if (shape !== currentShape) {
                    shape.selected = false;
                }
            });

            drawCanvas();
        });

        canvas.addEventListener('mousemove', function(e) {
            const mousePos = getMousePos(canvas, e);

            if (isDragging && currentShape) {
                currentShape.x = mousePos.x - currentShape.width / 2;
                currentShape.y = mousePos.y - currentShape.height / 2;
                drawCanvas();
            }

            if (isResizing && currentShape) {
                currentShape.resize(mousePos.x, mousePos.y, resizingCorner);
                drawCanvas();
            }
        });

        canvas.addEventListener('mouseup', function() {
            isDragging = false;
            isResizing = false;
            resizingCorner = null;
        });

        function getMousePos(canvas, evt) {
            const rect = canvas.getBoundingClientRect();
            return {
                x: (evt.clientX - rect.left) / zoomLevel,
                y: (evt.clientY - rect.top) / zoomLevel
            };
        }

        document.getElementById('addRect').addEventListener('click', function() {
            const rect = new Shape(100, 100, 100, 50, selectedColor);
            shapes.push(rect);
            drawCanvas();
        });

        document.getElementById('addCircle').addEventListener('click', function() {
            const circle = new Shape(200, 200, 100, 100, selectedColor);
            shapes.push(circle);
            drawCanvas();
        });

        document.getElementById('addText').addEventListener('click', function() {
            const textInput = document.getElementById('textInput').value || 'Text';
            const textShape = new Shape(150, 150, 0, 0, selectedColor);
            textShape.text = textInput;
            textShape.height = 30; // Đặt chiều cao font chữ
            textShape.type = 'text';
            shapes.push(textShape);
            drawCanvas();
        });

        window.addEventListener('keydown', function(e) {
            if (e.key === 'Delete' && currentShape) {
                shapes = shapes.filter(shape => shape !== currentShape);
                currentShape = null;
                drawCanvas();
            }
        });

        // Xuất file SVG
        document.getElementById('exportSVG').addEventListener('click', function() {
            let svgContent = `<svg xmlns="http://www.w3.org/2000/svg" width="${canvas.width}" height="${canvas.height}">`;

            shapes.forEach(shape => {
                if (shape.text) {
                    svgContent += `<text x="${shape.x}" y="${shape.y}" font-size="20" fill="${shape.color}">${shape.text}</text>`;
                } else {
                    svgContent += `<rect x="${shape.x}" y="${shape.y}" width="${shape.width}" height="${shape.height}" fill="${shape.color}" />`;
                }
            });

            svgContent += `</svg>`;

            const blob = new Blob([svgContent], { type: 'image/svg+xml' });
            const link = document.createElement('a');
            link.href = URL.createObjectURL(blob);
            link.download = 'design.svg';
            link.click();
        });

        // Xuất file PDF
        document.getElementById('exportPDF').addEventListener('click', function() {
            const pdf = new jsPDF('landscape', 'pt', [canvas.width, canvas.height]);
            shapes.forEach(shape => {
                if (shape.text) {
                    pdf.setTextColor(shape.color);
                    pdf.setFontSize(20);
                    pdf.text(shape.text, shape.x, shape.y);
                } else {
                    pdf.setFillColor(shape.color);
                    pdf.rect(shape.x, shape.y, shape.width, shape.height, 'F');
                }
            });

            pdf.save('design.pdf');
        });

        updateCanvasSize(canvasWidth, canvasHeight);
    </script>

</body>
</html>
kiểm tra và cải tiến mã,