{"id":550,"date":"2026-08-10T22:14:19","date_gmt":"2026-08-10T15:14:19","guid":{"rendered":"https:\/\/app.mitunet.com\/home\/?p=550"},"modified":"2026-08-11T11:15:31","modified_gmt":"2026-08-11T04:15:31","slug":"doi-duoi-anh","status":"publish","type":"post","link":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/","title":{"rendered":"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh mi\u1ec5n ph\u00ed"},"content":{"rendered":"<p>C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh online gi\u00fap b\u1ea1n chuy\u1ec3n \u0111\u1ed5i h\u00ecnh \u1ea3nh nhanh ch\u00f3ng m\u00e0 kh\u00f4ng c\u1ea7n c\u00e0i \u0111\u1eb7t ph\u1ea7n m\u1ec1m chuy\u00ean d\u1ee5ng. Ch\u1ec9 c\u1ea7n t\u1ea3i \u1ea3nh l\u00ean, l\u1ef1a ch\u1ecdn \u0111\u1ecbnh d\u1ea1ng mong mu\u1ed1n v\u00e0 th\u1ef1c hi\u1ec7n chuy\u1ec3n \u0111\u1ed5i, b\u1ea1n \u0111\u00e3 c\u00f3 th\u1ec3 s\u1eed d\u1ee5ng file \u1ea3nh m\u1edbi trong th\u1eddi gian ng\u1eafn. \u0110\u00e2y l\u00e0 gi\u1ea3i ph\u00e1p ti\u1ec7n l\u1ee3i khi c\u1ea7n chuy\u1ec3n JPG sang PNG, PNG sang JPG, WebP sang JPG ho\u1eb7c nhi\u1ec1u \u0111\u1ecbnh d\u1ea1ng kh\u00e1c.<\/p>\n\n        <div\n            class=\"itools-wrapper\"\n            id=\"itools_45217\"\n            data-tool=\"converter\"\n        >\n\n            \n                        <div class=\"itool-box\" data-module=\"converter\">\n\n            <div class=\"itool-title\">\n                <h2><span class=\"ez-toc-section\" id=\"%F0%9F%94%84_Chuyen_doi_dinh_dang\"><\/span>\ud83d\udd04 Chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng<span class=\"ez-toc-section-end\"><\/span><\/h2><div id=\"ez-toc-container\" class=\"ez-toc-v2_0_88 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">C\u00e1c N\u1ed9i Dung Ch\u00ednh<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #444444;color:#444444\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #444444;color:#444444\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 eztoc-toggle-hide-by-default' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"#\" data-href=\"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#%F0%9F%94%84_Chuyen_doi_dinh_dang\" >\ud83d\udd04 Chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"#\" data-href=\"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#%22escapeHtmlsheetName%22\" >\"+escapeHtml(sheetName)+\"<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"#\" data-href=\"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#Cach_doi_dinh_dang_anh_online\" >C\u00e1ch \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh online<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"#\" data-href=\"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#Khi_nao_nen_doi_JPG_sang_PNG\" >Khi n\u00e0o n\u00ean \u0111\u1ed5i JPG sang PNG?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"#\" data-href=\"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#Khi_nao_nen_chuyen_PNG_sang_JPG\" >Khi n\u00e0o n\u00ean chuy\u1ec3n PNG sang JPG?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"#\" data-href=\"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#Chuyen_WebP_sang_JPG_co_tac_dung_gi\" >Chuy\u1ec3n WebP sang JPG c\u00f3 t\u00e1c d\u1ee5ng g\u00ec?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"#\" data-href=\"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#Cong_cu_doi_dinh_dang_anh_online_co_can_cai_phan_mem_khong\" >C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh online c\u00f3 c\u1ea7n c\u00e0i ph\u1ea7n m\u1ec1m kh\u00f4ng?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"#\" data-href=\"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#Mot_so_luu_y_khi_chuyen_doi_dinh_dang_anh\" >M\u1ed9t s\u1ed1 l\u01b0u \u00fd khi chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"#\" data-href=\"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#Chuyen_doi_anh_nhanh_chong_ngay_tren_trinh_duyet\" >Chuy\u1ec3n \u0111\u1ed5i \u1ea3nh nhanh ch\u00f3ng ngay tr\u00ean tr\u00ecnh duy\u1ec7t<\/a><\/li><\/ul><\/nav><\/div>\n\n                <p>\u1ea2nh v\u00e0 m\u1ed9t s\u1ed1 file t\u00e0i li\u1ec7u \u0111\u01b0\u1ee3c x\u1eed l\u00fd tr\u1ef1c ti\u1ebfp tr\u00ean thi\u1ebft b\u1ecb.<\/p>\n            <\/div>\n\n            <div class=\"converter-mode-tabs\">\n                <button type=\"button\" class=\"converter-mode active\" data-convert-mode=\"image\">\n                    \ud83d\uddbc\ufe0f \u1ea2nh \u2192 \u1ea2nh\n                <\/button>\n\n                <button type=\"button\" class=\"converter-mode\" data-convert-mode=\"document\">\n                    \ud83d\udcc4 PDF \/ Office \u2192 \u1ea2nh\n                <\/button>\n            <\/div>\n\n            <div class=\"converter-mode-panel active\" data-convert-mode-panel=\"image\">\n\n                <label class=\"itool-upload\">\n                    <input\n                        type=\"file\"\n                        class=\"convert-files\"\n                        accept=\"image\/jpeg,image\/png,image\/webp,image\/avif,image\/bmp,image\/gif\"\n                        multiple\n                    >\n                    <span class=\"upload-icon\">\ud83d\udd04<\/span>\n                    <strong>Ch\u1ecdn \u1ea3nh<\/strong>\n                    <small>JPG, PNG, WebP, AVIF, BMP ho\u1eb7c GIF<\/small>\n                <\/label>\n\n                <div class=\"converter-options\">\n                    <label>\n                        Chuy\u1ec3n sang\n                        <select class=\"convert-format\">\n                            <option value=\"image\/webp\">WebP<\/option>\n                            <option value=\"image\/avif\">AVIF<\/option>\n                            <option value=\"image\/jpeg\">JPG<\/option>\n                            <option value=\"image\/png\">PNG<\/option>\n                            <option value=\"image\/bmp\">BMP<\/option>\n                        <\/select>\n                    <\/label>\n\n                    <label>\n                        Ch\u1ea5t l\u01b0\u1ee3ng\n                        <select class=\"convert-quality\">\n                            <option value=\"0.6\">60%<\/option>\n                            <option value=\"0.7\">70%<\/option>\n                            <option value=\"0.8\" selected>80%<\/option>\n                            <option value=\"0.9\">90%<\/option>\n                            <option value=\"0.95\">95%<\/option>\n                        <\/select>\n                    <\/label>\n                <\/div>\n\n                <div class=\"convert-list\"><\/div>\n\n                <button type=\"button\" class=\"itool-primary convert-start\">\n                    \ud83d\udd04 Chuy\u1ec3n \u0111\u1ed5i \u1ea3nh\n                <\/button>\n\n                <div class=\"itool-message\"><\/div>\n            <\/div>\n\n\n            <div class=\"converter-mode-panel\" data-convert-mode-panel=\"document\">\n\n                <div class=\"converter-notice\">\n                    <strong>\ud83d\udd12 C\u00f4ng c\u1ee5 chuy\u1ec3n c\u00e1c file Office th\u00e0nh h\u00ecnh \u1ea3nh<\/strong>\n                <\/div>\n\n                <label class=\"itool-upload document-upload\">\n                    <input\n                        type=\"file\"\n                        class=\"document-files\"\n                        accept=\".pdf,.docx,.doc,.xlsx,.xls,.pptx,.ppt,application\/pdf,application\/vnd.openxmlformats-officedocument.wordprocessingml.document,application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application\/vnd.openxmlformats-officedocument.presentationml.presentation\"\n                        multiple\n                    >\n                    <span class=\"upload-icon\">\ud83d\udcc4<\/span>\n                    <strong>Ch\u1ecdn PDF \/ Word \/ Excel \/ PowerPoint<\/strong>\n                    <small>PDF, DOCX, XLSX, PPTX v\u00e0 \u0111\u1ecbnh d\u1ea1ng c\u0169 DOC\/XLS\/PPT<\/small>\n                <\/label>\n\n                <div class=\"document-options\">\n\n                    <label>\n                        \u0110\u1ecbnh d\u1ea1ng \u1ea3nh\n                        <select class=\"document-output-format\">\n                            <option value=\"image\/png\" selected>PNG<\/option>\n                            <option value=\"image\/jpeg\">JPG<\/option>\n                            <option value=\"image\/webp\">WebP<\/option>\n                        <\/select>\n                    <\/label>\n\n                    <label>\n                        Ch\u1ea5t l\u01b0\u1ee3ng\n                        <select class=\"document-output-quality\">\n                            <option value=\"0.75\">75%<\/option>\n                            <option value=\"0.85\" selected>85%<\/option>\n                            <option value=\"0.95\">95%<\/option>\n                        <\/select>\n                    <\/label>\n\n                    <label class=\"document-page-limit-wrap\">\n                        Trang \/ Sheet\n                        <input\n                            type=\"text\"\n                            class=\"document-pages\"\n                            placeholder=\"V\u00ed d\u1ee5: all ho\u1eb7c 1,2,5\"\n                            value=\"all\"\n                        >\n                        <small class=\"field-help\">PDF\/DOCX\/PPTX: trang\/slides. Excel: sheet; all = t\u1ea5t c\u1ea3.<\/small>\n                    <\/label>\n\n                    <label class=\"document-scale-wrap\">\n                        \u0110\u1ed9 ph\u00e2n gi\u1ea3i\n                        <select class=\"document-scale\">\n                            <option value=\"1\">100%<\/option>\n                            <option value=\"1.5\" selected>150%<\/option>\n                            <option value=\"2\">200%<\/option>\n                        <\/select>\n                    <\/label>\n\n                <\/div>\n\n                <div class=\"document-list\"><\/div>\n\n                <button type=\"button\" class=\"itool-primary document-start\">\n                    \ud83d\udcc4 Chuy\u1ec3n file th\u00e0nh \u1ea3nh\n                <\/button>\n\n                <div class=\"itool-message\"><\/div>\n            <\/div>\n\n        <\/div>\n        \n            \n        <\/div>\n\n        <style>\n.itools-wrapper{\n    --it-primary:#111827;\n    --it-border:#e2e8f0;\n    --it-bg:#fff;\n    --it-soft:#f8fafc;\n    --it-text:#111827;\n    --it-muted:#64748b;\n    max-width:1100px;\n    margin:30px auto;\n    font-family:Arial,Helvetica,sans-serif;\n    color:var(--it-text)\n}\n.itools-wrapper *,.itools-wrapper *::before,.itools-wrapper *::after{box-sizing:border-box}\n.itools-tabs,.converter-mode-tabs,.editor-mode-tabs{\n    display:grid;\n    grid-template-columns:repeat(4,1fr);\n    gap:6px;\n    padding:6px;\n    margin-bottom:20px;\n    background:#f8fafc;\n    border:1px solid var(--it-border);\n    border-radius:14px\n}\n.converter-mode-tabs,.editor-mode-tabs{grid-template-columns:1fr 1fr}\n.itools-tab,.converter-mode,.editor-mode{\n    border:0;\n    background:transparent;\n    padding:14px 10px;\n    border-radius:10px;\n    cursor:pointer;\n    font-size:15px;\n    font-weight:600;\n    color:#64748b\n}\n.itools-tab:hover,.converter-mode:hover,.editor-mode:hover{background:#fff}\n.itools-tab.active,.converter-mode.active,.editor-mode.active{\n    background:#111827;\n    color:#fff\n}\n.itools-panel,.converter-mode-panel,.editor-mode-panel{display:none}\n.itools-panel.active,.converter-mode-panel.active,.editor-mode-panel.active{display:block}\n.itool-box{\n    background:#fff;\n    border:1px solid var(--it-border);\n    border-radius:18px;\n    padding:28px;\n    box-shadow:0 8px 30px rgba(15,23,42,.05)\n}\n.itool-title{text-align:center;margin-bottom:25px}\n.itool-title h2{margin:0 0 8px;font-size:27px}\n.itool-title p{margin:0;color:var(--it-muted)}\n.itool-upload{\n    min-height:170px;\n    border:2px dashed #cbd5e1;\n    border-radius:16px;\n    display:flex;\n    align-items:center;\n    justify-content:center;\n    flex-direction:column;\n    gap:7px;\n    cursor:pointer;\n    background:#fafafa;\n    transition:.2s\n}\n.itool-upload:hover,.itool-upload.dragover{\n    border-color:#111827;\n    background:#f1f5f9\n}\n.itool-upload input{display:none}\n.upload-icon{font-size:34px}\n.itool-upload strong{font-size:16px}\n.itool-upload small{color:#64748b;text-align:center}\n.small-upload{min-height:130px}\n.compress-options,.converter-options,.document-options,.batch-options{\n    display:grid;\n    grid-template-columns:repeat(2,1fr);\n    gap:15px;\n    margin:20px 0\n}\n.document-options{grid-template-columns:repeat(4,1fr)}\n.batch-options{grid-template-columns:repeat(3,1fr)}\n.itool-box label{\n    display:block;\n    font-size:14px;\n    font-weight:600;\n    color:#334155\n}\n.itool-box select,\n.itool-box input[type=number],\n.itool-box input[type=text]{\n    display:block;\n    width:100%;\n    margin-top:7px;\n    border:1px solid #cbd5e1;\n    border-radius:9px;\n    padding:11px;\n    background:#fff;\n    font-size:14px\n}\n.itool-box input[type=range]{width:100%;margin-top:12px}\n.itool-box input[type=color]{\n    display:block;\n    width:100%;\n    height:42px;\n    margin-top:7px;\n    padding:2px;\n    border:1px solid #cbd5e1;\n    border-radius:9px;\n    background:#fff\n}\n.field-help{\n    display:block;\n    margin-top:5px;\n    color:#94a3b8!important;\n    font-size:12px!important;\n    font-weight:400!important\n}\n.itool-primary{\n    width:100%;\n    border:0;\n    border-radius:10px;\n    padding:14px 18px;\n    background:#111827;\n    color:#fff;\n    font-size:15px;\n    font-weight:700;\n    cursor:pointer;\n    margin-top:20px\n}\n.itool-primary:hover{background:#000}\n.itool-primary:disabled{opacity:.6;cursor:not-allowed}\n.itool-secondary{\n    border:1px solid #cbd5e1;\n    background:#fff;\n    border-radius:9px;\n    padding:10px;\n    cursor:pointer;\n    font-weight:600\n}\n.itool-secondary:hover{background:#f8fafc}\n.itool-message{\n    margin-top:15px;\n    text-align:center;\n    color:#64748b;\n    font-size:14px\n}\n.compress-list,.convert-list,.document-list{margin-top:15px}\n.file-row{\n    display:flex;\n    align-items:center;\n    justify-content:space-between;\n    gap:12px;\n    padding:12px;\n    border:1px solid #e2e8f0;\n    border-radius:10px;\n    margin-bottom:8px;\n    background:#fff\n}\n.file-name{\n    overflow:hidden;\n    text-overflow:ellipsis;\n    white-space:nowrap\n}\n.file-size{color:#64748b;font-size:13px}\n.download-link{\n    display:inline-block;\n    margin-top:10px;\n    padding:9px 13px;\n    background:#111827;\n    color:#fff!important;\n    text-decoration:none;\n    border-radius:8px;\n    font-size:13px\n}\n.crop-editor{\n    display:grid;\n    grid-template-columns:minmax(0,1fr) 260px;\n    gap:20px;\n    margin-top:22px\n}\n.crop-stage{\n    position:relative;\n    min-height:450px;\n    background:#111827;\n    border-radius:12px;\n    overflow:hidden;\n    display:flex;\n    align-items:center;\n    justify-content:center;\n    touch-action:none\n}\n.crop-canvas{\n    display:block;\n    max-width:100%;\n    max-height:650px;\n    user-select:none;\n    pointer-events:none\n}\n.crop-selection{position:absolute;inset:0;pointer-events:none}\n.crop-shade{position:absolute;background:rgba(0,0,0,.58);pointer-events:none}\n.shade-top{left:0;right:0;top:0}\n.shade-left{left:0}\n.shade-right{right:0}\n.shade-bottom{left:0;right:0;bottom:0}\n.crop-box{\n    position:absolute;\n    border:2px solid #fff;\n    box-shadow:0 0 0 1px rgba(0,0,0,.4);\n    cursor:move;\n    pointer-events:auto;\n    touch-action:none\n}\n.crop-move-area{position:absolute;inset:10px;cursor:move}\n.crop-handle{\n    position:absolute;\n    width:13px;\n    height:13px;\n    background:#fff;\n    border:2px solid #111827;\n    border-radius:3px;\n    z-index:10\n}\n.crop-handle.nw{left:-7px;top:-7px;cursor:nwse-resize}\n.crop-handle.n{left:50%;top:-7px;transform:translateX(-50%);cursor:ns-resize}\n.crop-handle.ne{right:-7px;top:-7px;cursor:nesw-resize}\n.crop-handle.e{right:-7px;top:50%;transform:translateY(-50%);cursor:ew-resize}\n.crop-handle.se{right:-7px;bottom:-7px;cursor:nwse-resize}\n.crop-handle.s{left:50%;bottom:-7px;transform:translateX(-50%);cursor:ns-resize}\n.crop-handle.sw{left:-7px;bottom:-7px;cursor:nesw-resize}\n.crop-handle.w{left:-7px;top:50%;transform:translateY(-50%);cursor:ew-resize}\n.crop-controls{\n    display:flex;\n    flex-direction:column;\n    gap:15px;\n    padding:15px;\n    background:#f8fafc;\n    border:1px solid #e2e8f0;\n    border-radius:12px\n}\n.crop-control-title{font-size:17px;font-weight:700}\n.crop-zoom-title{font-size:14px;font-weight:600;margin-top:5px}\n.crop-buttons{display:grid;grid-template-columns:1fr 1fr;gap:8px}\n.check-line{display:flex!important;align-items:center;gap:8px}\n.check-line input{margin:0}\n.crop-info{\n    display:flex;\n    justify-content:space-between;\n    gap:15px;\n    padding:12px 0;\n    color:#64748b;\n    font-size:13px\n}\n.result-card{\n    margin-top:20px;\n    padding:18px;\n    background:#f8fafc;\n    border-radius:12px;\n    text-align:center\n}\n.result-card img{\n    max-width:100%;\n    max-height:500px;\n    display:block;\n    margin:0 auto 15px;\n    border-radius:8px\n}\n.watermark-upload-grid{\n    display:grid;\n    grid-template-columns:1fr 1fr;\n    gap:15px\n}\n.watermark-workspace{\n    display:grid;\n    grid-template-columns:minmax(0,1.5fr) minmax(270px,.7fr);\n    gap:25px;\n    margin-top:20px;\n    align-items:start\n}\n@media(min-width:751px){\n    .watermark-workspace{\n        height:min(650px,calc(100vh - 40px));\n        min-height:300px\n    }\n    .watermark-preview{\n        height:100%;\n        min-height:0\n    }\n    .watermark-settings{\n        height:100%;\n        max-height:100%;\n        overflow-y:auto;\n        overflow-x:hidden;\n        padding-right:5px;\n        box-sizing:border-box;\n        overscroll-behavior:contain;\n        min-height:0\n    }\n    .watermark-settings .text-style-buttons{display:flex;gap:6px;flex-wrap:wrap;margin-top:5px}\n.text-style-btn{min-width:36px;height:34px;padding:0 9px;border:1px solid #d1d5db;background:#fff;border-radius:8px;cursor:pointer;font-size:16px;line-height:1}\n.text-style-btn.active{background:#111827;color:#fff;border-color:#111827}\n.emoji-palette{\n        display:grid;\n        grid-template-columns:repeat(8,minmax(0,1fr));\n        width:100%;\n        max-width:100%;\n        box-sizing:border-box;\n        overflow:visible\n    }\n    .watermark-settings .emoji-choice{\n        min-width:0;\n        width:100%;\n        max-width:100%;\n        box-sizing:border-box;\n        display:flex;\n        align-items:center;\n        justify-content:center;\n        overflow:hidden\n    }\n}\n.watermark-workspace{\n    align-items:start\n}\n.watermark-preview{\n    min-height:300px;\n    height:min(650px,calc(100vh - 40px));\n    background:#e5e7eb;\n    border-radius:12px;\n    display:flex;\n    align-items:center;\n    justify-content:center;\n    overflow:hidden;\n    position:relative;\n    align-self:start;\n    touch-action:none;\n    box-sizing:border-box;\n    z-index:2\n}\n.watermark-canvas{\n    max-width:100%;\n    max-height:100%;\n    display:block\n}\n.watermark-drag-logo{\n    position:absolute;\n    border:2px dashed #111827;\n    cursor:move;\n    touch-action:none;\n    user-select:none;\n    line-height:0;\n    z-index:5\n}\n.watermark-logo-preview{\n    display:block;\n    width:100%;\n    height:100%;\n    object-fit:contain;\n    pointer-events:none;\n    user-select:none\n}\n.wm-resize-handle{\n    position:absolute;\n    width:15px;\n    height:15px;\n    right:-8px;\n    bottom:-8px;\n    background:#fff;\n    border:2px solid #111827;\n    border-radius:3px;\n    cursor:nwse-resize;\n    touch-action:none\n}\n.watermark-settings{\n    display:flex;\n    flex-direction:column;\n    gap:12px\n}\n.settings-section-title{\n    font-size:16px;\n    font-weight:800;\n    padding-top:5px;\n    border-top:1px solid #e2e8f0\n}\n.range-value,.opacity-value,.margin-value,\n.batch-size-value,.batch-opacity-value,.batch-margin-value{\n    display:block;\n    text-align:right;\n    color:#64748b;\n    font-size:12px\n}\n.editor-two-col{\n    display:grid;\n    grid-template-columns:1fr 1fr;\n    gap:10px\n}\n.editor-inline-buttons{\n    display:grid;\n    grid-template-columns:1fr 1fr;\n    gap:8px\n}\n.emoji-palette{\n    display:grid;\n    grid-template-columns:repeat(8,minmax(0,1fr));\n    gap:5px;\n    width:100%;\n    max-width:100%;\n    box-sizing:border-box;\n    overflow:hidden\n}\n.emoji-choice{\n    min-width:0!important;\n    width:100%!important;\n    max-width:100%!important;\n    min-height:42px!important;\n    box-sizing:border-box;\n    border:1px solid #e2e8f0!important;\n    background:#fff!important;\n    color:#111827!important;\n    -webkit-text-fill-color:#111827!important;\n    border-radius:8px;\n    padding:5px!important;\n    margin:0!important;\n    cursor:pointer!important;\n    font-family:\"Segoe UI Emoji\",\"Apple Color Emoji\",\"Noto Color Emoji\",\"Twemoji Mozilla\",\"Arial Unicode MS\",sans-serif!important;\n    font-size:24px!important;\n    font-style:normal!important;\n    font-weight:400!important;\n    line-height:1!important;\n    text-align:center!important;\n    text-indent:0!important;\n    text-transform:none!important;\n    letter-spacing:normal!important;\n    white-space:nowrap!important;\n    display:flex!important;\n    align-items:center!important;\n    justify-content:center!important;\n    visibility:visible!important;\n    opacity:1!important;\n    overflow:visible!important;\n    appearance:none!important;\n    -webkit-appearance:none!important;\n    text-shadow:none!important;\n    transform:none;\n}\n.emoji-choice:hover{background:#f8fafc}\n.editor-object-layer{\n    position:absolute;\n    inset:0;\n    pointer-events:none;\n    z-index:20!important;\n    overflow:visible;\n}\n.editor-object{\n    position:absolute;\n    transform:translate(-50%,-50%);\n    cursor:move;\n    user-select:none;\n    touch-action:none;\n    pointer-events:auto;\n    white-space:pre\n}\n.editor-object.selected{\n    outline:2px dashed #111827;\n    outline-offset:5px\n}\n.editor-object-resize-handle{\n    position:absolute;\n    width:14px;\n    height:14px;\n    right:-10px;\n    bottom:-10px;\n    box-sizing:border-box;\n    background:#fff;\n    border:2px solid #111827;\n    border-radius:50%;\n    cursor:nwse-resize;\n    touch-action:none;\n    z-index:10\n}\n.editor-object.text-object{\n    line-height:1.15;\n    text-align:center\n}\n.editor-object.emoji-object{\n    line-height:1\n}\n.converter-notice{\n    display:flex;\n    flex-direction:column;\n    gap:4px;\n    padding:14px;\n    background:#f8fafc;\n    border:1px solid #e2e8f0;\n    border-radius:10px;\n    margin-bottom:15px\n}\n.converter-notice span{\n    color:#64748b;\n    font-size:13px\n}\n.batch-file-list{\n    margin-top:15px;\n    max-height:300px;\n    overflow:auto\n}\n.batch-result{\n    margin-top:20px\n}\n.batch-result-grid{\n    display:grid;\n    grid-template-columns:repeat(3,1fr);\n    gap:10px\n}\n.batch-result-item{\n    padding:10px;\n    border:1px solid #e2e8f0;\n    border-radius:10px;\n    background:#f8fafc;\n    text-align:center\n}\n.batch-result-item img{\n    max-width:100%;\n    max-height:180px;\n    display:block;\n    margin:0 auto 8px;\n    border-radius:6px\n}\n.document-preview-host{\n    position:fixed;\n    left:-100000px;\n    top:-100000px;\n    width:1200px;\n    background:#fff;\n    z-index:-1\n}\n.document-render-page{\n    background:#fff;\n    box-sizing:border-box;\n    overflow:hidden;\n    margin:0;\n    padding:45px;\n    color:#111;\n    font-family:Arial,sans-serif;\n    width:1000px;\n    min-height:1414px\n}\n.document-render-page table{\n    width:100%;\n    border-collapse:collapse\n}\n.document-render-page td,\n.document-render-page th{\n    border:1px solid #999;\n    padding:5px;\n    font-size:14px\n}\n.document-render-page img{\n    max-width:100%\n}\n.document-render-page h1{font-size:30px}\n.document-render-page h2{font-size:25px}\n.document-render-page h3{font-size:21px}\n.document-render-page p{line-height:1.45}\n@media(max-width:750px){\n    .itools-wrapper{margin:15px auto}\n    .itool-box{padding:17px}\n    .itools-tabs{grid-template-columns:1fr 1fr}\n    .itool-title h2{font-size:23px}\n    .compress-options,\n    .converter-options,\n    .document-options,\n    .batch-options,\n    .crop-editor,\n    .watermark-workspace,\n    .watermark-upload-grid{grid-template-columns:1fr}\n    .crop-stage{min-height:320px}\n    .crop-info{flex-direction:column}\n    .file-row{flex-wrap:wrap}\n    .watermark-settings{\n        height:auto;\n        max-height:none;\n        overflow:visible\n        }\n    .watermark-preview{\n        position:relative;\n        height:auto;\n        min-height:300px\n    }\n    .watermark-canvas{\n        max-height:650px\n    }\n    .watermark-drag-logo{max-width:70%}\n    .emoji-palette{grid-template-columns:repeat(6,minmax(0,1fr))}\n    .batch-result-grid{grid-template-columns:1fr 1fr}\n}\n\n\/* =========================================================\n   V2 RESPONSIVE \/ MOBILE EDITOR FIXES\n   ========================================================= *\/\n.emoji-choice{\n    font-family:\"Segoe UI Emoji\",\"Apple Color Emoji\",\"Noto Color Emoji\",\"Twemoji Mozilla\",\"Arial Unicode MS\",sans-serif!important;\n    font-variant-emoji:emoji;\n    font-size:24px!important;\n    min-height:42px!important;\n    color:#111827!important;\n    -webkit-text-fill-color:#111827!important;\n    line-height:1!important;\n    visibility:visible!important;\n    opacity:1!important;\n}\n.emoji-choice.selected{background:#111827;color:#fff;border-color:#111827;transform:translateY(-1px)}\n.watermark-settings{overscroll-behavior:contain;}\n@media(max-width:750px){\n    .watermark-workspace{\n        grid-template-columns:1fr;\n        align-items:start;\n        gap:16px;\n    }\n    .watermark-preview{\n        position:sticky;\n        top:10px;\n        z-index:30;\n        height:min(48vh,420px);\n        min-height:230px;\n        max-height:420px;\n        overflow:hidden;\n        box-shadow:0 5px 18px rgba(0,0,0,.12);\n    }\n    .watermark-settings{\n        height:auto!important;\n        max-height:none!important;\n        overflow:visible!important;\n        min-height:0;\n        position:relative;\n        z-index:10;\n    }\n    .watermark-canvas{max-width:100%;max-height:100%;}\n    .watermark-drag-logo{max-width:70%;}\n    .emoji-palette{grid-template-columns:repeat(6,minmax(0,1fr));gap:6px;overflow:visible;}\n    .emoji-choice{font-size:22px;padding:7px 4px;min-height:40px;}\n}\n@media(max-width:750px) and (max-height:700px){\n    .watermark-preview{height:42vh;min-height:210px;max-height:330px;}\n}\n@media(min-width:751px){\n    .watermark-settings{overflow-y:auto;overflow-x:hidden;}\n}\n\/* Emoji rendering hardening: override common WordPress\/theme button rules. *\/\n.emoji-palette{\n    display:grid!important;\n    visibility:visible!important;\n    opacity:1!important;\n    position:relative;\n    z-index:25;\n    isolation:isolate;\n}\n.emoji-palette .emoji-choice{\n    visibility:visible!important;\n    opacity:1!important;\n    color:#111827!important;\n    -webkit-text-fill-color:#111827!important;\n    background:#fff!important;\n    font-family:\"Segoe UI Emoji\",\"Apple Color Emoji\",\"Noto Color Emoji\",\"Twemoji Mozilla\",\"Arial Unicode MS\",sans-serif!important;\n    font-size:24px!important;\n    line-height:1!important;\n    min-height:42px!important;\n    display:flex!important;\n}\n.emoji-palette .emoji-choice:hover,.emoji-palette .emoji-choice:focus{\n    color:#111827!important;\n    -webkit-text-fill-color:#111827!important;\n    background:#f8fafc!important;\n    outline:2px solid #cbd5e1;\n    outline-offset:1px;\n}\n.emoji-palette .emoji-choice.selected{\n    background:#111827!important;\n    color:#fff!important;\n    -webkit-text-fill-color:#fff!important;\n}\n@media(max-width:750px){\n    .emoji-palette .emoji-choice{font-size:24px!important;min-height:42px!important;padding:5px!important;}\n}\n\n\/* Emoji picker: use real image icons so WordPress emoji replacement cannot hide them. *\/\n.emoji-palette .emoji-choice .image-tools-emoji-icon,\n.emoji-palette .emoji-choice .image-tools-emoji-fallback{\n    display:block!important;\n    visibility:visible!important;\n    opacity:1!important;\n    pointer-events:none!important;\n}\n.emoji-palette .emoji-choice .image-tools-emoji-icon{\n    width:28px!important;height:28px!important;max-width:28px!important;max-height:28px!important;\n    object-fit:contain!important;border:0!important;background:transparent!important;\n}\n@media(max-width:750px){\n    .emoji-palette .emoji-choice .image-tools-emoji-icon{\n        width:28px!important;height:28px!important;max-width:28px!important;max-height:28px!important;\n    }\n}\n\n\/* =========================================================\n   V2.3 MOBILE EDITOR SCROLL + EMOJI HARD FIX\n   ========================================================= *\/\n@media(max-width:750px){\n    .watermark-workspace{\n        display:grid!important;\n        grid-template-columns:1fr!important;\n        grid-template-rows:minmax(220px,44vh) minmax(0,1fr)!important;\n        gap:12px!important;\n        height:calc(100svh - 24px)!important;\n        min-height:520px!important;\n        max-height:820px!important;\n        overflow:hidden!important;\n        align-items:stretch!important;\n    }\n    .watermark-preview{\n        position:relative!important;\n        inset:auto!important;\n        width:100%!important;\n        height:auto!important;\n        min-height:220px!important;\n        max-height:none!important;\n        overflow:hidden!important;\n        z-index:2!important;\n        align-self:stretch!important;\n        box-shadow:0 4px 14px rgba(0,0,0,.12)!important;\n    }\n    .watermark-settings{\n        grid-row:2!important;\n        height:auto!important;\n        min-height:0!important;\n        max-height:none!important;\n        overflow-y:auto!important;\n        overflow-x:hidden!important;\n        -webkit-overflow-scrolling:touch!important;\n        overscroll-behavior:contain!important;\n        touch-action:pan-y!important;\n        padding-right:4px!important;\n        position:relative!important;\n        z-index:10!important;\n    }\n}\n@media(max-width:750px) and (max-height:700px){\n    .watermark-workspace{\n        grid-template-rows:minmax(200px,40vh) minmax(0,1fr)!important;\n        min-height:500px!important;\n    }\n}\n\n\/* =========================================================\n   V4 EMOJI PICKER LAYOUT FIX\n   - Palette gets its own bounded scroll area.\n   - It can never overlap the controls below it.\n   - The parent settings panel remains independently scrollable.\n   - No high z-index on the palette.\n   ========================================================= *\/\n.emoji-palette{\n    display:grid!important;\n    grid-template-columns:repeat(8,minmax(0,1fr))!important;\n    gap:6px!important;\n    width:100%!important;\n    max-width:100%!important;\n    height:auto!important;\n    max-height:280px!important;\n    min-height:0!important;\n    box-sizing:border-box!important;\n    padding:2px!important;\n    margin:0!important;\n    visibility:visible!important;\n    opacity:1!important;\n    overflow-y:auto!important;\n    overflow-x:hidden!important;\n    position:relative!important;\n    z-index:1!important;\n    align-content:start!important;\n    overscroll-behavior:contain!important;\n    -webkit-overflow-scrolling:touch!important;\n}\n.emoji-palette .emoji-choice{\n    appearance:none!important;\n    -webkit-appearance:none!important;\n    display:flex!important;\n    align-items:center!important;\n    justify-content:center!important;\n    width:100%!important;\n    min-width:0!important;\n    min-height:42px!important;\n    height:42px!important;\n    padding:4px!important;\n    margin:0!important;\n    border:1px solid #dbe3ec!important;\n    border-radius:8px!important;\n    background:#fff!important;\n    color:#111827!important;\n    -webkit-text-fill-color:#111827!important;\n    opacity:1!important;\n    visibility:visible!important;\n    overflow:hidden!important;\n    cursor:pointer!important;\n    box-sizing:border-box!important;\n    flex:none!important;\n}\n.emoji-palette .emoji-choice img.image-tools-emoji-icon{\n    display:block!important;\n    width:30px!important;\n    height:30px!important;\n    max-width:30px!important;\n    max-height:30px!important;\n    min-width:30px!important;\n    min-height:30px!important;\n    object-fit:contain!important;\n    visibility:visible!important;\n    opacity:1!important;\n    pointer-events:none!important;\n    border:0!important;\n    background:transparent!important;\n    box-shadow:none!important;\n}\n.emoji-palette .emoji-choice .image-tools-emoji-fallback{\n    display:block!important;\n    font-family:\"Segoe UI Emoji\",\"Apple Color Emoji\",\"Noto Color Emoji\",\"Arial Unicode MS\",sans-serif!important;\n    font-size:27px!important;\n    line-height:1!important;\n    visibility:visible!important;\n    opacity:1!important;\n    pointer-events:none!important;\n}\n.emoji-palette .emoji-choice.selected{\n    background:#111827!important;\n    border-color:#111827!important;\n    color:#fff!important;\n    -webkit-text-fill-color:#fff!important;\n    transform:none!important;\n}\n\n\/* Desktop: keep the whole right settings column scrollable. *\/\n@media(min-width:751px){\n    .watermark-workspace{\n        height:min(650px,calc(100vh - 40px))!important;\n        min-height:300px!important;\n        align-items:stretch!important;\n    }\n    .watermark-settings{\n        height:100%!important;\n        max-height:100%!important;\n        min-height:0!important;\n        overflow-y:auto!important;\n        overflow-x:hidden!important;\n        padding-right:8px!important;\n        box-sizing:border-box!important;\n        overscroll-behavior:contain!important;\n    }\n}\n\n\/* Mobile: preview is a fixed-height first row; tools have their own scroll. *\/\n@media(max-width:750px){\n    .watermark-workspace{\n        display:grid!important;\n        grid-template-columns:1fr!important;\n        grid-template-rows:minmax(210px,42vh) minmax(0,1fr)!important;\n        gap:12px!important;\n        height:calc(100svh - 24px)!important;\n        min-height:500px!important;\n        max-height:900px!important;\n        overflow:hidden!important;\n        align-items:stretch!important;\n    }\n    .watermark-preview{\n        grid-row:1!important;\n        position:relative!important;\n        width:100%!important;\n        height:100%!important;\n        min-height:210px!important;\n        max-height:none!important;\n        overflow:hidden!important;\n        z-index:2!important;\n    }\n    .watermark-settings{\n        grid-row:2!important;\n        height:100%!important;\n        min-height:0!important;\n        max-height:none!important;\n        overflow-y:auto!important;\n        overflow-x:hidden!important;\n        -webkit-overflow-scrolling:touch!important;\n        overscroll-behavior:contain!important;\n        touch-action:pan-y!important;\n        padding-right:6px!important;\n        position:relative!important;\n        z-index:3!important;\n    }\n    .emoji-palette{\n        grid-template-columns:repeat(6,minmax(0,1fr))!important;\n        max-height:230px!important;\n        gap:6px!important;\n    }\n    .emoji-palette .emoji-choice{\n        min-height:40px!important;\n        height:40px!important;\n    }\n}\n@media(max-width:750px) and (max-height:700px){\n    .watermark-workspace{\n        grid-template-rows:minmax(190px,38vh) minmax(0,1fr)!important;\n        min-height:480px!important;\n    }\n    .emoji-palette{max-height:200px!important;}\n}\n\n\/* =========================================================\n   V5 FINAL EMOJI \/ SETTINGS SCROLL FIX\n   - Do NOT create a nested scrollbar inside the emoji palette.\n   - The entire tools panel (.watermark-settings) is the only\n     vertical scrolling area on desktop and mobile.\n   - The emoji list expands naturally, so no rows are clipped\n     or covered by the controls below.\n   ========================================================= *\/\n.emoji-palette{\n    display:grid!important;\n    grid-template-columns:repeat(8,minmax(0,1fr))!important;\n    gap:6px!important;\n    width:100%!important;\n    max-width:100%!important;\n    height:auto!important;\n    max-height:none!important;\n    min-height:0!important;\n    box-sizing:border-box!important;\n    padding:2px 2px 6px!important;\n    margin:0!important;\n    overflow:visible!important;\n    position:relative!important;\n    z-index:auto!important;\n    align-content:start!important;\n    isolation:auto!important;\n}\n.emoji-palette .emoji-choice{\n    position:relative!important;\n    z-index:1!important;\n    display:flex!important;\n    align-items:center!important;\n    justify-content:center!important;\n    width:100%!important;\n    min-width:0!important;\n    max-width:100%!important;\n    min-height:42px!important;\n    height:42px!important;\n    padding:4px!important;\n    margin:0!important;\n    box-sizing:border-box!important;\n    overflow:hidden!important;\n    flex:none!important;\n}\n.emoji-palette .emoji-choice img.image-tools-emoji-icon{\n    display:block!important;\n    width:30px!important;\n    height:30px!important;\n    min-width:30px!important;\n    min-height:30px!important;\n    max-width:30px!important;\n    max-height:30px!important;\n    object-fit:contain!important;\n    visibility:visible!important;\n    opacity:1!important;\n    pointer-events:none!important;\n    border:0!important;\n    background:transparent!important;\n    box-shadow:none!important;\n}\n.emoji-palette .emoji-choice .image-tools-emoji-fallback{\n    display:block!important;\n    font-family:\"Segoe UI Emoji\",\"Apple Color Emoji\",\"Noto Color Emoji\",\"Arial Unicode MS\",sans-serif!important;\n    font-size:27px!important;\n    line-height:1!important;\n    visibility:visible!important;\n    opacity:1!important;\n    pointer-events:none!important;\n}\n.emoji-palette .emoji-choice.selected{\n    background:#111827!important;\n    border-color:#111827!important;\n    color:#fff!important;\n    -webkit-text-fill-color:#fff!important;\n    transform:none!important;\n}\n\n\/* Desktop: the settings panel is the sole scroll container. *\/\n@media(min-width:751px){\n    .watermark-workspace{\n        height:min(650px,calc(100vh - 40px))!important;\n        min-height:300px!important;\n        align-items:stretch!important;\n        overflow:visible!important;\n    }\n    .watermark-settings{\n        height:100%!important;\n        max-height:100%!important;\n        min-height:0!important;\n        overflow-y:auto!important;\n        overflow-x:hidden!important;\n        overscroll-behavior:contain!important;\n        -webkit-overflow-scrolling:touch!important;\n        padding-right:8px!important;\n        box-sizing:border-box!important;\n    }\n}\n\n\/* Mobile: preview stays in the first row; the entire tools panel\n   in the second row scrolls as one continuous area. *\/\n@media(max-width:750px){\n    .watermark-workspace{\n        display:grid!important;\n        grid-template-columns:1fr!important;\n        grid-template-rows:minmax(210px,42vh) minmax(0,1fr)!important;\n        gap:12px!important;\n        height:calc(100svh - 24px)!important;\n        min-height:500px!important;\n        max-height:900px!important;\n        overflow:hidden!important;\n        align-items:stretch!important;\n    }\n    .watermark-preview{\n        grid-row:1!important;\n        position:relative!important;\n        width:100%!important;\n        height:100%!important;\n        min-height:210px!important;\n        max-height:none!important;\n        overflow:hidden!important;\n        z-index:2!important;\n    }\n    .watermark-settings{\n        grid-row:2!important;\n        height:100%!important;\n        min-height:0!important;\n        max-height:none!important;\n        overflow-y:auto!important;\n        overflow-x:hidden!important;\n        -webkit-overflow-scrolling:touch!important;\n        overscroll-behavior:contain!important;\n        touch-action:pan-y!important;\n        padding:0 6px 18px 0!important;\n        position:relative!important;\n        z-index:3!important;\n        box-sizing:border-box!important;\n    }\n    .emoji-palette{\n        grid-template-columns:repeat(6,minmax(0,1fr))!important;\n        gap:6px!important;\n        max-height:none!important;\n        overflow:visible!important;\n    }\n    .emoji-palette .emoji-choice{\n        min-height:40px!important;\n        height:40px!important;\n    }\n}\n@media(max-width:750px) and (max-height:700px){\n    .watermark-workspace{\n        grid-template-rows:minmax(190px,38vh) minmax(0,1fr)!important;\n        min-height:480px!important;\n    }\n}\n\n\n\n\/* =========================================================\n   V6 EMOJI GRID FINAL FIX\n   - Every emoji button is a true square.\n   - The palette participates in normal document flow.\n   - No emoji row can overlap the controls below it.\n   - Only .watermark-settings is a vertical scroll container.\n   ========================================================= *\/\n.emoji-palette{\n    display:grid!important;\n    grid-template-columns:repeat(8,minmax(0,1fr))!important;\n    grid-auto-rows:auto!important;\n    gap:6px!important;\n    width:100%!important;\n    height:auto!important;\n    min-height:0!important;\n    max-height:none!important;\n    margin:0!important;\n    padding:2px 2px 8px!important;\n    box-sizing:border-box!important;\n    overflow:visible!important;\n    position:relative!important;\n    z-index:1!important;\n    align-items:start!important;\n    align-content:start!important;\n}\n\n.emoji-palette > .emoji-choice{\n    position:relative!important;\n    display:flex!important;\n    align-items:center!important;\n    justify-content:center!important;\n    width:100%!important;\n    height:auto!important;\n    min-width:0!important;\n    min-height:0!important;\n    max-width:none!important;\n    max-height:none!important;\n    aspect-ratio:1 \/ 1!important;\n    box-sizing:border-box!important;\n    padding:2px!important;\n    margin:0!important;\n    border:1px solid #dbe3ec!important;\n    border-radius:9px!important;\n    background:#fff!important;\n    overflow:hidden!important;\n    flex:none!important;\n    line-height:1!important;\n    font-size:24px!important;\n}\n\n.emoji-palette > .emoji-choice > .image-tools-emoji-icon{\n    display:block!important;\n    width:72%!important;\n    height:72%!important;\n    min-width:0!important;\n    min-height:0!important;\n    max-width:100%!important;\n    max-height:100%!important;\n    object-fit:contain!important;\n    margin:0!important;\n    padding:0!important;\n    border:0!important;\n    box-shadow:none!important;\n    background:transparent!important;\n    pointer-events:none!important;\n}\n\n.emoji-palette > .emoji-choice > .image-tools-emoji-fallback{\n    display:flex!important;\n    align-items:center!important;\n    justify-content:center!important;\n    width:100%!important;\n    height:100%!important;\n    min-width:0!important;\n    min-height:0!important;\n    margin:0!important;\n    padding:0!important;\n    font-family:\"Segoe UI Emoji\",\"Apple Color Emoji\",\"Noto Color Emoji\",\"Arial Unicode MS\",sans-serif!important;\n    font-size:clamp(18px,2.2vw,28px)!important;\n    line-height:1!important;\n    white-space:nowrap!important;\n    pointer-events:none!important;\n}\n\n\/* Make sure the controls after the palette remain in normal flow. *\/\n.emoji-palette + *{\n    position:relative!important;\n    z-index:2!important;\n}\n\n@media(min-width:751px){\n    .emoji-palette{\n        grid-template-columns:repeat(8,minmax(0,1fr))!important;\n    }\n    .watermark-settings{\n        overflow-y:auto!important;\n        overflow-x:hidden!important;\n    }\n}\n\n@media(max-width:750px){\n    .emoji-palette{\n        grid-template-columns:repeat(6,minmax(0,1fr))!important;\n        gap:6px!important;\n        max-height:none!important;\n        overflow:visible!important;\n    }\n    .emoji-palette > .emoji-choice{\n        aspect-ratio:1 \/ 1!important;\n        min-height:0!important;\n        height:auto!important;\n        font-size:22px!important;\n    }\n    .watermark-settings{\n        overflow-y:auto!important;\n        overflow-x:hidden!important;\n        -webkit-overflow-scrolling:touch!important;\n    }\n}\n\n\n\/* =========================================================\n   V7 STABLE EMOJI LAYOUT\n   ========================================================= *\/\n.emoji-palette-shell{\n    display:block!important;width:100%!important;max-width:100%!important;\n    height:auto!important;min-height:0!important;max-height:none!important;\n    margin:0!important;padding:0!important;overflow:visible!important;\n    position:relative!important;clear:both!important;flex:0 0 auto!important;\n}\n.emoji-palette-shell .emoji-palette{\n    display:grid!important;grid-template-columns:repeat(8,minmax(0,1fr))!important;\n    grid-auto-flow:row!important;grid-auto-rows:auto!important;\n    align-items:start!important;align-content:start!important;justify-items:stretch!important;\n    gap:6px!important;width:100%!important;max-width:100%!important;\n    height:auto!important;min-height:0!important;max-height:none!important;\n    margin:0!important;padding:2px!important;overflow:visible!important;\n    position:static!important;box-sizing:border-box!important;\n}\n.emoji-palette-shell .emoji-palette > .emoji-choice{\n    display:flex!important;position:relative!important;width:100%!important;\n    max-width:100%!important;min-width:0!important;height:auto!important;\n    min-height:0!important;aspect-ratio:1 \/ 1!important;padding:3px!important;\n    margin:0!important;border:1px solid #dbe3ec!important;border-radius:9px!important;\n    box-sizing:border-box!important;align-items:center!important;justify-content:center!important;\n    overflow:hidden!important;line-height:1!important;white-space:normal!important;flex:none!important;\n}\n.emoji-palette-shell .emoji-palette > .emoji-choice img.image-tools-emoji-icon{\n    display:block!important;width:78%!important;height:78%!important;max-width:78%!important;\n    max-height:78%!important;min-width:0!important;min-height:0!important;object-fit:contain!important;\n    margin:0!important;padding:0!important;border:0!important;box-shadow:none!important;pointer-events:none!important;\n}\n.emoji-palette-shell .emoji-palette > .emoji-choice .image-tools-emoji-fallback{\n    display:block!important;width:auto!important;height:auto!important;max-width:100%!important;\n    max-height:100%!important;font-size:clamp(18px,5vw,28px)!important;line-height:1!important;\n    margin:0!important;padding:0!important;pointer-events:none!important;\n}\n.emoji-controls-row{\n    display:grid!important;grid-template-columns:minmax(0,1fr) minmax(0,1fr)!important;\n    gap:10px!important;width:100%!important;height:auto!important;min-height:0!important;\n    margin:0!important;padding:0!important;position:relative!important;clear:both!important;flex:0 0 auto!important;\n}\n.emoji-controls-row > label,.emoji-controls-row input{min-width:0!important;position:relative!important;}\n.emoji-controls-row ~ .itool-secondary{position:relative!important;clear:both!important;flex:0 0 auto!important;}\n.watermark-settings > *{position:relative;flex-shrink:0;}\n@media(min-width:751px){\n    .watermark-workspace{height:min(650px,calc(100vh - 40px))!important;min-height:300px!important;overflow:visible!important;align-items:stretch!important;}\n    .watermark-settings{height:100%!important;min-height:0!important;max-height:100%!important;overflow-y:auto!important;overflow-x:hidden!important;overscroll-behavior:contain!important;-webkit-overflow-scrolling:touch!important;padding-right:8px!important;}\n    .emoji-palette-shell .emoji-palette{grid-template-columns:repeat(8,minmax(0,1fr))!important;}\n}\n@media(max-width:750px){\n    .watermark-workspace{display:grid!important;grid-template-columns:minmax(0,1fr)!important;grid-template-rows:minmax(210px,42vh) minmax(0,1fr)!important;gap:12px!important;height:calc(100svh - 24px)!important;min-height:500px!important;max-height:900px!important;overflow:hidden!important;align-items:stretch!important;}\n    .watermark-preview{grid-row:1!important;width:100%!important;height:100%!important;min-height:210px!important;max-height:none!important;position:relative!important;overflow:hidden!important;z-index:2!important;}\n    .watermark-settings{grid-row:2!important;width:100%!important;height:100%!important;min-height:0!important;max-height:none!important;overflow-y:auto!important;overflow-x:hidden!important;overscroll-behavior:contain!important;-webkit-overflow-scrolling:touch!important;touch-action:pan-y!important;padding:0 6px 18px 0!important;position:relative!important;z-index:3!important;}\n    .emoji-palette-shell .emoji-palette{grid-template-columns:repeat(6,minmax(0,1fr))!important;gap:6px!important;}\n}\n@media(max-width:420px){.emoji-palette-shell .emoji-palette{grid-template-columns:repeat(6,minmax(0,1fr))!important;gap:5px!important;padding:2px!important;}}\n@media(max-width:750px) and (max-height:700px){.watermark-workspace{grid-template-rows:minmax(190px,38vh) minmax(0,1fr)!important;min-height:480px!important;}}\n\n\n\/* =========================================================\n   V8 IMAGE TRANSFORM + CROP\n   ========================================================= *\/\n.editor-transform-buttons,\n.editor-crop-buttons{display:grid;grid-template-columns:1fr 1fr;gap:8px}\n.editor-transform-status{\n    display:flex;flex-wrap:wrap;justify-content:space-between;gap:6px 12px;\n    padding:8px 10px;border:1px solid #e2e8f0;border-radius:9px;\n    background:#f8fafc;color:#475569;font-size:12px\n}\n.editor-transform-status strong{color:#111827}\n.editor-crop-help{display:block}\n.editor-crop-layer{\n    position:absolute;inset:0;z-index:15!important;pointer-events:none;\n    display:none;overflow:hidden;touch-action:none\n}\n.editor-crop-layer.active{display:block;z-index:40!important}\n.editor-crop-box{\n    position:absolute;left:5%;top:5%;width:90%;height:90%;\n    border:2px solid #fff;box-shadow:0 0 0 9999px rgba(15,23,42,.48),0 0 0 1px rgba(15,23,42,.85);\n    box-sizing:border-box;pointer-events:auto;touch-action:none;cursor:move\n}\n.editor-crop-box::before,.editor-crop-box::after{\n    content:\"\";position:absolute;pointer-events:none\n}\n.editor-crop-box::before{\n    inset:33.333% 0;border-top:1px solid rgba(255,255,255,.55);border-bottom:1px solid rgba(255,255,255,.55)\n}\n.editor-crop-box::after{\n    inset:0 33.333%;border-left:1px solid rgba(255,255,255,.55);border-right:1px solid rgba(255,255,255,.55)\n}\n.editor-crop-handle{\n    position:absolute;width:14px;height:14px;border:2px solid #111827;background:#fff;border-radius:3px;\n    box-sizing:border-box;pointer-events:auto;z-index:2\n}\n.editor-crop-handle.n{left:50%;top:-8px;transform:translateX(-50%);cursor:ns-resize}\n.editor-crop-handle.ne{right:-8px;top:-8px;cursor:nesw-resize}\n.editor-crop-handle.e{right:-8px;top:50%;transform:translateY(-50%);cursor:ew-resize}\n.editor-crop-handle.se{right:-8px;bottom:-8px;cursor:nwse-resize}\n.editor-crop-handle.s{left:50%;bottom:-8px;transform:translateX(-50%);cursor:ns-resize}\n.editor-crop-handle.sw{left:-8px;bottom:-8px;cursor:nesw-resize}\n.editor-crop-handle.w{left:-8px;top:50%;transform:translateY(-50%);cursor:ew-resize}\n.editor-crop-handle.nw{left:-8px;top:-8px;cursor:nwse-resize}\n.editor-crop-layer:not(.active){visibility:hidden}\n.watermark-preview.crop-mode .watermark-canvas{cursor:default}\n@media(max-width:750px){\n    .editor-transform-buttons,.editor-crop-buttons{grid-template-columns:1fr 1fr}\n    .editor-crop-handle{width:18px;height:18px}\n}\n\n<\/style>\n\n<script>\n(function(){\n\"use strict\";\n\n\/* =========================================================\n   GLOBAL HELPERS\n   ========================================================= *\/\n\nfunction formatBytes(bytes){\n    if(!bytes) return \"0 B\";\n    var u=[\"B\",\"KB\",\"MB\",\"GB\"], i=Math.min(\n        Math.floor(Math.log(bytes)\/Math.log(1024)),\n        u.length-1\n    );\n    return (bytes\/Math.pow(1024,i)).toFixed(i===0?0:2)+\" \"+u[i];\n}\n\nfunction extensionFromMime(type){\n    return ({\n        \"image\/jpeg\":\"jpg\",\n        \"image\/png\":\"png\",\n        \"image\/webp\":\"webp\",\n        \"image\/avif\":\"avif\",\n        \"image\/bmp\":\"bmp\"\n    })[type] || \"png\";\n}\n\nfunction loadImage(file){\n    return new Promise(function(resolve,reject){\n        var maxBytes=25*1024*1024;\n        var maxPixels=40000000;\n\n        if(!file || !\/^image\\\/\/i.test(file.type)){\n            reject(new Error(\"\u0110\u1ecbnh d\u1ea1ng \u1ea3nh kh\u00f4ng h\u1ee3p l\u1ec7.\"));\n            return;\n        }\n\n        if(file.size>maxBytes){\n            reject(new Error(\"\u1ea2nh v\u01b0\u1ee3t qu\u00e1 gi\u1edbi h\u1ea1n 25 MB.\"));\n            return;\n        }\n\n        var url=URL.createObjectURL(file);\n        var img=new Image();\n\n        img.onload=function(){\n            URL.revokeObjectURL(url);\n\n            if(!img.naturalWidth || !img.naturalHeight){\n                reject(new Error(\"\u1ea2nh kh\u00f4ng h\u1ee3p l\u1ec7.\"));\n                return;\n            }\n\n            if(img.naturalWidth*img.naturalHeight>maxPixels){\n                reject(new Error(\"\u1ea2nh c\u00f3 k\u00edch th\u01b0\u1edbc qu\u00e1 l\u1edbn. Gi\u1edbi h\u1ea1n 40 megapixel.\"));\n                return;\n            }\n\n            resolve(img);\n        };\n\n        img.onerror=function(){\n            URL.revokeObjectURL(url);\n            reject(new Error(\"Kh\u00f4ng th\u1ec3 \u0111\u1ecdc \u1ea3nh ho\u1eb7c \u0111\u1ecbnh d\u1ea1ng ch\u01b0a \u0111\u01b0\u1ee3c tr\u00ecnh duy\u1ec7t h\u1ed7 tr\u1ee3.\"));\n        };\n\n        img.src=url;\n    });\n}\n\nfunction canvasToBlob(canvas,type,quality){\n    return new Promise(function(resolve){\n        canvas.toBlob(\n            resolve,\n            type,\n            type===\"image\/png\" || type===\"image\/bmp\" ? undefined : quality\n        );\n    });\n}\n\nfunction bmpBlob(canvas){\n    var w=canvas.width,h=canvas.height;\n    var ctx=canvas.getContext(\"2d\");\n    var data=ctx.getImageData(0,0,w,h).data;\n    var rowSize=Math.floor((24*w+31)\/32)*4;\n    var pixelSize=rowSize*h;\n    var offset=54;\n    var total=offset+pixelSize;\n    var buf=new ArrayBuffer(total);\n    var dv=new DataView(buf);\n\n    function w16(p,v){dv.setUint16(p,v,true)}\n    function w32(p,v){dv.setUint32(p,v,true)}\n\n    w16(0,0x4d42);\n    w32(2,total);\n    w16(6,0);\n    w16(8,0);\n    w32(10,offset);\n    w32(14,40);\n    w32(18,w);\n    w32(22,h);\n    w16(26,1);\n    w16(28,24);\n    w32(30,0);\n    w32(34,pixelSize);\n    w32(38,2835);\n    w32(42,2835);\n    w32(46,0);\n    w32(50,0);\n\n    var p=offset;\n\n    for(var y=h-1;y>=0;y--){\n        for(var x=0;x<w;x++){\n            var i=(y*w+x)*4;\n            dv.setUint8(p++,data[i+2]);\n            dv.setUint8(p++,data[i+1]);\n            dv.setUint8(p++,data[i]);\n        }\n        while((p-offset)%rowSize)p++;\n    }\n\n    return new Blob([buf],{type:\"image\/bmp\"});\n}\n\nfunction makeBlob(canvas,type,quality){\n    if(type===\"image\/bmp\"){\n        return Promise.resolve(bmpBlob(canvas));\n    }\n    return canvasToBlob(canvas,type,quality);\n}\n\nfunction canEncode(type){\n    var c=document.createElement(\"canvas\");\n    try{\n        return !!c.toDataURL(type).startsWith(\"data:\"+type);\n    }catch(e){\n        return false;\n    }\n}\n\nfunction ensureCanvasSize(width,height,label){\n    var w=Math.ceil(Number(width)||0);\n    var h=Math.ceil(Number(height)||0);\n    var maxPixels=40000000;\n    var maxSide=32767;\n\n    if(w<1 || h<1){\n        throw new Error((label||\"\u1ea2nh\")+\" c\u00f3 k\u00edch th\u01b0\u1edbc kh\u00f4ng h\u1ee3p l\u1ec7.\");\n    }\n    if(w>maxSide || h>maxSide){\n        throw new Error((label||\"\u1ea2nh\")+\" v\u01b0\u1ee3t gi\u1edbi h\u1ea1n chi\u1ec1u d\u00e0i canvas c\u1ee7a tr\u00ecnh duy\u1ec7t.\");\n    }\n    if(w*h>maxPixels){\n        throw new Error((label||\"\u1ea2nh\")+\" qu\u00e1 l\u1edbn \u0111\u1ec3 x\u1eed l\u00fd an to\u00e0n tr\u00ean tr\u00ecnh duy\u1ec7t. Vui l\u00f2ng gi\u1ea3m \u0111\u1ed9 ph\u00e2n gi\u1ea3i ho\u1eb7c ch\u1ecdn scale th\u1ea5p h\u01a1n.\");\n    }\n    return {width:w,height:h};\n}\n\nfunction canvasPixelCount(canvas){\n    return (canvas.width||0)*(canvas.height||0);\n}\n\nfunction escapeHtml(text){\n    var d=document.createElement(\"div\");\n    d.textContent=text;\n    return d.innerHTML;\n}\n\nfunction setupDragDrop(upload){\n    if(!upload)return;\n\n    var input=upload.querySelector('input[type=\"file\"]');\n    if(!input)return;\n\n    [\"dragenter\",\"dragover\"].forEach(function(name){\n        upload.addEventListener(name,function(e){\n            e.preventDefault();\n            e.stopPropagation();\n            upload.classList.add(\"dragover\");\n        });\n    });\n\n    [\"dragleave\",\"drop\"].forEach(function(name){\n        upload.addEventListener(name,function(e){\n            e.preventDefault();\n            e.stopPropagation();\n            upload.classList.remove(\"dragover\");\n        });\n    });\n\n    upload.addEventListener(\"drop\",function(e){\n        var files=Array.from((e.dataTransfer&&e.dataTransfer.files)||[]);\n        if(!files.length)return;\n\n        try{\n            var dt=new DataTransfer();\n            if(input.multiple){\n                files.forEach(function(file){dt.items.add(file);});\n            }else{\n                dt.items.add(files[0]);\n            }\n            input.files=dt.files;\n            input.dispatchEvent(new Event(\"change\",{bubbles:true}));\n        }catch(err){\n            \/* M\u1ed9t s\u1ed1 browser c\u0169 kh\u00f4ng cho g\u00e1n input.files. Khi \u0111\u00f3\n             * ng\u01b0\u1eddi d\u00f9ng v\u1eabn c\u00f3 th\u1ec3 d\u00f9ng n\u00fat ch\u1ecdn file b\u00ecnh th\u01b0\u1eddng. *\/\n            console.warn(\"Drag & drop kh\u00f4ng th\u1ec3 g\u00e1n file v\u00e0o input:\",err);\n        }\n    });\n}\n\nfunction downloadBlob(blob,name){\n    var url=URL.createObjectURL(blob);\n    var a=document.createElement(\"a\");\n    a.href=url;\n    a.download=name;\n    document.body.appendChild(a);\n    a.click();\n    a.remove();\n    setTimeout(function(){URL.revokeObjectURL(url)},30000);\n}\n\nfunction parseSelection(value,max){\n    value=(value||\"all\").trim().toLowerCase();\n\n    if(value===\"all\" || !value){\n        var all=[];\n        for(var i=1;i<=max;i++)all.push(i);\n        return all;\n    }\n\n    if(!\/^\\d+(?:\\s*-\\s*\\d+)?(?:\\s*,\\s*\\d+(?:\\s*-\\s*\\d+)?)*$\/.test(value)){\n        throw new Error(\"Danh s\u00e1ch trang\/slide\/sheet kh\u00f4ng h\u1ee3p l\u1ec7. V\u00ed d\u1ee5: all, 1,2,5 ho\u1eb7c 1-5.\");\n    }\n\n    var out=[];\n    value.split(\",\").forEach(function(part){\n        part=part.trim();\n        if(part.indexOf(\"-\")>=0){\n            var a=part.split(\"-\");\n            var from=parseInt(a[0],10);\n            var to=parseInt(a[1],10);\n            if(from>to){var tmp=from;from=to;to=tmp;}\n            if(from<1 || to>max){\n                throw new Error(\"Trang\/slide\/sheet ph\u1ea3i n\u1eb1m trong kho\u1ea3ng 1 \u0111\u1ebfn \"+max+\".\");\n            }\n            for(var n=from;n<=to;n++){\n                if(out.indexOf(n)<0)out.push(n);\n            }\n        }else{\n            var p=parseInt(part,10);\n            if(p<1 || p>max){\n                throw new Error(\"Trang\/slide\/sheet \"+p+\" kh\u00f4ng t\u1ed3n t\u1ea1i. Ph\u1ea1m vi h\u1ee3p l\u1ec7: 1 \u0111\u1ebfn \"+max+\".\");\n            }\n            if(out.indexOf(p)<0)out.push(p);\n        }\n    });\n\n    if(!out.length){\n        throw new Error(\"Kh\u00f4ng c\u00f3 trang\/slide\/sheet h\u1ee3p l\u1ec7 \u0111\u1ec3 x\u1eed l\u00fd.\");\n    }\n\n    return out.sort(function(a,b){return a-b;});\n}\n\nfunction sleep(ms){\n    return new Promise(function(resolve){setTimeout(resolve,ms)});\n}\n\nfunction loadScriptOnce(src,testFn){\n    return new Promise(function(resolve,reject){\n        try{\n            if(testFn && testFn()){\n                resolve();\n                return;\n            }\n        }catch(e){}\n\n        var existing=document.querySelector('script[data-itools-src=\"'+src+'\"]');\n\n        if(existing){\n            existing.addEventListener(\"load\",function(){resolve()});\n            existing.addEventListener(\"error\",function(){reject(new Error(\"Kh\u00f4ng t\u1ea3i \u0111\u01b0\u1ee3c th\u01b0 vi\u1ec7n.\"))});\n            return;\n        }\n\n        var s=document.createElement(\"script\");\n        s.src=src;\n        s.async=true;\n        s.dataset.itoolsSrc=src;\n\n        s.onload=function(){resolve()};\n        s.onerror=function(){reject(new Error(\"Kh\u00f4ng t\u1ea3i \u0111\u01b0\u1ee3c th\u01b0 vi\u1ec7n: \"+src))};\n\n        document.head.appendChild(s);\n    });\n}\n\n\n\/* =========================================================\n   TABS\n   ========================================================= *\/\n\nfunction initTabs(wrapper){\n    var tabs=wrapper.querySelectorAll(\".itools-tab\");\n    var panels=wrapper.querySelectorAll(\".itools-panel\");\n\n    tabs.forEach(function(tab){\n        tab.addEventListener(\"click\",function(){\n            var target=tab.dataset.tab;\n\n            tabs.forEach(function(x){x.classList.remove(\"active\")});\n            panels.forEach(function(x){x.classList.remove(\"active\")});\n\n            tab.classList.add(\"active\");\n\n            var p=wrapper.querySelector('[data-panel=\"'+target+'\"]');\n            if(p)p.classList.add(\"active\");\n        });\n    });\n}\n\nfunction initInnerTabs(box,buttonSelector,panelSelector,dataName){\n    var buttons=box.querySelectorAll(buttonSelector);\n    var panels=box.querySelectorAll(panelSelector);\n\n    buttons.forEach(function(btn){\n        btn.addEventListener(\"click\",function(){\n            var target=btn.dataset[dataName];\n\n            buttons.forEach(function(x){x.classList.remove(\"active\")});\n            panels.forEach(function(x){x.classList.remove(\"active\")});\n\n            btn.classList.add(\"active\");\n\n            var p=box.querySelector(\n                panelSelector+'[data-'+dataName.replace(\/([A-Z])\/g,\"-$1\").toLowerCase()+'-panel=\"'+target+'\"]'\n            );\n\n            if(p)p.classList.add(\"active\");\n        });\n    });\n}\n\n\n\/* =========================================================\n   FILE LIST\n   ========================================================= *\/\n\nfunction renderFileList(list,files){\n    list.innerHTML=\"\";\n\n    files.forEach(function(file){\n        var row=document.createElement(\"div\");\n        row.className=\"file-row\";\n\n        row.innerHTML=\n            '<span class=\"file-name\">'+escapeHtml(file.name)+'<\/span>'+\n            '<span class=\"file-size\">'+formatBytes(file.size)+'<\/span>';\n\n        list.appendChild(row);\n    });\n}\n\n\n\/* =========================================================\n   COMPRESSOR\n   ========================================================= *\/\n\nfunction initCompressor(box){\n    var input=box.querySelector(\".compress-files\");\n    var list=box.querySelector(\".compress-list\");\n    var button=box.querySelector(\".compress-start\");\n    var quality=box.querySelector(\".compress-quality\");\n    var output=box.querySelector(\".compress-output\");\n    var message=box.querySelector(\".itool-message\");\n    var files=[];\n\n    input.addEventListener(\"change\",function(){\n        files=Array.from(input.files||[]);\n        renderFileList(list,files);\n    });\n\n    button.addEventListener(\"click\",async function(){\n        if(!files.length){\n            message.textContent=\"Vui l\u00f2ng ch\u1ecdn \u00edt nh\u1ea5t m\u1ed9t \u1ea3nh.\";\n            return;\n        }\n\n        var type=output.value;\n\n        if(type===\"image\/avif\"&&!canEncode(type)){\n            message.textContent=\"Tr\u00ecnh duy\u1ec7t n\u00e0y ch\u01b0a h\u1ed7 tr\u1ee3 xu\u1ea5t AVIF. H\u00e3y ch\u1ecdn WebP, JPG, PNG ho\u1eb7c BMP.\";\n            return;\n        }\n\n        button.disabled=true;\n        message.textContent=\"\u0110ang n\u00e9n \u1ea3nh...\";\n        list.innerHTML=\"\";\n\n        for(var file of files){\n            try{\n                var img=await loadImage(file);\n                var canvas=document.createElement(\"canvas\");\n\n                canvas.width=img.naturalWidth;\n                canvas.height=img.naturalHeight;\n\n                var ctx=canvas.getContext(\"2d\");\n\n                if(type===\"image\/jpeg\"){\n                    ctx.fillStyle=\"#fff\";\n                    ctx.fillRect(0,0,canvas.width,canvas.height);\n                }\n\n                ctx.drawImage(img,0,0);\n\n                var blob=await makeBlob(canvas,type,parseFloat(quality.value));\n\n                if(!blob)throw new Error(\"Kh\u00f4ng t\u1ea1o \u0111\u01b0\u1ee3c file.\");\n\n                var ext=extensionFromMime(type);\n                var name=file.name.replace(\/\\.[^\/.]+$\/,\"\");\n                var newName=name+\"-compressed.\"+ext;\n                var saving=Math.round((1-blob.size\/file.size)*100);\n\n                var row=document.createElement(\"div\");\n                row.className=\"file-row\";\n\n                var info=document.createElement(\"span\");\n                info.className=\"file-size\";\n                info.innerHTML=\n                    \"<strong>\"+escapeHtml(newName)+\"<\/strong><br>\"+\n                    formatBytes(file.size)+\" \u2192 \"+formatBytes(blob.size)+\" (\"+saving+\"%)\";\n\n                var link=document.createElement(\"a\");\n                link.className=\"download-link\";\n                link.textContent=\"\u2b07\ufe0f T\u1ea3i xu\u1ed1ng\";\n                link.href=URL.createObjectURL(blob);\n                link.download=newName;\n\n                row.appendChild(info);\n                row.appendChild(link);\n                list.appendChild(row);\n\n            }catch(e){\n                console.error(e);\n\n                var er=document.createElement(\"div\");\n                er.className=\"file-row\";\n                er.textContent=\"Kh\u00f4ng th\u1ec3 x\u1eed l\u00fd: \"+file.name;\n                list.appendChild(er);\n            }\n        }\n\n        message.textContent=\"\u0110\u00e3 x\u1eed l\u00fd \"+files.length+\" \u1ea3nh.\";\n        button.disabled=false;\n    });\n\n    setupDragDrop(input.closest(\".itool-upload\"));\n}\n\n\n\/* =========================================================\n   CROPPER\n   ========================================================= *\/\n\nfunction initCropper(box){\n    var input=box.querySelector(\".crop-file\");\n    var stage=box.querySelector(\".crop-stage\");\n    var canvas=box.querySelector(\".crop-canvas\");\n    var selection=box.querySelector(\".crop-selection\");\n    var cropBox=box.querySelector(\".crop-box\");\n    var ow=box.querySelector(\".crop-output-width\");\n    var oh=box.querySelector(\".crop-output-height\");\n    var lock=box.querySelector(\".crop-lock-ratio\");\n    var format=box.querySelector(\".crop-format\");\n    var zoomInput=box.querySelector(\".crop-zoom\");\n    var reset=box.querySelector(\".crop-reset\");\n    var center=box.querySelector(\".crop-center\");\n    var button=box.querySelector(\".crop-start\");\n    var result=box.querySelector(\".crop-result\");\n    var message=box.querySelector(\".itool-message\");\n    var infoSel=box.querySelector(\".crop-info-selection\");\n    var infoImg=box.querySelector(\".crop-info-image\");\n\n    var image=null;\n    var imageScale=1;\n    var imageLeft=0;\n    var imageTop=0;\n    var imageDisplayWidth=0;\n    var imageDisplayHeight=0;\n    var crop={x:0,y:0,width:0,height:0};\n    var drag=null;\n    var initialRatio=1;\n\n    function layout(){\n        if(!image)return;\n\n        var maxW=Math.max(100,stage.clientWidth-20);\n        var maxH=Math.min(650,Math.max(260,stage.clientHeight-20));\n\n        imageScale=Math.min(\n            maxW\/image.naturalWidth,\n            maxH\/image.naturalHeight,\n            1\n        );\n\n        imageDisplayWidth=image.naturalWidth*imageScale;\n        imageDisplayHeight=image.naturalHeight*imageScale;\n\n        canvas.width=Math.round(imageDisplayWidth);\n        canvas.height=Math.round(imageDisplayHeight);\n\n        canvas.style.width=canvas.width+\"px\";\n        canvas.style.height=canvas.height+\"px\";\n\n        imageLeft=(stage.clientWidth-imageDisplayWidth)\/2;\n        imageTop=(stage.clientHeight-imageDisplayHeight)\/2;\n\n        drawCanvas();\n        clampCrop();\n        updateUI();\n    }\n\n    function drawCanvas(){\n        var ctx=canvas.getContext(\"2d\");\n        ctx.clearRect(0,0,canvas.width,canvas.height);\n        ctx.drawImage(image,0,0,canvas.width,canvas.height);\n    }\n\n    function setupCrop(){\n        if(!image)return;\n\n        var z=parseFloat(zoomInput.value)||1;\n        var w=imageDisplayWidth\/z;\n        var h=imageDisplayHeight\/z;\n\n        w=Math.max(30,Math.min(w,imageDisplayWidth));\n        h=Math.max(30,Math.min(h,imageDisplayHeight));\n\n        crop.width=w;\n        crop.height=h;\n        crop.x=imageLeft+(imageDisplayWidth-w)\/2;\n        crop.y=imageTop+(imageDisplayHeight-h)\/2;\n\n        initialRatio=w\/h;\n        updateUI();\n    }\n\n    function clampCrop(){\n        crop.width=Math.min(crop.width,imageDisplayWidth);\n        crop.height=Math.min(crop.height,imageDisplayHeight);\n\n        crop.x=Math.max(\n            imageLeft,\n            Math.min(crop.x,imageLeft+imageDisplayWidth-crop.width)\n        );\n\n        crop.y=Math.max(\n            imageTop,\n            Math.min(crop.y,imageTop+imageDisplayHeight-crop.height)\n        );\n    }\n\n    function updateUI(){\n        if(!image)return;\n\n        clampCrop();\n\n        cropBox.style.left=crop.x+\"px\";\n        cropBox.style.top=crop.y+\"px\";\n        cropBox.style.width=crop.width+\"px\";\n        cropBox.style.height=crop.height+\"px\";\n\n        selection.style.display=\"block\";\n\n        var top=crop.y;\n        var left=crop.x;\n        var right=stage.clientWidth-(crop.x+crop.width);\n        var bottom=stage.clientHeight-(crop.y+crop.height);\n\n        selection.querySelector(\".shade-top\").style.height=top+\"px\";\n\n        selection.querySelector(\".shade-left\").style.top=top+\"px\";\n        selection.querySelector(\".shade-left\").style.width=left+\"px\";\n        selection.querySelector(\".shade-left\").style.height=crop.height+\"px\";\n\n        selection.querySelector(\".shade-right\").style.top=top+\"px\";\n        selection.querySelector(\".shade-right\").style.width=right+\"px\";\n        selection.querySelector(\".shade-right\").style.height=crop.height+\"px\";\n\n        selection.querySelector(\".shade-bottom\").style.height=bottom+\"px\";\n\n        infoSel.textContent=\n            \"V\u00f9ng ch\u1ecdn: \"+\n            Math.round(crop.width\/imageScale)+\n            \" \u00d7 \"+\n            Math.round(crop.height\/imageScale)+\n            \" px\";\n\n        infoImg.textContent=\n            \"\u1ea2nh g\u1ed1c: \"+\n            image.naturalWidth+\n            \" \u00d7 \"+\n            image.naturalHeight+\n            \" px\";\n    }\n\n    function point(e){\n        var r=stage.getBoundingClientRect();\n\n        return {\n            x:e.clientX-r.left,\n            y:e.clientY-r.top\n        };\n    }\n\n    function startDrag(e,mode){\n        if(!image)return;\n\n        e.preventDefault();\n\n        var p=point(e);\n\n        drag={\n            mode:mode,\n            sx:p.x,\n            sy:p.y,\n            orig:{\n                x:crop.x,\n                y:crop.y,\n                w:crop.width,\n                h:crop.height\n            }\n        };\n\n        if(mode===\"move\"){\n            initialRatio=crop.width\/crop.height;\n        }\n    }\n\n    function moveDrag(e){\n        if(!drag)return;\n\n        e.preventDefault();\n\n        var p=point(e);\n        var dx=p.x-drag.sx;\n        var dy=p.y-drag.sy;\n        var o=drag.orig;\n        var min=30;\n\n        var left=o.x;\n        var top=o.y;\n        var right=o.x+o.w;\n        var bottom=o.y+o.h;\n\n        if(drag.mode===\"move\"){\n            left=o.x+dx;\n            top=o.y+dy;\n\n            crop.x=Math.max(\n                imageLeft,\n                Math.min(left,imageLeft+imageDisplayWidth-o.w)\n            );\n\n            crop.y=Math.max(\n                imageTop,\n                Math.min(top,imageTop+imageDisplayHeight-o.h)\n            );\n\n            updateUI();\n            return;\n        }\n\n        var m=30;\n\n        if(drag.mode.indexOf(\"e\")>=0){\n            right=Math.max(\n                o.x+m,\n                Math.min(o.x+o.w+dx,imageLeft+imageDisplayWidth)\n            );\n        }\n\n        if(drag.mode.indexOf(\"w\")>=0){\n            left=Math.min(\n                o.x+o.w-m,\n                Math.max(o.x+dx,imageLeft)\n            );\n        }\n\n        if(drag.mode.indexOf(\"s\")>=0){\n            bottom=Math.max(\n                o.y+m,\n                Math.min(o.y+o.h+dy,imageTop+imageDisplayHeight)\n            );\n        }\n\n        if(drag.mode.indexOf(\"n\")>=0){\n            top=Math.min(\n                o.y+o.h-m,\n                Math.max(o.y+dy,imageTop)\n            );\n        }\n\n        if(lock.checked){\n            var ratio=initialRatio||o.w\/o.h;\n\n            if(\n                drag.mode.indexOf(\"e\")>=0 ||\n                drag.mode.indexOf(\"w\")>=0\n            ){\n                var nw=right-left;\n                var nh=nw\/ratio;\n\n                if(drag.mode.indexOf(\"n\")>=0){\n                    top=bottom-nh;\n                }else{\n                    bottom=top+nh;\n                }\n            }else{\n                var nh2=bottom-top;\n                var nw2=nh2*ratio;\n\n                if(drag.mode.indexOf(\"w\")>=0){\n                    left=right-nw2;\n                }else{\n                    right=left+nw2;\n                }\n            }\n\n            if(left<imageLeft){\n                left=imageLeft;\n                right=left+(bottom-top)*ratio;\n            }\n\n            if(right>imageLeft+imageDisplayWidth){\n                right=imageLeft+imageDisplayWidth;\n                left=right-(bottom-top)*ratio;\n            }\n\n            if(top<imageTop){\n                top=imageTop;\n                bottom=top+(right-left)\/ratio;\n            }\n\n            if(bottom>imageTop+imageDisplayHeight){\n                bottom=imageTop+imageDisplayHeight;\n                top=bottom-(right-left)\/ratio;\n            }\n        }\n\n        crop.x=left;\n        crop.y=top;\n        crop.width=Math.max(m,right-left);\n        crop.height=Math.max(m,bottom-top);\n\n        updateUI();\n    }\n\n    document.addEventListener(\"pointermove\",moveDrag);\n    document.addEventListener(\"pointerup\",function(){drag=null});\n\n    cropBox.querySelectorAll(\".crop-handle\").forEach(function(h){\n        h.addEventListener(\"pointerdown\",function(e){\n            startDrag(e,h.dataset.handle);\n        });\n    });\n\n    cropBox.querySelector(\".crop-move-area\").addEventListener(\n        \"pointerdown\",\n        function(e){startDrag(e,\"move\")}\n    );\n\n    input.addEventListener(\"change\",async function(){\n        var file=input.files&&input.files[0];\n\n        if(!file)return;\n\n        try{\n            image=await loadImage(file);\n            infoImg.textContent=\n                \"\u1ea2nh g\u1ed1c: \"+\n                image.naturalWidth+\n                \" \u00d7 \"+\n                image.naturalHeight+\n                \" px\";\n\n            layout();\n            setupCrop();\n\n        }catch(e){\n            message.textContent=e.message;\n        }\n    });\n\n    window.addEventListener(\"resize\",function(){\n        if(image)layout();\n    });\n\n    reset.addEventListener(\"click\",function(){\n        if(!image)return;\n\n        zoomInput.value=\"1\";\n        layout();\n        setupCrop();\n        message.textContent=\"\u0110\u00e3 reset v\u00f9ng c\u1eaft.\";\n    });\n\n    center.addEventListener(\"click\",function(){\n        if(!image)return;\n\n        crop.x=imageLeft+(imageDisplayWidth-crop.width)\/2;\n        crop.y=imageTop+(imageDisplayHeight-crop.height)\/2;\n        updateUI();\n    });\n\n    zoomInput.addEventListener(\"input\",function(){\n        if(!image)return;\n        setupCrop();\n    });\n\n    ow.addEventListener(\"input\",function(){\n        if(lock.checked&&crop.width){\n            var r=crop.width\/crop.height;\n            var v=parseInt(ow.value,10);\n\n            if(v)oh.value=Math.round(v\/r);\n        }\n    });\n\n    oh.addEventListener(\"input\",function(){\n        if(lock.checked&&crop.height){\n            var r=crop.width\/crop.height;\n            var v=parseInt(oh.value,10);\n\n            if(v)ow.value=Math.round(v*r);\n        }\n    });\n\n    button.addEventListener(\"click\",async function(){\n        if(!image){\n            message.textContent=\"Vui l\u00f2ng ch\u1ecdn \u1ea3nh.\";\n            return;\n        }\n\n        var tw=parseInt(ow.value,10);\n        var th=parseInt(oh.value,10);\n\n        if(!tw||!th){\n            message.textContent=\"K\u00edch th\u01b0\u1edbc xu\u1ea5t kh\u00f4ng h\u1ee3p l\u1ec7.\";\n            return;\n        }\n\n        var type=format.value;\n\n        if(type===\"image\/avif\"&&!canEncode(type)){\n            message.textContent=\"Tr\u00ecnh duy\u1ec7t ch\u01b0a h\u1ed7 tr\u1ee3 xu\u1ea5t AVIF.\";\n            return;\n        }\n\n        var sx=(crop.x-imageLeft)\/imageScale;\n        var sy=(crop.y-imageTop)\/imageScale;\n        var sw=crop.width\/imageScale;\n        var sh=crop.height\/imageScale;\n\n        var out=document.createElement(\"canvas\");\n        out.width=tw;\n        out.height=th;\n\n        var ctx=out.getContext(\"2d\");\n\n        if(type===\"image\/jpeg\"){\n            ctx.fillStyle=\"#fff\";\n            ctx.fillRect(0,0,tw,th);\n        }\n\n        ctx.imageSmoothingEnabled=true;\n        ctx.imageSmoothingQuality=\"high\";\n\n        ctx.drawImage(\n            image,\n            sx,sy,sw,sh,\n            0,0,tw,th\n        );\n\n        var blob=await makeBlob(out,type,.9);\n\n        if(!blob){\n            message.textContent=\"Kh\u00f4ng th\u1ec3 t\u1ea1o \u1ea3nh.\";\n            return;\n        }\n\n        var url=URL.createObjectURL(blob);\n        var ext=extensionFromMime(type);\n\n        result.innerHTML=\n            '<div class=\"result-card\">'+\n            '<img decoding=\"async\" src=\"'+url+'\" alt=\"\u1ea2nh \u0111\u00e3 c\u1eaft\">'+\n            '<strong>'+tw+\" \u00d7 \"+th+\" px<\/strong><br>\"+\n            '<span>'+formatBytes(blob.size)+'<\/span>'+\n            '<\/div>';\n\n        var link=document.createElement(\"a\");\n        link.className=\"download-link\";\n        link.textContent=\"\u2b07\ufe0f T\u1ea3i \u1ea3nh xu\u1ed1ng\";\n        link.href=url;\n        link.download=\"cropped-image.\"+ext;\n\n        result.querySelector(\".result-card\").appendChild(link);\n\n        message.textContent=\"\u0110\u00e3 c\u1eaft \u1ea3nh th\u00e0nh c\u00f4ng.\";\n    });\n\n    setupDragDrop(input.closest(\".itool-upload\"));\n}\n\n\n\/* =========================================================\n   IMAGE CONVERTER\n   ========================================================= *\/\n\nfunction initConverter(box){\n    var modeButtons=box.querySelectorAll(\".converter-mode\");\n    var modePanels=box.querySelectorAll(\".converter-mode-panel\");\n\n    modeButtons.forEach(function(btn){\n        btn.addEventListener(\"click\",function(){\n            var target=btn.dataset.convertMode;\n\n            modeButtons.forEach(function(x){x.classList.remove(\"active\")});\n            modePanels.forEach(function(x){x.classList.remove(\"active\")});\n\n            btn.classList.add(\"active\");\n\n            var p=box.querySelector(\n                '[data-convert-mode-panel=\"'+target+'\"]'\n            );\n\n            if(p)p.classList.add(\"active\");\n        });\n    });\n\n    initImageConverter(box);\n    initDocumentConverter(box);\n}\n\n\n\/* =========================================================\n   IMAGE CONVERTER\n   ========================================================= *\/\n\nfunction initImageConverter(box){\n    var input=box.querySelector(\".convert-files\");\n    var list=box.querySelector(\".convert-list\");\n    var button=box.querySelector(\".convert-start\");\n    var format=box.querySelector(\".convert-format\");\n    var quality=box.querySelector(\".convert-quality\");\n    var message=box.querySelector(\".converter-mode-panel[data-convert-mode-panel='image'] .itool-message\");\n    var files=[];\n\n    input.addEventListener(\"change\",function(){\n        files=Array.from(input.files||[]);\n        renderFileList(list,files);\n    });\n\n    button.addEventListener(\"click\",async function(){\n        if(!files.length){\n            message.textContent=\"Vui l\u00f2ng ch\u1ecdn \u1ea3nh.\";\n            return;\n        }\n\n        var type=format.value;\n\n        if(type===\"image\/avif\"&&!canEncode(type)){\n            message.textContent=\"Tr\u00ecnh duy\u1ec7t n\u00e0y ch\u01b0a h\u1ed7 tr\u1ee3 xu\u1ea5t AVIF.\";\n            return;\n        }\n\n        button.disabled=true;\n        message.textContent=\"\u0110ang chuy\u1ec3n \u0111\u1ed5i...\";\n        list.innerHTML=\"\";\n\n        for(var file of files){\n            try{\n                var img=await loadImage(file);\n                var canvas=document.createElement(\"canvas\");\n\n                canvas.width=img.naturalWidth;\n                canvas.height=img.naturalHeight;\n\n                var ctx=canvas.getContext(\"2d\");\n\n                if(type===\"image\/jpeg\"){\n                    ctx.fillStyle=\"#fff\";\n                    ctx.fillRect(0,0,canvas.width,canvas.height);\n                }\n\n                ctx.drawImage(img,0,0);\n\n                var blob=await makeBlob(\n                    canvas,\n                    type,\n                    parseFloat(quality.value)\n                );\n\n                if(!blob)throw new Error(\"Kh\u00f4ng t\u1ea1o \u0111\u01b0\u1ee3c file.\");\n\n                var ext=extensionFromMime(type);\n                var base=file.name.replace(\/\\.[^\/.]+$\/,\"\");\n                var newName=base+\"-converted.\"+ext;\n\n                var row=document.createElement(\"div\");\n                row.className=\"file-row\";\n\n                var info=document.createElement(\"span\");\n                info.className=\"file-size\";\n                info.innerHTML=\n                    \"<strong>\"+escapeHtml(newName)+\"<\/strong><br>\"+\n                    formatBytes(blob.size);\n\n                var link=document.createElement(\"a\");\n                link.className=\"download-link\";\n                link.textContent=\"\u2b07\ufe0f T\u1ea3i xu\u1ed1ng\";\n                link.href=URL.createObjectURL(blob);\n                link.download=newName;\n\n                row.appendChild(info);\n                row.appendChild(link);\n                list.appendChild(row);\n\n            }catch(e){\n                console.error(e);\n\n                var er=document.createElement(\"div\");\n                er.className=\"file-row\";\n                er.textContent=\"Kh\u00f4ng th\u1ec3 chuy\u1ec3n \u0111\u1ed5i: \"+file.name;\n                list.appendChild(er);\n            }\n        }\n\n        message.textContent=\"\u0110\u00e3 chuy\u1ec3n \u0111\u1ed5i \"+files.length+\" \u1ea3nh.\";\n        button.disabled=false;\n    });\n\n    setupDragDrop(input.closest(\".itool-upload\"));\n}\n\n\n\/* =========================================================\n   LAZY LIBRARIES\n   ========================================================= *\/\n\nvar libPromises={};\n\nfunction loadPDFJS(){\n    if(libPromises.pdf)return libPromises.pdf;\n\n    libPromises.pdf=import(\n        \"https:\/\/cdn.jsdelivr.net\/npm\/pdfjs-dist@6.1.200\/build\/pdf.mjs\"\n    ).then(function(pdfjsLib){\n        pdfjsLib.GlobalWorkerOptions.workerSrc=\n            \"https:\/\/cdn.jsdelivr.net\/npm\/pdfjs-dist@6.1.200\/build\/pdf.worker.mjs\";\n\n        window.__IT_PDFJS=pdfjsLib;\n        return pdfjsLib;\n    });\n\n    return libPromises.pdf;\n}\n\nfunction loadSheetJS(){\n    if(libPromises.xlsx)return libPromises.xlsx;\n\n    libPromises.xlsx=loadScriptOnce(\n        \"https:\/\/cdn.sheetjs.com\/xlsx-0.20.3\/package\/dist\/xlsx.full.min.js\",\n        function(){return !!window.XLSX}\n    ).then(function(){\n        if(!window.XLSX){\n            throw new Error(\"Kh\u00f4ng kh\u1edfi t\u1ea1o \u0111\u01b0\u1ee3c SheetJS.\");\n        }\n\n        return window.XLSX;\n    });\n\n    return libPromises.xlsx;\n}\n\nfunction loadMammoth(){\n    if(libPromises.mammoth)return libPromises.mammoth;\n\n    libPromises.mammoth=loadScriptOnce(\n        \"https:\/\/cdn.jsdelivr.net\/npm\/mammoth@1.12.0\/mammoth.browser.min.js\",\n        function(){return !!window.mammoth}\n    ).then(function(){\n        if(!window.mammoth){\n            throw new Error(\"Kh\u00f4ng kh\u1edfi t\u1ea1o \u0111\u01b0\u1ee3c Mammoth.\");\n        }\n\n        return window.mammoth;\n    });\n\n    return libPromises.mammoth;\n}\n\nfunction loadHtml2Canvas(){\n    if(libPromises.html2canvas)return libPromises.html2canvas;\n\n    libPromises.html2canvas=loadScriptOnce(\n        \"https:\/\/cdn.jsdelivr.net\/npm\/html2canvas@1.4.1\/dist\/html2canvas.min.js\",\n        function(){return !!window.html2canvas}\n    ).then(function(){\n        if(!window.html2canvas){\n            throw new Error(\"Kh\u00f4ng kh\u1edfi t\u1ea1o \u0111\u01b0\u1ee3c html2canvas.\");\n        }\n\n        return window.html2canvas;\n    });\n\n    return libPromises.html2canvas;\n}\n\nfunction loadPptxViewJS(){\n    if(libPromises.pptx)return libPromises.pptx;\n\n    libPromises.pptx=loadScriptOnce(\n        \"https:\/\/cdn.jsdelivr.net\/npm\/jszip@3.10.1\/dist\/jszip.min.js\",\n        function(){return !!window.JSZip}\n    ).then(function(){\n        return loadScriptOnce(\n            \"https:\/\/cdn.jsdelivr.net\/npm\/pptxviewjs@1.1.9\/dist\/PptxViewJS.min.js\",\n            function(){return !!window.PptxViewJS}\n        );\n    }).then(function(){\n        if(!window.PptxViewJS){\n            throw new Error(\"Kh\u00f4ng kh\u1edfi t\u1ea1o \u0111\u01b0\u1ee3c PptxViewJS.\");\n        }\n\n        return window.PptxViewJS;\n    });\n\n    return libPromises.pptx;\n}\n\n\n\/* =========================================================\n   DOCUMENT CONVERTER\n   ========================================================= *\/\n\nfunction initDocumentConverter(box){\n\n    var input=box.querySelector(\".document-files\");\n    var list=box.querySelector(\".document-list\");\n    var button=box.querySelector(\".document-start\");\n    var format=box.querySelector(\".document-output-format\");\n    var quality=box.querySelector(\".document-output-quality\");\n    var pageInput=box.querySelector(\".document-pages\");\n    var scaleInput=box.querySelector(\".document-scale\");\n    var message=box.querySelector(\".converter-mode-panel[data-convert-mode-panel='document'] .itool-message\");\n    var files=[];\n\n    input.addEventListener(\"change\",function(){\n        files=Array.from(input.files||[]);\n        renderFileList(list,files);\n    });\n\n    button.addEventListener(\"click\",async function(){\n\n        if(!files.length){\n            message.textContent=\"Vui l\u00f2ng ch\u1ecdn \u00edt nh\u1ea5t m\u1ed9t file.\";\n            return;\n        }\n\n        var type=format.value;\n\n        if(type===\"image\/avif\"&&!canEncode(type)){\n            message.textContent=\"Tr\u00ecnh duy\u1ec7t ch\u01b0a h\u1ed7 tr\u1ee3 AVIF.\";\n            return;\n        }\n\n        button.disabled=true;\n        cleanupListObjectURLs(list);\n        list.innerHTML=\"\";\n        message.textContent=\"\u0110ang t\u1ea3i th\u01b0 vi\u1ec7n c\u1ea7n thi\u1ebft...\";\n\n        var totalDone=0;\n\n        try{\n\n            for(var file of files){\n\n                var ext=file.name.toLowerCase().split(\".\").pop();\n\n                try{\n\n                    if(ext===\"pdf\"){\n                        totalDone+=await convertPDF(\n                            file,\n                            type,\n                            parseFloat(quality.value),\n                            pageInput.value,\n                            parseFloat(scaleInput.value),\n                            list,\n                            message\n                        );\n\n                    }else if(ext===\"xlsx\" || ext===\"xls\"){\n                        totalDone+=await convertExcel(\n                            file,\n                            type,\n                            parseFloat(quality.value),\n                            parseFloat(scaleInput.value),\n                            pageInput.value,\n                            list,\n                            message\n                        );\n\n                    }else if(ext===\"docx\"){\n                        totalDone+=await convertDOCX(\n                            file,\n                            type,\n                            parseFloat(quality.value),\n                            parseFloat(scaleInput.value),\n                            pageInput.value,\n                            list,\n                            message\n                        );\n\n                    }else if(ext===\"pptx\"){\n                        totalDone+=await convertPPTX(\n                            file,\n                            type,\n                            parseFloat(quality.value),\n                            pageInput.value,\n                            parseFloat(scaleInput.value),\n                            list,\n                            message\n                        );\n\n                    }else if(ext===\"doc\" || ext===\"ppt\"){\n                        addDocumentError(\n                            list,\n                            file.name,\n                            \"DOC\/PPT \u0111\u1eddi c\u0169 kh\u00f4ng \u0111\u01b0\u1ee3c parser browser-side n\u00e0y h\u1ed7 tr\u1ee3. H\u00e3y l\u01b0u l\u1ea1i th\u00e0nh DOCX\/PPTX r\u1ed3i th\u1eed l\u1ea1i.\"\n                        );\n\n                    }else{\n                        addDocumentError(\n                            list,\n                            file.name,\n                            \"\u0110\u1ecbnh d\u1ea1ng ch\u01b0a \u0111\u01b0\u1ee3c h\u1ed7 tr\u1ee3.\"\n                        );\n                    }\n\n                }catch(err){\n                    console.error(err);\n\n                    addDocumentError(\n                        list,\n                        file.name,\n                        err && err.message\n                            ? err.message\n                            : \"Kh\u00f4ng th\u1ec3 x\u1eed l\u00fd file.\"\n                    );\n                }\n            }\n\n            message.textContent=\n                \"Ho\u00e0n t\u1ea5t. \u0110\u00e3 t\u1ea1o \"+totalDone+\" \u1ea3nh.\";\n\n        }catch(e){\n            console.error(e);\n            message.textContent=e.message||\"C\u00f3 l\u1ed7i khi x\u1eed l\u00fd.\";\n        }\n\n        button.disabled=false;\n    });\n\n    setupDragDrop(input.closest(\".itool-upload\"));\n}\n\nfunction addDocumentError(list,name,error){\n    var row=document.createElement(\"div\");\n    row.className=\"file-row\";\n    row.innerHTML=\n        \"<span><strong>\"+escapeHtml(name)+\"<\/strong><br>\"+\n        '<span class=\"file-size\">'+escapeHtml(error)+'<\/span><\/span>';\n    list.appendChild(row);\n}\n\nasync function convertPDF(\n    file,\n    outputType,\n    quality,\n    selection,\n    scale,\n    list,\n    message\n){\n    var pdfjsLib=await loadPDFJS();\n    message.textContent=\"\u0110ang \u0111\u1ecdc PDF: \"+file.name;\n\n    var data=await file.arrayBuffer();\n    var pdf=await pdfjsLib.getDocument({data:data}).promise;\n    var pages=parseSelection(selection,pdf.numPages);\n    var done=0;\n\n    for(var i=0;i<pages.length;i++){\n        var pageNo=pages[i];\n        message.textContent=\"PDF \"+file.name+\": trang \"+(i+1)+\"\/\"+pages.length+\" (trang PDF \"+pageNo+\")\";\n\n        var page=await pdf.getPage(pageNo);\n        var viewport=page.getViewport({scale:Math.max(.5,parseFloat(scale)||1)*1.5});\n        var dims=ensureCanvasSize(viewport.width,viewport.height,\"Trang PDF\");\n        var canvas=document.createElement(\"canvas\");\n        canvas.width=dims.width;\n        canvas.height=dims.height;\n\n        try{\n            var ctx=canvas.getContext(\"2d\",{alpha:false});\n            await page.render({canvasContext:ctx,viewport:viewport}).promise;\n            var blob=await makeBlob(canvas,outputType,quality);\n            if(!blob)throw new Error(\"Kh\u00f4ng t\u1ea1o \u0111\u01b0\u1ee3c \u1ea3nh t\u1eeb PDF.\");\n\n            var ext=extensionFromMime(outputType);\n            var base=file.name.replace(\/\\.[^\/.]+$\/i,\"\");\n            var name=base+\"-page-\"+pageNo+\".\"+ext;\n            addDocumentDownload(list,name,blob,\"Trang \"+pageNo+\" \u2022 \"+formatBytes(blob.size));\n            done++;\n        }finally{\n            canvas.width=1;\n            canvas.height=1;\n        }\n        await sleep(0);\n    }\n    return done;\n}\n\nasync function convertExcel(\n    file,\n    outputType,\n    quality,\n    scale,\n    selection,\n    list,\n    message\n){\n    var XLSX=await loadSheetJS();\n    await loadHtml2Canvas();\n    message.textContent=\"\u0110ang \u0111\u1ecdc Excel: \"+file.name;\n\n    var buffer=await file.arrayBuffer();\n    var workbook=XLSX.read(buffer,{type:\"array\",cellStyles:true,cellDates:true});\n    if(!workbook.SheetNames.length)throw new Error(\"File Excel kh\u00f4ng c\u00f3 worksheet.\");\n\n    var selected=parseSelection(selection,workbook.SheetNames.length);\n    var done=0;\n\n    for(var i=0;i<selected.length;i++){\n        var sheetIndex=selected[i]-1;\n        var sheetName=workbook.SheetNames[sheetIndex];\n        message.textContent=\"Excel \"+file.name+\": sheet \"+(i+1)+\"\/\"+selected.length+\" \u2014 \"+sheetName;\n        var sheet=workbook.Sheets[sheetName];\n        var html=XLSX.utils.sheet_to_html(sheet,{header:\"\",footer:\"\",editable:false});\n\n        var host=document.createElement(\"div\");\n        host.className=\"document-preview-host\";\n        var page=document.createElement(\"div\");\n        page.className=\"document-render-page\";\n        page.style.width=(1000*parseFloat(scale))+\"px\";\n        page.style.minHeight=(1414*parseFloat(scale))+\"px\";\n        page.innerHTML=\"<h2><span class=\"ez-toc-section\" id=\"%22escapeHtmlsheetName%22\"><\/span>\"+escapeHtml(sheetName)+\"<span class=\"ez-toc-section-end\"><\/span><\/h2>\"+html;\n        host.appendChild(page);\n        document.body.appendChild(host);\n\n        try{\n            await sleep(50);\n            var shot=await window.html2canvas(page,{scale:Math.max(1,parseFloat(scale)||1),backgroundColor:\"#ffffff\",useCORS:true,logging:false});\n            ensureCanvasSize(shot.width,shot.height,\"Sheet Excel\");\n            var blob=await makeBlob(shot,outputType,quality);\n            if(!blob)throw new Error(\"Kh\u00f4ng t\u1ea1o \u0111\u01b0\u1ee3c \u1ea3nh t\u1eeb Excel.\");\n\n            var ext=extensionFromMime(outputType);\n            var base=file.name.replace(\/\\.[^\/.]+$\/i,\"\");\n            var name=base+\"-\"+safeFileName(sheetName)+\".\"+ext;\n            addDocumentDownload(list,name,blob,\"Sheet: \"+sheetName+\" \u2022 \"+formatBytes(blob.size));\n            done++;\n            shot.width=1; shot.height=1;\n        }finally{\n            host.remove();\n        }\n        await sleep(0);\n    }\n    return done;\n}\n\nasync function convertDOCX(\n    file,\n    outputType,\n    quality,\n    scale,\n    selection,\n    list,\n    message\n){\n    var mammoth=await loadMammoth();\n    await loadHtml2Canvas();\n    message.textContent=\"\u0110ang \u0111\u1ecdc Word: \"+file.name;\n\n    var arrayBuffer=await file.arrayBuffer();\n    var result=await mammoth.convertToHtml({arrayBuffer:arrayBuffer});\n    var html=result.value||\"\";\n    if(!html.trim())throw new Error(\"DOCX kh\u00f4ng c\u00f3 n\u1ed9i dung c\u00f3 th\u1ec3 hi\u1ec3n th\u1ecb.\");\n\n    var host=document.createElement(\"div\");\n    host.className=\"document-preview-host\";\n    var source=document.createElement(\"div\");\n    source.className=\"document-render-page\";\n    source.style.width=(1000*scale)+\"px\";\n    source.innerHTML=html;\n    host.appendChild(source);\n    document.body.appendChild(host);\n\n    var pageHeight=1414*scale;\n    var pageInnerHeight=Math.max(200,pageHeight-90*scale);\n    var nodes=Array.from(source.children);\n    var pages=[];\n    var current=null;\n\n    function newPage(){\n        var page=document.createElement(\"div\");\n        page.className=\"document-render-page\";\n        page.style.width=(1000*scale)+\"px\";\n        page.style.minHeight=pageHeight+\"px\";\n        page.style.height=pageHeight+\"px\";\n        page.style.overflow=\"hidden\";\n        current=page;\n        pages.push(page);\n        return page;\n    }\n\n    try{\n        newPage();\n        for(var i=0;i<nodes.length;i++){\n            var node=nodes[i];\n            current.appendChild(node);\n            await sleep(0);\n            if(current.scrollHeight>pageInnerHeight && current.children.length>1){\n                current.removeChild(node);\n                current=newPage();\n                current.appendChild(node);\n            }\n        }\n\n        \/* N\u1ebfu DOCX ch\u1ec9 c\u00f3 text node ngo\u00e0i element, \u0111\u1ea3m b\u1ea3o v\u1eabn c\u00f3 \u00edt nh\u1ea5t 1 trang. *\/\n        if(!pages.length)newPage();\n\n        var selected=parseSelection(selection,pages.length);\n        var base=file.name.replace(\/\\.[^\/.]+$\/i,\"\");\n        var ext=extensionFromMime(outputType);\n        var done=0;\n\n        for(var p=0;p<selected.length;p++){\n            var pageNo=selected[p];\n            message.textContent=\"Word \"+file.name+\": trang \"+(p+1)+\"\/\"+selected.length+\" (trang \"+pageNo+\")\";\n            document.body.appendChild(pages[pageNo-1]);\n            await sleep(30);\n            var shot=await window.html2canvas(pages[pageNo-1],{scale:Math.max(1,scale),backgroundColor:\"#ffffff\",useCORS:true,logging:false});\n            ensureCanvasSize(shot.width,shot.height,\"Trang DOCX\");\n            var blob=await makeBlob(shot,outputType,quality);\n            if(!blob)throw new Error(\"Kh\u00f4ng t\u1ea1o \u0111\u01b0\u1ee3c \u1ea3nh t\u1eeb DOCX.\");\n            var name=base+\"-page-\"+pageNo+\".\"+ext;\n            addDocumentDownload(list,name,blob,\"DOCX \u2192 HTML \u2192 \u1ea3nh \u2022 Trang \"+pageNo+\" \u2022 \"+formatBytes(blob.size));\n            done++;\n            shot.width=1; shot.height=1;\n            pages[pageNo-1].remove();\n            await sleep(0);\n        }\n\n        if(result.messages && result.messages.length){\n            var warning=document.createElement(\"div\");\n            warning.className=\"file-row\";\n            warning.innerHTML=\"<span class='file-size'>L\u01b0u \u00fd DOCX: \"+result.messages.length+\" c\u1ea3nh b\u00e1o v\u1ec1 \u0111\u1ecbnh d\u1ea1ng trong qu\u00e1 tr\u00ecnh chuy\u1ec3n \u0111\u1ed5i. B\u1ed1 c\u1ee5c c\u00f3 th\u1ec3 kh\u00e1c Microsoft Word.<\/span>\";\n            list.appendChild(warning);\n        }\n        return done;\n    }finally{\n        host.remove();\n        pages.forEach(function(page){if(page.parentNode)page.parentNode.removeChild(page);});\n    }\n}\n\nasync function convertPPTX(\n    file,\n    outputType,\n    quality,\n    selection,\n    scale,\n    list,\n    message\n){\n    var PptxViewJS=await loadPptxViewJS();\n\n    message.textContent=\"\u0110ang \u0111\u1ecdc PowerPoint: \"+file.name;\n\n    if(!PptxViewJS.PPTXViewer){\n        throw new Error(\"PptxViewJS kh\u00f4ng cung c\u1ea5p PPTXViewer.\");\n    }\n\n    var host=document.createElement(\"div\");\n    host.style.position=\"fixed\";\n    host.style.left=\"-100000px\";\n    host.style.top=\"-100000px\";\n    host.style.width=\"1280px\";\n    host.style.height=\"720px\";\n    host.style.background=\"#fff\";\n\n    var canvas=document.createElement(\"canvas\");\n    var pptDims=ensureCanvasSize(1280*scale,720*scale,\"Slide PPTX\");\n    canvas.width=pptDims.width;\n    canvas.height=pptDims.height;\n\n    host.appendChild(canvas);\n    document.body.appendChild(host);\n\n    var viewer=new PptxViewJS.PPTXViewer({\n        canvas:canvas\n    });\n\n    await viewer.loadFile(file);\n\n    var count=viewer.getSlideCount();\n    var slides=parseSelection(selection,count);\n    var done=0;\n\n    for(var i=0;i<slides.length;i++){\n\n        var slideNo=slides[i];\n\n        message.textContent=\n            \"PPTX \"+file.name+\": slide \"+(i+1)+\"\/\"+slides.length+\" (slide \"+slideNo+\")\";\n\n        await viewer.goToSlide(slideNo-1);\n        await viewer.render();\n\n        \/*\n         * Ch\u1edd browser ho\u00e0n t\u1ea5t paint c\u1ee7a canvas.\n         *\/\n        await sleep(80);\n\n        var copy=document.createElement(\"canvas\");\n        copy.width=canvas.width;\n        copy.height=canvas.height;\n\n        copy.getContext(\"2d\").drawImage(\n            canvas,\n            0,\n            0,\n            copy.width,\n            copy.height\n        );\n\n        var blob=await makeBlob(\n            copy,\n            outputType,\n            quality\n        );\n\n        if(!blob)throw new Error(\"Kh\u00f4ng t\u1ea1o \u0111\u01b0\u1ee3c \u1ea3nh t\u1eeb PPTX.\");\n\n        var ext=extensionFromMime(outputType);\n        var base=file.name.replace(\/\\.[^\/.]+$\/,\"\");\n        var name=base+\"-slide-\"+slideNo+\".\"+ext;\n\n        addDocumentDownload(\n            list,\n            name,\n            blob,\n            \"Slide \"+slideNo+\" \u2022 \"+formatBytes(blob.size)\n        );\n\n        done++;\n        await sleep(0);\n    }\n\n    host.remove();\n\n    return done;\n}\n\nfunction safeFileName(name){\n    return String(name||\"sheet\")\n        .replace(\/[\\\\\/:*?\"<>|]+\/g,\"-\")\n        .replace(\/\\s+\/g,\"-\")\n        .substring(0,80);\n}\n\nfunction addDocumentDownload(list,name,blob,meta){\n    if(!list._itoolObjectURLs)list._itoolObjectURLs=[];\n    var url=URL.createObjectURL(blob);\n    list._itoolObjectURLs.push(url);\n\n    var row=document.createElement(\"div\");\n    row.className=\"file-row\";\n    var info=document.createElement(\"span\");\n    info.className=\"file-size\";\n    info.innerHTML=\"<strong>\"+escapeHtml(name)+\"<\/strong><br>\"+escapeHtml(meta||formatBytes(blob.size));\n\n    var link=document.createElement(\"a\");\n    link.className=\"download-link\";\n    link.textContent=\"\u2b07\ufe0f T\u1ea3i xu\u1ed1ng\";\n    link.href=url;\n    link.download=name;\n\n    row.appendChild(info);\n    row.appendChild(link);\n    list.appendChild(row);\n}\n\nfunction cleanupListObjectURLs(list){\n    if(!list || !list._itoolObjectURLs)return;\n    list._itoolObjectURLs.forEach(function(url){\n        try{URL.revokeObjectURL(url);}catch(e){}\n    });\n    list._itoolObjectURLs=[];\n}\n\n\n\n\/* =========================================================\n   WATERMARK \/ EDITOR\n   ========================================================= *\/\n\nfunction initWatermark(box){\n\n    var editorButtons=box.querySelectorAll(\".editor-mode\");\n    var editorPanels=box.querySelectorAll(\".editor-mode-panel\");\n\n    editorButtons.forEach(function(btn){\n        btn.addEventListener(\"click\",function(){\n            var target=btn.dataset.editorMode;\n\n            editorButtons.forEach(function(x){x.classList.remove(\"active\")});\n            editorPanels.forEach(function(x){x.classList.remove(\"active\")});\n\n            btn.classList.add(\"active\");\n\n            var p=box.querySelector(\n                '[data-editor-mode-panel=\"'+target+'\"]'\n            );\n\n            if(p)p.classList.add(\"active\");\n        });\n    });\n\n    initSingleEditor(box);\n    initBatchWatermark(box);\n}\n\n\n\/* =========================================================\n   SINGLE EDITOR\n   ========================================================= *\/\n\nfunction initSingleEditor(box){\n    var imageInput=box.querySelector(\".watermark-image\");\n    var logoInput=box.querySelector(\".watermark-logo\");\n    var canvas=box.querySelector(\".watermark-canvas\");\n    var preview=box.querySelector(\".watermark-preview\");\n    var dragLogo=box.querySelector(\".watermark-drag-logo\");\n    var logoPreview=box.querySelector(\".watermark-logo-preview\");\n    var resizeHandle=box.querySelector(\".wm-resize-handle\");\n    var position=box.querySelector(\".watermark-position\");\n    var size=box.querySelector(\".watermark-size\");\n    var opacity=box.querySelector(\".watermark-opacity\");\n    var margin=box.querySelector(\".watermark-margin\");\n    var format=box.querySelector(\".watermark-format\");\n    var button=box.querySelector(\".watermark-start\");\n    var resetBtn=box.querySelector(\".watermark-reset\");\n    var result=box.querySelector(\".watermark-result\");\n    var message=box.querySelector(\".editor-mode-panel[data-editor-mode-panel='single'] .itool-message\");\n    var sizeValue=box.querySelector(\".range-value\");\n    var opacityValue=box.querySelector(\".opacity-value\");\n    var marginValue=box.querySelector(\".margin-value\");\n    var textInput=box.querySelector(\".editor-text-input\");\n    var textColor=box.querySelector(\".editor-text-color\");\n    var textSize=box.querySelector(\".editor-text-size\");\n    var textFont=box.querySelector(\".editor-text-font\");\n    var textWeight=box.querySelector(\".editor-text-weight\");\n    var addText=box.querySelector(\".add-text-object\");\n    var updateText=box.querySelector(\".update-text-object\");\n    var emojiInput=box.querySelector(\".editor-emoji-input\");\n    var emojiSize=box.querySelector(\".editor-emoji-size\");\n    var addEmoji=box.querySelector(\".add-emoji-object\");\n    var deleteObject=box.querySelector(\".delete-selected-object\");\n    var objectLayer=box.querySelector(\".editor-object-layer\");\n    var flipXBtn=box.querySelector(\".editor-flip-x\");\n    var flipYBtn=box.querySelector(\".editor-flip-y\");\n    var rotateLeftBtn=box.querySelector(\".editor-rotate-left\");\n    var rotateRightBtn=box.querySelector(\".editor-rotate-right\");\n    var resetTransformBtn=box.querySelector(\".editor-reset-transform\");\n    var rotationValue=box.querySelector(\".editor-rotation-value\");\n    var flipValue=box.querySelector(\".editor-transform-flip-value\");\n    var cropToggle=box.querySelector(\".editor-crop-toggle\");\n    var cropReset=box.querySelector(\".editor-crop-reset\");\n    var cropApply=box.querySelector(\".editor-crop-apply\");\n    var cropRatio=box.querySelector(\".editor-crop-ratio\");\n    var cropLayer=box.querySelector(\".editor-crop-layer\");\n    var cropBox=box.querySelector(\".editor-crop-box\");\n\n\n    var mainImage=null;\n    var logoImage=null;\n    var logoPreviewURL=null;\n    var resultURL=null;\n    var displayScale=1;\n    var logoState={x:.75,y:.75,w:.20,h:.20};\n    var logoDrag=null;\n    var objects=[];\n    var selectedObjectId=null;\n    var objectDrag=null;\n    var objectResize=null;\n    var transformState={rotation:0,flipX:false,flipY:false};\n    var cropState={active:false,x:0,y:0,w:1,h:1};\n    var cropDrag=null;\n\n    var MIN_SIZE=8;\n    var MAX_SIZE=300;\n    var MAX_OUTPUT_PIXELS=40000000;\n\n    function clamp(v,min,max){return Math.max(min,Math.min(max,v));}\n\n    function editorFont(obj,size){\n        if(obj.type===\"emoji\"){\n            return size+\"px \\\"Segoe UI Emoji\\\",\\\"Apple Color Emoji\\\",\\\"Noto Color Emoji\\\",\\\"Twemoji Mozilla\\\",sans-serif\";\n        }\n        return (obj.italic?\"italic \":\"\")+(obj.weight||400)+\" \"+size+\"px \\\"\"+String(obj.font||\"Arial\").replace(\/\\\"\/g,\"\\\\\\\"\")+\"\\\"\";\n    }\n\n    function getObjectMetrics(ctx,obj,size){\n        ctx.save();\n        ctx.font=editorFont(obj,size);\n        var m=ctx.measureText(obj.text||\"\");\n        var width=Math.max(1,m.width);\n        var ascent=m.actualBoundingBoxAscent||size*.75;\n        var descent=m.actualBoundingBoxDescent||size*.25;\n        ctx.restore();\n        return {width:width,height:Math.max(size,ascent+descent)};\n    }\n\n    function clampObjectPosition(obj){\n        if(!mainImage||!canvas.width||!canvas.height)return;\n        var ctx=canvas.getContext(\"2d\");\n        var metrics=getObjectMetrics(ctx,obj,obj.size);\n        var halfW=Math.min(.49,(metrics.width\/2)\/canvas.width);\n        var halfH=Math.min(.49,(metrics.height\/2)\/canvas.height);\n        obj.x=clamp(obj.x,halfW,1-halfW);\n        obj.y=clamp(obj.y,halfH,1-halfH);\n    }\n\n    function transformedSize(){\n        if(!mainImage)return {width:0,height:0};\n        var w=mainImage.naturalWidth,h=mainImage.naturalHeight;\n        if(transformState.rotation===90||transformState.rotation===270){\n            return {width:h,height:w};\n        }\n        return {width:w,height:h};\n    }\n\n    function drawTransformedImage(ctx,img,outWidth,outHeight){\n        ctx.save();\n        ctx.translate(outWidth\/2,outHeight\/2);\n        ctx.rotate(transformState.rotation*Math.PI\/180);\n        ctx.scale(transformState.flipX?-1:1,transformState.flipY?-1:1);\n        ctx.drawImage(img,-img.naturalWidth\/2,-img.naturalHeight\/2);\n        ctx.restore();\n    }\n\n    function updateTransformStatus(){\n        if(rotationValue)rotationValue.textContent=(transformState.rotation||0)+\"\u00b0\";\n        if(flipValue){\n            if(transformState.flipX&&transformState.flipY)flipValue.textContent=\"L\u1eadt ngang + d\u1ecdc\";\n            else if(transformState.flipX)flipValue.textContent=\"L\u1eadt ngang\";\n            else if(transformState.flipY)flipValue.textContent=\"L\u1eadt d\u1ecdc\";\n            else flipValue.textContent=\"B\u00ecnh th\u01b0\u1eddng\";\n        }\n    }\n\n\n    var emojiImageCache={};\n    function getEmojiImage(value){\n        if(!value)return null;\n        var key=value;\n        if(emojiImageCache[key])return emojiImageCache[key];\n        var img=new Image();\n        img.crossOrigin=\"anonymous\";\n        img.src=\"https:\/\/cdn.jsdelivr.net\/gh\/twitter\/twemoji@14.0.2\/assets\/svg\/\"+emojiCodePoints(value)+\".svg\";\n        emojiImageCache[key]=img;\n        img.onload=function(){ if(mainImage){ draw(); } };\n        return img;\n    }\n\n    function drawObjectToCanvas(ctx,obj,outWidth,outHeight){\n        var px=Math.max(MIN_SIZE,obj.size);\n        var x=obj.x*outWidth;\n        var y=obj.y*outHeight;\n        ctx.save();\n        ctx.textAlign=\"center\";\n        ctx.textBaseline=\"middle\";\n        ctx.font=editorFont(obj,px);\n\n        if(obj.type===\"text\"){\n            ctx.fillStyle=obj.color||\"#ffffff\";\n            if(obj.shadow){\n                ctx.shadowColor=\"rgba(0,0,0,.38)\";\n                ctx.shadowBlur=Math.max(2,px*.08);\n                ctx.shadowOffsetX=Math.max(1,px*.05);\n                ctx.shadowOffsetY=Math.max(1,px*.05);\n            }\n        }else{\n            ctx.fillStyle=\"#000000\";\n        }\n\n        if(obj.type===\"emoji\") {\n            var emojiImg=getEmojiImage(obj.text);\n            if(emojiImg && emojiImg.complete && emojiImg.naturalWidth){\n                var es=Math.max(MIN_SIZE,obj.size);\n                ctx.drawImage(emojiImg,x-es\/2,y-es\/2,es,es);\n            }else{\n                ctx.fillStyle=\"#111827\";\n                ctx.fillText(obj.text,x,y);\n            }\n        } else {\n            ctx.fillText(obj.text,x,y);\n        }\n\n        if(obj.type===\"text\" && obj.underline){\n            var metrics=getObjectMetrics(ctx,obj,px);\n            var lineY=y+metrics.height*.32;\n            ctx.save();\n            ctx.shadowColor=\"transparent\";\n            ctx.shadowBlur=0;\n            ctx.shadowOffsetX=0;\n            ctx.shadowOffsetY=0;\n            ctx.strokeStyle=obj.color||\"#ffffff\";\n            ctx.lineWidth=Math.max(1,px*.045);\n            ctx.beginPath();\n            ctx.moveTo(x-metrics.width\/2,lineY);\n            ctx.lineTo(x+metrics.width\/2,lineY);\n            ctx.stroke();\n            ctx.restore();\n        }\n        ctx.restore();\n    }\n\n    function draw(){\n        if(!mainImage)return;\n        var dims=transformedSize();\n        var mw=900,mh=650;\n        displayScale=Math.min(mw\/dims.width,mh\/dims.height,1);\n        var w=Math.max(1,Math.round(dims.width*displayScale));\n        var h=Math.max(1,Math.round(dims.height*displayScale));\n        canvas.width=w;\n        canvas.height=h;\n        var ctx=canvas.getContext(\"2d\");\n        ctx.clearRect(0,0,w,h);\n        drawTransformedImage(ctx,mainImage,w,h);\n\n        if(logoImage){\n            var lw=w*logoState.w;\n            var lh=logoImage.naturalHeight\/logoImage.naturalWidth*lw;\n            logoState.h=clamp(lh\/h,.001,1);\n            logoState.x=clamp(logoState.x,0,1-logoState.w);\n            logoState.y=clamp(logoState.y,0,1-logoState.h);\n            ctx.save();\n            ctx.globalAlpha=parseInt(opacity.value,10)\/100;\n            ctx.drawImage(logoImage,logoState.x*w,logoState.y*h,lw,lh);\n            ctx.restore();\n            updateLogoOverlay();\n        }else{\n            dragLogo.style.display=\"none\";\n        }\n\n        objects.forEach(clampObjectPosition);\n        renderObjectsOverlay();\n        updateCropOverlay();\n        updateTransformStatus();\n    }\n\n    function updateLogoOverlay(){\n        if(!mainImage||!logoImage)return;\n        var canvasRect=canvas.getBoundingClientRect();\n        var previewRect=preview.getBoundingClientRect();\n        if(!canvasRect.width||!canvasRect.height)return;\n        var lw=canvasRect.width*logoState.w;\n        var lh=canvasRect.height*logoState.h;\n        var ox=canvasRect.left-previewRect.left;\n        var oy=canvasRect.top-previewRect.top;\n        dragLogo.style.display=\"block\";\n        dragLogo.style.left=(ox+logoState.x*canvasRect.width)+\"px\";\n        dragLogo.style.top=(oy+logoState.y*canvasRect.height)+\"px\";\n        dragLogo.style.width=lw+\"px\";\n        dragLogo.style.height=lh+\"px\";\n        logoPreview.style.opacity=parseInt(opacity.value,10)\/100;\n    }\n\n    function setQuick(v){\n        if(!logoImage)return;\n        var m=parseInt(margin.value,10)\/100;\n        var w=logoState.w,h=logoState.h;\n        position.value=v;\n        switch(v){\n            case\"top-left\": logoState.x=m;logoState.y=m;break;\n            case\"top-center\": logoState.x=(1-w)\/2;logoState.y=m;break;\n            case\"top-right\": logoState.x=1-w-m;logoState.y=m;break;\n            case\"center\": logoState.x=(1-w)\/2;logoState.y=(1-h)\/2;break;\n            case\"bottom-left\": logoState.x=m;logoState.y=1-h-m;break;\n            case\"bottom-center\": logoState.x=(1-w)\/2;logoState.y=1-h-m;break;\n            case\"bottom-right\": logoState.x=1-w-m;logoState.y=1-h-m;break;\n        }\n        logoState.x=clamp(logoState.x,0,1-logoState.w);\n        logoState.y=clamp(logoState.y,0,1-logoState.h);\n        draw();\n    }\n\n    function pointer(e){\n        var r=preview.getBoundingClientRect();\n        return {x:e.clientX-r.left,y:e.clientY-r.top};\n    }\n\n    function startLogoDrag(e,mode){\n        if(!logoImage)return;\n        e.preventDefault();\n        e.stopPropagation();\n        if(dragLogo.setPointerCapture){try{dragLogo.setPointerCapture(e.pointerId);}catch(err){}}\n        var p=pointer(e);\n        logoDrag={mode:mode,sx:p.x,sy:p.y,x:logoState.x,y:logoState.y,w:logoState.w,h:logoState.h};\n        position.value=\"free\";\n    }\n\n    function moveLogo(e){\n        if(!logoDrag||!logoImage)return;\n        e.preventDefault();\n        var p=pointer(e),r=canvas.getBoundingClientRect();\n        if(!r.width||!r.height)return;\n        var dx=(p.x-logoDrag.sx)\/r.width,dy=(p.y-logoDrag.sy)\/r.height;\n        if(logoDrag.mode===\"move\"){\n            logoState.x=clamp(logoDrag.x+dx,0,1-logoDrag.w);\n            logoState.y=clamp(logoDrag.y+dy,0,1-logoDrag.h);\n        }else{\n            var ratio=logoImage.naturalHeight\/logoImage.naturalWidth;\n            var nw=clamp(logoDrag.w+dx,.03,1-logoDrag.x);\n            var nh=clamp(nw*ratio*r.width\/r.height,.03,1-logoDrag.y);\n            logoState.w=nw;logoState.h=nh;\n            size.value=Math.round(nw*100);\n            sizeValue.textContent=size.value+\"%\";\n        }\n        draw();\n    }\n\n    dragLogo.addEventListener(\"pointerdown\",function(e){startLogoDrag(e,\"move\")});\n    resizeHandle.addEventListener(\"pointerdown\",function(e){startLogoDrag(e,\"resize\")});\n    document.addEventListener(\"pointermove\",moveLogo,{passive:false});\n    document.addEventListener(\"pointerup\",function(){logoDrag=null;});\n\n    function toggleTextStyle(styleName){\n        var obj=selectedObject();\n        if(!obj||obj.type!==\"text\"){\n            message.textContent=\"H\u00e3y ch\u1ecdn m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng ch\u1eef tr\u01b0\u1edbc.\";\n            return;\n        }\n        obj[styleName]=!obj[styleName];\n        syncStyleButtons(obj);\n        renderObjectsOverlay();\n        draw();\n    }\n\n    function syncStyleButtons(obj){\n        box.querySelectorAll(\".text-style-btn\").forEach(function(btn){\n            btn.classList.toggle(\"active\",!!(obj&&obj[btn.dataset.textStyle]));\n        });\n    }\n\n    box.querySelectorAll(\".text-style-btn\").forEach(function(btn){\n        btn.addEventListener(\"click\",function(){toggleTextStyle(btn.dataset.textStyle);});\n    });\n\n    function createObject(type,value){\n        if(!mainImage){message.textContent=\"Vui l\u00f2ng ch\u1ecdn \u1ea3nh tr\u01b0\u1edbc.\";return;}\n        value=String(value||\"\").trim();\n        if(!value)return;\n        var id=\"obj_\"+Date.now()+\"_\"+Math.floor(Math.random()*10000);\n        var obj={\n            id:id,type:type,text:value,x:.5,y:.5,\n            size:clamp(parseInt(type===\"text\"?textSize.value:emojiSize.value,10)||48,MIN_SIZE,MAX_SIZE),\n            color:type===\"text\"?textColor.value:\"#ffffff\",\n            font:type===\"text\"?textFont.value:\"Arial\",\n            weight:type===\"text\"?parseInt(textWeight.value,10)||400:400,\n            italic:false,underline:false,shadow:false\n        };\n        objects.push(obj);selectedObjectId=id;\n        clampObjectPosition(obj);\n        renderObjectsOverlay();syncSelectedControls();draw();\n    }\n\n    function selectedObject(){\n        return objects.find(function(o){return o.id===selectedObjectId;})||null;\n    }\n\n    function renderObjectsOverlay(){\n        objectLayer.innerHTML=\"\";\n        if(!mainImage)return;\n        var canvasRect=canvas.getBoundingClientRect();\n        var previewRect=preview.getBoundingClientRect();\n        if(!canvasRect.width||!canvasRect.height)return;\n        var visualScale=canvasRect.width\/canvas.width;\n\n        objects.forEach(function(obj){\n            var el=document.createElement(\"div\");\n            el.className=\"editor-object \"+(obj.type===\"text\"?\"text-object\":\"emoji-object\");\n            if(obj.id===selectedObjectId)el.classList.add(\"selected\");\n            el.dataset.objectId=obj.id;\n            el.textContent=obj.text;\n            el.style.left=(canvasRect.left-previewRect.left+obj.x*canvasRect.width)+\"px\";\n            el.style.top=(canvasRect.top-previewRect.top+obj.y*canvasRect.height)+\"px\";\n            el.style.fontSize=(obj.size*visualScale)+\"px\";\n            if(obj.type===\"text\"){\n                el.style.color=obj.color;\n                el.style.fontFamily=obj.font;\n                el.style.fontWeight=obj.weight;\n                el.style.fontStyle=obj.italic?\"italic\":\"normal\";\n                el.style.textDecoration=obj.underline?\"underline\":\"none\";\n                el.style.textShadow=obj.shadow?\"2px 2px 3px rgba(0,0,0,.38)\":\"none\";\n            }else{\n                el.style.fontFamily='\"Segoe UI Emoji\",\"Apple Color Emoji\",\"Noto Color Emoji\",\"Twemoji Mozilla\",sans-serif';\n                el.style.fontVariantEmoji=\"emoji\";\n                \/* Keep the editable overlay visible even if WordPress rewrites emoji. *\/\n                el.innerHTML=\"\";\n                var emojiImg=document.createElement(\"img\");\n                emojiImg.alt=obj.text;\n                emojiImg.draggable=false;\n                emojiImg.src=\"https:\/\/cdn.jsdelivr.net\/gh\/twitter\/twemoji@14.0.2\/assets\/svg\/\"+emojiCodePoints(obj.text)+\".svg\";\n                emojiImg.style.cssText=\"display:block!important;width:\"+(obj.size*visualScale)+\"px!important;height:\"+(obj.size*visualScale)+\"px!important;max-width:none!important;object-fit:contain!important;pointer-events:none!important;\";\n                emojiImg.onerror=function(){\n                    emojiImg.remove();\n                    var span=document.createElement(\"span\");\n                    span.textContent=obj.text;\n                    span.style.cssText='font-family:\"Segoe UI Emoji\",\"Apple Color Emoji\",\"Noto Color Emoji\",sans-serif!important;font-size:'+(obj.size*visualScale)+'px!important;line-height:1!important;';\n                    el.appendChild(span);\n                };\n                el.appendChild(emojiImg);\n            }\n            if(obj.id===selectedObjectId){\n                var handle=document.createElement(\"span\");\n                handle.className=\"editor-object-resize-handle\";\n                handle.title=\"K\u00e9o \u0111\u1ec3 ph\u00f3ng to \/ thu nh\u1ecf\";\n                handle.addEventListener(\"pointerdown\",function(e){e.preventDefault();e.stopPropagation();startObjectResize(e,obj.id);});\n                el.appendChild(handle);\n            }\n            el.addEventListener(\"pointerdown\",function(e){\n                if(!e.target.classList || !e.target.classList.contains(\"editor-object-resize-handle\")){selectObject(obj.id);startObjectDrag(e,obj.id);}\n            });\n            objectLayer.appendChild(el);\n        });\n    }\n\n    function selectObject(id){\n        selectedObjectId=id;renderObjectsOverlay();syncSelectedControls();\n    }\n\n    function syncSelectedControls(){\n        var obj=selectedObject();\n        if(!obj){syncStyleButtons(null);return;}\n        if(obj.type===\"text\"){\n            textInput.value=obj.text;textColor.value=obj.color;textSize.value=obj.size;textFont.value=obj.font;textWeight.value=obj.weight;syncStyleButtons(obj);\n        }else{\n            emojiInput.value=obj.text;emojiSize.value=obj.size;syncStyleButtons(null);\n        }\n    }\n\n\n    function resetCropState(){\n        cropState={active:false,x:0,y:0,w:1,h:1};\n        if(cropLayer)cropLayer.classList.remove(\"active\");\n        if(preview)preview.classList.remove(\"crop-mode\");\n        if(cropToggle)cropToggle.textContent=\"\u2702\ufe0f B\u1eadt khung c\u1eaft\";\n    }\n\n    function resetCropRect(){\n        cropState.x=0;cropState.y=0;cropState.w=1;cropState.h=1;\n        updateCropOverlay();\n    }\n\n    function setCropRatio(value){\n        if(!mainImage||!canvas.width||!canvas.height)return;\n        if(value===\"free\"){updateCropOverlay();return;}\n        var parts=value.split(\":\");\n        var ratio=parseFloat(parts[0])\/parseFloat(parts[1]);\n        if(!isFinite(ratio)||ratio<=0)return;\n        var current=canvas.width\/canvas.height;\n        var x=cropState.x,y=cropState.y,w=cropState.w,h=cropState.h;\n        if(w\/h>ratio){\n            w=h*ratio;\n        }else{\n            h=w\/ratio;\n        }\n        w=clamp(w,.05,1);h=clamp(h,.05,1);\n        x=clamp(x+(cropState.w-w)\/2,0,1-w);\n        y=clamp(y+(cropState.h-h)\/2,0,1-h);\n        cropState.x=x;cropState.y=y;cropState.w=w;cropState.h=h;\n        updateCropOverlay();\n    }\n\n    function updateCropOverlay(){\n        if(!cropLayer||!cropBox||!canvas||!preview)return;\n        if(!cropState.active){\n            cropLayer.classList.remove(\"active\");\n            preview.classList.remove(\"crop-mode\");\n            return;\n        }\n        var cr=canvas.getBoundingClientRect();\n        var pr=preview.getBoundingClientRect();\n        if(!cr.width||!cr.height)return;\n        cropLayer.classList.add(\"active\");\n        preview.classList.add(\"crop-mode\");\n        var left=cr.left-pr.left+cropState.x*cr.width;\n        var top=cr.top-pr.top+cropState.y*cr.height;\n        cropBox.style.left=left+\"px\";\n        cropBox.style.top=top+\"px\";\n        cropBox.style.width=(cropState.w*cr.width)+\"px\";\n        cropBox.style.height=(cropState.h*cr.height)+\"px\";\n        cropLayer.setAttribute(\"aria-hidden\",\"false\");\n    }\n\n    function cropPointerPosition(e){\n        var r=canvas.getBoundingClientRect();\n        return {\n            x:clamp((e.clientX-r.left)\/Math.max(r.width,1),0,1),\n            y:clamp((e.clientY-r.top)\/Math.max(r.height,1),0,1)\n        };\n    }\n\n    function startCropDrag(e,mode){\n        if(!cropState.active||!mainImage)return;\n        e.preventDefault();e.stopPropagation();\n        var p=cropPointerPosition(e);\n        cropDrag={\n            mode:mode,sx:p.x,sy:p.y,\n            x:cropState.x,y:cropState.y,w:cropState.w,h:cropState.h\n        };\n        if(cropBox.setPointerCapture){try{cropBox.setPointerCapture(e.pointerId);}catch(err){}}\n    }\n\n    function applyCropRatioToRect(state,ratio,anchorX,anchorY){\n        if(ratio===\"free\")return state;\n        var parts=ratio.split(\":\");\n        var target=parseFloat(parts[0])\/parseFloat(parts[1]);\n        if(!isFinite(target)||target<=0)return state;\n        var maxW=state.w,maxH=state.h;\n        if(maxW\/maxH>target)maxW=maxH*target;\n        else maxH=maxW\/target;\n        var x=anchorX===\"right\"?state.x+state.w-maxW:anchorX===\"center\"?state.x+(state.w-maxW)\/2:state.x;\n        var y=anchorY===\"bottom\"?state.y+state.h-maxH:anchorY===\"center\"?state.y+(state.h-maxH)\/2:state.y;\n        state.w=clamp(maxW,.05,1);state.h=clamp(maxH,.05,1);\n        state.x=clamp(x,0,1-state.w);state.y=clamp(y,0,1-state.h);\n        return state;\n    }\n\n    function moveCrop(e){\n        if(!cropDrag)return;\n        e.preventDefault();\n        var p=cropPointerPosition(e);\n        var dx=p.x-cropDrag.sx,dy=p.y-cropDrag.sy;\n        var min=.03;\n        var s={\n            x:cropDrag.x,y:cropDrag.y,w:cropDrag.w,h:cropDrag.h\n        };\n        var mode=cropDrag.mode;\n        if(mode===\"move\"){\n            s.x=clamp(cropDrag.x+dx,0,1-cropDrag.w);\n            s.y=clamp(cropDrag.y+dy,0,1-cropDrag.h);\n        }else{\n            var left=cropDrag.x,top=cropDrag.y,right=cropDrag.x+cropDrag.w,bottom=cropDrag.y+cropDrag.h;\n            if(mode.indexOf(\"w\")>=0)left=clamp(cropDrag.x+dx,0,right-min);\n            if(mode.indexOf(\"e\")>=0)right=clamp(cropDrag.x+cropDrag.w+dx,left+min,1);\n            if(mode.indexOf(\"n\")>=0)top=clamp(cropDrag.y+dy,0,bottom-min);\n            if(mode.indexOf(\"s\")>=0)bottom=clamp(cropDrag.y+cropDrag.h+dy,top+min,1);\n            s.x=left;s.y=top;s.w=right-left;s.h=bottom-top;\n            if(cropRatio.value!==\"free\"){\n                var parts=cropRatio.value.split(\":\");\n                var target=parseFloat(parts[0])\/parseFloat(parts[1]);\n                if(isFinite(target)&&target>0){\n                    var fixedX=mode.indexOf(\"w\")>=0?\"right\":mode.indexOf(\"e\")>=0?\"left\":\"center\";\n                    var fixedY=mode.indexOf(\"n\")>=0?\"bottom\":mode.indexOf(\"s\")>=0?\"top\":\"center\";\n                    if(s.w\/s.h>target){\n                        s.w=s.h*target;\n                    }else{\n                        s.h=s.w\/target;\n                    }\n                    if(fixedX===\"right\")s.x=cropDrag.x+cropDrag.w-s.w;\n                    else if(fixedX===\"center\")s.x=cropDrag.x+(cropDrag.w-s.w)\/2;\n                    if(fixedY===\"bottom\")s.y=cropDrag.y+cropDrag.h-s.h;\n                    else if(fixedY===\"center\")s.y=cropDrag.y+(cropDrag.h-s.h)\/2;\n                    s.x=clamp(s.x,0,1-s.w);s.y=clamp(s.y,0,1-s.h);\n                }\n            }\n        }\n        cropState.x=s.x;cropState.y=s.y;cropState.w=s.w;cropState.h=s.h;\n        updateCropOverlay();\n    }\n\n    document.addEventListener(\"pointermove\",moveCrop,{passive:false});\n    document.addEventListener(\"pointerup\",function(){cropDrag=null;});\n\n    if(cropBox){\n        cropBox.addEventListener(\"pointerdown\",function(e){\n            var handle=e.target.closest ? e.target.closest(\"[data-crop-handle]\") : null;\n            startCropDrag(e,handle?handle.getAttribute(\"data-crop-handle\"):\"move\");\n        });\n    }\n\n    if(cropToggle)cropToggle.addEventListener(\"click\",function(){\n        if(!mainImage){message.textContent=\"Vui l\u00f2ng ch\u1ecdn \u1ea3nh tr\u01b0\u1edbc khi b\u1eadt khung c\u1eaft.\";return;}\n        cropState.active=!cropState.active;\n        if(cropState.active){\n            if(cropState.w>=.999&&cropState.h>=.999){cropState.x=.05;cropState.y=.05;cropState.w=.9;cropState.h=.9;}\n            cropToggle.textContent=\"\u2705 \u0110ang ch\u1ec9nh khung c\u1eaft\";\n        }else{\n            cropToggle.textContent=\"\u2702\ufe0f B\u1eadt khung c\u1eaft\";\n        }\n        updateCropOverlay();\n    });\n\n    if(cropReset)cropReset.addEventListener(\"click\",function(){\n        resetCropRect();\n        message.textContent=\"\u0110\u00e3 \u0111\u1eb7t l\u1ea1i v\u00f9ng c\u1eaft v\u1ec1 to\u00e0n b\u1ed9 \u1ea3nh.\";\n    });\n\n    if(cropRatio)cropRatio.addEventListener(\"change\",function(){\n        if(!mainImage)return;\n        setCropRatio(cropRatio.value);\n    });\n\n    async function applyEditorCrop(){\n        if(!mainImage){\n            message.textContent=\"Vui l\u00f2ng ch\u1ecdn \u1ea3nh tr\u01b0\u1edbc khi c\u1eaft.\";\n            return;\n        }\n        if(!cropState.active){\n            message.textContent=\"H\u00e3y b\u1eadt khung c\u1eaft v\u00e0 ch\u1ecdn v\u00f9ng c\u1ea7n c\u1eaft tr\u01b0\u1edbc.\";\n            return;\n        }\n        var dims=transformedSize();\n        var sx=Math.max(0,Math.round(cropState.x*dims.width));\n        var sy=Math.max(0,Math.round(cropState.y*dims.height));\n        var sw=Math.max(1,Math.round(cropState.w*dims.width));\n        var sh=Math.max(1,Math.round(cropState.h*dims.height));\n        sw=Math.min(sw,dims.width-sx);\n        sh=Math.min(sh,dims.height-sy);\n        if(sw<2||sh<2){\n            message.textContent=\"V\u00f9ng c\u1eaft qu\u00e1 nh\u1ecf.\";\n            return;\n        }\n        if(sw*sh>MAX_OUTPUT_PIXELS){\n            message.textContent=\"V\u00f9ng c\u1eaft qu\u00e1 l\u1edbn \u0111\u1ec3 x\u1eed l\u00fd an to\u00e0n.\";\n            return;\n        }\n\n        if(cropApply)cropApply.disabled=true;\n        try{\n            \/* Render only the transformed base image. Logo\/Text\/Emoji are NOT\n               baked into the crop, so the user can continue editing them. *\/\n            var full=document.createElement(\"canvas\");\n            full.width=dims.width;\n            full.height=dims.height;\n            var fctx=full.getContext(\"2d\");\n            drawTransformedImage(fctx,mainImage,full.width,full.height);\n\n            var out=document.createElement(\"canvas\");\n            out.width=sw;\n            out.height=sh;\n            var octx=out.getContext(\"2d\");\n            octx.drawImage(full,sx,sy,sw,sh,0,0,sw,sh);\n\n            var blob=await new Promise(function(resolve,reject){\n                out.toBlob(function(b){b?resolve(b):reject(new Error(\"Kh\u00f4ng th\u1ec3 t\u1ea1o \u1ea3nh sau khi c\u1eaft.\"));},\"image\/png\");\n            });\n            var url=URL.createObjectURL(blob);\n            var img=new Image();\n            img.onload=function(){\n                \/* Map existing overlay positions from the old transformed\n                   canvas into the new cropped canvas. *\/\n                var oldW=dims.width,oldH=dims.height;\n                if(logoImage){\n                    var oldLX=logoState.x*oldW;\n                    var oldLY=logoState.y*oldH;\n                    var oldLW=logoState.w*oldW;\n                    var oldLH=logoState.h*oldH;\n                    logoState.x=(oldLX-sx)\/sw;\n                    logoState.y=(oldLY-sy)\/sh;\n                    logoState.w=oldLW\/sw;\n                    logoState.h=oldLH\/sh;\n                    logoState.x=clamp(logoState.x,0,Math.max(0,1-logoState.w));\n                    logoState.y=clamp(logoState.y,0,Math.max(0,1-logoState.h));\n                    logoState.w=clamp(logoState.w,.001,1);\n                    logoState.h=clamp(logoState.h,.001,1);\n                }\n                objects.forEach(function(obj){\n                    var oldX=obj.x*oldW,oldY=obj.y*oldH;\n                    obj.x=(oldX-sx)\/sw;\n                    obj.y=(oldY-sy)\/sh;\n                    clampObjectPosition(obj);\n                });\n\n                mainImage=img;\n                transformState={rotation:0,flipX:false,flipY:false};\n                cropState={active:false,x:0,y:0,w:1,h:1};\n                if(cropLayer)cropLayer.classList.remove(\"active\");\n                if(preview)preview.classList.remove(\"crop-mode\");\n                if(cropToggle)cropToggle.textContent=\"\u2702\ufe0f B\u1eadt khung c\u1eaft\";\n                URL.revokeObjectURL(url);\n                full.width=1;full.height=1;out.width=1;out.height=1;\n                draw();\n                message.textContent=\"\u0110\u00e3 c\u1eaft \u1ea3nh theo \u0111\u00fang v\u00f9ng b\u1ea1n ch\u1ecdn. B\u1ea1n c\u00f3 th\u1ec3 ti\u1ebfp t\u1ee5c ch\u1ec9nh s\u1eeda.\";\n                if(cropApply)cropApply.disabled=false;\n            };\n            img.onerror=function(){\n                URL.revokeObjectURL(url);\n                if(cropApply)cropApply.disabled=false;\n                message.textContent=\"Kh\u00f4ng th\u1ec3 t\u1ea3i \u1ea3nh sau khi c\u1eaft.\";\n            };\n            img.src=url;\n        }catch(err){\n            console.error(err);\n            if(cropApply)cropApply.disabled=false;\n            message.textContent=err.message||\"Kh\u00f4ng th\u1ec3 \u00e1p d\u1ee5ng v\u00f9ng c\u1eaft.\";\n        }\n    }\n\n    if(cropApply)cropApply.addEventListener(\"click\",function(){applyEditorCrop();});\n\n    function applyTransform(action){\n        if(!mainImage){message.textContent=\"Vui l\u00f2ng ch\u1ecdn \u1ea3nh tr\u01b0\u1edbc.\";return;}\n        if(action===\"flipX\")transformState.flipX=!transformState.flipX;\n        if(action===\"flipY\")transformState.flipY=!transformState.flipY;\n        if(action===\"left\")transformState.rotation=(transformState.rotation+270)%360;\n        if(action===\"right\")transformState.rotation=(transformState.rotation+90)%360;\n        draw();\n        message.textContent=\"\u0110\u00e3 c\u1eadp nh\u1eadt bi\u1ebfn \u0111\u1ed5i \u1ea3nh.\";\n    }\n\n    if(flipXBtn)flipXBtn.addEventListener(\"click\",function(){applyTransform(\"flipX\");});\n    if(flipYBtn)flipYBtn.addEventListener(\"click\",function(){applyTransform(\"flipY\");});\n    if(rotateLeftBtn)rotateLeftBtn.addEventListener(\"click\",function(){applyTransform(\"left\");});\n    if(rotateRightBtn)rotateRightBtn.addEventListener(\"click\",function(){applyTransform(\"right\");});\n    if(resetTransformBtn)resetTransformBtn.addEventListener(\"click\",function(){\n        transformState={rotation:0,flipX:false,flipY:false};\n        draw();\n        message.textContent=\"\u0110\u00e3 \u0111\u1eb7t l\u1ea1i l\u1eadt\/xoay \u1ea3nh.\";\n    });\n\n    function startObjectDrag(e,id){\n        e.preventDefault();e.stopPropagation();\n        var obj=objects.find(function(o){return o.id===id;});if(!obj)return;\n        if(e.currentTarget&&e.currentTarget.setPointerCapture){try{e.currentTarget.setPointerCapture(e.pointerId);}catch(err){}}\n        var p=pointer(e);\n        objectDrag={id:id,sx:p.x,sy:p.y,x:obj.x,y:obj.y};\n    }\n\n    function moveObject(e){\n        if(!objectDrag)return;e.preventDefault();\n        var obj=objects.find(function(o){return o.id===objectDrag.id;});if(!obj)return;\n        var p=pointer(e),r=canvas.getBoundingClientRect();if(!r.width||!r.height)return;\n        obj.x=objectDrag.x+(p.x-objectDrag.sx)\/r.width;\n        obj.y=objectDrag.y+(p.y-objectDrag.sy)\/r.height;\n        clampObjectPosition(obj);renderObjectsOverlay();\n    }\n    document.addEventListener(\"pointermove\",moveObject,{passive:false});\n\n    function startObjectResize(e,id){\n        e.preventDefault();e.stopPropagation();\n        var obj=objects.find(function(o){return o.id===id;});if(!obj)return;\n        var p=pointer(e);objectResize={id:id,sx:p.x,sy:p.y,size:obj.size};selectedObjectId=id;\n    }\n\n    function moveObjectResize(e){\n        if(!objectResize)return;e.preventDefault();\n        var obj=objects.find(function(o){return o.id===objectResize.id;});if(!obj||!mainImage)return;\n        var p=pointer(e),r=canvas.getBoundingClientRect();if(!r.width)return;\n        var delta=((p.x-objectResize.sx)+(p.y-objectResize.sy))\/2;\n        var visualScale=r.width\/mainImage.naturalWidth;\n        obj.size=clamp(Math.round(objectResize.size+delta\/Math.max(visualScale,.0001)),MIN_SIZE,MAX_SIZE);\n        if(obj.type===\"text\")textSize.value=obj.size;else emojiSize.value=obj.size;\n        clampObjectPosition(obj);renderObjectsOverlay();\n    }\n    document.addEventListener(\"pointermove\",moveObjectResize,{passive:false});\n    document.addEventListener(\"pointerup\",function(){objectDrag=null;objectResize=null;logoDrag=null;});\n\n    imageInput.addEventListener(\"change\",async function(){\n        var f=imageInput.files&&imageInput.files[0];if(!f)return;\n        try{mainImage=await loadImage(f);objects=[];selectedObjectId=null;transformState={rotation:0,flipX:false,flipY:false};cropState={active:false,x:0,y:0,w:1,h:1};if(cropToggle)cropToggle.textContent=\"\u2702\ufe0f B\u1eadt khung c\u1eaft\";if(cropLayer)cropLayer.classList.remove(\"active\");draw();message.textContent=\"\u0110\u00e3 t\u1ea3i \u1ea3nh: \"+f.name;}catch(e){message.textContent=e.message;}\n    });\n\n    logoInput.addEventListener(\"change\",async function(){\n        var f=logoInput.files&&logoInput.files[0];if(!f)return;\n        try{\n            logoImage=await loadImage(f);\n            if(logoPreviewURL)URL.revokeObjectURL(logoPreviewURL);\n            logoPreviewURL=URL.createObjectURL(f);logoPreview.src=logoPreviewURL;\n            var w=parseInt(size.value,10)\/100;\n            logoState.w=w;logoState.x=clamp(1-w-parseInt(margin.value,10)\/100,0,1-w);\n            logoState.y=clamp(1-logoState.h-parseInt(margin.value,10)\/100,0,1-logoState.h);\n            draw();\n        }catch(e){message.textContent=e.message;}\n    });\n\n    position.addEventListener(\"change\",function(){if(position.value!==\"free\")setQuick(position.value);});\n    size.addEventListener(\"input\",function(){sizeValue.textContent=size.value+\"%\";if(logoImage){logoState.w=clamp(parseInt(size.value,10)\/100,.03,.95);logoState.x=clamp(logoState.x,0,1-logoState.w);draw();}});\n    opacity.addEventListener(\"input\",function(){opacityValue.textContent=opacity.value+\"%\";draw();});\n    margin.addEventListener(\"input\",function(){marginValue.textContent=margin.value+\"%\";if(position.value!==\"free\")setQuick(position.value);});\n\n    resetBtn.addEventListener(\"click\",function(){\n        if(!logoImage)return;\n        logoState.w=clamp(parseInt(size.value,10)\/100,.03,.95);\n        logoState.x=clamp(1-logoState.w-parseInt(margin.value,10)\/100,0,1-logoState.w);\n        logoState.y=clamp(1-logoState.h-parseInt(margin.value,10)\/100,0,1-logoState.h);\n        position.value=\"free\";draw();message.textContent=\"\u0110\u00e3 \u0111\u1eb7t l\u1ea1i v\u1ecb tr\u00ed logo.\";\n    });\n\n    addText.addEventListener(\"click\",function(){createObject(\"text\",textInput.value);});\n\n    updateText.addEventListener(\"click\",function(){\n        var obj=selectedObject();\n        if(!obj||obj.type!==\"text\"){message.textContent=\"H\u00e3y ch\u1ecdn m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng ch\u1eef tr\u01b0\u1edbc.\";return;}\n        obj.text=textInput.value||\"\";obj.color=textColor.value;obj.size=clamp(parseInt(textSize.value,10)||48,MIN_SIZE,MAX_SIZE);obj.font=textFont.value;obj.weight=parseInt(textWeight.value,10)||400;\n        clampObjectPosition(obj);renderObjectsOverlay();draw();message.textContent=\"\u0110\u00e3 c\u1eadp nh\u1eadt ch\u1eef.\";\n    });\n\n    \/* =========================================================\n       EMOJI PALETTE - V2.3\n       Build the picker entirely from data-emoji attributes.\n       This prevents WordPress wp-emoji from replacing the source\n       glyphs before our JavaScript can read them.\n       ========================================================= *\/\n    function emojiCodePoints(value){\n        return Array.from(value).map(function(ch){return ch.codePointAt(0).toString(16);}).join(\"-\");\n    }\n\n    function emojiValueFromButton(btn){\n        var v=btn.getAttribute(\"data-emoji\");\n        if(v)return v;\n        var img=btn.querySelector(\"img.emoji\");\n        if(img){\n            v=img.getAttribute(\"alt\")||img.getAttribute(\"aria-label\")||\"\";\n            if(v)return v.trim();\n        }\n        return String(btn.textContent||\"\").trim();\n    }\n\n    function makeEmojiIcon(btn,value){\n        btn.innerHTML=\"\";\n        var img=document.createElement(\"img\");\n        img.className=\"image-tools-emoji-icon\";\n        img.alt=\"\";\n        img.setAttribute(\"aria-hidden\",\"true\");\n        img.draggable=false;\n        img.decoding=\"async\";\n        img.loading=\"eager\";\n        var cp=emojiCodePoints(value);\n        var urls=[\n            \"https:\/\/cdn.jsdelivr.net\/gh\/twitter\/twemoji@14.0.2\/assets\/svg\/\"+cp+\".svg\",\n            \"https:\/\/unpkg.com\/@twitter\/twemoji@latest\/assets\/svg\/\"+cp+\".svg\"\n        ];\n        var n=0;\n        function fail(){\n            n++;\n            if(n<urls.length){img.src=urls[n];return;}\n            img.remove();\n            var span=document.createElement(\"span\");\n            span.className=\"image-tools-emoji-fallback\";\n            span.textContent=value;\n            btn.appendChild(span);\n        }\n        img.onerror=fail;\n        img.src=urls[0];\n        btn.appendChild(img);\n    }\n\n    function setupEmojiPalette(box){\n        var palette=box.querySelector(\".emoji-palette\");\n        if(!palette)return;\n        var source=Array.prototype.slice.call(palette.querySelectorAll(\".emoji-choice\"));\n        source.forEach(function(btn){\n            var value=emojiValueFromButton(btn);\n            if(!value)return;\n            btn.setAttribute(\"data-emoji\",value);\n            btn.setAttribute(\"aria-label\",value);\n            btn.setAttribute(\"title\",value+\" \u2014 nh\u1ea5n \u0111\u1ec3 ch\u1ecdn; nh\u1ea5n 2 l\u1ea7n \u0111\u1ec3 ch\u00e8n\");\n            makeEmojiIcon(btn,value);\n        });\n        return source;\n    }\n\n    var emojiButtons=setupEmojiPalette(box) || [];\n\n    function getSelectedEmoji(btn){\n        return btn.getAttribute(\"data-emoji\")||emojiValueFromButton(btn);\n    }\n\n    emojiButtons.forEach(function(btn){\n        btn.addEventListener(\"click\",function(e){\n            e.preventDefault();\n            e.stopPropagation();\n            var value=getSelectedEmoji(btn);\n            if(!value)return;\n            emojiInput.value=value;\n            emojiButtons.forEach(function(x){x.classList.remove(\"selected\");});\n            btn.classList.add(\"selected\");\n            message.textContent=mainImage?\"\u0110\u00e3 ch\u1ecdn \"+value+\". Nh\u1ea5n \u201c\u2795 Th\u00eam emoji\u201d \u0111\u1ec3 ch\u00e8n v\u00e0o \u1ea3nh.\":\"\u0110\u00e3 ch\u1ecdn \"+value+\". H\u00e3y t\u1ea3i \u1ea3nh tr\u01b0\u1edbc.\";\n        });\n        btn.addEventListener(\"dblclick\",function(e){\n            e.preventDefault();\n            e.stopPropagation();\n            var value=getSelectedEmoji(btn);\n            if(!value)return;\n            emojiInput.value=value;\n            if(mainImage){\n                createObject(\"emoji\",value);\n                message.textContent=\"\u0110\u00e3 ch\u00e8n emoji \"+value+\" v\u00e0o \u1ea3nh.\";\n            }else{\n                message.textContent=\"\u0110\u00e3 ch\u1ecdn \"+value+\". H\u00e3y t\u1ea3i \u1ea3nh tr\u01b0\u1edbc.\";\n            }\n        });\n    });\n\n    \/* Clicking the Add button is the primary insertion path. *\/\n    addEmoji.addEventListener(\"click\",function(e){\n        e.preventDefault();\n        e.stopPropagation();\n        var emoji=String(emojiInput.value||\"\").trim();\n        if(!emoji){message.textContent=\"H\u00e3y ch\u1ecdn m\u1ed9t emoji trong danh s\u00e1ch ho\u1eb7c nh\u1eadp emoji tr\u01b0\u1edbc.\";return;}\n        if(!mainImage){message.textContent=\"Vui l\u00f2ng ch\u1ecdn \u1ea3nh tr\u01b0\u1edbc khi ch\u00e8n emoji.\";return;}\n        createObject(\"emoji\",emoji);\n        message.textContent=\"\u0110\u00e3 ch\u00e8n emoji \"+emoji+\" v\u00e0o \u1ea3nh.\";\n    });\n\n    deleteObject.addEventListener(\"click\",function(){\n        if(!selectedObjectId){message.textContent=\"Ch\u01b0a ch\u1ecdn \u0111\u1ed1i t\u01b0\u1ee3ng.\";return;}\n        objects=objects.filter(function(o){return o.id!==selectedObjectId;});\n        selectedObjectId=null;renderObjectsOverlay();syncSelectedControls();draw();message.textContent=\"\u0110\u00e3 x\u00f3a \u0111\u1ed1i t\u01b0\u1ee3ng.\";\n    });\n\n    button.addEventListener(\"click\",async function(){\n        if(!mainImage){message.textContent=\"Vui l\u00f2ng ch\u1ecdn \u1ea3nh ch\u00ednh.\";return;}\n        var type=format.value;\n        if(type===\"image\/avif\"&&!canEncode(type)){message.textContent=\"Tr\u00ecnh duy\u1ec7t ch\u01b0a h\u1ed7 tr\u1ee3 xu\u1ea5t AVIF.\";return;}\n        if(mainImage.naturalWidth*mainImage.naturalHeight>MAX_OUTPUT_PIXELS){message.textContent=\"\u1ea2nh qu\u00e1 l\u1edbn \u0111\u1ec3 xu\u1ea5t an to\u00e0n. Vui l\u00f2ng d\u00f9ng \u1ea3nh nh\u1ecf h\u01a1n 40 MP.\";return;}\n        button.disabled=true;\n        try{\n            var dims=transformedSize();\n            ensureCanvasSize(dims.width,dims.height,\"\u1ea2nh xu\u1ea5t\");\n            var full=document.createElement(\"canvas\");\n            full.width=dims.width;full.height=dims.height;\n            var fctx=full.getContext(\"2d\");\n            if(type===\"image\/jpeg\"){fctx.fillStyle=\"#fff\";fctx.fillRect(0,0,full.width,full.height);}\n            drawTransformedImage(fctx,mainImage,full.width,full.height);\n            if(logoImage){\n                var lw=full.width*logoState.w,lh=logoImage.naturalHeight\/logoImage.naturalWidth*lw;\n                fctx.save();fctx.globalAlpha=parseInt(opacity.value,10)\/100;\n                fctx.drawImage(logoImage,logoState.x*full.width,logoState.y*full.height,lw,lh);\n                fctx.restore();\n            }\n            objects.forEach(function(obj){clampObjectPosition(obj);drawObjectToCanvas(fctx,obj,full.width,full.height);});\n\n            var sx=0,sy=0,sw=full.width,sh=full.height;\n            if(cropState.active){\n                sx=Math.max(0,Math.round(cropState.x*full.width));\n                sy=Math.max(0,Math.round(cropState.y*full.height));\n                sw=Math.max(1,Math.round(cropState.w*full.width));\n                sh=Math.max(1,Math.round(cropState.h*full.height));\n                sw=Math.min(sw,full.width-sx);\n                sh=Math.min(sh,full.height-sy);\n            }\n            ensureCanvasSize(sw,sh,\"V\u00f9ng c\u1eaft\");\n            var out=document.createElement(\"canvas\");\n            out.width=sw;out.height=sh;\n            var ctx=out.getContext(\"2d\");\n            if(type===\"image\/jpeg\"){ctx.fillStyle=\"#fff\";ctx.fillRect(0,0,out.width,out.height);}\n            ctx.drawImage(full,sx,sy,sw,sh,0,0,sw,sh);\n            var blob=await makeBlob(out,type,.9);\n            if(!blob)throw new Error(\"Kh\u00f4ng th\u1ec3 t\u1ea1o \u1ea3nh.\");\n            if(resultURL)URL.revokeObjectURL(resultURL);\n            resultURL=URL.createObjectURL(blob);\n            var ext=extensionFromMime(type);\n            result.innerHTML='<div class=\"result-card\"><img decoding=\"async\" src=\"'+resultURL+'\" alt=\"\u1ea2nh \u0111\u00e3 ch\u1ec9nh s\u1eeda\"><strong>'+formatBytes(blob.size)+'<\/strong><\/div>';\n            var link=document.createElement(\"a\");link.className=\"download-link\";link.textContent=\"\u2b07\ufe0f T\u1ea3i \u1ea3nh xu\u1ed1ng\";link.href=resultURL;link.download=\"edited-image.\"+ext;result.querySelector(\".result-card\").appendChild(link);\n            message.textContent=\"\u0110\u00e3 xu\u1ea5t \u1ea3nh th\u00e0nh c\u00f4ng.\";\n            out.width=1;out.height=1;full.width=1;full.height=1;\n        }catch(e){console.error(e);message.textContent=e.message||\"Kh\u00f4ng th\u1ec3 xu\u1ea5t \u1ea3nh.\";}\n        finally{button.disabled=false;}\n    });\n\n    window.addEventListener(\"resize\",function(){if(mainImage){requestAnimationFrame(function(){updateLogoOverlay();renderObjectsOverlay();updateCropOverlay();});}});\n    if(window.ResizeObserver){var ro=new ResizeObserver(function(){if(mainImage){updateLogoOverlay();renderObjectsOverlay();updateCropOverlay();}});ro.observe(preview);}\n\n    setupDragDrop(imageInput.closest(\".itool-upload\"));\n    setupDragDrop(logoInput.closest(\".itool-upload\"));\n}\n\n\n\/* =========================================================\n   BATCH LOGO\n   ========================================================= *\/\n\nfunction initBatchWatermark(box){\n\n    var imagesInput=box.querySelector(\".batch-images\");\n    var logoInput=box.querySelector(\".batch-logo\");\n\n    var position=box.querySelector(\".batch-position\");\n    var size=box.querySelector(\".batch-size\");\n    var opacity=box.querySelector(\".batch-opacity\");\n    var margin=box.querySelector(\".batch-margin\");\n    var format=box.querySelector(\".batch-format\");\n\n    var sizeValue=box.querySelector(\".batch-size-value\");\n    var opacityValue=box.querySelector(\".batch-opacity-value\");\n    var marginValue=box.querySelector(\".batch-margin-value\");\n\n    var list=box.querySelector(\".batch-file-list\");\n    var button=box.querySelector(\".batch-start\");\n    var result=box.querySelector(\".batch-result\");\n    var message=box.querySelector(\".editor-mode-panel[data-editor-mode-panel='batch'] .itool-message\");\n\n    var images=[];\n    var logo=null;\n    var resultURLs=[];\n    var MAX_BATCH_FILES=100;\n    var MAX_BATCH_PIXELS=40000000;\n\n    imagesInput.addEventListener(\n        \"change\",\n        function(){\n            images=Array.from(imagesInput.files||[]);\n            if(images.length>MAX_BATCH_FILES){\n                images=images.slice(0,MAX_BATCH_FILES);\n                message.textContent=\"Batch t\u1ed1i \u0111a \"+MAX_BATCH_FILES+\" \u1ea3nh m\u1ed7i l\u1ea7n.\";\n            }\n\n            list.innerHTML=\"\";\n\n            images.forEach(\n                function(file,index){\n\n                    var row=document.createElement(\"div\");\n                    row.className=\"file-row\";\n\n                    row.innerHTML=\n                        \"<span>\"+\n                        \"<strong>\"+(index+1)+\". \"+\n                        escapeHtml(file.name)+\n                        \"<\/strong>\"+\n                        \"<\/span>\"+\n                        '<span class=\"file-size\">'+\n                        formatBytes(file.size)+\n                        \"<\/span>\";\n\n                    list.appendChild(row);\n                }\n            );\n\n            message.textContent=\n                \"\u0110\u00e3 ch\u1ecdn \"+images.length+\" \u1ea3nh.\";\n        }\n    );\n\n    logoInput.addEventListener(\n        \"change\",\n        function(){\n            logo=logoInput.files&&logoInput.files[0]\n                ? logoInput.files[0]\n                : null;\n\n            if(logo){\n                message.textContent=\n                    \"\u0110\u00e3 ch\u1ecdn logo: \"+logo.name;\n            }\n        }\n    );\n\n    size.addEventListener(\n        \"input\",\n        function(){\n            sizeValue.textContent=\n                size.value+\"%\";\n        }\n    );\n\n    opacity.addEventListener(\n        \"input\",\n        function(){\n            opacityValue.textContent=\n                opacity.value+\"%\";\n        }\n    );\n\n    margin.addEventListener(\n        \"input\",\n        function(){\n            marginValue.textContent=\n                margin.value+\"%\";\n        }\n    );\n\n    button.addEventListener(\n        \"click\",\n        async function(){\n\n            if(!images.length){\n                message.textContent=\n                    \"Vui l\u00f2ng ch\u1ecdn \u00edt nh\u1ea5t m\u1ed9t \u1ea3nh.\";\n                return;\n            }\n\n            if(!logo){\n                message.textContent=\n                    \"Vui l\u00f2ng ch\u1ecdn logo.\";\n                return;\n            }\n\n            var type=format.value;\n\n            if(\n                type===\"image\/avif\" &&\n                !canEncode(type)\n            ){\n                message.textContent=\n                    \"Tr\u00ecnh duy\u1ec7t ch\u01b0a h\u1ed7 tr\u1ee3 AVIF.\";\n                return;\n            }\n\n            button.disabled=true;\n            resultURLs.forEach(function(url){try{URL.revokeObjectURL(url);}catch(e){}});\n            resultURLs=[];\n            result.innerHTML=\"\";\n            message.textContent=\n                \"\u0110ang \u0111\u1ecdc logo...\";\n\n            try{\n\n                var logoImage=await loadImage(logo);\n\n                var grid=document.createElement(\"div\");\n                grid.className=\"batch-result-grid\";\n\n                result.appendChild(grid);\n\n                var logoSize=\n                    parseInt(size.value,10)\/100;\n\n                var alpha=\n                    parseInt(opacity.value,10)\/100;\n\n                var marginPct=\n                    parseInt(margin.value,10)\/100;\n\n                for(var i=0;i<images.length;i++){\n\n                    var file=images[i];\n\n                    message.textContent=\n                        \"\u0110ang x\u1eed l\u00fd \"+(i+1)+\"\/\"+\n                        images.length+\": \"+\n                        file.name;\n\n                    try{\n\n                        var image=await loadImage(file);\n\n                        ensureCanvasSize(image.naturalWidth,image.naturalHeight,\"\u1ea2nh batch\");\n                        var out=document.createElement(\"canvas\");\n                        out.width=image.naturalWidth;\n                        out.height=image.naturalHeight;\n\n                        var ctx=out.getContext(\"2d\");\n\n                        if(type===\"image\/jpeg\"){\n                            ctx.fillStyle=\"#fff\";\n                            ctx.fillRect(\n                                0,\n                                0,\n                                out.width,\n                                out.height\n                            );\n                        }\n\n                        ctx.drawImage(\n                            image,\n                            0,\n                            0,\n                            out.width,\n                            out.height\n                        );\n\n                        var lw=\n                            out.width*logoSize;\n\n                        var lh=\n                            logoImage.naturalHeight\/\n                            logoImage.naturalWidth*\n                            lw;\n\n                        var x=marginPct*out.width;\n                        var y=marginPct*out.height;\n\n                        switch(position.value){\n\n                            case\"top-left\":\n                                break;\n\n                            case\"top-center\":\n                                x=(out.width-lw)\/2;\n                                break;\n\n                            case\"top-right\":\n                                x=out.width-lw-marginPct*out.width;\n                                break;\n\n                            case\"center\":\n                                x=(out.width-lw)\/2;\n                                y=(out.height-lh)\/2;\n                                break;\n\n                            case\"bottom-left\":\n                                y=out.height-lh-marginPct*out.height;\n                                break;\n\n                            case\"bottom-center\":\n                                x=(out.width-lw)\/2;\n                                y=out.height-lh-marginPct*out.height;\n                                break;\n\n                            case\"bottom-right\":\n                                x=out.width-lw-marginPct*out.width;\n                                y=out.height-lh-marginPct*out.height;\n                                break;\n                        }\n\n                        x=Math.max(\n                            0,\n                            Math.min(\n                                out.width-lw,\n                                x\n                            )\n                        );\n\n                        y=Math.max(\n                            0,\n                            Math.min(\n                                out.height-lh,\n                                y\n                            )\n                        );\n\n                        ctx.save();\n\n                        ctx.globalAlpha=alpha;\n\n                        ctx.drawImage(\n                            logoImage,\n                            x,\n                            y,\n                            lw,\n                            lh\n                        );\n\n                        ctx.restore();\n\n                        var blob=await makeBlob(\n                            out,\n                            type,\n                            .9\n                        );\n\n                        if(!blob){\n                            throw new Error(\n                                \"Kh\u00f4ng t\u1ea1o \u0111\u01b0\u1ee3c \u1ea3nh.\"\n                            );\n                        }\n\n                        var ext=\n                            extensionFromMime(type);\n\n                        var base=\n                            file.name.replace(\n                                \/\\.[^\/.]+$\/,\n                                \"\"\n                            );\n\n                        var name=\n                            base+\n                            \"-logo.\"+\n                            ext;\n\n                        var item=\n                            document.createElement(\"div\");\n\n                        item.className=\n                            \"batch-result-item\";\n\n                        var url=URL.createObjectURL(blob);\n                        resultURLs.push(url);\n\n                        item.innerHTML=\n                            '<img decoding=\"async\" src=\"'+url+'\" alt=\"\">'+\n                            \"<strong>\"+\n                            escapeHtml(name)+\n                            \"<\/strong><br>\"+\n                            '<span class=\"file-size\">'+\n                            formatBytes(blob.size)+\n                            \"<\/span><br>\";\n\n                        var link=\n                            document.createElement(\"a\");\n\n                        link.className=\n                            \"download-link\";\n\n                        link.textContent=\n                            \"\u2b07\ufe0f T\u1ea3i\";\n\n                        link.href=url;\n                        link.download=name;\n\n                        item.appendChild(link);\n                        grid.appendChild(item);\n\n                        out.width=1;\n                        out.height=1;\n\n                    }catch(e){\n\n                        var err=\n                            document.createElement(\"div\");\n\n                        err.className=\n                            \"batch-result-item\";\n\n                        err.textContent=\n                            \"L\u1ed7i: \"+file.name;\n\n                        grid.appendChild(err);\n\n                        console.error(e);\n                    }\n\n                    await sleep(0);\n                }\n\n                message.textContent=\n                    \"\u0110\u00e3 x\u1eed l\u00fd xong \"+images.length+\" \u1ea3nh. \"+\n                    \"B\u1ea1n c\u00f3 th\u1ec3 t\u1ea3i t\u1eebng \u1ea3nh \u1edf ph\u00eda tr\u00ean.\";\n\n            }catch(e){\n\n                console.error(e);\n\n                message.textContent=\n                    e.message||\n                    \"Kh\u00f4ng th\u1ec3 x\u1eed l\u00fd h\u00e0ng lo\u1ea1t.\";\n\n            }finally{\n                button.disabled=false;\n            }\n        }\n    );\n\n    setupDragDrop(\n        imagesInput.closest(\".itool-upload\")\n    );\n\n    setupDragDrop(\n        logoInput.closest(\".itool-upload\")\n    );\n}\n\n\n\/* =========================================================\n   INITIALIZE\n   ========================================================= *\/\n\nfunction initialize(){\n\n    document.querySelectorAll(\n        \".itools-wrapper\"\n    ).forEach(function(wrapper){\n\n        if(wrapper.dataset.initialized===\"1\"){\n            return;\n        }\n\n        wrapper.dataset.initialized=\"1\";\n\n        initTabs(wrapper);\n\n        wrapper.querySelectorAll(\n            \"[data-module]\"\n        ).forEach(function(box){\n\n            var m=box.dataset.module;\n\n            if(m===\"compressor\"){\n                initCompressor(box);\n            }\n\n            if(m===\"cropper\"){\n                initCropper(box);\n            }\n\n            if(m===\"converter\"){\n                initConverter(box);\n            }\n\n            if(m===\"watermark\"){\n                initWatermark(box);\n            }\n        });\n    });\n}\n\nif(document.readyState===\"loading\"){\n    document.addEventListener(\n        \"DOMContentLoaded\",\n        initialize\n    );\n}else{\n    initialize();\n}\n\n})();\n<\/script>\n        \n<p>S\u1eed d\u1ee5ng <strong>c\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh tr\u1ef1c tuy\u1ebfn<\/strong> mang l\u1ea1i nhi\u1ec1u ti\u1ec7n l\u1ee3i khi b\u1ea1n ch\u1ec9 c\u1ea7n chuy\u1ec3n \u0111\u1ed5i m\u1ed9t ho\u1eb7c m\u1ed9t v\u00e0i h\u00ecnh \u1ea3nh. Kh\u00f4ng c\u1ea7n c\u00e0i \u0111\u1eb7t ph\u1ea7n m\u1ec1m, kh\u00f4ng c\u1ea7n th\u1ef1c hi\u1ec7n c\u00e1c b\u01b0\u1edbc ch\u1ec9nh s\u1eeda ph\u1ee9c t\u1ea1p v\u00e0 c\u00f3 th\u1ec3 thao t\u00e1c tr\u1ef1c ti\u1ebfp tr\u00ean tr\u00ecnh duy\u1ec7t.<\/p>\n<p>\u0110\u1eb7c bi\u1ec7t, c\u00f4ng c\u1ee5 <strong>\u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh online<\/strong> c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng tr\u00ean m\u00e1y t\u00ednh, \u0111i\u1ec7n tho\u1ea1i ho\u1eb7c m\u00e1y t\u00ednh b\u1ea3ng, mi\u1ec5n l\u00e0 thi\u1ebft b\u1ecb c\u00f3 tr\u00ecnh duy\u1ec7t web h\u1ed7 tr\u1ee3.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Cach_doi_dinh_dang_anh_online\"><\/span>C\u00e1ch \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh online<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>\u0110\u1ec3 chuy\u1ec3n \u0111\u1ed5i h\u00ecnh \u1ea3nh, b\u1ea1n th\u01b0\u1eddng ch\u1ec9 c\u1ea7n th\u1ef1c hi\u1ec7n c\u00e1c b\u01b0\u1edbc \u0111\u01a1n gi\u1ea3n:<\/p>\n<ul>\n<li>T\u1ea3i \u1ea3nh l\u00ean: Ch\u1ecdn h\u00ecnh \u1ea3nh c\u1ea7n chuy\u1ec3n \u0111\u1ed5i t\u1eeb thi\u1ebft b\u1ecb.<\/li>\n<li>Ch\u1ecdn \u0111\u1ecbnh d\u1ea1ng: L\u1ef1a ch\u1ecdn \u0111\u1ecbnh d\u1ea1ng \u1ea3nh mu\u1ed1n chuy\u1ec3n sang, ch\u1eb3ng h\u1ea1n JPG, PNG ho\u1eb7c WebP.<\/li>\n<li>B\u1eaft \u0111\u1ea7u chuy\u1ec3n \u0111\u1ed5i: Nh\u1ea5n n\u00fat chuy\u1ec3n \u0111\u1ed5i v\u00e0 ch\u1edd qu\u00e1 tr\u00ecnh x\u1eed l\u00fd ho\u00e0n t\u1ea5t.<\/li>\n<li>T\u1ea3i \u1ea3nh xu\u1ed1ng: Sau khi chuy\u1ec3n \u0111\u1ed5i th\u00e0nh c\u00f4ng, t\u1ea3i file \u1ea3nh m\u1edbi v\u1ec1 thi\u1ebft b\u1ecb.<\/li>\n<\/ul>\n<p>V\u1edbi nh\u1eefng c\u00f4ng c\u1ee5 x\u1eed l\u00fd tr\u1ef1c ti\u1ebfp tr\u00ean tr\u00ecnh duy\u1ec7t, qu\u00e1 tr\u00ecnh chuy\u1ec3n \u0111\u1ed5i c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c th\u1ef1c hi\u1ec7n nhanh ch\u00f3ng m\u00e0 kh\u00f4ng c\u1ea7n c\u00e0i \u0111\u1eb7t th\u00eam ph\u1ea7n m\u1ec1m.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Khi_nao_nen_doi_JPG_sang_PNG\"><\/span>Khi n\u00e0o n\u00ean \u0111\u1ed5i JPG sang PNG?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>B\u1ea1n n\u00ean chuy\u1ec3n JPG sang PNG khi c\u1ea7n s\u1eed d\u1ee5ng h\u00ecnh \u1ea3nh \u1edf \u0111\u1ecbnh d\u1ea1ng PNG, \u0111\u1eb7c bi\u1ec7t trong tr\u01b0\u1eddng h\u1ee3p h\u00ecnh \u1ea3nh c\u1ea7n h\u1ed7 tr\u1ee3 n\u1ec1n trong su\u1ed1t ho\u1eb7c c\u1ea7n ti\u1ebfp t\u1ee5c x\u1eed l\u00fd b\u1eb1ng c\u00e1c ph\u1ea7n m\u1ec1m \u0111\u1ed3 h\u1ecda.<\/p>\n<p>Tuy nhi\u00ean, vi\u1ec7c chuy\u1ec3n JPG sang PNG kh\u00f4ng th\u1ec3 kh\u00f4i ph\u1ee5c nh\u1eefng th\u00f4ng tin \u0111\u00e3 b\u1ecb m\u1ea5t do qu\u00e1 tr\u00ecnh n\u00e9n JPG tr\u01b0\u1edbc \u0111\u00f3. V\u00ec v\u1eady, n\u1ebfu \u1ea3nh JPG ban \u0111\u1ea7u \u0111\u00e3 b\u1ecb gi\u1ea3m ch\u1ea5t l\u01b0\u1ee3ng th\u00ec chuy\u1ec3n sang PNG s\u1ebd kh\u00f4ng l\u00e0m \u1ea3nh tr\u1edf l\u1ea1i ch\u1ea5t l\u01b0\u1ee3ng ban \u0111\u1ea7u.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Khi_nao_nen_chuyen_PNG_sang_JPG\"><\/span>Khi n\u00e0o n\u00ean chuy\u1ec3n PNG sang JPG?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Chuy\u1ec3n PNG sang JPG th\u01b0\u1eddng ph\u00f9 h\u1ee3p khi b\u1ea1n mu\u1ed1n gi\u1ea3m dung l\u01b0\u1ee3ng file ho\u1eb7c c\u1ea7n s\u1eed d\u1ee5ng h\u00ecnh \u1ea3nh tr\u00ean m\u1ed9t n\u1ec1n t\u1ea3ng ch\u1ec9 h\u1ed7 tr\u1ee3 JPG. JPG th\u01b0\u1eddng c\u00f3 dung l\u01b0\u1ee3ng nh\u1ecf h\u01a1n PNG \u0111\u1ed1i v\u1edbi nhi\u1ec1u lo\u1ea1i \u1ea3nh, \u0111\u1eb7c bi\u1ec7t l\u00e0 \u1ea3nh ch\u1ee5p.<\/p>\n<p>N\u1ebfu h\u00ecnh \u1ea3nh c\u00f3 n\u1ec1n trong su\u1ed1t, c\u1ea7n l\u01b0u \u00fd r\u1eb1ng JPG kh\u00f4ng h\u1ed7 tr\u1ee3 transparency. Ph\u1ea7n n\u1ec1n trong su\u1ed1t c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c thay th\u1ebf b\u1eb1ng m\u1ed9t m\u00e0u n\u1ec1n khi chuy\u1ec3n \u0111\u1ed5i.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Chuyen_WebP_sang_JPG_co_tac_dung_gi\"><\/span>Chuy\u1ec3n WebP sang JPG c\u00f3 t\u00e1c d\u1ee5ng g\u00ec?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>WebP \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng ph\u1ed5 bi\u1ebfn tr\u00ean website nh\u1edd kh\u1ea3 n\u0103ng t\u1ed1i \u01b0u dung l\u01b0\u1ee3ng h\u00ecnh \u1ea3nh. Tuy nhi\u00ean, m\u1ed9t s\u1ed1 ph\u1ea7n m\u1ec1m, thi\u1ebft b\u1ecb ho\u1eb7c n\u1ec1n t\u1ea3ng c\u00f3 th\u1ec3 ch\u01b0a h\u1ed7 tr\u1ee3 WebP theo c\u00e1ch b\u1ea1n c\u1ea7n. Trong tr\u01b0\u1eddng h\u1ee3p \u0111\u00f3, chuy\u1ec3n WebP sang JPG gi\u00fap h\u00ecnh \u1ea3nh d\u1ec5 t\u01b0\u01a1ng th\u00edch h\u01a1n v\u1edbi nhi\u1ec1u \u1ee9ng d\u1ee5ng v\u00e0 n\u1ec1n t\u1ea3ng.<\/p>\n<p>Ng\u01b0\u1ee3c l\u1ea1i, n\u1ebfu m\u1ee5c ti\u00eau c\u1ee7a b\u1ea1n l\u00e0 t\u1ed1i \u01b0u h\u00ecnh \u1ea3nh cho website, JPG sang WebP c\u00f3 th\u1ec3 l\u00e0 l\u1ef1a ch\u1ecdn \u0111\u00e1ng c\u00e2n nh\u1eafc \u0111\u1ec3 gi\u1ea3m dung l\u01b0\u1ee3ng file v\u00e0 c\u1ea3i thi\u1ec7n t\u1ed1c \u0111\u1ed9 t\u1ea3i trang.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Cong_cu_doi_dinh_dang_anh_online_co_can_cai_phan_mem_khong\"><\/span>C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh online c\u00f3 c\u1ea7n c\u00e0i ph\u1ea7n m\u1ec1m kh\u00f4ng?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Kh\u00f4ng. B\u1ea1n c\u00f3 th\u1ec3 s\u1eed d\u1ee5ng <strong>c\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh<\/strong> ngay tr\u00ean tr\u00ecnh duy\u1ec7t m\u00e0 kh\u00f4ng c\u1ea7n c\u00e0i \u0111\u1eb7t th\u00eam ph\u1ea7n m\u1ec1m chuy\u00ean d\u1ee5ng. \u0110i\u1ec1u n\u00e0y gi\u00fap qu\u00e1 tr\u00ecnh chuy\u1ec3n \u0111\u1ed5i \u1ea3nh tr\u1edf n\u00ean nhanh ch\u00f3ng v\u00e0 thu\u1eadn ti\u1ec7n h\u01a1n, \u0111\u1eb7c bi\u1ec7t khi b\u1ea1n ch\u1ec9 c\u1ea7n <a href=\"https:\/\/app.mitunet.com\/home\/chinh-sua-anh\/\">ch\u1ec9nh s\u1eeda h\u00ecnh \u1ea3nh<\/a> kh\u00f4ng th\u01b0\u1eddng xuy\u00ean.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Mot_so_luu_y_khi_chuyen_doi_dinh_dang_anh\"><\/span>M\u1ed9t s\u1ed1 l\u01b0u \u00fd khi chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Tr\u01b0\u1edbc khi t\u1ea3i \u1ea3nh v\u1ec1, b\u1ea1n n\u00ean ki\u1ec3m tra l\u1ea1i \u0111\u1ecbnh d\u1ea1ng v\u00e0 ch\u1ea5t l\u01b0\u1ee3ng file sau khi chuy\u1ec3n \u0111\u1ed5i. N\u1ebfu h\u00ecnh \u1ea3nh c\u1ea7n s\u1eed d\u1ee5ng tr\u00ean website, h\u00e3y c\u00e2n nh\u1eafc c\u1ea3 ch\u1ea5t l\u01b0\u1ee3ng v\u00e0 dung l\u01b0\u1ee3ng \u0111\u1ec3 tr\u00e1nh l\u00e0m t\u0103ng th\u1eddi gian t\u1ea3i trang.<\/p>\n<p>Ngo\u00e0i ra, v\u1edbi nh\u1eefng h\u00ecnh \u1ea3nh quan tr\u1ecdng, b\u1ea1n n\u00ean gi\u1eef l\u1ea1i file g\u1ed1c \u0111\u1ec3 c\u00f3 th\u1ec3 s\u1eed d\u1ee5ng ho\u1eb7c chuy\u1ec3n \u0111\u1ed5i sang \u0111\u1ecbnh d\u1ea1ng kh\u00e1c khi c\u1ea7n.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Chuyen_doi_anh_nhanh_chong_ngay_tren_trinh_duyet\"><\/span>Chuy\u1ec3n \u0111\u1ed5i \u1ea3nh nhanh ch\u00f3ng ngay tr\u00ean tr\u00ecnh duy\u1ec7t<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Thay v\u00ec c\u00e0i \u0111\u1eb7t ph\u1ea7n m\u1ec1m ch\u1ec9 \u0111\u1ec3 th\u1ef1c hi\u1ec7n m\u1ed9t thao t\u00e1c \u0111\u01a1n gi\u1ea3n, b\u1ea1n c\u00f3 th\u1ec3 s\u1eed d\u1ee5ng <strong>c\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh online<\/strong> ngay tr\u00ean trang n\u00e0y. Ch\u1ecdn h\u00ecnh \u1ea3nh c\u1ea7n chuy\u1ec3n \u0111\u1ed5i, l\u1ef1a ch\u1ecdn \u0111\u1ecbnh d\u1ea1ng \u0111\u1ea7u ra ph\u00f9 h\u1ee3p v\u00e0 t\u1ea3i file m\u1edbi v\u1ec1 thi\u1ebft b\u1ecb.<\/p>\n<p>C\u00f4ng c\u1ee5 ph\u00f9 h\u1ee3p cho nhi\u1ec1u nhu c\u1ea7u nh\u01b0 \u0111\u1ed5i JPG sang PNG, PNG sang JPG, JPG sang WebP, WebP sang JPG v\u00e0 c\u00e1c tr\u01b0\u1eddng h\u1ee3p c\u1ea7n thay \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh kh\u00e1c.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh online gi\u00fap b\u1ea1n chuy\u1ec3n \u0111\u1ed5i h\u00ecnh \u1ea3nh nhanh ch\u00f3ng m\u00e0 kh\u00f4ng c\u1ea7n c\u00e0i \u0111\u1eb7t ph\u1ea7n m\u1ec1m chuy\u00ean d\u1ee5ng. Ch\u1ec9 c\u1ea7n t\u1ea3i \u1ea3nh l\u00ean, l\u1ef1a ch\u1ecdn \u0111\u1ecbnh d\u1ea1ng mong mu\u1ed1n v\u00e0 th\u1ef1c hi\u1ec7n chuy\u1ec3n \u0111\u1ed5i, b\u1ea1n \u0111\u00e3 c\u00f3 th\u1ec3 s\u1eed d\u1ee5ng file \u1ea3nh m\u1edbi trong th\u1eddi gian ng\u1eafn. \u0110\u00e2y l\u00e0 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":557,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[27],"class_list":["post-550","post","type-post","status-publish","format-standard","has-post-thumbnail","category-tien-ich","tag-hinh-anh"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh mi\u1ec5n ph\u00ed | App Mitunet<\/title>\n<meta name=\"description\" content=\"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online mi\u1ec5n ph\u00ed, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh tr\u1ef1c tuy\u1ebfn hi\u1ec7u qu\u1ea3, \u0111\u01a1n gi\u1ea3n, nhanh ch\u00f3ng, gi\u1ea3i ph\u00e1p chuy\u1ec3n \u0111\u1ed5i h\u00ecnh \u1ea3nh nhanh ch\u00f3ng...\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/\" \/>\n<meta property=\"og:locale\" content=\"vi_VN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh mi\u1ec5n ph\u00ed | App Mitunet\" \/>\n<meta property=\"og:description\" content=\"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online mi\u1ec5n ph\u00ed, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh tr\u1ef1c tuy\u1ebfn hi\u1ec7u qu\u1ea3, \u0111\u01a1n gi\u1ea3n, nhanh ch\u00f3ng, gi\u1ea3i ph\u00e1p chuy\u1ec3n \u0111\u1ed5i h\u00ecnh \u1ea3nh nhanh ch\u00f3ng...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/\" \/>\n<meta property=\"og:site_name\" content=\"App Mitunet\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-10T15:14:19+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-11T04:15:31+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/app.mitunet.com\/home\/wp-content\/uploads\/2026\/08\/doi-dinh-dang-anh-online.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"800\" \/>\n\t<meta property=\"og:image:height\" content=\"600\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Ho\u00e0ng Nguy\u1ec5n\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u0110\u01b0\u1ee3c vi\u1ebft b\u1edfi\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ho\u00e0ng Nguy\u1ec5n\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u01af\u1edbc t\u00ednh th\u1eddi gian \u0111\u1ecdc\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 ph\u00fat\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/doi-duoi-anh\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/doi-duoi-anh\\\/\"},\"author\":{\"name\":\"Ho\u00e0ng Nguy\u1ec5n\",\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/#\\\/schema\\\/person\\\/f50dd8aac6139374e226f02e17793e76\"},\"headline\":\"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh mi\u1ec5n ph\u00ed\",\"datePublished\":\"2026-08-10T15:14:19+00:00\",\"dateModified\":\"2026-08-11T04:15:31+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/doi-duoi-anh\\\/\"},\"wordCount\":1244,\"image\":{\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/doi-duoi-anh\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/doi-dinh-dang-anh-online.jpg\",\"keywords\":[\"H\u00ecnh \u1ea3nh\"],\"articleSection\":[\"Ti\u1ec7n \u00edch\"],\"inLanguage\":\"vi\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/doi-duoi-anh\\\/\",\"url\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/doi-duoi-anh\\\/\",\"name\":\"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh mi\u1ec5n ph\u00ed | App Mitunet\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/doi-duoi-anh\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/doi-duoi-anh\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/doi-dinh-dang-anh-online.jpg\",\"datePublished\":\"2026-08-10T15:14:19+00:00\",\"dateModified\":\"2026-08-11T04:15:31+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/#\\\/schema\\\/person\\\/f50dd8aac6139374e226f02e17793e76\"},\"description\":\"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online mi\u1ec5n ph\u00ed, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh tr\u1ef1c tuy\u1ebfn hi\u1ec7u qu\u1ea3, \u0111\u01a1n gi\u1ea3n, nhanh ch\u00f3ng, gi\u1ea3i ph\u00e1p chuy\u1ec3n \u0111\u1ed5i h\u00ecnh \u1ea3nh nhanh ch\u00f3ng...\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/doi-duoi-anh\\\/#breadcrumb\"},\"inLanguage\":\"vi\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/app.mitunet.com\\\/home\\\/doi-duoi-anh\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"vi\",\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/doi-duoi-anh\\\/#primaryimage\",\"url\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/doi-dinh-dang-anh-online.jpg\",\"contentUrl\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/doi-dinh-dang-anh-online.jpg\",\"width\":800,\"height\":600,\"caption\":\"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh online mi\u1ec5n ph\u00ed\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/doi-duoi-anh\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Trang ch\u1ee7\",\"item\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh mi\u1ec5n ph\u00ed\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/#website\",\"url\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/\",\"name\":\"App Mitunet\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"vi\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/app.mitunet.com\\\/home\\\/#\\\/schema\\\/person\\\/f50dd8aac6139374e226f02e17793e76\",\"name\":\"Ho\u00e0ng Nguy\u1ec5n\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"vi\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f94a5ef7ba48bd05196f8ba8340b297655b9adc8975f99cf481c8e14dea06e48?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f94a5ef7ba48bd05196f8ba8340b297655b9adc8975f99cf481c8e14dea06e48?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f94a5ef7ba48bd05196f8ba8340b297655b9adc8975f99cf481c8e14dea06e48?s=96&d=mm&r=g\",\"caption\":\"Ho\u00e0ng Nguy\u1ec5n\"},\"description\":\"Hi, m\u00ecnh l\u00e0 Ho\u00e0ng Nguy\u1ec5n, \u0111\u00e3 t\u1eebng c\u00f3 h\u01a1n 10 n\u0103m kinh nghi\u1ec7m trong l\u0129nh v\u1ef1c Marketing Online nh\u01b0 SEO, qu\u1ea3ng c\u00e1o, content, qu\u1ea3n tr\u1ecb web, ph\u00e1t tri\u1ec5n website... Hy v\u1ecdng v\u1edbi nh\u1eefng n\u1ed9i dung c\u1ee7a m\u00ecnh s\u1ebb gi\u00fap b\u1ea1n hi\u1ec7u qu\u1ea3 h\u01a1n trong vi\u1ec7c marketing online.\",\"sameAs\":[\"https:\\\/\\\/app.mitunet.com\\\/home\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh mi\u1ec5n ph\u00ed | App Mitunet","description":"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online mi\u1ec5n ph\u00ed, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh tr\u1ef1c tuy\u1ebfn hi\u1ec7u qu\u1ea3, \u0111\u01a1n gi\u1ea3n, nhanh ch\u00f3ng, gi\u1ea3i ph\u00e1p chuy\u1ec3n \u0111\u1ed5i h\u00ecnh \u1ea3nh nhanh ch\u00f3ng...","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/","og_locale":"vi_VN","og_type":"article","og_title":"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh mi\u1ec5n ph\u00ed | App Mitunet","og_description":"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online mi\u1ec5n ph\u00ed, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh tr\u1ef1c tuy\u1ebfn hi\u1ec7u qu\u1ea3, \u0111\u01a1n gi\u1ea3n, nhanh ch\u00f3ng, gi\u1ea3i ph\u00e1p chuy\u1ec3n \u0111\u1ed5i h\u00ecnh \u1ea3nh nhanh ch\u00f3ng...","og_url":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/","og_site_name":"App Mitunet","article_published_time":"2026-08-10T15:14:19+00:00","article_modified_time":"2026-08-11T04:15:31+00:00","og_image":[{"width":800,"height":600,"url":"https:\/\/app.mitunet.com\/home\/wp-content\/uploads\/2026\/08\/doi-dinh-dang-anh-online.jpg","type":"image\/jpeg"}],"author":"Ho\u00e0ng Nguy\u1ec5n","twitter_card":"summary_large_image","twitter_misc":{"\u0110\u01b0\u1ee3c vi\u1ebft b\u1edfi":"Ho\u00e0ng Nguy\u1ec5n","\u01af\u1edbc t\u00ednh th\u1eddi gian \u0111\u1ecdc":"5 ph\u00fat"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#article","isPartOf":{"@id":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/"},"author":{"name":"Ho\u00e0ng Nguy\u1ec5n","@id":"https:\/\/app.mitunet.com\/home\/#\/schema\/person\/f50dd8aac6139374e226f02e17793e76"},"headline":"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh mi\u1ec5n ph\u00ed","datePublished":"2026-08-10T15:14:19+00:00","dateModified":"2026-08-11T04:15:31+00:00","mainEntityOfPage":{"@id":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/"},"wordCount":1244,"image":{"@id":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#primaryimage"},"thumbnailUrl":"https:\/\/app.mitunet.com\/home\/wp-content\/uploads\/2026\/08\/doi-dinh-dang-anh-online.jpg","keywords":["H\u00ecnh \u1ea3nh"],"articleSection":["Ti\u1ec7n \u00edch"],"inLanguage":"vi"},{"@type":"WebPage","@id":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/","url":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/","name":"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh mi\u1ec5n ph\u00ed | App Mitunet","isPartOf":{"@id":"https:\/\/app.mitunet.com\/home\/#website"},"primaryImageOfPage":{"@id":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#primaryimage"},"image":{"@id":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#primaryimage"},"thumbnailUrl":"https:\/\/app.mitunet.com\/home\/wp-content\/uploads\/2026\/08\/doi-dinh-dang-anh-online.jpg","datePublished":"2026-08-10T15:14:19+00:00","dateModified":"2026-08-11T04:15:31+00:00","author":{"@id":"https:\/\/app.mitunet.com\/home\/#\/schema\/person\/f50dd8aac6139374e226f02e17793e76"},"description":"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online mi\u1ec5n ph\u00ed, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh tr\u1ef1c tuy\u1ebfn hi\u1ec7u qu\u1ea3, \u0111\u01a1n gi\u1ea3n, nhanh ch\u00f3ng, gi\u1ea3i ph\u00e1p chuy\u1ec3n \u0111\u1ed5i h\u00ecnh \u1ea3nh nhanh ch\u00f3ng...","breadcrumb":{"@id":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#breadcrumb"},"inLanguage":"vi","potentialAction":[{"@type":"ReadAction","target":["https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/"]}]},{"@type":"ImageObject","inLanguage":"vi","@id":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#primaryimage","url":"https:\/\/app.mitunet.com\/home\/wp-content\/uploads\/2026\/08\/doi-dinh-dang-anh-online.jpg","contentUrl":"https:\/\/app.mitunet.com\/home\/wp-content\/uploads\/2026\/08\/doi-dinh-dang-anh-online.jpg","width":800,"height":600,"caption":"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng \u1ea3nh online mi\u1ec5n ph\u00ed"},{"@type":"BreadcrumbList","@id":"https:\/\/app.mitunet.com\/home\/doi-duoi-anh\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Trang ch\u1ee7","item":"https:\/\/app.mitunet.com\/home\/"},{"@type":"ListItem","position":2,"name":"C\u00f4ng c\u1ee5 \u0111\u1ed5i \u0111u\u00f4i \u1ea3nh online, chuy\u1ec3n \u0111\u1ed5i \u0111\u1ecbnh d\u1ea1ng h\u00ecnh \u1ea3nh mi\u1ec5n ph\u00ed"}]},{"@type":"WebSite","@id":"https:\/\/app.mitunet.com\/home\/#website","url":"https:\/\/app.mitunet.com\/home\/","name":"App Mitunet","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/app.mitunet.com\/home\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"vi"},{"@type":"Person","@id":"https:\/\/app.mitunet.com\/home\/#\/schema\/person\/f50dd8aac6139374e226f02e17793e76","name":"Ho\u00e0ng Nguy\u1ec5n","image":{"@type":"ImageObject","inLanguage":"vi","@id":"https:\/\/secure.gravatar.com\/avatar\/f94a5ef7ba48bd05196f8ba8340b297655b9adc8975f99cf481c8e14dea06e48?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/f94a5ef7ba48bd05196f8ba8340b297655b9adc8975f99cf481c8e14dea06e48?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f94a5ef7ba48bd05196f8ba8340b297655b9adc8975f99cf481c8e14dea06e48?s=96&d=mm&r=g","caption":"Ho\u00e0ng Nguy\u1ec5n"},"description":"Hi, m\u00ecnh l\u00e0 Ho\u00e0ng Nguy\u1ec5n, \u0111\u00e3 t\u1eebng c\u00f3 h\u01a1n 10 n\u0103m kinh nghi\u1ec7m trong l\u0129nh v\u1ef1c Marketing Online nh\u01b0 SEO, qu\u1ea3ng c\u00e1o, content, qu\u1ea3n tr\u1ecb web, ph\u00e1t tri\u1ec5n website... Hy v\u1ecdng v\u1edbi nh\u1eefng n\u1ed9i dung c\u1ee7a m\u00ecnh s\u1ebb gi\u00fap b\u1ea1n hi\u1ec7u qu\u1ea3 h\u01a1n trong vi\u1ec7c marketing online.","sameAs":["https:\/\/app.mitunet.com\/home"]}]}},"_links":{"self":[{"href":"https:\/\/app.mitunet.com\/home\/wp-json\/wp\/v2\/posts\/550","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/app.mitunet.com\/home\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/app.mitunet.com\/home\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/app.mitunet.com\/home\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/app.mitunet.com\/home\/wp-json\/wp\/v2\/comments?post=550"}],"version-history":[{"count":6,"href":"https:\/\/app.mitunet.com\/home\/wp-json\/wp\/v2\/posts\/550\/revisions"}],"predecessor-version":[{"id":556,"href":"https:\/\/app.mitunet.com\/home\/wp-json\/wp\/v2\/posts\/550\/revisions\/556"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/app.mitunet.com\/home\/wp-json\/wp\/v2\/media\/557"}],"wp:attachment":[{"href":"https:\/\/app.mitunet.com\/home\/wp-json\/wp\/v2\/media?parent=550"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/app.mitunet.com\/home\/wp-json\/wp\/v2\/categories?post=550"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/app.mitunet.com\/home\/wp-json\/wp\/v2\/tags?post=550"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}