{"id":4573,"date":"2025-01-01T16:37:00","date_gmt":"2025-01-01T11:07:00","guid":{"rendered":"https:\/\/rajmedical.co.in\/?p=4573"},"modified":"2026-06-21T20:04:05","modified_gmt":"2026-06-21T14:34:05","slug":"image-compressor-cum-converter","status":"publish","type":"post","link":"https:\/\/rajmedical.co.in\/hi\/image-compressor-cum-converter\/","title":{"rendered":"Image Compressor cum Converter"},"content":{"rendered":"\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Density \u2014 image compressor &amp; converter<\/title>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin=\"\">\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Big+Shoulders+Display:wght@600;700;800&amp;family=IBM+Plex+Mono:wght@400;500;600&amp;family=IBM+Plex+Sans:wght@400;500;600&amp;display=swap\" rel=\"stylesheet\">\n<style>\n  :root {\n    --bg: #1c1b19;\n    --panel: #242220;\n    --panel-2: #2b2925;\n    --border: #3a3733;\n    --paper: #f2ede4;\n    --paper-dim: #c9c2b4;\n    --steel: #8fa3a8;\n    --steel-dim: #6c7a7e;\n    --safelight: #ff6a2b;\n    --safelight-dim: #c9531f;\n    --good: #6fcf7a;\n    --warn: #ffb84d;\n    --radius: 4px;\n  }\n  *{ box-sizing: border-box; }\n  html,body{ margin:0; padding:0; }\n  .wrap{\n    background: var(--bg);\n    color: var(--paper);\n    font-family: 'IBM Plex Sans', sans-serif;\n    -webkit-font-smoothing: antialiased;\n    min-height: 100vh;\n  }\n  .mono{ font-family: 'IBM Plex Mono', monospace; }\n  a{ color: inherit; }\n  button{ font-family: inherit; }\n  ::selection{ background: var(--safelight); color: #1c1b19; }\n\n  :focus-visible{ outline: 2px solid var(--safelight); outline-offset: 2px; }\n\n  .wrap{\n    max-width: 980px;\n    margin: 0 auto;\n    padding: 56px 24px 80px;\n  }\n\n  \/* ---------- Header ---------- *\/\n  header{ margin-bottom: 40px; }\n  .eyebrow{\n    font-size: 11px;\n    letter-spacing: 0.16em;\n    text-transform: uppercase;\n    color: var(--steel);\n    margin: 0 0 10px;\n  }\n  .eyebrow .dot{ color: var(--safelight); }\n  h1.wordmark{\n    font-family: 'Big Shoulders Display', sans-serif;\n    font-weight: 800;\n    font-size: clamp(56px, 11vw, 96px);\n    line-height: 0.86;\n    letter-spacing: -0.01em;\n    margin: 0 0 16px;\n    color: var(--paper);\n    opacity: 0;\n    transform: translateY(10px);\n    animation: rise 0.6s ease forwards;\n  }\n  h1.wordmark span{ color: var(--safelight); }\n  @keyframes rise{ to{ opacity:1; transform: translateY(0); } }\n  .tagline{\n    font-size: 16px;\n    line-height: 1.55;\n    color: var(--paper-dim);\n    max-width: 52ch;\n    margin: 0;\n  }\n\n  \/* ---------- Dropzone ---------- *\/\n  .dropzone{\n    position: relative;\n    border: 2px dashed var(--border);\n    border-radius: var(--radius);\n    padding: 64px 24px;\n    text-align: center;\n    cursor: pointer;\n    transition: border-color 0.15s ease, background-color 0.15s ease;\n    margin-bottom: 28px;\n  }\n  .dropzone:hover{ border-color: #545049; }\n  .dropzone.active{\n    border-color: var(--safelight);\n    background: rgba(255,106,43,0.06);\n  }\n  .dropzone .corner{\n    position: absolute;\n    width: 18px; height: 18px;\n    border: 0 solid var(--safelight);\n    opacity: 0.85;\n  }\n  .dropzone .corner.tl{ top:10px; left:10px; border-top-width:2px; border-left-width:2px; }\n  .dropzone .corner.tr{ top:10px; right:10px; border-top-width:2px; border-right-width:2px; }\n  .dropzone .corner.bl{ bottom:10px; left:10px; border-bottom-width:2px; border-left-width:2px; }\n  .dropzone .corner.br{ bottom:10px; right:10px; border-bottom-width:2px; border-right-width:2px; }\n  .dropzone svg{ color: var(--steel); margin-bottom: 14px; }\n  .dropzone .dz-title{ font-size: 19px; font-weight: 500; margin: 0 0 8px; color: var(--paper); }\n  .dropzone .dz-sub{ font-size: 12px; color: var(--steel); letter-spacing: 0.02em; }\n  .dropzone input[type=file]{ display:none; }\n\n  \/* ---------- Global stats \/ controls bar ---------- *\/\n  .statsbar{\n    display:none;\n    background: var(--panel);\n    border: 1px solid var(--border);\n    border-radius: var(--radius);\n    padding: 14px 18px;\n    margin-bottom: 20px;\n    align-items: center;\n    justify-content: space-between;\n    gap: 16px;\n    flex-wrap: wrap;\n  }\n  .statsbar.show{ display:flex; }\n  .statsbar .totals{ font-size: 13px; color: var(--paper-dim); }\n  .statsbar .totals b{ color: var(--paper); font-weight: 600; }\n  .statsbar .totals .pct{ color: var(--good); font-weight: 600; }\n  .statsbar .actions{ display:flex; gap:8px; flex-wrap: wrap; align-items:center; }\n\n  select.mini, .num-input{\n    background: var(--panel-2);\n    border: 1px solid var(--border);\n    color: var(--paper);\n    font-family: 'IBM Plex Mono', monospace;\n    font-size: 12px;\n    padding: 6px 8px;\n    border-radius: var(--radius);\n  }\n\n  .btn{\n    font-family: 'IBM Plex Mono', monospace;\n    font-size: 11px;\n    letter-spacing: 0.06em;\n    text-transform: uppercase;\n    padding: 8px 14px;\n    border-radius: var(--radius);\n    border: 1px solid var(--border);\n    background: transparent;\n    color: var(--paper-dim);\n    cursor: pointer;\n    transition: all 0.12s ease;\n  }\n  .btn:hover{ border-color: var(--steel); color: var(--paper); }\n  .btn.primary{\n    background: var(--safelight);\n    border-color: var(--safelight);\n    color: #1c1503;\n    font-weight: 600;\n  }\n  .btn.primary:hover{ background: #ff7d45; }\n  .btn:disabled{ opacity:0.35; cursor: not-allowed; }\n\n  \/* ---------- Cards ---------- *\/\n  .cards{ display:flex; flex-direction: column; gap: 16px; }\n  .card{\n    background: var(--panel);\n    border: 1px solid var(--border);\n    border-radius: var(--radius);\n    padding: 18px;\n    position: relative;\n    opacity: 0;\n    transform: translateY(6px);\n    animation: cardin 0.25s ease forwards;\n  }\n  @keyframes cardin{ to{ opacity:1; transform: translateY(0); } }\n  .card.skeleton .card-grid{ opacity: 0.4; }\n  .card.error{ border-color: var(--safelight-dim); }\n  .card .remove{\n    position:absolute; top:12px; right:12px;\n    background:none; border:none; color: var(--steel-dim);\n    font-size: 18px; line-height:1; cursor:pointer; padding: 4px;\n  }\n  .card .remove:hover{ color: var(--safelight); }\n\n  .card-grid{\n    display:grid;\n    grid-template-columns: 150px 1fr;\n    gap: 20px;\n  }\n  @media (max-width: 640px){\n    .card-grid{ grid-template-columns: 1fr; }\n  }\n\n  .thumb-wrap{\n    width:100%;\n    aspect-ratio: 1\/1;\n    border-radius: 2px;\n    border: 1px solid var(--border);\n    overflow:hidden;\n    background-image:\n      linear-gradient(45deg, #2b2925 25%, transparent 25%),\n      linear-gradient(-45deg, #2b2925 25%, transparent 25%),\n      linear-gradient(45deg, transparent 75%, #2b2925 75%),\n      linear-gradient(-45deg, transparent 75%, #2b2925 75%);\n    background-size: 16px 16px;\n    background-position: 0 0, 0 8px, 8px -8px, -8px 0px;\n    background-color: #1f1e1b;\n  }\n  .thumb-wrap img{ width:100%; height:100%; object-fit: contain; display:block; }\n\n  .card-head{ display:flex; align-items:flex-start; justify-content:space-between; gap: 10px; margin-bottom: 14px; padding-right: 20px; }\n  .card-name{ font-size: 13px; color: var(--paper); word-break: break-all; margin: 0 0 6px; }\n  .pill-row{ display:flex; gap:8px; flex-wrap: wrap; align-items:center; }\n  .pill{\n    font-size: 10px;\n    letter-spacing: 0.05em;\n    text-transform: uppercase;\n    border: 1px solid var(--steel-dim);\n    color: var(--steel);\n    padding: 2px 6px;\n    border-radius: 2px;\n  }\n  .meta-text{ font-size: 11px; color: var(--steel); }\n\n  \/* Quality control *\/\n  .ctrl-block{ margin-bottom: 16px; }\n  .ctrl-label{\n    font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;\n    color: var(--steel); display:flex; justify-content: space-between; margin-bottom: 8px;\n  }\n  .ctrl-label .val{ color: var(--paper); }\n  input[type=range]{\n    -webkit-appearance: none;\n    width: 100%;\n    height: 4px;\n    background: var(--border);\n    border-radius: 2px;\n    margin: 0;\n  }\n  input[type=range]::-webkit-slider-thumb{\n    -webkit-appearance: none;\n    width: 15px; height: 15px;\n    border-radius: 50%;\n    background: var(--safelight);\n    cursor: pointer;\n    border: 2px solid #1c1b19;\n    box-shadow: 0 0 0 1px var(--safelight);\n  }\n  input[type=range]::-moz-range-thumb{\n    width: 15px; height: 15px;\n    border-radius: 50%;\n    background: var(--safelight);\n    cursor: pointer;\n    border: 2px solid #1c1b19;\n  }\n  .stops-row{\n    display:flex; justify-content: space-between;\n    margin-top: 6px;\n    font-size: 10px;\n    color: var(--steel-dim);\n    font-family: 'IBM Plex Mono', monospace;\n  }\n  .stops-row span{ flex: 1; text-align:center; }\n  .stops-row span:first-child{ text-align:left; }\n  .stops-row span:last-child{ text-align:right; }\n  .stops-row span.active{ color: var(--safelight); font-weight: 600; }\n\n  \/* Format chips *\/\n  .chip-row{ display:flex; gap:8px; flex-wrap: wrap; }\n  .chip{\n    flex: 1 1 90px;\n    position: relative;\n    border: 1px solid var(--border);\n    background: var(--panel-2);\n    border-radius: var(--radius);\n    padding: 8px 10px;\n    text-align:left;\n    cursor: pointer;\n    transition: border-color 0.12s ease, background-color 0.12s ease;\n  }\n  .chip:hover:not(:disabled){ border-color: var(--steel); }\n  .chip.active{ border-color: var(--safelight); background: rgba(255,106,43,0.08); }\n  .chip:disabled{ opacity: 0.32; cursor: not-allowed; }\n  .chip .fmt{ display:block; font-family:'IBM Plex Mono', monospace; font-size: 11px; font-weight:600; letter-spacing:0.04em; color: var(--paper); text-transform: uppercase; }\n  .chip .sz{ display:block; font-family:'IBM Plex Mono', monospace; font-size: 11px; color: var(--steel); margin-top:3px; }\n  .chip.active .fmt{ color: var(--safelight); }\n  .chip .badge{\n    position:absolute; top:-7px; right:6px;\n    font-size: 8px; letter-spacing: 0.05em; text-transform: uppercase;\n    background: var(--good); color: #0e1f10; padding: 1px 4px; border-radius: 2px; font-weight:700;\n  }\n\n  \/* Resize row *\/\n  .resize-row{ display:flex; align-items:center; gap:10px; margin-top: 14px; font-size: 12px; color: var(--paper-dim); flex-wrap: wrap; }\n  .resize-row label.cb{ display:flex; align-items:center; gap:7px; cursor:pointer; }\n  input[type=checkbox]{\n    appearance: none; width:15px; height:15px; border:1px solid var(--steel-dim); border-radius:2px;\n    background: var(--panel-2); cursor:pointer; position: relative; flex-shrink:0;\n  }\n  input[type=checkbox]:checked{ background: var(--safelight); border-color: var(--safelight); }\n  input[type=checkbox]:checked::after{\n    content:''; position:absolute; left:4px; top:1px; width:4px; height:8px;\n    border: solid #1c1b19; border-width: 0 2px 2px 0; transform: rotate(45deg);\n  }\n  .resize-row .num-input{ width: 80px; }\n  .resize-fields{ display:flex; align-items:center; gap:8px; }\n  .resize-fields[hidden]{ display:none; }\n\n  \/* Result row *\/\n  .result-row{\n    margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border);\n    display:flex; align-items:center; justify-content: space-between; gap: 14px; flex-wrap: wrap;\n  }\n  .result-figures{ display:flex; align-items:baseline; gap: 12px; flex-wrap: wrap; }\n  .fig{ }\n  .fig .flabel{ font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--steel); display:block; margin-bottom: 3px; }\n  .fig .fval{ font-family: 'IBM Plex Mono', monospace; font-size: 14px; color: var(--steel); }\n  .fig.out .fval{ font-family: 'Big Shoulders Display', sans-serif; font-size: 26px; font-weight: 700; color: var(--paper); }\n  .arrow{ color: var(--steel-dim); font-size: 16px; }\n  .pct-badge{\n    font-family: 'IBM Plex Mono', monospace; font-size: 12px; font-weight: 600;\n    padding: 3px 8px; border-radius: 3px;\n  }\n  .pct-badge.down{ color: var(--good); background: rgba(111,207,122,0.12); }\n  .pct-badge.up{ color: var(--warn); background: rgba(255,184,77,0.12); }\n\n  .err-msg{ font-size: 13px; color: var(--warn); margin: 4px 0 0; }\n\n  \/* ---------- Footer ---------- *\/\n  footer{ margin-top: 56px; text-align:center; }\n  footer p{ font-size: 11px; color: var(--steel-dim); letter-spacing: 0.02em; line-height:1.6; }\n\n  @media (prefers-reduced-motion: reduce){\n    *{ animation: none !important; transition: none !important; }\n  }\n<\/style>\n\n\n<div class=\"wrap\">\n\n  <header>\n    <p class=\"eyebrow\"><span class=\"dot\">\u25cf<\/span> client-side photo lab<\/p>\n    <h1 class=\"wordmark\">DENSI<span>TY<\/span><\/h1>\n    <p class=\"tagline\">Drop in an image, dial down its density, and carry it out lighter \u2014 JPEG, PNG, WebP or AVIF.<\/p>\n  <\/header>\n\n  <section class=\"dropzone\" id=\"dropzone\" tabindex=\"0\" role=\"button\" aria-label=\"Upload images\">\n    <span class=\"corner tl\"><\/span><span class=\"corner tr\"><\/span><span class=\"corner bl\"><\/span><span class=\"corner br\"><\/span>\n    <svg width=\"34\" height=\"34\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.4\">\n      <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"><\/rect>\n      <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"><\/circle>\n      <path d=\"M21 15l-5-5L5 21\"><\/path>\n    <\/svg>\n    <p class=\"dz-title\">Drop images here, or click to browse<\/p>\n    <p class=\"dz-sub mono\">JPEG \u00b7 PNG \u00b7 WEBP \u00b7 GIF \u00b7 BMP \u00b7 SVG \u00b7 AVIF \u2014 not saved in our server.<\/p>\n    <input type=\"file\" id=\"fileInput\" accept=\"image\/*\" multiple=\"\">\n  <\/section>\n\n  <section class=\"statsbar\" id=\"statsbar\">\n    <div class=\"totals mono\" id=\"totalsText\">0 images<\/div>\n    <div class=\"actions\">\n      <select class=\"mini\" id=\"globalQuality\">\n        <option value=\"0\">f\/2.8 \u2014 best<\/option>\n        <option value=\"1\">f\/4 \u2014 high<\/option>\n        <option value=\"2\" selected=\"\">f\/5.6 \u2014 balanced<\/option>\n        <option value=\"3\">f\/8 \u2014 standard<\/option>\n        <option value=\"4\">f\/11 \u2014 aggressive<\/option>\n        <option value=\"5\">f\/16 \u2014 max<\/option>\n      <\/select>\n      <select class=\"mini\" id=\"globalFormat\">\n        <option value=\"webp\">WebP<\/option>\n        <option value=\"jpeg\">JPEG<\/option>\n        <option value=\"png\">PNG<\/option>\n        <option value=\"avif\">AVIF<\/option>\n      <\/select>\n      <button class=\"btn\" id=\"applyAllBtn\">Apply to all<\/button>\n      <button class=\"btn primary\" id=\"downloadAllBtn\">Download all (.zip)<\/button>\n      <button class=\"btn\" id=\"clearAllBtn\">Clear<\/button>\n    <\/div>\n  <\/section>\n\n  <section class=\"cards\" id=\"cards\"><\/section>\n\n  <footer>\n    <p><!-- Compression and format conversion run on-device using the Canvas API.<br>No file ever leaves your browser. --> AVIF\/WebP output availability depends on your browser&#8217;s encoder support.<p><\/p>\n  <\/footer>\n\n<\/div>\n\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jszip\/3.10.1\/jszip.min.js\"><\/script>\n<script>\n(function(){\n  \"use strict\";\n\n  \/\/ ---------- Config ----------\n  var QUALITY_STOPS = [\n    { q: 95, label: 'f\/2.8', desc: 'Best quality' },\n    { q: 85, label: 'f\/4',   desc: 'High quality' },\n    { q: 75, label: 'f\/5.6', desc: 'Balanced' },\n    { q: 60, label: 'f\/8',   desc: 'Standard' },\n    { q: 40, label: 'f\/11',  desc: 'Aggressive' },\n    { q: 20, label: 'f\/16',  desc: 'Max compression' }\n  ];\n  var MIME = { jpeg: 'image\/jpeg', png: 'image\/png', webp: 'image\/webp', avif: 'image\/avif' };\n  var EXT  = { jpeg: 'jpg', png: 'png', webp: 'webp', avif: 'avif' };\n  var ALL_FORMATS = ['jpeg', 'png', 'webp', 'avif'];\n\n  var state = { cards: new Map(), order: [] };\n  var SUPPORTED = { jpeg: true, png: true, webp: false, avif: false };\n  var defaultFormat = 'jpeg';\n\n  \/\/ ---------- Utilities ----------\n  function formatBytes(bytes){\n    if (bytes == null) return '\u2014';\n    if (bytes < 1024) return bytes + ' B';\n    var units = ['KB','MB','GB'];\n    var val = bytes, i = -1;\n    do { val \/= 1024; i++; } while (val >= 1024 && i < units.length - 1);\n    return val.toFixed(val < 10 ? 2 : 1) + ' ' + units[i];\n  }\n\n  function pctChange(orig, now){\n    if (!orig) return 0;\n    return (1 - (now \/ orig)) * 100;\n  }\n\n  function uid(){\n    return 'c' + Math.random().toString(36).slice(2, 10) + Date.now().toString(36);\n  }\n\n  function baseName(filename){\n    var idx = filename.lastIndexOf('.');\n    return idx > 0 ? filename.slice(0, idx) : filename;\n  }\n\n  function extOf(filename){\n    var idx = filename.lastIndexOf('.');\n    return idx > 0 ? filename.slice(idx + 1).toUpperCase() : '?';\n  }\n\n  function detectFormatSupport(mime){\n    return new Promise(function(resolve){\n      try{\n        var c = document.createElement('canvas');\n        c.width = 2; c.height = 2;\n        c.toBlob(function(blob){\n          resolve(!!blob && blob.type === mime);\n        }, mime);\n      } catch(e){ resolve(false); }\n    });\n  }\n\n  function triggerDownload(url, filename){\n    var a = document.createElement('a');\n    a.href = url;\n    a.download = filename;\n    document.body.appendChild(a);\n    a.click();\n    a.remove();\n  }\n\n  \/\/ ---------- Canvas processing ----------\n  function getTargetDims(card){\n    var w = card.naturalWidth, h = card.naturalHeight;\n    if (card.settings.resize){\n      var longSide = Math.max(w, h);\n      var maxSide = card.settings.maxDim;\n      if (maxSide < longSide){\n        var scale = maxSide \/ longSide;\n        w = Math.max(1, Math.round(w * scale));\n        h = Math.max(1, Math.round(h * scale));\n      }\n    }\n    return { w: w, h: h };\n  }\n\n  function computeBlob(img, fmt, quality, w, h){\n    return new Promise(function(resolve, reject){\n      try{\n        var canvas = document.createElement('canvas');\n        canvas.width = w; canvas.height = h;\n        var ctx = canvas.getContext('2d');\n        if (fmt === 'jpeg'){\n          ctx.fillStyle = '#FFFFFF';\n          ctx.fillRect(0, 0, w, h);\n        }\n        ctx.drawImage(img, 0, 0, w, h);\n        var mime = MIME[fmt];\n        var q = (fmt === 'png') ? undefined : (quality \/ 100);\n        canvas.toBlob(function(blob){\n          if (blob) resolve(blob); else reject(new Error('encode failed'));\n        }, mime, q);\n      } catch(e){ reject(e); }\n    });\n  }\n\n  \/\/ ---------- Card lifecycle ----------\n  function addCard(file){\n    var id = uid();\n    var card = {\n      id: id,\n      file: file,\n      name: file.name,\n      base: baseName(file.name),\n      originalExt: extOf(file.name),\n      originalSize: file.size,\n      img: null,\n      naturalWidth: 0,\n      naturalHeight: 0,\n      objectURL: null,\n      downloadURL: null,\n      blobs: {},\n      settings: { format: defaultFormat, qIndex: 2, resize: false, maxDim: 1920 },\n      el: null,\n      busy: false,\n      errored: false,\n      debounceTimer: null\n    };\n    state.cards.set(id, card);\n    state.order.push(id);\n\n    var el = document.createElement('div');\n    el.className = 'card skeleton';\n    el.dataset.id = id;\n    el.innerHTML =\n      '<button class=\"remove\" aria-label=\"Remove\" data-action=\"remove\">\u00d7<\/button>' +\n      '<div class=\"card-grid\">' +\n        '<div class=\"thumb-wrap\"><\/div>' +\n        '<div><p class=\"card-name\">' + escapeHtml(file.name) + '<\/p>' +\n        '<p class=\"meta-text mono\">Reading file\u2026<\/p><\/div>' +\n      '<\/div>';\n    document.getElementById('cards').appendChild(el);\n    card.el = el;\n    el.querySelector('[data-action=\"remove\"]').addEventListener('click', function(){ removeCard(id); });\n\n    var url = URL.createObjectURL(file);\n    card.objectURL = url;\n    var img = new Image();\n    img.onload = function(){\n      card.img = img;\n      card.naturalWidth = img.naturalWidth;\n      card.naturalHeight = img.naturalHeight;\n      card.settings.maxDim = Math.max(img.naturalWidth, img.naturalHeight);\n      buildCardBody(card);\n      recomputeCard(card);\n    };\n    img.onerror = function(){\n      card.errored = true;\n      el.classList.remove('skeleton');\n      el.classList.add('error');\n      el.querySelector('.card-grid').innerHTML =\n        '<div class=\"thumb-wrap\"><\/div>' +\n        '<div><p class=\"card-name\">' + escapeHtml(file.name) + '<\/p>' +\n        '<p class=\"err-msg\">Couldn\\'t open this file \u2014 your browser can\\'t decode this format, or the file is corrupted.<\/p><\/div>';\n    };\n    img.src = url;\n\n    updateAggregate();\n    showGlobalControls();\n  }\n\n  function escapeHtml(s){\n    var d = document.createElement('div');\n    d.textContent = s;\n    return d.innerHTML;\n  }\n\n  function buildCardBody(card){\n    var el = card.el;\n    el.classList.remove('skeleton');\n\n    var chipButtons = ALL_FORMATS.map(function(fmt){\n      var supported = SUPPORTED[fmt];\n      return '<button class=\"chip\" data-fmt=\"' + fmt + '\" ' + (supported ? '' : 'disabled title=\"Not supported by this browser\"') + '>' +\n        '<span class=\"fmt\">' + fmt + '<\/span>' +\n        '<span class=\"sz mono\">\u2026<\/span>' +\n      '<\/button>';\n    }).join('');\n\n    var stopsHtml = QUALITY_STOPS.map(function(s, i){\n      return '<span data-i=\"' + i + '\">' + s.label + '<\/span>';\n    }).join('');\n\n    el.innerHTML =\n      '<button class=\"remove\" aria-label=\"Remove\" data-action=\"remove\">\u00d7<\/button>' +\n      '<div class=\"card-grid\">' +\n        '<div class=\"thumb-wrap\"><img decoding=\"async\" src=\"' + card.objectURL + '\" alt=\"\" \/><\/div>' +\n        '<div>' +\n          '<div class=\"card-head\">' +\n            '<div>' +\n              '<p class=\"card-name\">' + escapeHtml(card.name) + '<\/p>' +\n              '<div class=\"pill-row\">' +\n                '<span class=\"pill\">' + card.originalExt + '<\/span>' +\n                '<span class=\"meta-text mono\">' + card.naturalWidth + '\u00d7' + card.naturalHeight + '<\/span>' +\n                '<span class=\"meta-text mono\">' + formatBytes(card.originalSize) + '<\/span>' +\n              '<\/div>' +\n            '<\/div>' +\n          '<\/div>' +\n\n          '<div class=\"ctrl-block\">' +\n            '<div class=\"ctrl-label\"><span>Compression<\/span><span class=\"val mono\" data-role=\"qlabel\">f\/5.6 \u00b7 75% \u00b7 Balanced<\/span><\/div>' +\n            '<input type=\"range\" min=\"0\" max=\"5\" step=\"1\" value=\"2\" data-role=\"quality\" \/>' +\n            '<div class=\"stops-row\" data-role=\"stops\">' + stopsHtml + '<\/div>' +\n          '<\/div>' +\n\n          '<div class=\"ctrl-block\">' +\n            '<div class=\"ctrl-label\"><span>Convert to<\/span><\/div>' +\n            '<div class=\"chip-row\" data-role=\"chips\">' + chipButtons + '<\/div>' +\n            '<div class=\"resize-row\">' +\n              '<label class=\"cb\"><input type=\"checkbox\" data-role=\"resize-toggle\" \/> Resize<\/label>' +\n              '<div class=\"resize-fields\" data-role=\"resize-fields\" hidden>' +\n                'max side <input type=\"number\" class=\"num-input mono\" data-role=\"resize-input\" min=\"50\" max=\"20000\" step=\"10\" value=\"' + card.settings.maxDim + '\" \/> px' +\n              '<\/div>' +\n            '<\/div>' +\n          '<\/div>' +\n\n          '<div class=\"result-row\">' +\n            '<div class=\"result-figures\">' +\n              '<div class=\"fig\"><span class=\"flabel\">Original density<\/span><span class=\"fval mono\">' + formatBytes(card.originalSize) + '<\/span><\/div>' +\n              '<span class=\"arrow\">\u2192<\/span>' +\n              '<div class=\"fig out\"><span class=\"flabel\">New density<\/span><span class=\"fval mono\" data-role=\"outsize\">\u2026<\/span><\/div>' +\n              '<span class=\"pct-badge\" data-role=\"pct\">\u2026<\/span>' +\n            '<\/div>' +\n            '<button class=\"btn primary\" data-role=\"download\" disabled>Download<\/button>' +\n          '<\/div>' +\n        '<\/div>' +\n      '<\/div>';\n\n    el.querySelector('[data-action=\"remove\"]').addEventListener('click', function(){ removeCard(card.id); });\n\n    var qSlider = el.querySelector('[data-role=\"quality\"]');\n    qSlider.addEventListener('input', function(){\n      card.settings.qIndex = parseInt(qSlider.value, 10);\n      updateQualityLabel(card);\n      setChipsLoading(card);\n      clearTimeout(card.debounceTimer);\n      card.debounceTimer = setTimeout(function(){ recomputeCard(card); }, 300);\n    });\n\n    el.querySelectorAll('[data-role=\"chips\"] .chip').forEach(function(btn){\n      btn.addEventListener('click', function(){\n        if (btn.disabled) return;\n        var fmt = btn.dataset.fmt;\n        card.settings.format = fmt;\n        updateChipActive(card);\n        updateResultPanel(card);\n      });\n    });\n\n    var resizeToggle = el.querySelector('[data-role=\"resize-toggle\"]');\n    var resizeFields = el.querySelector('[data-role=\"resize-fields\"]');\n    resizeToggle.addEventListener('change', function(){\n      card.settings.resize = resizeToggle.checked;\n      resizeFields.hidden = !resizeToggle.checked;\n      recomputeCard(card);\n    });\n\n    var resizeInput = el.querySelector('[data-role=\"resize-input\"]');\n    resizeInput.addEventListener('input', function(){\n      var v = parseInt(resizeInput.value, 10);\n      if (isNaN(v)) return;\n      v = Math.max(50, Math.min(20000, v));\n      card.settings.maxDim = v;\n      clearTimeout(card.debounceTimer);\n      card.debounceTimer = setTimeout(function(){ recomputeCard(card); }, 400);\n    });\n\n    el.querySelector('[data-role=\"download\"]').addEventListener('click', function(){\n      if (!card.downloadURL) return;\n      var fmt = card.settings.format;\n      triggerDownload(card.downloadURL, card.base + '.' + EXT[fmt]);\n    });\n\n    updateQualityLabel(card);\n    updateChipActive(card);\n  }\n\n  function updateQualityLabel(card){\n    var stop = QUALITY_STOPS[card.settings.qIndex];\n    var labelEl = card.el.querySelector('[data-role=\"qlabel\"]');\n    if (labelEl) labelEl.textContent = stop.label + ' \u00b7 ' + stop.q + '% \u00b7 ' + stop.desc;\n    card.el.querySelectorAll('[data-role=\"stops\"] span').forEach(function(s){\n      s.classList.toggle('active', parseInt(s.dataset.i, 10) === card.settings.qIndex);\n    });\n  }\n\n  function updateChipActive(card){\n    card.el.querySelectorAll('[data-role=\"chips\"] .chip').forEach(function(btn){\n      btn.classList.toggle('active', btn.dataset.fmt === card.settings.format);\n    });\n  }\n\n  function setChipsLoading(card){\n    card.el.querySelectorAll('[data-role=\"chips\"] .chip').forEach(function(btn){\n      if (!btn.disabled){\n        var sz = btn.querySelector('.sz');\n        if (sz) sz.textContent = '\u2026';\n        var badge = btn.querySelector('.badge');\n        if (badge) badge.remove();\n      }\n    });\n    var outSize = card.el.querySelector('[data-role=\"outsize\"]');\n    if (outSize) outSize.textContent = '\u2026';\n  }\n\n  function recomputeCard(card){\n    if (card.errored) return;\n    card.busy = true;\n    setChipsLoading(card);\n    var dims = getTargetDims(card);\n    card.outW = dims.w; card.outH = dims.h;\n    var q = QUALITY_STOPS[card.settings.qIndex].q;\n\n    var jobs = ALL_FORMATS.filter(function(f){ return SUPPORTED[f]; }).map(function(fmt){\n      return computeBlob(card.img, fmt, q, dims.w, dims.h)\n        .then(function(blob){ return { fmt: fmt, blob: blob }; })\n        .catch(function(){ return { fmt: fmt, blob: null }; });\n    });\n\n    Promise.all(jobs).then(function(results){\n      var blobs = {};\n      results.forEach(function(r){ blobs[r.fmt] = r.blob; });\n      card.blobs = blobs;\n      card.busy = false;\n      updateCardUI(card);\n      updateAggregate();\n    });\n  }\n\n  function updateCardUI(card){\n    \/\/ update chips\n    var smallestFmt = null, smallestSize = Infinity;\n    Object.keys(card.blobs).forEach(function(fmt){\n      var blob = card.blobs[fmt];\n      if (blob && blob.size < smallestSize){ smallestSize = blob.size; smallestFmt = fmt; }\n    });\n\n    card.el.querySelectorAll('[data-role=\"chips\"] .chip').forEach(function(btn){\n      var fmt = btn.dataset.fmt;\n      var blob = card.blobs[fmt];\n      var sz = btn.querySelector('.sz');\n      if (!SUPPORTED[fmt]){\n        if (sz) sz.textContent = 'unsupported';\n        return;\n      }\n      if (sz) sz.textContent = blob ? formatBytes(blob.size) : 'failed';\n      var existingBadge = btn.querySelector('.badge');\n      if (existingBadge) existingBadge.remove();\n      if (fmt === smallestFmt){\n        var badge = document.createElement('span');\n        badge.className = 'badge';\n        badge.textContent = 'smallest';\n        btn.appendChild(badge);\n      }\n    });\n\n    updateResultPanel(card);\n  }\n\n  function updateResultPanel(card){\n    var fmt = card.settings.format;\n    var blob = card.blobs ? card.blobs[fmt] : null;\n    var outEl = card.el.querySelector('[data-role=\"outsize\"]');\n    var pctEl = card.el.querySelector('[data-role=\"pct\"]');\n    var dlBtn = card.el.querySelector('[data-role=\"download\"]');\n\n    if (card.downloadURL){\n      URL.revokeObjectURL(card.downloadURL);\n      card.downloadURL = null;\n    }\n\n    if (!blob){\n      if (outEl) outEl.textContent = SUPPORTED[fmt] ? '\u2026' : 'unsupported';\n      if (pctEl) pctEl.textContent = '';\n      if (dlBtn) dlBtn.disabled = true;\n      return;\n    }\n\n    card.downloadURL = URL.createObjectURL(blob);\n    if (outEl) outEl.textContent = formatBytes(blob.size);\n    var pct = pctChange(card.originalSize, blob.size);\n    if (pctEl){\n      pctEl.textContent = (pct >= 0 ? '\u2212' : '+') + Math.abs(pct).toFixed(0) + '%';\n      pctEl.className = 'pct-badge ' + (pct >= 0 ? 'down' : 'up');\n    }\n    if (dlBtn) dlBtn.disabled = false;\n  }\n\n  function removeCard(id){\n    var card = state.cards.get(id);\n    if (!card) return;\n    if (card.objectURL) URL.revokeObjectURL(card.objectURL);\n    if (card.downloadURL) URL.revokeObjectURL(card.downloadURL);\n    if (card.el && card.el.parentNode) card.el.parentNode.removeChild(card.el);\n    state.cards.delete(id);\n    state.order = state.order.filter(function(x){ return x !== id; });\n    updateAggregate();\n    if (state.cards.size === 0) hideGlobalControls();\n  }\n\n  \/\/ ---------- Aggregate \/ global controls ----------\n  function updateAggregate(){\n    var n = state.cards.size;\n    var totalOrig = 0, totalOut = 0, anyOut = false;\n    state.cards.forEach(function(card){\n      totalOrig += card.originalSize || 0;\n      var blob = card.blobs ? card.blobs[card.settings.format] : null;\n      if (blob){ totalOut += blob.size; anyOut = true; }\n      else { totalOut += card.originalSize || 0; }\n    });\n    var el = document.getElementById('totalsText');\n    if (!el) return;\n    if (n === 0){\n      el.textContent = '0 images';\n      return;\n    }\n    var text = n + (n === 1 ? ' image' : ' images') + '  \u00b7  ' + formatBytes(totalOrig) + ' \u2192 ' + formatBytes(totalOut);\n    if (anyOut && totalOrig > 0){\n      var pct = pctChange(totalOrig, totalOut);\n      text += '  \u00b7  <span class=\"pct\">' + (pct >= 0 ? '\u2212' : '+') + Math.abs(pct).toFixed(0) + '%<\/span>';\n    }\n    el.innerHTML = text;\n  }\n\n  function showGlobalControls(){\n    document.getElementById('statsbar').classList.add('show');\n  }\n  function hideGlobalControls(){\n    document.getElementById('statsbar').classList.remove('show');\n  }\n\n  function applyToAll(){\n    var qIndex = parseInt(document.getElementById('globalQuality').value, 10);\n    var fmt = document.getElementById('globalFormat').value;\n    state.cards.forEach(function(card){\n      if (card.errored) return;\n      card.settings.qIndex = qIndex;\n      card.settings.format = fmt;\n      updateQualityLabel(card);\n      updateChipActive(card);\n      var qSlider = card.el.querySelector('[data-role=\"quality\"]');\n      if (qSlider) qSlider.value = qIndex;\n      recomputeCard(card);\n    });\n  }\n\n  function clearAll(){\n    Array.from(state.order).forEach(function(id){ removeCard(id); });\n  }\n\n  function downloadAll(){\n    if (typeof JSZip === 'undefined'){\n      alert('The zip library failed to load (check your network connection).');\n      return;\n    }\n    var zip = new JSZip();\n    var used = {};\n    var any = false;\n    state.cards.forEach(function(card){\n      if (card.errored) return;\n      var fmt = card.settings.format;\n      var blob = card.blobs ? card.blobs[fmt] : null;\n      if (!blob) return;\n      var name = card.base + '.' + EXT[fmt];\n      if (used[name]){\n        used[name]++;\n        name = card.base + '-' + used[name] + '.' + EXT[fmt];\n      } else {\n        used[name] = 1;\n      }\n      zip.file(name, blob);\n      any = true;\n    });\n    if (!any){ alert('Nothing ready to download yet \u2014 wait for processing to finish.'); return; }\n    zip.generateAsync({ type: 'blob' }).then(function(content){\n      var url = URL.createObjectURL(content);\n      triggerDownload(url, 'Rajmedical-export.zip');\n      setTimeout(function(){ URL.revokeObjectURL(url); }, 4000);\n    });\n  }\n\n  \/\/ ---------- File intake ----------\n  function handleFiles(fileList){\n    var files = Array.from(fileList).filter(function(f){\n      return f.type.indexOf('image\/') === 0 || \/\\.(heic|heif|avif)$\/i.test(f.name);\n    });\n    files.forEach(addCard);\n  }\n\n  function wireDropzone(){\n    var dz = document.getElementById('dropzone');\n    var input = document.getElementById('fileInput');\n    dz.addEventListener('click', function(){ input.click(); });\n    dz.addEventListener('keydown', function(e){\n      if (e.key === 'Enter' || e.key === ' '){ e.preventDefault(); input.click(); }\n    });\n    input.addEventListener('change', function(){\n      handleFiles(input.files);\n      input.value = '';\n    });\n    ['dragenter','dragover'].forEach(function(evt){\n      dz.addEventListener(evt, function(e){ e.preventDefault(); dz.classList.add('active'); });\n    });\n    ['dragleave','drop'].forEach(function(evt){\n      dz.addEventListener(evt, function(e){ e.preventDefault(); dz.classList.remove('active'); });\n    });\n    dz.addEventListener('drop', function(e){\n      if (e.dataTransfer && e.dataTransfer.files) handleFiles(e.dataTransfer.files);\n    });\n  }\n\n  function init(){\n    Promise.all([\n      detectFormatSupport('image\/webp'),\n      detectFormatSupport('image\/avif')\n    ]).then(function(res){\n      SUPPORTED.webp = res[0];\n      SUPPORTED.avif = res[1];\n      defaultFormat = SUPPORTED.webp ? 'webp' : 'jpeg';\n      document.getElementById('globalFormat').value = defaultFormat;\n    });\n\n    wireDropzone();\n    document.getElementById('applyAllBtn').addEventListener('click', applyToAll);\n    document.getElementById('downloadAllBtn').addEventListener('click', downloadAll);\n    document.getElementById('clearAllBtn').addEventListener('click', clearAll);\n  }\n\n  document.addEventListener('DOMContentLoaded', init);\n})();\n\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>Density \u2014 image compressor &amp; converter \u25cf client-side photo lab DENSITY Drop in an image, dial down its density, and [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_eb_attr":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[76],"tags":[],"class_list":["post-4573","post","type-post","status-publish","format-standard","hentry","category-tools"],"_links":{"self":[{"href":"https:\/\/rajmedical.co.in\/hi\/wp-json\/wp\/v2\/posts\/4573","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rajmedical.co.in\/hi\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rajmedical.co.in\/hi\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rajmedical.co.in\/hi\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rajmedical.co.in\/hi\/wp-json\/wp\/v2\/comments?post=4573"}],"version-history":[{"count":19,"href":"https:\/\/rajmedical.co.in\/hi\/wp-json\/wp\/v2\/posts\/4573\/revisions"}],"predecessor-version":[{"id":4597,"href":"https:\/\/rajmedical.co.in\/hi\/wp-json\/wp\/v2\/posts\/4573\/revisions\/4597"}],"wp:attachment":[{"href":"https:\/\/rajmedical.co.in\/hi\/wp-json\/wp\/v2\/media?parent=4573"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rajmedical.co.in\/hi\/wp-json\/wp\/v2\/categories?post=4573"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rajmedical.co.in\/hi\/wp-json\/wp\/v2\/tags?post=4573"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}