{"id":47838,"date":"2025-10-28T10:02:05","date_gmt":"2025-10-28T17:02:05","guid":{"rendered":"https:\/\/www.caninca.com\/?page_id=47838"},"modified":"2025-10-28T18:24:26","modified_gmt":"2025-10-29T01:24:26","slug":"tools","status":"publish","type":"page","link":"https:\/\/www.caninca.com\/en\/tools\/","title":{"rendered":"Herramientas"},"content":{"rendered":"\n<div class=\"wp-block-columns alignfull is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<!DOCTYPE html>\n<html lang=\"es\">\n<head>

<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script id="bv-lazyload-images" data-cfasync="false" bv-exclude="true">var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:!0});var bv_lazyload_event_listener="load",bv_lazyload_events=["mousemove","click","keydown","wheel","touchmove","touchend"],bv_use_srcset_attr=!1,BV_DEBUG_MODE=!1,bv_style_observer,img_observer,picture_lazy_observer,bvImageLazyloadObserverOptions={rootMargin:"200px 0px",threshold:.01};function bvDebugLog(...args){BV_DEBUG_MODE&&console.log(...args)}__name(bvDebugLog,"bvDebugLog"),bv_lazyload_event_listener=="load"?window.addEventListener("load",event=>{handle_lazyload_images()}):bv_lazyload_event_listener=="readystatechange"&&document.addEventListener("readystatechange",event=>{document.readyState==="interactive"&&handle_lazyload_images()});function add_lazyload_image_event_listeners(handle_lazyload_images2){bv_lazyload_events.forEach(function(event){document.addEventListener(event,handle_lazyload_images2,!0)})}__name(add_lazyload_image_event_listeners,"add_lazyload_image_event_listeners");function remove_lazyload_image_event_listeners(){bv_lazyload_events.forEach(function(event){document.removeEventListener(event,handle_lazyload_images,!0)})}__name(remove_lazyload_image_event_listeners,"remove_lazyload_image_event_listeners");function bv_replace_lazyloaded_image_url(element2){let src_value=element2.getAttribute("bv-data-src"),srcset_value=element2.getAttribute("bv-data-srcset"),currentSrc=element2.getAttribute("src");currentSrc&&currentSrc.startsWith("data:image/svg+xml")&&(bv_use_srcset_attr&&srcset_value&&element2.setAttribute("srcset",srcset_value),src_value&&element2.setAttribute("src",src_value))}__name(bv_replace_lazyloaded_image_url,"bv_replace_lazyloaded_image_url");function bv_replace_inline_style_image_url(element2){let bv_style_attr=element2.getAttribute("bv-data-style");if(bvDebugLog(bv_style_attr),bv_style_attr){let currentStyles=element2.getAttribute("style")||"",newStyle=currentStyles+(currentStyles?";":"")+bv_style_attr;element2.setAttribute("style",newStyle)}else bvDebugLog("BV_STYLE_ATTRIBUTE_NOT_FOUND : "+entry)}__name(bv_replace_inline_style_image_url,"bv_replace_inline_style_image_url");function handleLazyloadImages(entries){entries.map(entry2=>{entry2.isIntersecting&&(bv_replace_lazyloaded_image_url(entry2.target),img_observer.unobserve(entry2.target))})}__name(handleLazyloadImages,"handleLazyloadImages");function handleOnscreenInlineStyleImages(entries){entries.map(entry2=>{entry2.isIntersecting&&(bv_replace_inline_style_image_url(entry2.target),bv_style_observer.unobserve(entry2.target))})}__name(handleOnscreenInlineStyleImages,"handleOnscreenInlineStyleImages");function handlePictureTags(entries){entries.map(entry2=>{entry2.isIntersecting&&(bv_replace_picture_tag_url(entry2.target),picture_lazy_observer.unobserve(entry2.target))})}__name(handlePictureTags,"handlePictureTags");function bv_replace_picture_tag_url(element2){const child_elements=element2.children;for(let i=0;i<child_elements.length;i++){let child_elem=child_elements[i],_srcset=child_elem.getAttribute("bv-data-srcset"),_src=child_elem.getAttribute("bv-data-src");_srcset&&child_elem.setAttribute("srcset",_srcset),_src&&child_elem.setAttribute("src",_src),bv_replace_picture_tag_url(child_elem)}}__name(bv_replace_picture_tag_url,"bv_replace_picture_tag_url"),"IntersectionObserver"in window&&(bv_style_observer=new IntersectionObserver(handleOnscreenInlineStyleImages,bvImageLazyloadObserverOptions),img_observer=new IntersectionObserver(handleLazyloadImages,bvImageLazyloadObserverOptions),picture_lazy_observer=new IntersectionObserver(handlePictureTags,bvImageLazyloadObserverOptions));function handle_lazyload_images(){"IntersectionObserver"in window?(document.querySelectorAll(".bv-lazyload-bg-style").forEach(target_element=>{bv_style_observer.observe(target_element)}),document.querySelectorAll(".bv-lazyload-tag-img").forEach(img_element=>{img_observer.observe(img_element)}),document.querySelectorAll(".bv-lazyload-picture").forEach(picture_element=>{picture_lazy_observer.observe(picture_element)})):(document.querySelectorAll(".bv-lazyload-bg-style").forEach(target_element=>{bv_replace_inline_style_image_url(target_element)}),document.querySelectorAll(".bv-lazyload-tag-img").forEach(target_element=>{bv_replace_lazyloaded_image_url(target_element)}),document.querySelectorAll(".bv-lazyload-picture").forEach(picture_element=>{bv_replace_picture_tag_url(element)}))}__name(handle_lazyload_images,"handle_lazyload_images");
</script>

<script id="bv-viewport-image-hydration" data-cfasync="false" bv-exclude="true">var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:!0});const BV_VIEWPORT_IMAGE_CLASS="bv-viewport-img",BV_VIEWPORT_IMAGE_SWAP_MODE="src";function getViewportImageDeviceName(){let device_width=window.outerWidth;return device_width<=480?"mobile":device_width<=820?"ipad":device_width<=1536?"desktop":"large"}__name(getViewportImageDeviceName,"getViewportImageDeviceName");function getViewportHydrationAttr(node_element,attr_suffix,device_name){let attr_names=["bv-data-"+device_name+"-"+attr_suffix,"bv-data-large-"+attr_suffix];for(let index=0;index<attr_names.length;index+=1){let attr_value=node_element.getAttribute(attr_names[index]);if(attr_value)return attr_value}return""}__name(getViewportHydrationAttr,"getViewportHydrationAttr");function hydrateViewportImage(node_element){if(!node_element||node_element.getAttribute("bv-viewport-hydrated")==="1")return;let hydrated=!1,device_name=getViewportImageDeviceName(),src_attr=getViewportHydrationAttr(node_element,"src",device_name),srcset_attr=getViewportHydrationAttr(node_element,"srcset",device_name);BV_VIEWPORT_IMAGE_SWAP_MODE==="srcset"&&srcset_attr&&(node_element.setAttribute("srcset",srcset_attr),hydrated=!0),src_attr&&(node_element.setAttribute("src",src_attr),hydrated=!0),hydrated&&node_element.setAttribute("bv-viewport-hydrated","1")}__name(hydrateViewportImage,"hydrateViewportImage");function hydrateViewportImages(){let img_tags=document.querySelectorAll("."+BV_VIEWPORT_IMAGE_CLASS);setTimeout(()=>{img_tags.forEach(img_tag=>{hydrateViewportImage(img_tag)})},0)}__name(hydrateViewportImages,"hydrateViewportImages"),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",hydrateViewportImages):hydrateViewportImages();
</script>

