UNPKG

eiip

Version:

EIIP (Elite India Image Processing) - Complete client-side image processing library with SVG/raster conversion, layer merging, cropping, effects/filters, PDF conversion, resizing, rotation, compression, watermarks, and color adjustment. Compatible with Re

19 lines โ€ข 46.8 kB
/** * EIIP (Elite India Image Processing) - Complete Frontend Image Processing Library * * MIT License * Copyright (c) 2025 Saleem Ahmad (Elite India) * * A comprehensive JavaScript library for frontend image processing including: * - SVG to Raster conversion with knitting effects * - Raster to SVG conversion with color optimization * - Advanced image layer merging and composition * * Compatible with React, Angular, Vue.js, and vanilla JavaScript * Works in both browser and Node.js environments * * @version 1.0.0 * @author Saleem Ahmad (Elite India) * @license MIT */ !function(t,e){"undefined"!=typeof module&&module.exports?module.exports=e():"function"==typeof define&&define.amd?define(e):t.EIIP=e()}("undefined"!=typeof window?window:this,function(){"use strict";class t{static OUTPUT_FORMATS={PNG:"png",JPG:"jpg",JPEG:"jpeg",WEBP:"webp"};static QUALITY_PRESETS={LOW:.3,MEDIUM:.7,HIGH:.9,MAXIMUM:1};static SIZE_PRESETS={THUMBNAIL:{width:150,height:150},SMALL:{width:300,height:300},MEDIUM:{width:600,width:600},LARGE:{width:1200,height:1200},HD:{width:1920,height:1080},UHD:{width:3840,height:2160}};static KNITTING_PATTERNS={BASIC:"basic",CABLE:"cable",RIBBED:"ribbed",SEED:"seed",STOCKINETTE:"stockinette",GARTER:"garter"};static YARN_TYPES={WOOL:"wool",COTTON:"cotton",ACRYLIC:"acrylic",SILK:"silk",CASHMERE:"cashmere",ALPACA:"alpaca"};constructor(e={}){this.width=e.width||null,this.height=e.height||null,this.format=e.format||t.OUTPUT_FORMATS.PNG,this.quality=e.quality||t.QUALITY_PRESETS.HIGH,this.backgroundColor=e.backgroundColor||"transparent",this.scaleFactor=e.scaleFactor||1,this.preserveAspectRatio=!1!==e.preserveAspectRatio,this.debug=e.debug||!1,this.canvas=null,this.ctx=null,t.injectKnittingCSS(),this.debug&&(console.log("โœ… SvgToRaster initialized"),console.log("๐Ÿงถ Knitting effects CSS available"))}async convertFromFile(t){return new Promise((e,a)=>{if(!t||!t.type.match(/image\/svg\+xml|text\/xml|application\/xml/))return void a(new Error("Invalid file type. Please provide an SVG file."));const i=new FileReader;i.onload=t=>{this.convertFromSvgString(t.target.result).then(e).catch(a)},i.onerror=()=>a(new Error("Failed to read file")),i.readAsText(t)})}async convertFromUrl(t){return new Promise((e,a)=>{fetch(t).then(t=>{if(!t.ok)throw new Error(`Failed to fetch SVG: ${t.status}`);return t.text()}).then(t=>{this.convertFromSvgString(t).then(e).catch(a)}).catch(t=>{a(new Error(`Failed to load SVG from URL: ${t.message}`))})})}async convertFromSvgString(t){try{const e=this.cleanSvgString(t),a=this.parseSvgDimensions(e),i=this.calculateOutputDimensions(a);this.setupCanvas(i);const o=await this.renderSvgToCanvas(e,i);return this.debug&&console.log("โœ… SVG conversion completed successfully"),o}catch(t){throw this.debug&&console.error("โŒ Conversion failed:",t),new Error(`Conversion failed: ${t.message}`)}}cleanSvgString(t){let e=t.replace(/<\?xml[^>]*\?>/i,"").trim();return e.includes("xmlns")||(e=e.replace("<svg",'<svg xmlns="http://www.w3.org/2000/svg"')),e=e.replace(/<!DOCTYPE[^>]*>/i,"").trim(),e}parseSvgDimensions(t){const e=(new DOMParser).parseFromString(t,"image/svg+xml").documentElement;if("parsererror"===e.tagName)throw new Error("Invalid SVG format");let a=e.getAttribute("width"),i=e.getAttribute("height");const o=e.getAttribute("viewBox");if(a&&(a=parseFloat(a.replace(/[^\d.]/g,""))),i&&(i=parseFloat(i.replace(/[^\d.]/g,""))),(!a||!i)&&o){const t=o.split(/[,\s]+/).map(t=>parseFloat(t));4===t.length&&(a=a||t[2],i=i||t[3])}return a=a||300,i=i||300,{width:a,height:i,viewBox:o}}calculateOutputDimensions(t){let{width:e,height:a}=t;return this.width&&this.height?(e=this.width,a=this.height):this.width?(e=this.width,a=this.preserveAspectRatio?this.width/t.width*t.height:t.height):this.height&&(a=this.height,e=this.preserveAspectRatio?this.height/t.height*t.width:t.width),e*=this.scaleFactor,a*=this.scaleFactor,{width:Math.round(e),height:Math.round(a)}}setupCanvas(t){this.canvas=document.createElement("canvas"),this.canvas.width=t.width,this.canvas.height=t.height,this.ctx=this.canvas.getContext("2d"),this.backgroundColor&&"transparent"!==this.backgroundColor&&(this.ctx.fillStyle=this.backgroundColor,this.ctx.fillRect(0,0,t.width,t.height))}async renderSvgToCanvas(t,e){return new Promise((a,i)=>{const o=new Image,n=new Blob([t],{type:"image/svg+xml;charset=utf-8"}),r=URL.createObjectURL(n);o.onload=()=>{this.ctx.drawImage(o,0,0,e.width,e.height),URL.revokeObjectURL(r);const t=this.canvas.toDataURL(`image/${this.format}`,this.quality),i=new Image;i.src=t,a({dataUrl:t,canvas:this.canvas,imageElement:i,width:e.width,height:e.height,format:this.format})},o.onerror=()=>{URL.revokeObjectURL(r),i(new Error("Failed to render SVG"))},o.src=r})}downloadImage(t,e="converted-image.png"){const a=document.createElement("a");a.download=e,a.href=t,document.body.appendChild(a),a.click(),document.body.removeChild(a)}async convertToBlob(t){return new Promise(e=>{const a=document.createElement("canvas"),i=a.getContext("2d"),o=new Image;o.onload=()=>{a.width=o.width,a.height=o.height,i.drawImage(o,0,0),a.toBlob(e,`image/${this.format}`,this.quality)},o.src=t})}async applyKnittingEffect(e,a={}){try{const i={intensity:a.intensity||.5,patternType:a.patternType||t.KNITTING_PATTERNS.BASIC,stitchSize:a.stitchSize||8,threadColor:a.threadColor||"rgba(255,255,255,0.3)",addTexture:!1!==a.addTexture,...a};this.debug&&console.log("๐Ÿงถ Applying knitting effect with options:",i);const o=document.createElement("canvas"),n=o.getContext("2d",{willReadFrequently:!0}),r=await this.loadImageFromDataUrl(e);o.width=r.width,o.height=r.height,n.clearRect(0,0,o.width,o.height),n.drawImage(r,0,0),await this.applyKnittingPattern(n,o.width,o.height,i),i.addTexture&&this.addFabricTexture(n,o.width,o.height,i);const s=o.toDataURL(`image/${this.format}`,this.quality),l=new Image;return l.src=s,this.debug&&console.log("โœ… Knitting effect applied successfully"),{status:!0,dataUrl:s,imageElement:l,width:o.width,height:o.height,effect:"knitting",effectOptions:i,message:"Knitting effect has been applied successfully"}}catch(t){throw this.debug&&console.error("โŒ Knitting effect failed:",t),new Error(`Knitting effect failed: ${t.message}`)}}async convertToKnittingDesign(t,e={},a={}){try{const i=await this.convertFromSvgString(t,e);return{...await this.applyKnittingEffect(i.dataUrl,a),originalSvg:t,conversionOptions:e,knittingOptions:a,message:"SVG converted to knitting design successfully"}}catch(t){throw this.debug&&console.error("โŒ Knitting design conversion failed:",t),new Error(`Knitting design conversion failed: ${t.message}`)}}async applyYarnKnittingEffect(e,a={}){try{const i={yarnType:a.yarnType||t.YARN_TYPES.WOOL,twist:a.twist||"medium",colors:a.colors||["#8B7355","#A0916B"],density:a.density||.8,...a};this.debug&&console.log("๐Ÿงถ Applying yarn knitting effect:",i);const o=document.createElement("canvas"),n=o.getContext("2d",{willReadFrequently:!0}),r=await this.loadImageFromDataUrl(e);o.width=r.width,o.height=r.height,n.drawImage(r,0,0),this.applyYarnTexture(n,o.width,o.height,i);const s=o.toDataURL(`image/${this.format}`,this.quality),l=new Image;return l.src=s,this.debug&&console.log("โœ… Yarn knitting effect applied successfully"),{status:!0,dataUrl:s,imageElement:l,width:o.width,height:o.height,effect:"yarn-knitting",yarnOptions:i,message:"Yarn knitting effect applied successfully"}}catch(t){throw this.debug&&console.error("โŒ Yarn knitting effect failed:",t),new Error(`Yarn knitting effect failed: ${t.message}`)}}async loadImageFromDataUrl(t){return new Promise((e,a)=>{const i=new Image;i.onload=()=>e(i),i.onerror=()=>a(new Error("Failed to load image")),i.src=t})}async applyKnittingPattern(t,e,a,i){const o=t.getImageData(0,0,e,a),n=this.createKnittingPattern(i),r=document.createElement("canvas"),s=r.getContext("2d");r.width=e,r.height=a;for(let t=0;t<e;t+=n.width)for(let e=0;e<a;e+=n.height)s.drawImage(n,t,e);const l=s.getImageData(0,0,e,a);this.applyPatternToVisibleAreas(t,o,l,i)}createKnittingPattern(e){const a=document.createElement("canvas"),i=a.getContext("2d"),o=e.stitchSize||16;a.width=o,a.height=o;const n=e.threadColor||"rgba(255, 255, 255, 0.3)";switch(e.patternType){case t.KNITTING_PATTERNS.CABLE:this.drawCableKnitPattern(i,o,n);break;case t.KNITTING_PATTERNS.RIBBED:this.drawRibbedPattern(i,o,n);break;case t.KNITTING_PATTERNS.SEED:this.drawSeedPattern(i,o,n);break;case t.KNITTING_PATTERNS.BASIC:default:this.drawBasicKnitPattern(i,o,n)}return a}drawBasicKnitPattern(t,e,a){t.fillStyle=a;const i=e/2;t.beginPath(),t.moveTo(0,i),t.lineTo(i,0),t.lineTo(e,i),t.lineTo(i,e),t.closePath(),t.fill(),t.strokeStyle=a,t.lineWidth=1,t.stroke()}drawCableKnitPattern(t,e,a){t.fillStyle=a,t.strokeStyle=a,t.lineWidth=2,t.beginPath(),t.moveTo(0,0),t.bezierCurveTo(e/4,e/2,3*e/4,e/2,e,e),t.stroke(),t.beginPath(),t.moveTo(e,0),t.bezierCurveTo(3*e/4,e/2,e/4,e/2,0,e),t.stroke()}drawRibbedPattern(t,e,a){t.fillStyle=a;const i=e/4;for(let a=0;a<4;a++)a%2==0&&t.fillRect(a*i,0,i,e)}drawSeedPattern(t,e,a){t.fillStyle=a;const i=e/8;for(let a=0;a<e;a+=e/4)for(let o=0;o<e;o+=e/4)(a+o)%(e/2)==0&&(t.beginPath(),t.arc(a+i,o+i,i,0,2*Math.PI),t.fill())}applyPatternToVisibleAreas(t,e,a,i){const o=t.getImageData(0,0,e.width,e.height),n=o.data,r=a.data,s=i.intensity||.5;for(let t=0;t<n.length;t+=4){if(n[t+3]>0){const e=r[t]||0,a=r[t+1]||0,i=r[t+2]||0;n[t]=Math.min(255,n[t]+e*s),n[t+1]=Math.min(255,n[t+1]+a*s),n[t+2]=Math.min(255,n[t+2]+i*s)}}t.putImageData(o,0,0)}addFabricTexture(t,e,a,i){const o=.3*(i.intensity||.5);t.globalCompositeOperation="multiply",t.globalAlpha=o;for(let i=0;i<e;i+=2)for(let e=0;e<a;e+=2){const a=50*Math.random()+200;t.fillStyle=`rgb(${a}, ${a}, ${a})`,t.fillRect(i,e,1,1)}t.globalCompositeOperation="source-over",t.globalAlpha=1}applyYarnTexture(e,a,i,o){const{yarnType:n,density:r,colors:s}=o;switch(e.globalAlpha=r||.8,n){case t.YARN_TYPES.WOOL:this.applyWoolTexture(e,a,i,s);break;case t.YARN_TYPES.COTTON:this.applyCottonTexture(e,a,i,s);break;case t.YARN_TYPES.SILK:this.applySilkTexture(e,a,i,s);break;default:this.applyWoolTexture(e,a,i,s)}e.globalAlpha=1}applyWoolTexture(t,e,a,i){const o=i[0]||"#8B7355";t.fillStyle=o;for(let i=0;i<e*a/100;i++){const i=Math.random()*e,o=Math.random()*a,n=3*Math.random()+1;t.beginPath(),t.arc(i,o,n,0,2*Math.PI),t.fill()}}applyCottonTexture(t,e,a,i){const o=i[0]||"#F5F5DC";t.strokeStyle=o,t.lineWidth=1;for(let i=0;i<a;i+=4)t.beginPath(),t.moveTo(0,i),t.lineTo(e,i),t.stroke()}applySilkTexture(t,e,a,i){const o=i[0]||"#FFF8DC",n=t.createLinearGradient(0,0,e,a);n.addColorStop(0,o),n.addColorStop(.5,"rgba(240, 230, 140, 0.3)"),n.addColorStop(1,o),t.fillStyle=n,t.fillRect(0,0,e,a)}static injectKnittingCSS(){if("undefined"!=typeof document&&!document.getElementById("eiip-knitting-styles")){const t=document.createElement("style");t.id="eiip-knitting-styles",t.textContent="\n .knitting-preview {\n display: inline-block;\n width: 50px;\n height: 50px;\n border-radius: 5px;\n margin-right: 10px;\n vertical-align: middle;\n }\n\n .knitting-basic {\n background-image: \n repeating-linear-gradient(\n 0deg,\n rgba(255,255,255,0.1) 0px,\n rgba(255,255,255,0.3) 2px,\n rgba(255,255,255,0.1) 4px,\n transparent 6px,\n transparent 8px\n ),\n repeating-linear-gradient(\n 90deg,\n rgba(255,255,255,0.1) 0px,\n rgba(255,255,255,0.2) 2px,\n rgba(255,255,255,0.1) 4px,\n transparent 6px,\n transparent 8px\n );\n background-color: #8e9aaf;\n }\n\n .knitting-cable {\n background-image: \n radial-gradient(ellipse 4px 2px at 50% 25%, rgba(255,255,255,0.4) 0%, transparent 50%),\n radial-gradient(ellipse 4px 2px at 50% 75%, rgba(255,255,255,0.3) 0%, transparent 50%),\n repeating-linear-gradient(\n 45deg,\n rgba(255,255,255,0.1) 0px,\n rgba(255,255,255,0.2) 1px,\n transparent 2px,\n transparent 8px\n );\n background-color: #6c7b7f;\n }\n\n .knitting-ribbed {\n background-image: \n repeating-linear-gradient(\n 90deg,\n rgba(255,255,255,0.3) 0px,\n rgba(255,255,255,0.3) 2px,\n rgba(0,0,0,0.1) 2px,\n rgba(0,0,0,0.1) 4px,\n rgba(255,255,255,0.2) 4px,\n rgba(255,255,255,0.2) 6px,\n rgba(0,0,0,0.1) 6px,\n rgba(0,0,0,0.1) 8px\n );\n background-color: #a8b8c8;\n }\n\n .knitting-seed {\n background-image: \n radial-gradient(circle 1px at 25% 25%, rgba(255,255,255,0.5) 0%, transparent 50%),\n radial-gradient(circle 1px at 75% 75%, rgba(255,255,255,0.5) 0%, transparent 50%),\n radial-gradient(circle 1px at 75% 25%, rgba(0,0,0,0.2) 0%, transparent 50%),\n radial-gradient(circle 1px at 25% 75%, rgba(0,0,0,0.2) 0%, transparent 50%);\n background-size: 8px 8px;\n background-color: #95a5a6;\n }\n\n .yarn-wool {\n background: linear-gradient(45deg, #8B7355 25%, #A0916B 25%, #A0916B 50%, #8B7355 50%, #8B7355 75%, #A0916B 75%);\n background-size: 8px 8px;\n }\n\n .yarn-cotton {\n background: linear-gradient(90deg, #F5F5DC 0%, #DDBF94 50%, #F5F5DC 100%);\n background-size: 4px 4px;\n }\n\n .yarn-acrylic {\n background: linear-gradient(0deg, #E6E6FA 0%, #D8BFD8 50%, #E6E6FA 100%);\n background-size: 6px 6px;\n }\n\n .yarn-silk {\n background: radial-gradient(circle, #FFF8DC 0%, #F0E68C 50%, #FFF8DC 100%);\n background-size: 10px 10px;\n }\n\n .yarn-cashmere {\n background: linear-gradient(135deg, #F5F5F5 25%, #E8E8E8 25%, #E8E8E8 50%, #F5F5F5 50%, #F5F5F5 75%, #E8E8E8 75%);\n background-size: 12px 12px;\n }\n\n .yarn-alpaca {\n background: linear-gradient(60deg, #D2B48C 25%, #DEB887 25%, #DEB887 50%, #D2B48C 50%, #D2B48C 75%, #DEB887 75%);\n background-size: 10px 10px;\n }\n ",document.head.appendChild(t)}}}class e{static COLOR_PRESETS={FULL_COLOR:0,VERY_HIGH:4,HIGH:8,MEDIUM:16,LOW:32,VERY_LOW:64,MINIMAL:85,EIGHT_COLORS:128,MONOCHROME:255};static getReductionLevelForColors(t){if(t<=0)return 0;const e=Math.round(Math.pow(t,1/3)),a=Math.floor(256/e);return Math.max(1,a)}static getAvailableColorCounts(){const t=[];for(let e=1;e<=255;e++){const a=Math.floor(256/e),i=Math.pow(a,3);i>0&&!t.includes(i)&&t.push(i)}return t.sort((t,e)=>e-t)}constructor(t={}){this.scaleFactor=t.scaleFactor||1,this.colorReductionLevel=t.colorReductionLevel||0,this.exactColorCount=t.exactColorCount||null,this.debug=t.debug||!1,this.canvas=null,this.ctx=null,this.exactColorCount&&(this.colorReductionLevel=e.getReductionLevelForColors(this.exactColorCount)),this.debug&&console.log("โœ… RasterToSvg initialized")}async convertFromFile(t){return new Promise((e,a)=>{if(!t||!t.type.match(/image\/(png|jpe?g|gif|bmp|webp)/))return void a(new Error("Invalid file type. Please provide a raster image file."));const i=new FileReader;i.onload=t=>{this.convertFromDataUrl(t.target.result).then(e).catch(a)},i.onerror=()=>a(new Error("Failed to read file")),i.readAsDataURL(t)})}async convertFromUrl(t){return new Promise((e,a)=>{const i=new Image;i.crossOrigin="anonymous",i.onload=()=>{this.convertFromImageElement(i).then(e).catch(a)},i.onerror=()=>{a(new Error(`Failed to load image from URL: ${t}`))},i.src=t})}async convertFromDataUrl(t){return new Promise((e,a)=>{const i=new Image;i.onload=()=>{this.convertFromImageElement(i).then(e).catch(a)},i.onerror=()=>{a(new Error("Failed to load image from data URL"))},i.src=t})}async convertFromImageElement(t){try{const e=Math.round(t.width*this.scaleFactor),a=Math.round(t.height*this.scaleFactor);this.setupCanvas(e,a),this.ctx.drawImage(t,0,0,e,a);const i=this.ctx.getImageData(0,0,e,a),o=this.processImageData(i),n=this.generateSvg(o,e,a);return this.debug&&console.log("โœ… Raster to SVG conversion completed"),{svg:n,width:e,height:a,originalWidth:t.width,originalHeight:t.height,colorCount:o.colorCount}}catch(t){throw this.debug&&console.error("โŒ Conversion failed:",t),new Error(`Conversion failed: ${t.message}`)}}setupCanvas(t,e){this.canvas=document.createElement("canvas"),this.canvas.width=t,this.canvas.height=e,this.ctx=this.canvas.getContext("2d")}processImageData(t){const e=t.data,a=[],i=new Map;for(let t=0;t<e.length;t+=4){let o=e[t],n=e[t+1],r=e[t+2];const s=e[t+3];this.colorReductionLevel>0&&(o=Math.floor(o/this.colorReductionLevel)*this.colorReductionLevel,n=Math.floor(n/this.colorReductionLevel)*this.colorReductionLevel,r=Math.floor(r/this.colorReductionLevel)*this.colorReductionLevel);const l=`rgba(${o},${n},${r},${s/255})`,h=`${o},${n},${r},${s}`;i.has(h)||i.set(h,l),a.push(l)}return{pixels:a,colorMap:i,colorCount:i.size}}generateSvg(t,e,a){const{pixels:i,colorMap:o}=t;let n=`<svg xmlns="http://www.w3.org/2000/svg" width="${e}" height="${a}" viewBox="0 0 ${e} ${a}">`;const r=new Map;return i.forEach((t,a)=>{r.has(t)||r.set(t,[]);const i=a%e,o=Math.floor(a/e);r.get(t).push({x:i,y:o})}),r.forEach((t,e)=>{"rgba(0,0,0,0)"!==e&&(n+=`<g fill="${e}">`,t.forEach(t=>{n+=`<rect x="${t.x}" y="${t.y}" width="1" height="1"/>`}),n+="</g>")}),n+="</svg>",n}downloadSvg(t,e="converted-image.svg"){const a=new Blob([t],{type:"image/svg+xml"}),i=URL.createObjectURL(a),o=document.createElement("a");o.download=e,o.href=i,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(i)}}class a{static getPixelDimensions(t,e,a){return{width:Math.round(t*a),height:Math.round(e*a)}}static scaleLayersForOutput(t,e,a){const i=a.width/e.width,o=a.height/e.height;return t.map(t=>{let e=t.x,a=t.y;"number"==typeof e&&(e*=i),"number"==typeof a&&(a*=o);let n=t.width*i,r=t.height*o;return{...t,x:e,y:a,width:n,height:r}})}constructor(t={}){this.width=t.width||800,this.height=t.height||600,this.backgroundColor=t.backgroundColor||"transparent",this.debug=t.debug||!1,this.debug&&console.log("โœ… ImageLayerMerger initialized")}async mergeImages(t,e={}){try{const a=document.createElement("canvas");a.width=this.width,a.height=this.height;const i=a.getContext("2d");this.backgroundColor&&"transparent"!==this.backgroundColor&&(i.fillStyle=this.backgroundColor,i.fillRect(0,0,this.width,this.height));const o=[...t].sort((t,e)=>(t.zIndex||0)-(e.zIndex||0));for(const t of o){const e=await this.loadImage(t);await this.drawLayer(i,e,{width:this.width,height:this.height})}const n=e.format||"png",r=e.quality||.9,s=a.toDataURL(`image/${n}`,r);return this.debug&&console.log("โœ… Image layers merged successfully"),{canvas:a,dataUrl:s,blob:await this.convertToBlob(a,n,r),width:this.width,height:this.height}}catch(t){throw this.debug&&console.error("โŒ Layer merging failed:",t),new Error(`Layer merging failed: ${t.message}`)}}async loadImage(t){return new Promise((e,a)=>{const i=new Image;if(i.crossOrigin="anonymous",i.onload=()=>{e({image:i,...t})},i.onerror=()=>{a(new Error(`Failed to load image: ${t.src}`))},"string"==typeof t.src)i.src=t.src;else if(t.src instanceof File){const e=new FileReader;e.onload=t=>{i.src=t.target.result},e.readAsDataURL(t.src)}else a(new Error("Invalid image source"))})}async drawLayer(t,e,a){const{image:i,x:o,y:n,width:r,height:s,scale:l,opacity:h,rotation:c}=e;t.save(),void 0!==h&&(t.globalAlpha=h);let g=o||0,d=n||0,m=r||i.width,w=s||i.height;"center"===g&&(g=(a.width-m)/2),"center"===d&&(d=(a.height-w)/2),l&&(m*=l,w*=l),c&&(t.translate(g+m/2,d+w/2),t.rotate(c*Math.PI/180),t.translate(-m/2,-w/2),g=0,d=0),t.drawImage(i,g,d,m,w),t.restore()}async convertToBlob(t,e="png",a=.9){return new Promise(i=>{t.toBlob(i,`image/${e}`,a)})}async getDataURL(t,e={}){return(await this.mergeImages(t,e)).dataUrl}async getBlob(t,e={}){return(await this.mergeImages(t,e)).blob}downloadImage(t,e="merged-image.png"){const a=document.createElement("a");a.download=e,a.href=t,document.body.appendChild(a),a.click(),document.body.removeChild(a)}}class i{constructor(t={}){this.debug=t.debug||!1,this.debug&&console.log("โœ… ImageCropper initialized")}async crop(t,e,a={}){try{const i=await this.loadImage(t),o=document.createElement("canvas"),n=o.getContext("2d"),{x:r=0,y:s=0,width:l,height:h}=e;o.width=l,o.height=h,n.drawImage(i,r,s,l,h,0,0,l,h);const c=a.format||"png",g=a.quality||.92,d=o.toDataURL(`image/${c}`,g);return this.debug&&console.log(`โœ… Image cropped to ${l}x${h}`),{canvas:o,dataUrl:d,blob:await this.convertToBlob(o,c,g),width:l,height:h}}catch(t){throw this.debug&&console.error("โŒ Crop failed:",t),new Error(`Crop failed: ${t.message}`)}}async cropToCircle(t,e={}){try{const a=await this.loadImage(t),i=e.size||Math.min(a.width,a.height),o=document.createElement("canvas"),n=o.getContext("2d");o.width=i,o.height=i,n.beginPath(),n.arc(i/2,i/2,i/2,0,2*Math.PI),n.closePath(),n.clip();const r=e.x||(a.width-i)/2,s=e.y||(a.height-i)/2;n.drawImage(a,-r,-s);const l=e.format||"png",h=e.quality||.92,c=o.toDataURL(`image/${l}`,h);return this.debug&&console.log(`โœ… Image cropped to circle (${i}px)`),{canvas:o,dataUrl:c,blob:await this.convertToBlob(o,l,h),width:i,height:i}}catch(t){throw this.debug&&console.error("โŒ Circle crop failed:",t),new Error(`Circle crop failed: ${t.message}`)}}async cropToAspectRatio(t,e,a={}){try{const i=await this.loadImage(t),o=e.width/e.height;let n,r,s,l;return i.width/i.height>o?(r=i.height,n=r*o,s=(i.width-n)/2,l=0):(n=i.width,r=n/o,s=0,l=(i.height-r)/2),await this.crop(i,{x:s,y:l,width:n,height:r},a)}catch(t){throw this.debug&&console.error("โŒ Aspect ratio crop failed:",t),new Error(`Aspect ratio crop failed: ${t.message}`)}}async loadImage(t){return t instanceof HTMLImageElement?t:new Promise((e,a)=>{const i=new Image;if(i.onload=()=>e(i),i.onerror=()=>a(new Error("Failed to load image")),"string"==typeof t)t.startsWith("data:")||t.startsWith("http")||t.startsWith("/")?i.src=t:a(new Error("Invalid image URL"));else if(t instanceof File||t instanceof Blob){const e=new FileReader;e.onload=t=>{i.src=t.target.result},e.onerror=()=>a(new Error("Failed to read file")),e.readAsDataURL(t)}else a(new Error("Invalid input type"))})}async convertToBlob(t,e="png",a=.9){return new Promise((i,o)=>{t.toBlob(t=>{t?i(t):o(new Error("Failed to create blob"))},`image/${e}`,a)})}static ASPECT_RATIOS={SQUARE:{width:1,height:1},LANDSCAPE:{width:16,height:9},PORTRAIT:{width:9,height:16},CLASSIC:{width:4,height:3},WIDE:{width:21,height:9},INSTAGRAM:{width:4,height:5},FACEBOOK_COVER:{width:820,height:312}}}class o{constructor(t={}){this.debug=t.debug||!1,this.debug&&console.log("โœ… ImageEffects initialized")}async applyEffect(t,e,a=1,i={}){try{const n=await this.loadImage(t),r=document.createElement("canvas"),s=r.getContext("2d");r.width=n.width,r.height=n.height,s.drawImage(n,0,0);const l=o.EFFECTS[e.toUpperCase()];if(!l)throw new Error(`Unknown effect: ${e}`);await l.call(this,s,r,a);const h=i.format||"png",c=i.quality||.92,g=r.toDataURL(`image/${h}`,c);return this.debug&&console.log(`โœ… Applied effect: ${e}`),{canvas:r,dataUrl:g,blob:await this.convertToBlob(r,h,c),width:r.width,height:r.height,effect:e}}catch(t){throw this.debug&&console.error("โŒ Effect failed:",t),new Error(`Effect failed: ${t.message}`)}}async applyEffects(t,e,a={}){let i=t;for(const t of e){i=(await this.applyEffect(i,t.name,t.intensity||1,a)).canvas}const o=a.format||"png",n=a.quality||.92,r=i.toDataURL(`image/${o}`,n);return{canvas:i,dataUrl:r,blob:await this.convertToBlob(i,o,n),width:i.width,height:i.height,effects:e.map(t=>t.name)}}async loadImage(t){if(t instanceof HTMLCanvasElement){const e=new Image;return e.src=t.toDataURL(),await new Promise(t=>{e.onload=t}),e}return t instanceof HTMLImageElement?t:new Promise((e,a)=>{const i=new Image;if(i.onload=()=>e(i),i.onerror=()=>a(new Error("Failed to load image")),"string"==typeof t)t.startsWith("data:")||t.startsWith("http")||t.startsWith("/")?i.src=t:a(new Error("Invalid image URL"));else if(t instanceof File||t instanceof Blob){const e=new FileReader;e.onload=t=>{i.src=t.target.result},e.onerror=()=>a(new Error("Failed to read file")),e.readAsDataURL(t)}else a(new Error("Invalid input type"))})}async convertToBlob(t,e="png",a=.9){return new Promise((i,o)=>{t.toBlob(t=>{t?i(t):o(new Error("Failed to create blob"))},`image/${e}`,a)})}static EFFECTS={GRAYSCALE:function(t,e,a){const i=t.getImageData(0,0,e.width,e.height),o=i.data;for(let t=0;t<o.length;t+=4){const e=.299*o[t]+.587*o[t+1]+.114*o[t+2];o[t]=o[t]*(1-a)+e*a,o[t+1]=o[t+1]*(1-a)+e*a,o[t+2]=o[t+2]*(1-a)+e*a}t.putImageData(i,0,0)},SEPIA:function(t,e,a){const i=t.getImageData(0,0,e.width,e.height),o=i.data;for(let t=0;t<o.length;t+=4){const e=o[t],i=o[t+1],n=o[t+2],r=.393*e+.769*i+.189*n,s=.349*e+.686*i+.168*n,l=.272*e+.534*i+.131*n;o[t]=Math.min(255,e*(1-a)+r*a),o[t+1]=Math.min(255,i*(1-a)+s*a),o[t+2]=Math.min(255,n*(1-a)+l*a)}t.putImageData(i,0,0)},BRIGHTNESS:function(t,e,a){const i=t.getImageData(0,0,e.width,e.height),o=i.data,n=100*(a-.5);for(let t=0;t<o.length;t+=4)o[t]=Math.max(0,Math.min(255,o[t]+n)),o[t+1]=Math.max(0,Math.min(255,o[t+1]+n)),o[t+2]=Math.max(0,Math.min(255,o[t+2]+n));t.putImageData(i,0,0)},CONTRAST:function(t,e,a){const i=t.getImageData(0,0,e.width,e.height),o=i.data,n=259*(255*a+255)/(255*(259-255*a));for(let t=0;t<o.length;t+=4)o[t]=Math.max(0,Math.min(255,n*(o[t]-128)+128)),o[t+1]=Math.max(0,Math.min(255,n*(o[t+1]-128)+128)),o[t+2]=Math.max(0,Math.min(255,n*(o[t+2]-128)+128));t.putImageData(i,0,0)},INVERT:function(t,e,a){const i=t.getImageData(0,0,e.width,e.height),o=i.data;for(let t=0;t<o.length;t+=4)o[t]=o[t]*(1-a)+(255-o[t])*a,o[t+1]=o[t+1]*(1-a)+(255-o[t+1])*a,o[t+2]=o[t+2]*(1-a)+(255-o[t+2])*a;t.putImageData(i,0,0)},BLUR:function(t,e,a){const i=Math.max(1,10*a);t.filter=`blur(${i}px)`;const o=document.createElement("canvas");o.width=e.width,o.height=e.height;o.getContext("2d").drawImage(e,0,0),t.clearRect(0,0,e.width,e.height),t.drawImage(o,0,0),t.filter="none"},SHARPEN:function(t,e,a){const i=t.getImageData(0,0,e.width,e.height),o=i.data,n=e.width,r=[0,-1*a,0,-1*a,1+4*a,-1*a,0,-1*a,0],s=new Uint8ClampedArray(o);for(let t=1;t<e.height-1;t++)for(let a=1;a<e.width-1;a++)for(let e=0;e<3;e++){const i=4*(t*n+a)+e;let l=0;for(let i=-1;i<=1;i++)for(let o=-1;o<=1;o++){l+=s[4*((t+i)*n+(a+o))+e]*r[3*(i+1)+(o+1)]}o[i]=Math.max(0,Math.min(255,l))}t.putImageData(i,0,0)},VINTAGE:function(t,e,a){o.EFFECTS.SEPIA(t,e,.8*a);const i=t.createRadialGradient(e.width/2,e.height/2,0,e.width/2,e.height/2,Math.max(e.width,e.height)/2);i.addColorStop(0,"rgba(0,0,0,0)"),i.addColorStop(1,`rgba(0,0,0,${.5*a})`),t.fillStyle=i,t.fillRect(0,0,e.width,e.height)},WARM:function(t,e,a){const i=t.getImageData(0,0,e.width,e.height),o=i.data;for(let t=0;t<o.length;t+=4)o[t]=Math.min(255,o[t]+30*a),o[t+1]=Math.min(255,o[t+1]+15*a),o[t+2]=Math.max(0,o[t+2]-20*a);t.putImageData(i,0,0)},COOL:function(t,e,a){const i=t.getImageData(0,0,e.width,e.height),o=i.data;for(let t=0;t<o.length;t+=4)o[t]=Math.max(0,o[t]-20*a),o[t+1]=Math.min(255,o[t+1]+10*a),o[t+2]=Math.min(255,o[t+2]+30*a);t.putImageData(i,0,0)}};static EFFECT_NAMES=Object.keys(o.EFFECTS)}class n{constructor(t={}){this.debug=t.debug||!1,this.pageSize=t.pageSize||n.PAGE_SIZES.A4,this.orientation=t.orientation||"portrait",this.margin=t.margin||20,this.debug&&console.log("โœ… PDFConverter initialized")}async convertImageToPDF(t,e={}){try{const a=[{img:await this.loadImage(t),options:e.imageOptions||{}}];return await this.createPDF(a,e)}catch(t){throw this.debug&&console.error("โŒ PDF conversion failed:",t),new Error(`PDF conversion failed: ${t.message}`)}}async convertImagesToPDF(t,e={}){try{const a=[];for(const e of t){const t=await this.loadImage("object"==typeof e&&e.src?e.src:e),i="object"==typeof e&&e.options||{};a.push({img:t,options:i})}return await this.createPDF(a,e)}catch(t){throw this.debug&&console.error("โŒ PDF conversion failed:",t),new Error(`PDF conversion failed: ${t.message}`)}}async createPDF(t,e={}){const a=e.pageSize||this.pageSize,i=e.orientation||this.orientation,o=void 0!==e.margin?e.margin:this.margin;let n,r;"landscape"===i?(n=a.height,r=a.width):(n=a.width,r=a.height);const s=n-2*o,l=r-2*o,h=[];for(const{img:e,options:a}of t){const t=document.createElement("canvas");t.width=n,t.height=r;const i=t.getContext("2d");i.fillStyle="#ffffff",i.fillRect(0,0,n,r);const c=e.width/e.height,g=s/l;let d,m,w,u;const f=a.fit||"contain";"contain"===f?(c>g?(d=s,m=s/c):(m=l,d=l*c),w=o+(s-d)/2,u=o+(l-m)/2):"cover"===f?(c>g?(m=l,d=l*c):(d=s,m=s/c),w=o+(s-d)/2,u=o+(l-m)/2):(d=s,m=l,w=o,u=o),i.drawImage(e,w,u,d,m),h.push(t)}const c=h.map(t=>t.toDataURL("image/jpeg",.95));return this.debug&&console.log(`โœ… Created PDF with ${h.length} page(s)`),{pages:h,dataUrls:c,pageCount:h.length,pageSize:a,orientation:i,downloadUrl:await this.createSimplePDF(h,a,i)}}async createSimplePDF(t,e,a){if(1===t.length)return t[0].toDataURL("image/jpeg",.95);const i=t.reduce((t,e)=>t+e.height+20,0),o=Math.max(...t.map(t=>t.width)),n=document.createElement("canvas");n.width=o,n.height=i;const r=n.getContext("2d");r.fillStyle="#cccccc",r.fillRect(0,0,o,i);let s=0;for(const e of t){const t=(o-e.width)/2;r.drawImage(e,t,s),s+=e.height+20}return n.toDataURL("image/jpeg",.95)}async loadImage(t){return t instanceof HTMLImageElement?t:new Promise((e,a)=>{const i=new Image;if(i.onload=()=>e(i),i.onerror=()=>a(new Error("Failed to load image")),"string"==typeof t)t.startsWith("data:")||t.startsWith("http")||t.startsWith("/")?i.src=t:a(new Error("Invalid image URL"));else if(t instanceof File||t instanceof Blob){const e=new FileReader;e.onload=t=>{i.src=t.target.result},e.onerror=()=>a(new Error("Failed to read file")),e.readAsDataURL(t)}else a(new Error("Invalid input type"))})}static PAGE_SIZES={A4:{width:595,height:842},LETTER:{width:612,height:792},LEGAL:{width:612,height:1008},A3:{width:842,height:1191},A5:{width:420,height:595},TABLOID:{width:792,height:1224}};static ORIENTATIONS={PORTRAIT:"portrait",LANDSCAPE:"landscape"}}class r{constructor(t={}){this.debug=t.debug||!1,this.debug&&console.log("โœ… ImageResizer initialized")}async resize(t,e={}){try{const a=await this.loadImage(t),i=document.createElement("canvas"),o=i.getContext("2d"),{width:n,height:r,fit:s="contain",quality:l=.92,format:h="png"}=e;let c=n||a.width,g=r||a.height,d=0,m=0,w=a.width,u=a.height;if("contain"===s){const t=Math.min(c/a.width,g/a.height);c=a.width*t,g=a.height*t}else if("cover"===s){const t=Math.max(c/a.width,g/a.height);a.width,a.height;d=(a.width-c/t)/2,m=(a.height-g/t)/2,w=c/t,u=g/t}i.width=Math.round(c),i.height=Math.round(g),o.imageSmoothingEnabled=!0,o.imageSmoothingQuality="high","cover"===s?o.drawImage(a,d,m,w,u,0,0,c,g):o.drawImage(a,0,0,c,g);const f=i.toDataURL(`image/${h}`,l);return this.debug&&console.log(`โœ… Resized from ${a.width}x${a.height} to ${i.width}x${i.height}`),{canvas:i,dataUrl:f,blob:await this.convertToBlob(i,h,l),width:i.width,height:i.height,originalWidth:a.width,originalHeight:a.height}}catch(t){throw this.debug&&console.error("โŒ Resize failed:",t),new Error(`Resize failed: ${t.message}`)}}async createThumbnail(t,e={}){const a=e.size||150;return await this.resize(t,{width:a,height:a,fit:"cover",quality:e.quality||.85,format:e.format||"webp"})}async scale(t,e,a={}){try{const i=await this.loadImage(t),o=Math.round(i.width*e),n=Math.round(i.height*e);return await this.resize(i,{width:o,height:n,fit:"fill",quality:a.quality||.92,format:a.format||"png"})}catch(t){throw this.debug&&console.error("โŒ Scale failed:",t),new Error(`Scale failed: ${t.message}`)}}async loadImage(t){if(t instanceof HTMLImageElement)return t;if(t instanceof HTMLCanvasElement){const e=new Image;return e.src=t.toDataURL(),await new Promise(t=>{e.onload=t}),e}return new Promise((e,a)=>{const i=new Image;if(i.onload=()=>e(i),i.onerror=()=>a(new Error("Failed to load image")),"string"==typeof t)i.src=t;else if(t instanceof File||t instanceof Blob){const e=new FileReader;e.onload=t=>{i.src=t.target.result},e.onerror=()=>a(new Error("Failed to read file")),e.readAsDataURL(t)}else a(new Error("Invalid input type"))})}async convertToBlob(t,e="png",a=.9){return new Promise((i,o)=>{t.toBlob(t=>t?i(t):o(new Error("Failed to create blob")),`image/${e}`,a)})}static FIT_MODES={CONTAIN:"contain",COVER:"cover",FILL:"fill"}}class s{constructor(t={}){this.debug=t.debug||!1,this.debug&&console.log("โœ… ImageRotator initialized")}async rotate(t,e,a={}){try{const i=await this.loadImage(t),o=document.createElement("canvas"),n=o.getContext("2d"),r=e*Math.PI/180;e%180!=0?(o.width=i.height,o.height=i.width):(o.width=i.width,o.height=i.height),n.translate(o.width/2,o.height/2),n.rotate(r),n.drawImage(i,-i.width/2,-i.height/2);const s=a.format||"png",l=a.quality||.92,h=o.toDataURL(`image/${s}`,l);return this.debug&&console.log(`โœ… Rotated image by ${e}ยฐ`),{canvas:o,dataUrl:h,blob:await this.convertToBlob(o,s,l),width:o.width,height:o.height,rotation:e}}catch(t){throw this.debug&&console.error("โŒ Rotation failed:",t),new Error(`Rotation failed: ${t.message}`)}}async flip(t,e="horizontal",a={}){try{const i=await this.loadImage(t),o=document.createElement("canvas"),n=o.getContext("2d");o.width=i.width,o.height=i.height,n.save(),"horizontal"===e?(n.scale(-1,1),n.drawImage(i,-i.width,0)):(n.scale(1,-1),n.drawImage(i,0,-i.height)),n.restore();const r=a.format||"png",s=a.quality||.92,l=o.toDataURL(`image/${r}`,s);return this.debug&&console.log(`โœ… Flipped image ${e}`),{canvas:o,dataUrl:l,blob:await this.convertToBlob(o,r,s),width:o.width,height:o.height,flip:e}}catch(t){throw this.debug&&console.error("โŒ Flip failed:",t),new Error(`Flip failed: ${t.message}`)}}async loadImage(t){if(t instanceof HTMLImageElement)return t;if(t instanceof HTMLCanvasElement){const e=new Image;return e.src=t.toDataURL(),await new Promise(t=>{e.onload=t}),e}return new Promise((e,a)=>{const i=new Image;if(i.onload=()=>e(i),i.onerror=()=>a(new Error("Failed to load image")),"string"==typeof t)i.src=t;else if(t instanceof File||t instanceof Blob){const e=new FileReader;e.onload=t=>{i.src=t.target.result},e.onerror=()=>a(new Error("Failed to read file")),e.readAsDataURL(t)}else a(new Error("Invalid input type"))})}async convertToBlob(t,e="png",a=.9){return new Promise((i,o)=>{t.toBlob(t=>t?i(t):o(new Error("Failed to create blob")),`image/${e}`,a)})}static DIRECTIONS={HORIZONTAL:"horizontal",VERTICAL:"vertical"}}class l{constructor(t={}){this.debug=t.debug||!1,this.debug&&console.log("โœ… ImageCompressor initialized")}async compress(t,e={}){try{const a=await this.loadImage(t),i=e.maxSizeKB||500,o=e.format||"jpeg";let n=e.quality||.9;const r=document.createElement("canvas"),s=r.getContext("2d");let l,h=a.width,c=a.height;e.maxWidth&&h>e.maxWidth&&(c=e.maxWidth/h*c,h=e.maxWidth),e.maxHeight&&c>e.maxHeight&&(h=e.maxHeight/c*h,c=e.maxHeight),r.width=Math.round(h),r.height=Math.round(c),s.imageSmoothingEnabled=!0,s.imageSmoothingQuality="high",s.drawImage(a,0,0,r.width,r.height);let g=0;const d=10;for(;g<d;){l=await this.convertToBlob(r,o,n);if(l.size/1024<=i||n<=.1)break;n-=.1,g++}const m=await this.blobToDataUrl(l),w=(l.size/1024).toFixed(2);return this.debug&&console.log(`โœ… Compressed to ${w}KB (quality: ${n.toFixed(2)})`),{canvas:r,dataUrl:m,blob:l,width:r.width,height:r.height,sizeKB:parseFloat(w),quality:n,originalSize:a.width*a.height,compressionRatio:(100*(1-l.size/(a.width*a.height*4))).toFixed(2)}}catch(t){throw this.debug&&console.error("โŒ Compression failed:",t),new Error(`Compression failed: ${t.message}`)}}async optimize(t,e={}){return await this.compress(t,{maxSizeKB:e.maxSizeKB||800,maxWidth:e.maxWidth||1920,maxHeight:e.maxHeight||1080,format:e.format||"webp",quality:e.quality||.85})}async loadImage(t){if(t instanceof HTMLImageElement)return t;if(t instanceof HTMLCanvasElement){const e=new Image;return e.src=t.toDataURL(),await new Promise(t=>{e.onload=t}),e}return new Promise((e,a)=>{const i=new Image;if(i.onload=()=>e(i),i.onerror=()=>a(new Error("Failed to load image")),"string"==typeof t)i.src=t;else if(t instanceof File||t instanceof Blob){const e=new FileReader;e.onload=t=>{i.src=t.target.result},e.onerror=()=>a(new Error("Failed to read file")),e.readAsDataURL(t)}else a(new Error("Invalid input type"))})}async convertToBlob(t,e="jpeg",a=.9){return new Promise((i,o)=>{t.toBlob(t=>t?i(t):o(new Error("Failed to create blob")),`image/${e}`,a)})}async blobToDataUrl(t){return new Promise((e,a)=>{const i=new FileReader;i.onload=()=>e(i.result),i.onerror=a,i.readAsDataURL(t)})}}class h{constructor(t={}){this.debug=t.debug||!1,this.debug&&console.log("โœ… TextOverlay initialized")}async addWatermark(t,e={}){try{const a=await this.loadImage(t),i=document.createElement("canvas"),o=i.getContext("2d");i.width=a.width,i.height=a.height,o.drawImage(a,0,0);const n=e.text||"ยฉ Watermark",r=e.fontSize||Math.max(20,Math.floor(a.width/30)),s=e.fontFamily||"Arial",l=e.color||"#ffffff",h=void 0!==e.opacity?e.opacity:.5,c=e.position||"bottom-right",g=e.padding||20;o.font=`${r}px ${s}`,o.fillStyle=l,o.globalAlpha=h;const d=o.measureText(n).width,m=r;let w,u;"bottom-right"===c?(w=i.width-d-g,u=i.height-g):"bottom-left"===c?(w=g,u=i.height-g):"top-right"===c?(w=i.width-d-g,u=m+g):"top-left"===c?(w=g,u=m+g):"center"===c&&(w=(i.width-d)/2,u=i.height/2),e.shadow&&(o.shadowColor="rgba(0,0,0,0.5)",o.shadowBlur=4,o.shadowOffsetX=2,o.shadowOffsetY=2),o.fillText(n,w,u),o.globalAlpha=1,o.shadowColor="transparent";const f=e.format||"png",p=e.quality||.92,b=i.toDataURL(`image/${f}`,p);return this.debug&&console.log(`โœ… Watermark added: "${n}"`),{canvas:i,dataUrl:b,blob:await this.convertToBlob(i,f,p),width:i.width,height:i.height,watermark:n}}catch(t){throw this.debug&&console.error("โŒ Watermark failed:",t),new Error(`Watermark failed: ${t.message}`)}}async addText(t,e={}){try{const a=await this.loadImage(t),i=document.createElement("canvas"),o=i.getContext("2d");i.width=a.width,i.height=a.height,o.drawImage(a,0,0);const n=e.text||"Text",r=e.fontSize||48,s=e.fontFamily||"Arial",l=e.color||"#000000",h=e.backgroundColor,c="center"===e.x?null:e.x||0,g="center"===e.y?null:e.y||50,d=e.align||"left",m=e.bold?"bold ":"",w=e.italic?"italic ":"";o.font=`${m}${w}${r}px ${s}`,o.fillStyle=l,o.textAlign=d;const u=o.measureText(n);let f=null!==c?c:i.width/2,p=null!==g?g:i.height/2;if(null===c&&(o.textAlign="center"),h){const t=10,e=f-(null===c?u.width/2:0)-t,a=p-r-t,i=u.width+2*t,n=r+2*t;o.fillStyle=h,o.fillRect(e,a,i,n),o.fillStyle=l}e.shadow&&(o.shadowColor=e.shadowColor||"rgba(0,0,0,0.5)",o.shadowBlur=e.shadowBlur||4,o.shadowOffsetX=e.shadowOffsetX||2,o.shadowOffsetY=e.shadowOffsetY||2),o.fillText(n,f,p),e.stroke&&(o.strokeStyle=e.strokeColor||"#ffffff",o.lineWidth=e.strokeWidth||2,o.strokeText(n,f,p)),o.shadowColor="transparent";const b=e.format||"png",y=e.quality||.92,v=i.toDataURL(`image/${b}`,y);return this.debug&&console.log(`โœ… Text added: "${n}"`),{canvas:i,dataUrl:v,blob:await this.convertToBlob(i,b,y),width:i.width,height:i.height,text:n}}catch(t){throw this.debug&&console.error("โŒ Text overlay failed:",t),new Error(`Text overlay failed: ${t.message}`)}}async loadImage(t){if(t instanceof HTMLImageElement)return t;if(t instanceof HTMLCanvasElement){const e=new Image;return e.src=t.toDataURL(),await new Promise(t=>{e.onload=t}),e}return new Promise((e,a)=>{const i=new Image;if(i.onload=()=>e(i),i.onerror=()=>a(new Error("Failed to load image")),"string"==typeof t)i.src=t;else if(t instanceof File||t instanceof Blob){const e=new FileReader;e.onload=t=>{i.src=t.target.result},e.onerror=()=>a(new Error("Failed to read file")),e.readAsDataURL(t)}else a(new Error("Invalid input type"))})}async convertToBlob(t,e="png",a=.9){return new Promise((i,o)=>{t.toBlob(t=>t?i(t):o(new Error("Failed to create blob")),`image/${e}`,a)})}static POSITIONS={TOP_LEFT:"top-left",TOP_RIGHT:"top-right",BOTTOM_LEFT:"bottom-left",BOTTOM_RIGHT:"bottom-right",CENTER:"center"}}class c{constructor(t={}){this.debug=t.debug||!1,this.debug&&console.log("โœ… ColorAdjuster initialized")}async adjustColors(t,e={}){try{const a=await this.loadImage(t),i=document.createElement("canvas"),o=i.getContext("2d");i.width=a.width,i.height=a.height,o.drawImage(a,0,0);const n=o.getImageData(0,0,i.width,i.height),r=n.data,s=void 0!==e.saturation?e.saturation:1,l=void 0!==e.hue?e.hue:0,h=void 0!==e.lightness?e.lightness:0,c=void 0!==e.vibrance?e.vibrance:1;for(let t=0;t<r.length;t+=4){let e=r[t],a=r[t+1],i=r[t+2],o=this.rgbToHsl(e,a,i);o.h=(o.h+l/360)%1,o.s=Math.max(0,Math.min(1,o.s*s*c)),o.l=Math.max(0,Math.min(1,o.l+h/100));const n=this.hslToRgb(o.h,o.s,o.l);r[t]=n.r,r[t+1]=n.g,r[t+2]=n.b}o.putImageData(n,0,0);const g=e.format||"png",d=e.quality||.92,m=i.toDataURL(`image/${g}`,d);return this.debug&&console.log("โœ… Color adjustments applied"),{canvas:i,dataUrl:m,blob:await this.convertToBlob(i,g,d),width:i.width,height:i.height,adjustments:{saturation:s,hue:l,lightness:h,vibrance:c}}}catch(t){throw this.debug&&console.error("โŒ Color adjustment failed:",t),new Error(`Color adjustment failed: ${t.message}`)}}rgbToHsl(t,e,a){t/=255,e/=255,a/=255;const i=Math.max(t,e,a),o=Math.min(t,e,a);let n,r,s=(i+o)/2;if(i===o)n=r=0;else{const l=i-o;switch(r=s>.5?l/(2-i-o):l/(i+o),i){case t:n=((e-a)/l+(e<a?6:0))/6;break;case e:n=((a-t)/l+2)/6;break;case a:n=((t-e)/l+4)/6}}return{h:n,s:r,l:s}}hslToRgb(t,e,a){let i,o,n;if(0===e)i=o=n=a;else{const r=(t,e,a)=>(a<0&&(a+=1),a>1&&(a-=1),a<1/6?t+6*(e-t)*a:a<.5?e:a<2/3?t+(e-t)*(2/3-a)*6:t),s=a<.5?a*(1+e):a+e-a*e,l=2*a-s;i=r(l,s,t+1/3),o=r(l,s,t),n=r(l,s,t-1/3)}return{r:Math.round(255*i),g:Math.round(255*o),b:Math.round(255*n)}}async loadImage(t){if(t instanceof HTMLImageElement)return t;if(t instanceof HTMLCanvasElement){const e=new Image;return e.src=t.toDataURL(),await new Promise(t=>{e.onload=t}),e}return new Promise((e,a)=>{const i=new Image;if(i.onload=()=>e(i),i.onerror=()=>a(new Error("Failed to load image")),"string"==typeof t)i.src=t;else if(t instanceof File||t instanceof Blob){const e=new FileReader;e.onload=t=>{i.src=t.target.result},e.onerror=()=>a(new Error("Failed to read file")),e.readAsDataURL(t)}else a(new Error("Invalid input type"))})}async convertToBlob(t,e="png",a=.9){return new Promise((i,o)=>{t.toBlob(t=>t?i(t):o(new Error("Failed to create blob")),`image/${e}`,a)})}}return class{constructor(g={}){this.debug=g.debug||!1,this.svgToRaster=new t(g),this.rasterToSvg=new e(g),this.layerMerger=new a(g),this.imageCropper=new i(g),this.imageEffects=new o(g),this.pdfConverter=new n(g),this.imageResizer=new r(g),this.imageRotator=new s(g),this.imageCompressor=new l(g),this.textOverlay=new h(g),this.colorAdjuster=new c(g),t.injectKnittingCSS(),this.debug&&(console.log("โœ… EIIP (Elite India Image Processing) initialized"),console.log("๐Ÿงถ Knitting effects CSS automatically loaded"),console.log("โœ‚๏ธ Image Cropper ready"),console.log("๐ŸŽจ Image Effects ready"),console.log("๐Ÿ“„ PDF Converter ready"),console.log("๐Ÿ“ Image Resizer ready"),console.log("๐Ÿ”„ Image Rotator ready"),console.log("๐Ÿ—œ๏ธ Image Compressor ready"),console.log("๐Ÿ’ฌ Text Overlay ready"),console.log("๐ŸŽจ Color Adjuster ready"))}async convertSvgToRaster(e,a={}){const i=new t({...a,debug:this.debug});if("string"==typeof e)return e.startsWith("http")||e.startsWith("/")?await i.convertFromUrl(e):await i.convertFromSvgString(e);if(e instanceof File)return await i.convertFromFile(e);if(e instanceof SVGElement)return await i.convertFromSvgElement(e);throw new Error("Invalid input type for SVG conversion")}async convertRasterToSvg(t,a={}){const i=new e({...a,debug:this.debug});if("string"==typeof t)return t.startsWith("data:")?await i.convertFromDataUrl(t):await i.convertFromUrl(t);if(t instanceof File)return await i.convertFromFile(t);if(t instanceof HTMLImageElement)return await i.convertFromImageElement(t);throw new Error("Invalid input type for raster conversion")}async mergeLayers(t,e={}){const i=new a({...e,debug:this.debug});return await i.mergeImages(t,e)}async cropImage(t,e,a={}){const o=new i({...a,debug:this.debug});return await o.crop(t,e,a)}async cropToCircle(t,e={}){const a=new i({...e,debug:this.debug});return await a.cropToCircle(t,e)}async cropToAspectRatio(t,e,a={}){const o=new i({...a,debug:this.debug});return await o.cropToAspectRatio(t,e,a)}async applyEffect(t,e,a=1,i={}){const n=new o({...i,debug:this.debug});return await n.applyEffect(t,e,a,i)}async applyEffects(t,e,a={}){const i=new o({...a,debug:this.debug});return await i.applyEffects(t,e,a)}async convertImageToPDF(t,e={}){const a=new n({...e,debug:this.debug});return await a.convertImageToPDF(t,e)}async convertImagesToPDF(t,e={}){const a=new n({...e,debug:this.debug});return await a.convertImagesToPDF(t,e)}async resizeImage(t,e={}){const a=new r({...e,debug:this.debug});return await a.resize(t,e)}async scaleImage(t,e,a={}){const i=new r({...a,debug:this.debug});return await i.scale(t,e,a)}async createThumbnail(t,e={}){const a=new r({...e,debug:this.debug});return await a.createThumbnail(t,e)}async rotateImage(t,e,a={}){const i=new s({...a,debug:this.debug});return await i.rotate(t,e,a)}async flipImage(t,e="horizontal",a={}){const i=new s({...a,debug:this.debug});return await i.flip(t,e,a)}async compressImage(t,e={}){const a=new l({...e,debug:this.debug});return await a.compress(t,e)}async optimizeImage(t,e={}){const a=new l({...e,debug:this.debug});return await a.optimize(t,e)}async addWatermark(t,e={}){const a=new h({...e,debug:this.debug});return await a.addWatermark(t,e)}async addText(t,e={}){const a=new h({...e,debug:this.debug});return await a.addText(t,e)}async adjustColors(t,e={}){const a=new c({...e,debug:this.debug});return await a.adjustColors(t,e)}downloadImage(t,e){const a=document.createElement("a");a.download=e,a.href=t,document.body.appendChild(a),a.click(),document.body.removeChild(a)}static SvgToRaster=t;static RasterToSvg=e;static ImageLayerMerger=a;static ImageCropper=i;static ImageEffects=o;static PDFConverter=n;static ImageResizer=r;static ImageRotator=s;static ImageCompressor=l;static TextOverlay=h;static ColorAdjuster=c;static version="1.2.0";static author="Saleem Ahmad (Elite India)"}});