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
JavaScript
/**
* 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)"}});