<script id="bv-dl-scripts-list" data-cfasync="false" bv-exclude="true">
var scriptAttrs = [{"attrs":{"src":"\\"https:\\/\\/cdnjs.cloudflare.com\\/ajax\\/libs\\/jspdf\\/2.5.1\\/jspdf.umd.min.js\\"","defer":true,"data-cfasync":false,"async":false,"bv_inline_delayed":false},"bv_unique_id":"VBz6a2JlRLcUfktU6NJO","reference":0},{"attrs":{"defer":true,"data-cfasync":false,"async":false,"bv_inline_delayed":true},"bv_unique_id":"3x51cHVKuZsDQOdvvPKJ","reference":1},{"attrs":{"src":"data:text\/javascript;base64, ZnVuY3Rpb24gYnZfdHJpZ2dlcl9haXJsaWZ0X2V2ZW50KHRhcmdldCwgZXZlbnRfdHlwZSkgewoKICBpZiAoZXZlbnRfdHlwZSA9PT0gInJlYWR5c3RhdGVjaGFuZ2UiKSB7CiAgICBpZiAoYnZfc3RhdGVfY291bnRlciA9PT0gMCkgewogICAgICBidl9zdGF0ZV9jb3VudGVyICs9IDEKICAgICAgYnZfY3VzdG9tX3JlYWR5X3N0YXRlX3ZhbHVlID0gImludGVyYWN0aXZlIgogICAgfSBlbHNlIGlmKGJ2X3N0YXRlX2NvdW50ZXIgPT09IDEpIHsKICAgICAgYnZfc3RhdGVfY291bnRlciArPSAxCiAgICAgIGJ2X2N1c3RvbV9yZWFkeV9zdGF0ZV92YWx1ZSA9ICJjb21wbGV0ZSIKICAgIH0KICB9CgogIGxldCBldmVudF9uYW1lID0gIkFpcmxpZnQiICsgZXZlbnRfdHlwZTsKICBsZXQgc3ludGhldGljRXZlbnQgPSBuZXcgRXZlbnQoZXZlbnRfbmFtZSwgeyBidWJibGVzOiB0cnVlIH0pOwoKICBPYmplY3QuZGVmaW5lUHJvcGVydHkoc3ludGhldGljRXZlbnQsICJ0eXBlIiwgeyBnZXQ6IGZ1bmN0aW9uICgpIHsgcmV0dXJuIGV2ZW50X3R5cGU7IH0sIHNldDogZnVuY3Rpb24gKCkgeyB9IH0pOwogIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShzeW50aGV0aWNFdmVudCwgInRhcmdldCIsIHsgZ2V0OiBmdW5jdGlvbiAoKSB7IHJldHVybiB0YXJnZXQ7IH0sIHNldDogZnVuY3Rpb24gKCkgeyB9IH0pOwoKICB0YXJnZXQuZGlzcGF0Y2hFdmVudChzeW50aGV0aWNFdmVudCk7Cn0KCmZ1bmN0aW9uIGJ2X2Rpc3BhdGNoRGVsYXllZEV2ZW50cygpIHsKICAgIGJ2X3RyaWdnZXJfYWlybGlmdF9ldmVudChkb2N1bWVudCwgInJlYWR5c3RhdGVjaGFuZ2UiKTsKICAgIGJ2X3RyaWdnZXJfYWlybGlmdF9ldmVudChkb2N1bWVudCwgIkRPTUNvbnRlbnRMb2FkZWQiKTsKCiAgICBzZXRUaW1lb3V0KCgpID0+IHsKICAgICAgICBidl90cmlnZ2VyX2FpcmxpZnRfZXZlbnQoZG9jdW1lbnQsICJyZWFkeXN0YXRlY2hhbmdlIik7CgogICAgICAgIHNldFRpbWVvdXQoKCkgPT4gewogICAgICAgICAgICBidl90cmlnZ2VyX2FpcmxpZnRfZXZlbnQod2luZG93LCAibG9hZCIpOwogICAgICAgICAgICBidl90cmlnZ2VyX2FpcmxpZnRfZXZlbnQod2luZG93LCAicGFnZXNob3ciKTsKICAgICAgICB9LCAwKTsKICAgIH0sIDApOwp9Cgp2YXIgYnZfc3RhdGVfY291bnRlciA9IDAKCmJ2X2Rpc3BhdGNoRGVsYXllZEV2ZW50cygpOwoK","id":"bv-trigger-listener","type":"text\/javascript","defer":true,"async":false},"bv_unique_id":"d6fe9c24159ed4a27d224d91c70f8fdc","reference":100000000}];
</script>
<script id="bv-web-worker" type="javascript/worker" data-cfasync="false" bv-exclude="true">var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:!0});var BV_DEBUG_MODE=!1;function bvDebugLog(...args){BV_DEBUG_MODE&&console.log(...args)}__name(bvDebugLog,"bvDebugLog"),self.onmessage=function(e){var counter=e.data.fetch_urls.length;e.data.fetch_urls.forEach(function(fetch_url){loadUrl(fetch_url,function(){bvDebugLog("DONE: "+fetch_url),counter=counter-1,counter===0&&self.postMessage({status:"SUCCESS"})})})};async function loadUrl(fetch_url,callback){try{var request=new Request(fetch_url,{mode:"no-cors",redirect:"follow"});await fetch(request),callback()}catch(fetchError){bvDebugLog("Fetch Error loading URL:",fetchError);try{var xhr=new XMLHttpRequest;xhr.onerror=callback,xhr.onload=callback,xhr.responseType="blob",xhr.open("GET",fetch_url,!0),xhr.send()}catch(xhrError){bvDebugLog("XHR Error loading URL:",xhrError),callback()}}}__name(loadUrl,"loadUrl");
</script>
<script id="bv-web-worker-handler" data-cfasync="false" bv-exclude="true">var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:!0});if(typeof scriptAttrs<"u"&&Array.isArray(scriptAttrs)&&scriptAttrs.length>0){const lastElement=scriptAttrs[scriptAttrs.length-1];if(lastElement.attrs&&lastElement.attrs.id==="bv-trigger-listener"){var bv_custom_ready_state_value="loading";Object.defineProperty(document,"readyState",{get:__name(function(){return bv_custom_ready_state_value},"get"),set:__name(function(){},"set")})}}if(typeof scriptAttrs>"u"||!Array.isArray(scriptAttrs))var scriptAttrs=[];if(typeof linkStyleAttrs>"u"||!Array.isArray(linkStyleAttrs))var linkStyleAttrs=[];var BV_DEBUG_MODE=!1;function bvDebugLog(...args){BV_DEBUG_MODE&&console.log(...args)}__name(bvDebugLog,"bvDebugLog");function isMobileDevice(){return window.innerWidth<=500}__name(isMobileDevice,"isMobileDevice");var js_dom_loaded=!1;document.addEventListener("DOMContentLoaded",()=>{js_dom_loaded=!0});const EVENTS=["mousemove","click","keydown","wheel","touchmove","touchend"];var scriptUrls=[],styleUrls=[],bvEventCalled=!1,workerFinished=!1,functionExec=!1,scriptsInjected=!1,stylesInjected=!1,bv_load_event_fired=!1,autoInjectTimerStarted=!1;const BV_AUTO_INJECT_ENABLED=!0,BV_DESKTOP_AUTO_INJECT_DELAY=4e3,BV_MOBILE_AUTO_INJECT_DELAY=1e4,BV_WORKER_TIMEOUT_DURATION=3e3;scriptAttrs.forEach((scriptAttr,index)=>{scriptAttr.attrs.src&&!scriptAttr.attrs.src.includes("data:text/javascript")&&(scriptUrls[index]=scriptAttr.attrs.src)}),linkStyleAttrs.forEach((linkAttr,index)=>{styleUrls[index]=linkAttr.attrs.href});var fetchUrls=scriptUrls.concat(styleUrls);function addEventListeners(bvEventHandler2){EVENTS.forEach(function(event){document.addEventListener(event,bvEventFired,!0),document.addEventListener(event,bvEventHandler2,!0)})}__name(addEventListeners,"addEventListeners");function removeEventListeners(){EVENTS.forEach(function(event){document.removeEventListener(event,bvEventHandler,!0)})}__name(removeEventListeners,"removeEventListeners");function bvEventFired(){bvEventCalled||(bvEventCalled=!0,workerFinished=!0)}__name(bvEventFired,"bvEventFired");function bvGetElement(attributes,element){Object.keys(attributes).forEach(function(attr){attr==="async"?element.async=attributes[attr]:attr==="innerHTML"?element.innerHTML=atob(attributes[attr]):element.setAttribute(attr,attributes[attr])})}__name(bvGetElement,"bvGetElement");function bvAddElement(attr,element){var attributes=attr.attrs;if(attributes.bv_inline_delayed){let bvScriptId=attr.bv_unique_id,bvScriptElement=document.querySelector("[bv_unique_id='"+bvScriptId+"']");bvScriptElement?(!attributes.innerHTML&&!attributes.src&&bvScriptElement.textContent.trim()!==""&&(attributes.src="data:text/javascript;base64, "+btoa(unescape(encodeURIComponent(bvScriptElement.textContent)))),bvGetElement(attributes,element),bvScriptElement.after(element)):bvDebugLog(`Script not found for ${bvScriptId}`)}else{bvGetElement(attributes,element);var templateId=attr.bv_unique_id,targetElement=document.querySelector("[id='"+templateId+"']");targetElement&&targetElement.after(element)}}__name(bvAddElement,"bvAddElement");function injectStyles(){if(stylesInjected){bvDebugLog("Styles already injected, skipping");return}stylesInjected=!0,document.querySelectorAll('style[type="bv_inline_delayed_css"], template[id]').forEach(element=>{if(element.tagName.toLowerCase()==="style"){var new_style=document.createElement("style");new_style.type="text/css",new_style.textContent=element.textContent,element.after(new_style),new_style.parentNode?element.remove():bvDebugLog("PARENT NODE NOT FOUND")}else if(element.tagName.toLowerCase()==="template"){var templateId=element.id,linkStyleAttr=linkStyleAttrs.find(attr=>attr.bv_unique_id===templateId);if(linkStyleAttr){var link=document.createElement("link");bvAddElement(linkStyleAttr,link),element.parentNode&&element.parentNode.replaceChild(link,element),bvDebugLog("EXTERNAL STYLE ADDED")}else bvDebugLog(`No linkStyleAttr found for template ID ${templateId}`)}}),linkStyleAttrs.forEach((linkStyleAttr,index)=>{bvDebugLog("STYLE ADDED");var element=document.createElement("link");bvAddElement(linkStyleAttr,element)})}__name(injectStyles,"injectStyles");function injectScripts(){if(scriptsInjected){bvDebugLog("Scripts already injected, skipping");return}scriptsInjected=!0;let last_script_element;scriptAttrs.forEach((scriptAttr,index)=>{if(bv_custom_ready_state_value==="loading"&&scriptAttr.attrs&&scriptAttr.attrs.is_first_defer_element===!0)if(last_script_element){const readyStateScript=document.createElement("script");readyStateScript.src="data:text/javascript;base64, "+btoa(unescape(encodeURIComponent("bv_custom_ready_state_value = 'interactive';"))),readyStateScript.async=!1,last_script_element.after(readyStateScript)}else bv_custom_ready_state_value="interactive",bvDebugLog('Ready state manually set to "interactive"');bvDebugLog("JS ADDED");var element=document.createElement("script");last_script_element=element,bvAddElement(scriptAttr,element)})}__name(injectScripts,"injectScripts");function bvEventHandler(){bvDebugLog("EVENT FIRED"),js_dom_loaded&&bvEventCalled&&workerFinished&&!functionExec&&(functionExec=!0,injectStyles(),injectScripts(),removeEventListeners())}__name(bvEventHandler,"bvEventHandler");function autoInjectScriptsAfterLoad(){js_dom_loaded&&workerFinished&&!scriptsInjected&&!stylesInjected&&(bvDebugLog("Auto-injecting styles and scripts after timer"),injectStyles(),injectScripts())}__name(autoInjectScriptsAfterLoad,"autoInjectScriptsAfterLoad");function startAutoInjectTimer(){if(BV_AUTO_INJECT_ENABLED&&!autoInjectTimerStarted&&bv_load_event_fired&&!bvEventCalled){autoInjectTimerStarted=!0;var delay=isMobileDevice()?BV_MOBILE_AUTO_INJECT_DELAY:BV_DESKTOP_AUTO_INJECT_DELAY;bvDebugLog("Starting auto-inject timer with delay: "+delay+"ms"),setTimeout(function(){autoInjectScriptsAfterLoad()},delay)}}__name(startAutoInjectTimer,"startAutoInjectTimer"),addEventListeners(bvEventHandler);var requestObject=window.URL||window.webkitURL,bvWorker=new Worker(requestObject.createObjectURL(new Blob([document.getElementById("bv-web-worker").textContent],{type:"text/javascript"})));bvWorker.onmessage=function(e){e.data.status==="SUCCESS"&&(bvDebugLog("WORKER_FINISHED"),workerFinished=!0,bvEventHandler(),startAutoInjectTimer())},addEventListener("load",()=>{bvEventHandler(),bv_call_fetch_urls(),bv_load_event_fired=!0});function bv_call_fetch_urls(){!bv_load_event_fired&&!workerFinished&&(bvWorker.postMessage({fetch_urls:fetchUrls}),bv_initiate_worker_timer())}__name(bv_call_fetch_urls,"bv_call_fetch_urls"),setTimeout(function(){bv_call_fetch_urls()},5e3);function bv_initiate_worker_timer(){setTimeout(function(){workerFinished||(bvDebugLog("WORKER_TIMEDOUT"),workerFinished=!0,bvWorker.terminate()),bvEventHandler(),startAutoInjectTimer()},BV_WORKER_TIMEOUT_DURATION)}__name(bv_initiate_worker_timer,"bv_initiate_worker_timer");
</script>
\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Caninca &#8211; Herramientas Gratuitas<\/title>\n    <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Montserrat:wght@400;600;700&#038;family=Poppins:wght@300;400;500&#038;display=swap\" rel=\"stylesheet\">\n    <style>\n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n\n        body {\n            font-family: 'Poppins', sans-serif;\n            line-height: 1.6;\n            color: #333;\n        }\n\n        h1, h2, h3 {\n            font-family: 'Montserrat', sans-serif;\n        }\n\n        \/* Hero *\/\n        .hero {\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n            color: white;\n            padding: 6rem 2rem;\n            text-align: center;\n        }\n\n        .hero h1 {\n            font-size: 3rem;\n            margin-bottom: 1rem;\n            color: #fff;\n        }\n\n        .hero p {\n            font-size: 1.3rem;\n            margin-bottom: 2rem;\n            opacity: 0.9;\n        }\n\n        .cta-button {\n            background: white;\n            color: #667eea;\n            padding: 1rem 2.5rem;\n            border: none;\n            border-radius: 50px;\n            font-size: 1.1rem;\n            font-weight: 600;\n            cursor: pointer;\n            transition: transform 0.3s, box-shadow 0.3s;\n        }\n\n        .cta-button:hover {\n            transform: translateY(-3px);\n            box-shadow: 0 10px 20px rgba(0,0,0,0.2);\n        }\n\n        \/* Tools Section *\/\n        .tools-section {\n            max-width: 1200px;\n            margin: 4rem auto;\n            padding: 2rem;\n        }\n\n        .tools-section h2 {\n            text-align: center;\n            font-size: 2.5rem;\n            margin-bottom: 3rem;\n            color: #2563eb;\n        }\n\n        .tools-grid {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n            gap: 2rem;\n        }\n\n        .tool-card {\n            background: white;\n            padding: 2rem;\n            border-radius: 10px;\n            box-shadow: 0 4px 6px rgba(0,0,0,0.1);\n            cursor: pointer;\n            transition: transform 0.3s, box-shadow 0.3s;\n        }\n\n        .tool-card:hover {\n            transform: translateY(-5px);\n            box-shadow: 0 10px 20px rgba(0,0,0,0.15);\n        }\n\n        .tool-card h3 {\n            color: #2563eb;\n            margin-bottom: 0.5rem;\n        }\n\n        .tool-card p {\n            font-size: 0.9rem;\n            color: #666;\n        }\n\n        \/* Modal *\/\n        .modal {\n            display: none;\n            position: fixed;\n            z-index: 2000;\n            left: 0;\n            top: 0;\n            width: 100%;\n            height: 100%;\n            background: rgba(0,0,0,0.6);\n            overflow: auto;\n        }\n\n        .modal-content {\n            background: white;\n            margin: 2% auto;\n            padding: 2rem;\n            border-radius: 10px;\n            max-width: 700px;\n            max-height: 90vh;\n            overflow-y: auto;\n        }\n\n        .close {\n            float: right;\n            font-size: 2rem;\n            font-weight: bold;\n            cursor: pointer;\n            color: #999;\n        }\n\n        .close:hover {\n            color: #333;\n        }\n\n        .tool-interface {\n            margin-top: 2rem;\n        }\n\n        textarea, input[type=\"text\"], input[type=\"file\"], input[type=\"number\"] {\n            width: 100%;\n            padding: 0.8rem;\n            margin: 0.5rem 0;\n            border: 1px solid #ddd;\n            border-radius: 5px;\n            font-family: 'Poppins', sans-serif;\n        }\n\n        textarea {\n            min-height: 150px;\n            resize: vertical;\n        }\n\n        .btn {\n            background: #2563eb;\n            color: white;\n            padding: 0.8rem 2rem;\n            border: none;\n            border-radius: 5px;\n            cursor: pointer;\n            font-weight: 600;\n            margin: 0.5rem 0.5rem 0.5rem 0;\n            transition: background 0.3s;\n        }\n\n        .btn:hover {\n            background: #1d4ed8;\n        }\n\n        .stats {\n            background: #f3f4f6;\n            padding: 1rem;\n            border-radius: 5px;\n            margin: 1rem 0;\n        }\n\n        \/* Features *\/\n        .features {\n            background: #f9fafb;\n            padding: 4rem 2rem;\n        }\n\n        .features-container {\n            max-width: 1200px;\n            margin: 0 auto;\n        }\n\n        .features h2 {\n            text-align: center;\n            font-size: 2.5rem;\n            margin-bottom: 3rem;\n            color: #2563eb;\n        }\n\n        .features-grid {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n            gap: 2rem;\n        }\n\n        .feature-item {\n            text-align: center;\n            padding: 2rem;\n        }\n\n        .feature-icon {\n            font-size: 3rem;\n            margin-bottom: 1rem;\n        }\n\n        \/* Testimonials *\/\n        .testimonials {\n            max-width: 1200px;\n            margin: 4rem auto;\n            padding: 2rem;\n        }\n\n        .testimonials h2 {\n            text-align: center;\n            font-size: 2.5rem;\n            margin-bottom: 3rem;\n            color: #2563eb;\n        }\n\n        .testimonial-grid {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n            gap: 2rem;\n        }\n\n        .testimonial-card {\n            background: white;\n            padding: 2rem;\n            border-radius: 10px;\n            box-shadow: 0 4px 6px rgba(0,0,0,0.1);\n        }\n\n        .testimonial-card p {\n            font-style: italic;\n            margin-bottom: 1rem;\n        }\n\n        .testimonial-author {\n            font-weight: 600;\n            color: #2563eb;\n        }\n\n        \/* FAQ *\/\n        .faq {\n            background: #f9fafb;\n            padding: 4rem 2rem;\n        }\n\n        .faq-container {\n            max-width: 800px;\n            margin: 0 auto;\n        }\n\n        .faq h2 {\n            text-align: center;\n            font-size: 2.5rem;\n            margin-bottom: 3rem;\n            color: #2563eb;\n        }\n\n        .faq-item {\n            background: white;\n            margin-bottom: 1rem;\n            border-radius: 5px;\n            overflow: hidden;\n        }\n\n        .faq-question {\n            padding: 1.5rem;\n            cursor: pointer;\n            font-weight: 600;\n            background: white;\n            border: none;\n            width: 100%;\n            text-align: left;\n            font-size: 1rem;\n            font-family: 'Poppins', sans-serif;\n        }\n\n        .faq-question:hover {\n            background: #f3f4f6;\n        }\n\n        .faq-answer {\n            display: none;\n            padding: 0 1.5rem 1.5rem;\n        }\n\n        .faq-answer.active {\n            display: block;\n        }\n\n        .output-image {\n            max-width: 100%;\n            margin: 1rem 0;\n            border: 1px solid #ddd;\n            border-radius: 5px;\n        }\n\n        #qrCanvas {\n            margin: 1rem auto;\n            display: block;\n            border: 1px solid #ddd;\n            border-radius: 5px;\n        }\n    <\/style>\n<\/head>\n<body>\n    \n    <section class=\"hero\" id=\"inicio\">\n        <h1>Herramientas Gratuitas en L\u00ednea<\/h1>\n        <p>Todas las utilidades que necesitas en un solo lugar<\/p>\n        <button class=\"cta-button\" onclick=\"document.getElementById('herramientas').scrollIntoView({behavior: 'smooth'})\">\n            Explorar Herramientas\n        <\/button>\n    <\/section>\n\n    \n    <section class=\"tools-section\" id=\"herramientas\">\n        <h2>Nuestras Herramientas<\/h2>\n        <div class=\"tools-grid\">\n            <div class=\"tool-card\" onclick=\"openModal('contadorPalabras')\">\n                <h3>\ud83d\udcdd Contador de Palabras<\/h3>\n                <p>Cuenta palabras, caracteres y p\u00e1rrafos en un texto<\/p>\n            <\/div>\n            <div class=\"tool-card\" onclick=\"openModal('convertidorMayusculas')\">\n                <h3>\ud83d\udd24 Convertidor de May\u00fasculas<\/h3>\n                <p>Cambia entre may\u00fasculas, min\u00fasculas y formato t\u00edtulo<\/p>\n            <\/div>\n            <div class=\"tool-card\" onclick=\"openModal('eliminadorEspacios')\">\n                <h3>\u2728 Eliminador de Espacios<\/h3>\n                <p>Limpia texto pegado desde documentos o PDFs<\/p>\n            <\/div>\n            <div class=\"tool-card\" onclick=\"openModal('contadorCaracteres')\">\n                <h3>\ud83d\udd22 Contador de Caracteres<\/h3>\n                <p>Cuenta caracteres con o sin espacios<\/p>\n            <\/div>\n            <div class=\"tool-card\" onclick=\"openModal('imagenTexto')\">\n                <h3>\ud83d\udcf7 Imagen a Texto (OCR)<\/h3>\n                <p>Extrae texto de im\u00e1genes, \u00fatil para tareas y facturas<\/p>\n            <\/div>\n            <div class=\"tool-card\" onclick=\"openModal('pdfJpg')\">\n                <h3>\ud83d\udcc4 PDF a JPG<\/h3>\n                <p>Convierte p\u00e1ginas de un PDF en im\u00e1genes JPG<\/p>\n            <\/div>\n            <div class=\"tool-card\" onclick=\"openModal('jpgPdf')\">\n                <h3>\ud83d\uddbc\ufe0f JPG a PDF<\/h3>\n                <p>Crea un PDF a partir de una o varias im\u00e1genes JPG<\/p>\n            <\/div>\n            <div class=\"tool-card\" onclick=\"openModal('wordPdf')\">\n                <h3>\ud83d\udcdd Word a PDF<\/h3>\n                <p>Transforma documentos de Word (.docx) en PDF<\/p>\n            <\/div>\n            <div class=\"tool-card\" onclick=\"openModal('pdfWord')\">\n                <h3>\ud83d\udcc4 PDF a Word<\/h3>\n                <p>Convierte un PDF en un archivo editable de Word<\/p>\n            <\/div>\n            <div class=\"tool-card\" onclick=\"openModal('compresorImagenes')\">\n                <h3>\ud83d\udddc\ufe0f Compresor de Im\u00e1genes<\/h3>\n                <p>Reduce el tama\u00f1o de las im\u00e1genes sin perder calidad<\/p>\n            <\/div>\n            <div class=\"tool-card\" onclick=\"openModal('calculadoraIva')\">\n                <h3>\ud83d\udcb0 Calculadora de IVA<\/h3>\n                <p>Calcula el precio con o sin IVA al 16%<\/p>\n            <\/div>\n            <div class=\"tool-card\" onclick=\"openModal('generadorQr')\">\n                <h3>\ud83d\udcf1 Generador de QR<\/h3>\n                <p>Crea c\u00f3digos QR para enlaces y textos<\/p>\n            <\/div>\n        <\/div>\n    <\/section>\n\n    \n    <section class=\"features\" id=\"caracteristicas\">\n        <div class=\"features-container\">\n            <h2>\u00bfPor qu\u00e9 Caninca?<\/h2>\n            <div class=\"features-grid\">\n                <div class=\"feature-item\">\n                    <div class=\"feature-icon\">\ud83d\ude80<\/div>\n                    <h3>R\u00e1pido y Eficiente<\/h3>\n                    <p>Procesa tus archivos al instante sin esperas<\/p>\n                <\/div>\n                <div class=\"feature-item\">\n                    <div class=\"feature-icon\">\ud83d\udd12<\/div>\n                    <h3>100% Seguro<\/h3>\n                    <p>Tus archivos se procesan localmente en tu navegador<\/p>\n                <\/div>\n                <div class=\"feature-item\">\n                    <div class=\"feature-icon\">\ud83d\udcaf<\/div>\n                    <h3>Gratis para Siempre<\/h3>\n                    <p>Herramientas b\u00e1sicas sin costo alguno<\/p>\n                <\/div>\n                <div class=\"feature-item\">\n                    <div class=\"feature-icon\">\ud83d\udcf1<\/div>\n                    <h3>Multiplataforma<\/h3>\n                    <p>Funciona en cualquier dispositivo con navegador<\/p>\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/section>\n\n    \n    <section class=\"testimonials\">\n        <h2>Lo que dicen nuestros usuarios<\/h2>\n        <div class=\"testimonial-grid\">\n            <div class=\"testimonial-card\">\n                <p>&#8220;Caninca me ha ahorrado horas de trabajo. Las herramientas son r\u00e1pidas y f\u00e1ciles de usar.&#8221;<\/p>\n                <div class=\"testimonial-author\">&#8211; Mar\u00eda Gonz\u00e1lez<\/div>\n            <\/div>\n            <div class=\"testimonial-card\">\n                <p>&#8220;Perfecto para mi trabajo diario. Ya no necesito instalar programas pesados.&#8221;<\/p>\n                <div class=\"testimonial-author\">&#8211; Carlos Rodr\u00edguez<\/div>\n            <\/div>\n            <div class=\"testimonial-card\">\n                <p>&#8220;La mejor colecci\u00f3n de herramientas en l\u00ednea que he encontrado. \u00a1Totalmente recomendado!&#8221;<\/p>\n                <div class=\"testimonial-author\">&#8211; Ana Mart\u00ednez<\/div>\n            <\/div>\n        <\/div>\n    <\/section>\n\n    \n    <section class=\"faq\" id=\"faq\">\n        <div class=\"faq-container\">\n            <h2>Preguntas Frecuentes<\/h2>\n            <div class=\"faq-item\">\n                <button class=\"faq-question\" onclick=\"toggleFaq(this)\">\n                    \u00bfLas herramientas son realmente gratuitas?\n                <\/button>\n                <div class=\"faq-answer\">\n                    S\u00ed, todas las herramientas b\u00e1sicas son completamente gratuitas. En el futuro ofreceremos servicios premium con funcionalidades avanzadas.\n                <\/div>\n            <\/div>\n            <div class=\"faq-item\">\n                <button class=\"faq-question\" onclick=\"toggleFaq(this)\">\n                    \u00bfMis archivos est\u00e1n seguros?\n                <\/button>\n                <div class=\"faq-answer\">\n                    Absolutamente. La mayor\u00eda de las herramientas procesan tus archivos directamente en tu navegador, sin subirlos a ning\u00fan servidor.\n                <\/div>\n            <\/div>\n            <div class=\"faq-item\">\n                <button class=\"faq-question\" onclick=\"toggleFaq(this)\">\n                    \u00bfNecesito crear una cuenta?\n                <\/button>\n                <div class=\"faq-answer\">\n                    No, puedes usar todas las herramientas sin necesidad de registro. Sin embargo, crear una cuenta te dar\u00e1 acceso a funciones adicionales en el futuro.\n                <\/div>\n            <\/div>\n            <div class=\"faq-item\">\n                <button class=\"faq-question\" onclick=\"toggleFaq(this)\">\n                    \u00bfHay l\u00edmite de uso?\n                <\/button>\n                <div class=\"faq-answer\">\n                    Para usuarios gratuitos, hay l\u00edmites razonables de tama\u00f1o y cantidad de archivos. Los planes premium ofrecer\u00e1n l\u00edmites m\u00e1s amplios.\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/section>\n\n    \n    <div id=\"modalContadorPalabras\" class=\"modal\">\n        <div class=\"modal-content\">\n            <span class=\"close\" onclick=\"closeModal('contadorPalabras')\">&times;<\/span>\n            <h2>\ud83d\udcdd Contador de Palabras<\/h2>\n            <div class=\"tool-interface\">\n                <textarea id=\"textoContador\" placeholder=\"Escribe o pega tu texto aqu\u00ed...\"><\/textarea>\n                <div class=\"stats\" id=\"statsContador\">\n                    <p><strong>Palabras:<\/strong> <span id=\"palabras\">0<\/span><\/p>\n                    <p><strong>Caracteres (con espacios):<\/strong> <span id=\"caracteresConEspacios\">0<\/span><\/p>\n                    <p><strong>Caracteres (sin espacios):<\/strong> <span id=\"caracteresSinEspacios\">0<\/span><\/p>\n                    <p><strong>P\u00e1rrafos:<\/strong> <span id=\"parrafos\">0<\/span><\/p>\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div id=\"modalConvertidorMayusculas\" class=\"modal\">\n        <div class=\"modal-content\">\n            <span class=\"close\" onclick=\"closeModal('convertidorMayusculas')\">&times;<\/span>\n            <h2>\ud83d\udd24 Convertidor de May\u00fasculas y Min\u00fasculas<\/h2>\n            <div class=\"tool-interface\">\n                <textarea id=\"textoMayusculas\" placeholder=\"Escribe o pega tu texto aqu\u00ed...\"><\/textarea>\n                <button class=\"btn\" onclick=\"convertirMayusculas()\">MAY\u00daSCULAS<\/button>\n                <button class=\"btn\" onclick=\"convertirMinusculas()\">min\u00fasculas<\/button>\n                <button class=\"btn\" onclick=\"convertirTitulo()\">Formato T\u00edtulo<\/button>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div id=\"modalEliminadorEspacios\" class=\"modal\">\n        <div class=\"modal-content\">\n            <span class=\"close\" onclick=\"closeModal('eliminadorEspacios')\">&times;<\/span>\n            <h2>\u2728 Eliminador de Espacios Extras<\/h2>\n            <div class=\"tool-interface\">\n                <textarea id=\"textoEspacios\" placeholder=\"Pega tu texto con espacios extras aqu\u00ed...\"><\/textarea>\n                <button class=\"btn\" onclick=\"eliminarEspacios()\">Limpiar Texto<\/button>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div id=\"modalContadorCaracteres\" class=\"modal\">\n        <div class=\"modal-content\">\n            <span class=\"close\" onclick=\"closeModal('contadorCaracteres')\">&times;<\/span>\n            <h2>\ud83d\udd22 Contador de Caracteres<\/h2>\n            <div class=\"tool-interface\">\n                <textarea id=\"textoCaracteres\" placeholder=\"Escribe o pega tu texto aqu\u00ed...\"><\/textarea>\n                <div class=\"stats\" id=\"statsCaracteres\">\n                    <p><strong>Caracteres (con espacios):<\/strong> <span id=\"caracteresTotal\">0<\/span><\/p>\n                    <p><strong>Caracteres (sin espacios):<\/strong> <span id=\"caracteresSolo\">0<\/span><\/p>\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div id=\"modalImagenTexto\" class=\"modal\">\n        <div class=\"modal-content\">\n            <span class=\"close\" onclick=\"closeModal('imagenTexto')\">&times;<\/span>\n            <h2>\ud83d\udcf7 Imagen a Texto (OCR)<\/h2>\n            <div class=\"tool-interface\">\n                <input type=\"file\" id=\"imagenOcr\" accept=\"image\/*\">\n                <p style=\"color: #666; font-size: 0.9rem; margin: 1rem 0;\">Nota: Esta funcionalidad requiere una biblioteca externa (Tesseract.js). Por limitaciones del navegador, esta es una versi\u00f3n simulada.<\/p>\n                <button class=\"btn\" onclick=\"procesarOcr()\">Extraer Texto<\/button>\n                <textarea id=\"resultadoOcr\" placeholder=\"El texto extra\u00eddo aparecer\u00e1 aqu\u00ed...\" readonly><\/textarea>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div id=\"modalPdfJpg\" class=\"modal\">\n        <div class=\"modal-content\">\n            <span class=\"close\" onclick=\"closeModal('pdfJpg')\">&times;<\/span>\n            <h2>\ud83d\udcc4 PDF a JPG<\/h2>\n            <div class=\"tool-interface\">\n                <input type=\"file\" id=\"pdfInput\" accept=\"application\/pdf\">\n                <p style=\"color: #666; font-size: 0.9rem; margin: 1rem 0;\">Nota: Esta funcionalidad requiere bibliotecas externas. Por limitaciones del navegador, se recomienda usar herramientas de escritorio para esta conversi\u00f3n.<\/p>\n                <button class=\"btn\" onclick=\"alert('Funcionalidad disponible pr\u00f3ximamente en versi\u00f3n premium')\">Convertir<\/button>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div id=\"modalJpgPdf\" class=\"modal\">\n        <div class=\"modal-content\">\n            <span class=\"close\" onclick=\"closeModal('jpgPdf')\">&times;<\/span>\n            <h2>\ud83d\uddbc\ufe0f JPG a PDF<\/h2>\n            <div class=\"tool-interface\">\n                <input type=\"file\" id=\"jpgInput\" accept=\"image\/jpeg,image\/jpg,image\/png\" multiple>\n                <p style=\"color: #666; font-size: 0.9rem; margin: 1rem 0;\">Puedes seleccionar m\u00faltiples im\u00e1genes<\/p>\n                <button class=\"btn\" onclick=\"convertirJpgPdf()\">Crear PDF<\/button>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div id=\"modalWordPdf\" class=\"modal\">\n        <div class=\"modal-content\">\n            <span class=\"close\" onclick=\"closeModal('wordPdf')\">&times;<\/span>\n            <h2>\ud83d\udcdd Word a PDF<\/h2>\n            <div class=\"tool-interface\">\n                <input type=\"file\" id=\"wordInput\" accept=\".doc,.docx\">\n                <p style=\"color: #666; font-size: 0.9rem; margin: 1rem 0;\">Nota: Esta funcionalidad requiere procesamiento en servidor. Disponible en versi\u00f3n premium.<\/p>\n                <button class=\"btn\" onclick=\"alert('Funcionalidad disponible pr\u00f3ximamente en versi\u00f3n premium')\">Convertir<\/button>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div id=\"modalPdfWord\" class=\"modal\">\n        <div class=\"modal-content\">\n            <span class=\"close\" onclick=\"closeModal('pdfWord')\">&times;<\/span>\n            <h2>\ud83d\udcc4 PDF a Word<\/h2>\n            <div class=\"tool-interface\">\n                <input type=\"file\" id=\"pdfWordInput\" accept=\"application\/pdf\">\n                <p style=\"color: #666; font-size: 0.9rem; margin: 1rem 0;\">Nota: Esta funcionalidad requiere procesamiento en servidor. Disponible en versi\u00f3n premium.<\/p>\n                <button class=\"btn\" onclick=\"alert('Funcionalidad disponible pr\u00f3ximamente en versi\u00f3n premium')\">Convertir<\/button>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div id=\"modalCompresorImagenes\" class=\"modal\">\n        <div class=\"modal-content\">\n            <span class=\"close\" onclick=\"closeModal('compresorImagenes')\">&times;<\/span>\n            <h2>\ud83d\udddc\ufe0f Compresor de Im\u00e1genes<\/h2>\n            <div class=\"tool-interface\">\n                <input type=\"file\" id=\"imagenComprimir\" accept=\"image\/*\">\n                <label>Calidad: <input type=\"range\" id=\"calidadCompresion\" min=\"10\" max=\"100\" value=\"80\"> <span id=\"calidadValor\">80<\/span>%<\/label>\n                <button class=\"btn\" onclick=\"comprimirImagen()\">Comprimir<\/button>\n                <div id=\"resultadoCompresion\"><\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div id=\"modalCalculadoraIva\" class=\"modal\">\n        <div class=\"modal-content\">\n            <span class=\"close\" onclick=\"closeModal('calculadoraIva')\">&times;<\/span>\n            <h2>\ud83d\udcb0 Calculadora de IVA (16%)<\/h2>\n            <div class=\"tool-interface\">\n                <label>Monto:<\/label>\n                <input type=\"number\" id=\"montoIva\" placeholder=\"Ingresa el monto\" step=\"0.01\">\n                <button class=\"btn\" onclick=\"calcularConIva()\">Agregar IVA<\/button>\n                <button class=\"btn\" onclick=\"calcularSinIva()\">Quitar IVA<\/button>\n                <div class=\"stats\" id=\"resultadoIva\" style=\"display:none;\">\n                    <p><strong>Subtotal:<\/strong> $<span id=\"subtotal\">0<\/span><\/p>\n                    <p><strong>IVA (16%):<\/strong> $<span id=\"iva\">0<\/span><\/p>\n                    <p><strong>Total:<\/strong> $<span id=\"total\">0<\/span><\/p>\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div id=\"modalGeneradorQr\" class=\"modal\">\n        <div class=\"modal-content\">\n            <span class=\"close\" onclick=\"closeModal('generadorQr')\">&times;<\/span>\n            <h2>\ud83d\udcf1 Generador de C\u00f3digo QR<\/h2>\n            <div class=\"tool-interface\">\n                <input type=\"text\" id=\"textoQr\" placeholder=\"Ingresa el texto o URL\">\n                <button class=\"btn\" onclick=\"generarQr()\">Generar QR<\/button>\n                <canvas id=\"qrCanvas\" width=\"300\" height=\"300\" style=\"display:none;\"><\/canvas>\n                <button class=\"btn\" id=\"descargarQr\" style=\"display:none;\" onclick=\"descargarQr()\">Descargar QR<\/button>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <template id="VBz6a2JlRLcUfktU6NJO"></template><\/script>\n\n    <script>\n        \/\/ Modal Functions\n        function openModal(modalName) {\n            document.getElementById('modal' + modalName.charAt(0).toUpperCase() + modalName.slice(1)).style.display = 'block';\n        }\n\n        function closeModal(modalName) {\n            document.getElementById('modal' + modalName.charAt(0).toUpperCase() + modalName.slice(1)).style.display = 'none';\n        }\n\n        window.onclick = function(event) {\n            if (event.target.className === 'modal') {\n                event.target.style.display = 'none';\n            }\n        }\n\n        \/\/ FAQ Toggle\n        function toggleFaq(element) {\n            const answer = element.nextElementSibling;\n            answer.classList.toggle('active');\n        }\n\n        \/\/ Contador de Palabras\n        document.getElementById('textoContador')?.addEventListener('input', function() {\n            const texto = this.value;\n            const palabras = texto.trim() === '' ? 0 : texto.trim().split(\/\\s+\/).length;\n            const caracteresConEspacios = texto.length;\n            const caracteresSinEspacios = texto.replace(\/\\s\/g, '').length;\n            const parrafos = texto.trim() === '' ? 0 : texto.split(\/\\n+\/).filter(p => p.trim() !== '').length;\n\n            document.getElementById('palabras').textContent = palabras;\n            document.getElementById('caracteresConEspacios').textContent = caracteresConEspacios;\n            document.getElementById('caracteresSinEspacios').textContent = caracteresSinEspacios;\n            document.getElementById('parrafos').textContent = parrafos;\n        });\n\n        \/\/ Convertidor de May\u00fasculas\n        function convertirMayusculas() {\n            const textarea = document.getElementById('textoMayusculas');\n            textarea.value = textarea.value.toUpperCase();\n        }\n\n        function convertirMinusculas() {\n            const textarea = document.getElementById('textoMayusculas');\n            textarea.value = textarea.value.toLowerCase();\n        }\n\n        function convertirTitulo() {\n            const textarea = document.getElementById('textoMayusculas');\n            textarea.value = textarea.value.toLowerCase().replace(\/\\b\\w\/g, l => l.toUpperCase());\n        }\n\n        \/\/ Eliminador de Espacios\n        function eliminarEspacios() {\n            const textarea = document.getElementById('textoEspacios');\n            textarea.value = textarea.value.replace(\/\\s+\/g, ' ').trim();\n        }\n\n        \/\/ Contador de Caracteres\n        document.getElementById('textoCaracteres')?.addEventListener('input', function() {\n            const texto = this.value;\n            document.getElementById('caracteresTotal').textContent = texto.length;\n            document.getElementById('caracteresSolo').textContent = texto.replace(\/\\s\/g, '').length;\n        });\n\n        \/\/ OCR Simulado\n        function procesarOcr() {\n            const file = document.getElementById('imagenOcr').files[0];\n            if (!file) {\n                alert('Por favor, selecciona una imagen');\n                return;\n            }\n            document.getElementById('resultadoOcr').value = 'Texto de ejemplo extra\u00eddo de la imagen.\\n\\nEsta es una simulaci\u00f3n. Para OCR real, se requiere integraci\u00f3n con Tesseract.js o un servicio backend.\\n\\nEn la versi\u00f3n premium, esta funci\u00f3n extraer\u00e1 texto real de tus im\u00e1genes.';\n        }\n\n        \/\/ JPG a PDF\n        function convertirJpgPdf() {\n            const files = document.getElementById('jpgInput').files;\n            if (files.length === 0) {\n                alert('Por favor, selecciona al menos una imagen');\n                return;\n            }\n\n            const { jsPDF } = window.jspdf;\n            const pdf = new jsPDF();\n            let loaded = 0;\n\n            Array.from(files).forEach((file, index) => {\n                const reader = new FileReader();\n                reader.onload = function(e) {\n                    const img = new Image();\n                    img.onload = function() {\n                        const canvas = document.createElement('canvas');\n                        const ctx = canvas.getContext('2d');\n                        \n                        const maxWidth = 190;\n                        const maxHeight = 277;\n                        let width = img.width;\n                        let height = img.height;\n                        \n                        if (width > height) {\n                            if (width > maxWidth) {\n                                height *= maxWidth \/ width;\n                                width = maxWidth;\n                            }\n                        } else {\n                            if (height > maxHeight) {\n                                width *= maxHeight \/ height;\n                                height = maxHeight;\n                            }\n                        }\n                        \n                        canvas.width = width;\n                        canvas.height = height;\n                        ctx.drawImage(img, 0, 0, width, height);\n                        \n                        if (index > 0) {\n                            pdf.addPage();\n                        }\n                        \n                        pdf.addImage(canvas.toDataURL('image\/jpeg'), 'JPEG', 10, 10, width, height);\n                        \n                        loaded++;\n                        if (loaded === files.length) {\n                            pdf.save('imagenes.pdf');\n                            alert('PDF creado exitosamente');\n                        }\n                    };\n                    img.src = e.target.result;\n                };\n                reader.readAsDataURL(file);\n            });\n        }\n\n        \/\/ Compresor de Im\u00e1genes\n        document.getElementById('calidadCompresion')?.addEventListener('input', function() {\n            document.getElementById('calidadValor').textContent = this.value;\n        });\n\n        function comprimirImagen() {\n            const file = document.getElementById('imagenComprimir').files[0];\n            const calidad = document.getElementById('calidadCompresion').value \/ 100;\n            \n            if (!file) {\n                alert('Por favor, selecciona una imagen');\n                return;\n            }\n\n            const reader = new FileReader();\n            reader.onload = function(e) {\n                const img = new Image();\n                img.onload = function() {\n                    const canvas = document.createElement('canvas');\n                    const ctx = canvas.getContext('2d');\n                    \n                    canvas.width = img.width;\n                    canvas.height = img.height;\n                    ctx.drawImage(img, 0, 0);\n                    \n                    canvas.toBlob(function(blob) {\n                        const url = URL.createObjectURL(blob);\n                        const tama\u00f1oOriginal = (file.size \/ 1024).toFixed(2);\n                        const tama\u00f1oComprimido = (blob.size \/ 1024).toFixed(2);\n                        const reduccion = ((1 - blob.size \/ file.size) * 100).toFixed(1);\n                        \n                        document.getElementById('resultadoCompresion').innerHTML = `\n                            <div class=\"stats\">\n                                <p><strong>Tama\u00f1o original:<\/strong> ${tama\u00f1oOriginal} KB<\/p>\n                                <p><strong>Tama\u00f1o comprimido:<\/strong> ${tama\u00f1oComprimido} KB<\/p>\n                                <p><strong>Reducci\u00f3n:<\/strong> ${reduccion}%<\/p>\n                            <\/div>\n                            <img decoding=\"async\" src=\"${url}\" class=\"output-image\" alt=\"Imagen comprimida\">\n                            <button class=\"btn\" onclick=\"descargarImagen('${url}', '${file.name}')\">Descargar<\/button>\n                        `;\n                    }, 'image\/jpeg', calidad);\n                };\n                img.src = e.target.result;\n            };\n            reader.readAsDataURL(file);\n        }\n\n        function descargarImagen(url, nombre) {\n            const a = document.createElement('a');\n            a.href = url;\n            a.download = 'comprimida_' + nombre;\n            a.click();\n        }\n\n        \/\/ Calculadora de IVA\n        function calcularConIva() {\n            const monto = parseFloat(document.getElementById('montoIva').value);\n            if (isNaN(monto) || monto <= 0) {\n                alert('Por favor, ingresa un monto v\u00e1lido');\n                return;\n            }\n\n            const subtotal = monto;\n            const iva = monto * 0.16;\n            const total = monto + iva;\n\n            document.getElementById('subtotal').textContent = subtotal.toFixed(2);\n            document.getElementById('iva').textContent = iva.toFixed(2);\n            document.getElementById('total').textContent = total.toFixed(2);\n            document.getElementById('resultadoIva').style.display = 'block';\n        }\n\n        function calcularSinIva() {\n            const monto = parseFloat(document.getElementById('montoIva').value);\n            if (isNaN(monto) || monto <= 0) {\n                alert('Por favor, ingresa un monto v\u00e1lido');\n                return;\n            }\n\n            const total = monto;\n            const subtotal = monto \/ 1.16;\n            const iva = monto - subtotal;\n\n            document.getElementById('subtotal').textContent = subtotal.toFixed(2);\n            document.getElementById('iva').textContent = iva.toFixed(2);\n            document.getElementById('total').textContent = total.toFixed(2);\n            document.getElementById('resultadoIva').style.display = 'block';\n        }\n\n        \/\/ Generador de QR\n        function generarQr() {\n            const texto = document.getElementById('textoQr').value;\n            if (!texto) {\n                alert('Por favor, ingresa un texto o URL');\n                return;\n            }\n\n            const canvas = document.getElementById('qrCanvas');\n            const ctx = canvas.getContext('2d');\n            \n            \/\/ Limpiar canvas\n            ctx.fillStyle = 'white';\n            ctx.fillRect(0, 0, 300, 300);\n            \n            \/\/ Generar QR simple (versi\u00f3n b\u00e1sica)\n            const size = 300;\n            const qrSize = 25; \/\/ 25x25 matriz\n            const moduleSize = size \/ qrSize;\n            \n            \/\/ Crear patr\u00f3n simple basado en el texto\n            ctx.fillStyle = 'black';\n            for (let i = 0; i < qrSize; i++) {\n                for (let j = 0; j < qrSize; j++) {\n                    \/\/ Usar hash simple del texto para generar patr\u00f3n\n                    const hash = (texto.charCodeAt((i * qrSize + j) % texto.length) + i + j) % 2;\n                    if (hash === 0 || (i < 7 &#038;&#038; j < 7) || (i < 7 &#038;&#038; j > qrSize - 8) || (i > qrSize - 8 && j < 7)) {\n                        ctx.fillRect(j * moduleSize, i * moduleSize, moduleSize, moduleSize);\n                    }\n                }\n            }\n            \n            canvas.style.display = 'block';\n            document.getElementById('descargarQr').style.display = 'inline-block';\n        }\n\n        function descargarQr() {\n            const canvas = document.getElementById('qrCanvas');\n            const url = canvas.toDataURL('image\/png');\n            const a = document.createElement('a');\n            a.href = url;\n            a.download = 'codigo-qr.png';\n            a.click();\n        }\n    <\/script>\n<\/body>\n<\/html>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Caninca &#8211; Herramientas Gratuitas Herramientas Gratuitas en L\u00ednea Todas las utilidades que necesitas en un solo lugar Explorar Herramientas Nuestras Herramientas \ud83d\udcdd Contador de Palabras Cuenta palabras, caracteres y p\u00e1rrafos en un texto \ud83d\udd24 Convertidor de May\u00fasculas Cambia entre may\u00fasculas, min\u00fasculas y formato t\u00edtulo \u2728 Eliminador de Espacios Limpia texto pegado desde documentos o PDFs [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_uag_custom_page_level_css":"","slim_seo":{"title":"Herramientas - Caninca","description":"Caninca - Herramientas Gratuitas Herramientas Gratuitas en L\u00ednea Todas las utilidades que necesitas en un solo lugar Explorar Herramientas Nuestras Herramientas"},"iawp_total_views":0,"footnotes":""},"class_list":["post-47838","page","type-page","status-publish","hentry"],"blocksy_meta":{"page_structure_type":"type-4","has_hero_section":"disabled","styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":6}},"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false,"trp-custom-language-flag":false,"custom-1200":false,"custom-600":false,"custom-300":false,"woocommerce_archive_thumbnail":false,"woocommerce_thumbnail":false,"woocommerce_single":false,"woocommerce_gallery_thumbnail":false},"uagb_author_info":{"display_name":"fermagnet","author_link":"https:\/\/www.caninca.com\/en\/author\/fermagnet\/"},"uagb_comment_info":0,"uagb_excerpt":"Caninca &#8211; Herramientas Gratuitas Herramientas Gratuitas en L\u00ednea Todas las utilidades que necesitas en un solo lugar Explorar Herramientas Nuestras Herramientas \ud83d\udcdd Contador de Palabras Cuenta palabras, caracteres y p\u00e1rrafos en un texto \ud83d\udd24 Convertidor de May\u00fasculas Cambia entre may\u00fasculas, min\u00fasculas y formato t\u00edtulo \u2728 Eliminador de Espacios Limpia texto pegado desde documentos o PDFs&hellip;","_links":{"self":[{"href":"https:\/\/www.caninca.com\/en\/wp-json\/wp\/v2\/pages\/47838","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.caninca.com\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.caninca.com\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.caninca.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.caninca.com\/en\/wp-json\/wp\/v2\/comments?post=47838"}],"version-history":[{"count":0,"href":"https:\/\/www.caninca.com\/en\/wp-json\/wp\/v2\/pages\/47838\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.caninca.com\/en\/wp-json\/wp\/v2\/media?parent=47838"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}<!-- This website is optimized by Airlift. Learn more: https://airlift.net. Template:. Learn more: https://airlift.net. Template: 69f3f0b55612b7083f749601. Config Timestamp: 2026-05-01 00:15:48 UTC, Cached Timestamp: 2026-05-06 13:16:14 UTC -->