{"id":10467,"date":"2026-11-09T13:47:38","date_gmt":"2026-11-09T13:47:38","guid":{"rendered":"https:\/\/goldvertise.com\/?page_id=10467"},"modified":"2026-11-09T15:52:11","modified_gmt":"2026-11-09T15:52:11","slug":"xchange-set-card-generator","status":"publish","type":"page","link":"https:\/\/goldvertise.com\/en\/xchange-set-card-generator\/","title":{"rendered":"Xchange Set-Card Generator"},"content":{"rendered":"<div class=\"fusion-fullwidth fullwidth-box fusion-builder-row-1 fusion-flex-container has-pattern-background has-mask-background nonhundred-percent-fullwidth non-hundred-percent-height-scrolling\" style=\"--awb-border-radius-top-left:0px;--awb-border-radius-top-right:0px;--awb-border-radius-bottom-right:0px;--awb-border-radius-bottom-left:0px;--awb-flex-wrap:wrap;\" ><div class=\"fusion-builder-row fusion-row fusion-flex-align-items-flex-start fusion-flex-content-wrap\" style=\"max-width:1248px;margin-left: calc(-4% \/ 2 );margin-right: calc(-4% \/ 2 );\"><div class=\"fusion-layout-column fusion_builder_column fusion-builder-column-0 fusion_builder_column_1_1 1_1 fusion-flex-column\" style=\"--awb-bg-size:cover;--awb-width-large:100%;--awb-margin-top-large:0px;--awb-spacing-right-large:1.92%;--awb-margin-bottom-large:0px;--awb-spacing-left-large:1.92%;--awb-width-medium:100%;--awb-order-medium:0;--awb-spacing-right-medium:1.92%;--awb-spacing-left-medium:1.92%;--awb-width-small:100%;--awb-order-small:0;--awb-spacing-right-small:1.92%;--awb-spacing-left-small:1.92%;\"><div class=\"fusion-column-wrapper fusion-column-has-shadow fusion-flex-justify-content-flex-start fusion-content-layout-column\"><!-- Goldvertise Xchange Set Card Generator \u2013 English Avada Code Block -->\n<style>\n  #xchange-card-generator,\n  #xchange-card-generator * {\n    box-sizing: border-box;\n  }\n\n  #xchange-card-generator {\n    --xg-purple: #31105c;\n    --xg-purple-dark: #1b082f;\n    --xg-pink: #ed168c;\n    --xg-orange: #ff7139;\n    --xg-text: #22152e;\n    --xg-muted: #6f6578;\n    width: 100%;\n    max-width: 1240px;\n    margin: 32px auto;\n    padding: 24px;\n    font-family: Montserrat, Arial, sans-serif;\n    color: var(--xg-text);\n  }\n\n  #xchange-card-generator .xg-shell {\n    display: grid;\n    grid-template-columns: minmax(300px, 390px) minmax(320px, 1fr);\n    gap: 40px;\n    align-items: start;\n  }\n\n  #xchange-card-generator .xg-shell > * {\n    min-width: 0;\n  }\n\n  #xchange-card-generator .xg-panel {\n    padding: 28px;\n    border: 1px solid rgba(49, 16, 92, 0.12);\n    border-radius: 24px;\n    background: #ffffff;\n    box-shadow: 0 18px 55px rgba(40, 10, 67, 0.12);\n  }\n\n  #xchange-card-generator .xg-panel h2 {\n    margin: 0 0 8px;\n    color: var(--xg-purple-dark);\n    font-size: clamp(25px, 3vw, 34px);\n    line-height: 1.08;\n  }\n\n  #xchange-card-generator .xg-intro {\n    margin: 0 0 24px;\n    color: var(--xg-muted);\n    font-size: 15px;\n    line-height: 1.55;\n  }\n\n  #xchange-card-generator .xg-field {\n    margin-bottom: 17px;\n  }\n\n  #xchange-card-generator .xg-field-row {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 14px;\n  }\n\n  #xchange-card-generator label {\n    display: block;\n    margin-bottom: 7px;\n    color: var(--xg-purple-dark);\n    font-size: 14px;\n    font-weight: 700;\n  }\n\n  #xchange-card-generator input[type=\"text\"],\n  #xchange-card-generator input[type=\"file\"] {\n    width: 100%;\n    min-height: 48px;\n    padding: 11px 13px;\n    border: 1px solid #d9d2df;\n    border-radius: 12px;\n    background: #ffffff;\n    color: var(--xg-text);\n    font: inherit;\n    font-size: 15px;\n  }\n\n  #xchange-card-generator input:focus {\n    border-color: var(--xg-pink);\n    outline: 3px solid rgba(237, 22, 140, 0.12);\n  }\n\n  #xchange-card-generator .xg-help {\n    display: block;\n    margin-top: 6px;\n    color: var(--xg-muted);\n    font-size: 12px;\n    line-height: 1.4;\n  }\n\n  #xchange-card-generator .xg-actions {\n    display: grid;\n    gap: 10px;\n    margin-top: 22px;\n  }\n\n  #xchange-card-generator button {\n    min-height: 52px;\n    padding: 13px 18px;\n    border: 0;\n    border-radius: 999px;\n    font: inherit;\n    font-size: 15px;\n    font-weight: 800;\n    cursor: pointer;\n    transition: transform 0.15s ease, box-shadow 0.15s ease;\n  }\n\n  #xchange-card-generator button:hover {\n    transform: translateY(-1px);\n  }\n\n  #xchange-card-generator .xg-download {\n    background: linear-gradient(90deg, var(--xg-orange), var(--xg-pink));\n    color: #ffffff;\n    box-shadow: 0 10px 24px rgba(237, 22, 140, 0.24);\n  }\n\n  #xchange-card-generator .xg-reset {\n    border: 1px solid rgba(49, 16, 92, 0.18);\n    background: #f7f4f9;\n    color: var(--xg-purple);\n  }\n\n  #xchange-card-generator .xg-privacy {\n    margin: 18px 0 0;\n    color: var(--xg-muted);\n    font-size: 12px;\n    line-height: 1.5;\n  }\n\n  #xchange-card-generator .xg-preview-wrap {\n    position: sticky;\n    top: 24px;\n    padding: 20px;\n    border-radius: 26px;\n    background: linear-gradient(145deg, #efe9f4, #ffffff);\n    box-shadow: 0 18px 55px rgba(40, 10, 67, 0.12);\n  }\n\n  #xchange-card-generator .xg-preview-title {\n    display: flex;\n    justify-content: space-between;\n    gap: 16px;\n    margin: 0 0 12px;\n    color: var(--xg-purple-dark);\n    font-size: 14px;\n    font-weight: 800;\n  }\n\n  #xchange-card-generator canvas {\n    display: block;\n    width: 100%;\n    height: auto;\n    border-radius: 16px;\n    background: #2b0b40;\n    box-shadow: 0 12px 32px rgba(32, 6, 51, 0.24);\n    touch-action: manipulation;\n  }\n\n  #xchange-card-generator canvas:focus-visible {\n    outline: 4px solid var(--xg-pink);\n    outline-offset: 4px;\n  }\n\n  #xchange-card-generator .xg-status {\n    min-height: 20px;\n    margin: 12px 4px 0;\n    color: var(--xg-muted);\n    font-size: 12px;\n    text-align: center;\n  }\n\n  @media (max-width: 900px) {\n    #xchange-card-generator {\n      max-width: 680px;\n      margin: 18px auto;\n      padding: 12px;\n    }\n\n    #xchange-card-generator .xg-shell {\n      grid-template-columns: minmax(0, 1fr);\n      gap: 20px;\n    }\n\n    #xchange-card-generator .xg-panel {\n      padding: 24px;\n    }\n\n    #xchange-card-generator .xg-preview-wrap {\n      position: static;\n      width: 100%;\n      padding: 16px;\n    }\n\n    #xchange-card-generator canvas {\n      width: 100%;\n      max-width: 540px;\n      margin: 0 auto;\n    }\n  }\n\n  @media (max-width: 600px) {\n    #xchange-card-generator {\n      margin: 8px auto;\n      padding: 8px;\n    }\n\n    #xchange-card-generator .xg-panel,\n    #xchange-card-generator .xg-preview-wrap {\n      padding: 16px;\n      border-radius: 16px;\n    }\n\n    #xchange-card-generator .xg-field-row {\n      grid-template-columns: 1fr;\n      gap: 0;\n    }\n\n    #xchange-card-generator .xg-panel h2 {\n      font-size: 26px;\n      line-height: 1.12;\n    }\n\n    #xchange-card-generator .xg-intro {\n      margin-bottom: 20px;\n      font-size: 14px;\n    }\n\n    #xchange-card-generator .xg-field {\n      margin-bottom: 14px;\n    }\n\n    #xchange-card-generator label {\n      font-size: 13.5px;\n    }\n\n    #xchange-card-generator input[type=\"text\"] {\n      min-height: 48px;\n      font-size: 16px;\n    }\n\n    #xchange-card-generator input[type=\"file\"] {\n      min-height: 48px;\n      padding: 8px;\n      font-size: 14px;\n    }\n\n    #xchange-card-generator .xg-help {\n      font-size: 11.5px;\n    }\n\n    #xchange-card-generator .xg-actions {\n      gap: 8px;\n      margin-top: 18px;\n    }\n\n    #xchange-card-generator button {\n      width: 100%;\n      min-height: 50px;\n      font-size: 14.5px;\n    }\n\n    #xchange-card-generator .xg-privacy {\n      margin: 14px 2px 0;\n      font-size: 11px;\n    }\n\n    #xchange-card-generator .xg-preview-title {\n      margin-bottom: 9px;\n      padding: 0 2px;\n      font-size: 12px;\n    }\n\n    #xchange-card-generator .xg-preview-title span:last-child {\n      display: none;\n    }\n\n    #xchange-card-generator canvas {\n      border-radius: 12px;\n      box-shadow: 0 8px 24px rgba(32, 6, 51, 0.22);\n    }\n\n    #xchange-card-generator .xg-status {\n      margin: 8px 2px 0;\n      font-size: 11px;\n    }\n  }\n\n  @media (max-width: 380px) {\n    #xchange-card-generator {\n      padding: 4px;\n    }\n\n    #xchange-card-generator .xg-panel,\n    #xchange-card-generator .xg-preview-wrap {\n      padding: 14px;\n    }\n\n    #xchange-card-generator .xg-panel h2 {\n      font-size: 23px;\n    }\n\n    #xchange-card-generator button {\n      font-size: 14px;\n    }\n  }\n<\/style>\n\n<div id=\"xchange-card-generator\">\n  <div class=\"xg-shell\">\n    <section class=\"xg-panel\" aria-labelledby=\"xg-heading\">\n      <h2 id=\"xg-heading\">Create your Xchange Set Card<\/h2>\n      <p class=\"xg-intro\">\n        Enter your details, upload a portrait photo and download your personalised set card for LinkedIn.\n      <\/p>\n\n      <div class=\"xg-field-row\">\n        <div class=\"xg-field\">\n          <label for=\"xg-first-name\">First name<\/label>\n          <input id=\"xg-first-name\" type=\"text\" maxlength=\"22\" autocomplete=\"given-name\" placeholder=\"Sarah\">\n        <\/div>\n\n        <div class=\"xg-field\">\n          <label for=\"xg-last-name\">Last name<\/label>\n          <input id=\"xg-last-name\" type=\"text\" maxlength=\"28\" autocomplete=\"family-name\" placeholder=\"Smith\">\n        <\/div>\n      <\/div>\n\n      <div class=\"xg-field\">\n        <label for=\"xg-role\">Job title<\/label>\n        <input id=\"xg-role\" type=\"text\" maxlength=\"52\" autocomplete=\"organization-title\" placeholder=\"Senior Business Development Leader\">\n      <\/div>\n\n      <div class=\"xg-field\">\n        <label for=\"xg-company\">Company<\/label>\n        <input id=\"xg-company\" type=\"text\" maxlength=\"40\" autocomplete=\"organization\" placeholder=\"Company\">\n      <\/div>\n\n      <div class=\"xg-field\">\n        <label for=\"xg-photo\">Portrait photo<\/label>\n        <input id=\"xg-photo\" type=\"file\" accept=\"image\/jpeg,image\/png,image\/webp\">\n        <span class=\"xg-help\">A front-facing portrait photo works best. Maximum file size: 10 MB.<\/span>\n      <\/div>\n\n      <div class=\"xg-actions\">\n        <button id=\"xg-download\" class=\"xg-download\" type=\"button\">Download set card as PNG<\/button>\n        <button id=\"xg-reset\" class=\"xg-reset\" type=\"button\">Reset details<\/button>\n      <\/div>\n\n      <p class=\"xg-privacy\">\n        Your photo and details are processed only in your browser and are not stored on our website.\n      <\/p>\n    <\/section>\n\n    <section class=\"xg-preview-wrap\" aria-label=\"Set card preview\">\n      <div class=\"xg-preview-title\">\n        <span>Live preview<\/span>\n        <span>1080 \u00d7 1350 px<\/span>\n      <\/div>\n\n      <canvas id=\"xg-canvas\" width=\"1080\" height=\"1350\"><\/canvas>\n      <p id=\"xg-status\" class=\"xg-status\">Loading background and logo \u2026<\/p>\n    <\/section>\n  <\/div>\n<\/div>\n\n<script>\n(function () {\n  \"use strict\";\n\n  const BACKGROUND_URL = \"https:\/\/goldvertise.com\/wp-content\/uploads\/2026\/11\/Xchange-Hintergrund-Set-Card.png\";\n  const LOGO_URL = \"https:\/\/goldvertise.com\/wp-content\/uploads\/2026\/11\/Xchange-Logo.png\";\n  const EVENT_URL = \"https:\/\/goldvertise.com\/en\/events\/goldvertise-xchange-future-of-tv-3\/\";\n\n  const CTA_BOUNDS = {\n    x: 190,\n    y: 1140,\n    width: 700,\n    height: 112\n  };\n\n  const canvas = document.getElementById(\"xg-canvas\");\n  const ctx = canvas.getContext(\"2d\");\n  const status = document.getElementById(\"xg-status\");\n\n  canvas.setAttribute(\"role\", \"link\");\n  canvas.setAttribute(\"aria-label\", \"Join the Goldvertise Xchange event\");\n  canvas.setAttribute(\"title\", \"JOIN NOW \u2013 Goldvertise Xchange\");\n  canvas.tabIndex = 0;\n\n  const fields = {\n    firstName: document.getElementById(\"xg-first-name\"),\n    lastName: document.getElementById(\"xg-last-name\"),\n    role: document.getElementById(\"xg-role\"),\n    company: document.getElementById(\"xg-company\"),\n    photo: document.getElementById(\"xg-photo\")\n  };\n\n  const backgroundImage = new Image();\n  const logoImage = new Image();\n  let portraitImage = null;\n  let backgroundReady = false;\n  let logoReady = false;\n  let drawQueued = false;\n\n  backgroundImage.crossOrigin = \"anonymous\";\n  logoImage.crossOrigin = \"anonymous\";\n\n  backgroundImage.onload = function () {\n    backgroundReady = true;\n    updateStatus();\n    queueDraw();\n  };\n\n  backgroundImage.onerror = function () {\n    status.textContent = \"The background could not be loaded.\";\n    queueDraw();\n  };\n\n  logoImage.onload = function () {\n    logoReady = true;\n    updateStatus();\n    queueDraw();\n  };\n\n  logoImage.onerror = function () {\n    status.textContent = \"The logo could not be loaded.\";\n    queueDraw();\n  };\n\n  backgroundImage.src = BACKGROUND_URL;\n  logoImage.src = LOGO_URL;\n\n  function updateStatus() {\n    if (backgroundReady && logoReady) {\n      status.textContent = \"Generator is ready \u2713\";\n    }\n  }\n\n  function queueDraw() {\n    if (drawQueued) return;\n    drawQueued = true;\n\n    window.requestAnimationFrame(function () {\n      drawQueued = false;\n      drawCard();\n    });\n  }\n\n  function roundRectPath(context, x, y, width, height, radius) {\n    const r = Math.min(radius, width \/ 2, height \/ 2);\n    context.beginPath();\n    context.moveTo(x + r, y);\n    context.arcTo(x + width, y, x + width, y + height, r);\n    context.arcTo(x + width, y + height, x, y + height, r);\n    context.arcTo(x, y + height, x, y, r);\n    context.arcTo(x, y, x + width, y, r);\n    context.closePath();\n  }\n\n  function drawImageCover(context, image, x, y, width, height) {\n    if (!image || !image.naturalWidth || !image.naturalHeight) return;\n\n    const imageRatio = image.naturalWidth \/ image.naturalHeight;\n    const targetRatio = width \/ height;\n    let sourceX = 0;\n    let sourceY = 0;\n    let sourceWidth = image.naturalWidth;\n    let sourceHeight = image.naturalHeight;\n\n    if (imageRatio > targetRatio) {\n      sourceWidth = image.naturalHeight * targetRatio;\n      sourceX = (image.naturalWidth - sourceWidth) \/ 2;\n    } else {\n      sourceHeight = image.naturalWidth \/ targetRatio;\n      sourceY = (image.naturalHeight - sourceHeight) \/ 2;\n    }\n\n    context.drawImage(\n      image,\n      sourceX,\n      sourceY,\n      sourceWidth,\n      sourceHeight,\n      x,\n      y,\n      width,\n      height\n    );\n  }\n\n  function drawImageContain(context, image, x, y, width, height) {\n    if (!image || !image.naturalWidth || !image.naturalHeight) return;\n\n    const scale = Math.min(width \/ image.naturalWidth, height \/ image.naturalHeight);\n    const drawWidth = image.naturalWidth * scale;\n    const drawHeight = image.naturalHeight * scale;\n    const drawX = x + (width - drawWidth) \/ 2;\n    const drawY = y + (height - drawHeight) \/ 2;\n\n    context.drawImage(image, drawX, drawY, drawWidth, drawHeight);\n  }\n\n  function setFont(size, weight) {\n    ctx.font = `${weight || 700} ${size}px Montserrat, Arial, sans-serif`;\n  }\n\n  function fitFontSize(text, maximumWidth, startSize, minimumSize, weight) {\n    let size = startSize;\n    setFont(size, weight);\n\n    while (size > minimumSize && ctx.measureText(text).width > maximumWidth) {\n      size -= 1;\n      setFont(size, weight);\n    }\n\n    return size;\n  }\n\n  function wrapLines(text, maximumWidth, maximumLines, size, weight) {\n    const words = String(text || \"\").trim().split(\/\\s+\/).filter(Boolean);\n    const lines = [];\n    let currentLine = \"\";\n\n    setFont(size, weight);\n\n    words.forEach(function (word) {\n      const testLine = currentLine ? `${currentLine} ${word}` : word;\n\n      if (ctx.measureText(testLine).width <= maximumWidth || !currentLine) {\n        currentLine = testLine;\n      } else {\n        lines.push(currentLine);\n        currentLine = word;\n      }\n    });\n\n    if (currentLine) lines.push(currentLine);\n\n    if (lines.length > maximumLines) {\n      const keptLines = lines.slice(0, maximumLines);\n      let lastLine = keptLines[maximumLines - 1];\n\n      while (lastLine.length > 1 && ctx.measureText(`${lastLine}\u2026`).width > maximumWidth) {\n        lastLine = lastLine.slice(0, -1).trim();\n      }\n\n      keptLines[maximumLines - 1] = `${lastLine}\u2026`;\n      return keptLines;\n    }\n\n    return lines;\n  }\n\n  function drawPortraitCircle(centerX, centerY, radius) {\n    const circleGradient = ctx.createLinearGradient(\n      centerX - radius,\n      centerY - radius,\n      centerX + radius,\n      centerY + radius\n    );\n    circleGradient.addColorStop(0, \"#d14d8b\");\n    circleGradient.addColorStop(1, \"#651254\");\n\n    ctx.save();\n    ctx.beginPath();\n    ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);\n    ctx.closePath();\n    ctx.fillStyle = circleGradient;\n    ctx.fill();\n    ctx.clip();\n\n    if (portraitImage) {\n      drawImageCover(\n        ctx,\n        portraitImage,\n        centerX - radius,\n        centerY - radius,\n        radius * 2,\n        radius * 2\n      );\n    } else {\n      ctx.fillStyle = \"rgba(255,255,255,0.88)\";\n      ctx.textAlign = \"center\";\n      ctx.textBaseline = \"middle\";\n      setFont(27, 700);\n      ctx.fillText(\"UPLOAD PORTRAIT\", centerX, centerY);\n    }\n\n    ctx.restore();\n\n    ctx.save();\n    ctx.beginPath();\n    ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);\n    ctx.strokeStyle = \"rgba(255,255,255,0.20)\";\n    ctx.lineWidth = 5;\n    ctx.stroke();\n    ctx.restore();\n  }\n\n  function drawNameRibbon() {\n    const x = 520;\n    const y = 700;\n    const width = 505;\n    const height = 164;\n\n    const gradient = ctx.createLinearGradient(x, y, x + width, y);\n    gradient.addColorStop(0, \"#dc4a91\");\n    gradient.addColorStop(1, \"#67124f\");\n\n    ctx.save();\n    roundRectPath(ctx, x, y, width, height, 52);\n    ctx.fillStyle = gradient;\n    ctx.shadowColor = \"rgba(30, 2, 34, 0.28)\";\n    ctx.shadowBlur = 22;\n    ctx.shadowOffsetY = 10;\n    ctx.fill();\n    ctx.restore();\n\n    const name = [fields.firstName.value.trim(), fields.lastName.value.trim()]\n      .filter(Boolean)\n      .join(\" \") || \"YOUR NAME\";\n\n    const roleAndCompany = [fields.role.value.trim(), fields.company.value.trim()]\n      .filter(Boolean)\n      .join(\" \u00b7 \") || \"JOB TITLE \u00b7 COMPANY\";\n\n    ctx.fillStyle = \"#ffffff\";\n    ctx.textAlign = \"center\";\n    ctx.textBaseline = \"middle\";\n\n    const nameSize = fitFontSize(name, width - 64, 52, 30, 800);\n    setFont(nameSize, 800);\n    ctx.fillText(name, x + width \/ 2, y + 61);\n\n    const roleSize = 23;\n    const roleLines = wrapLines(roleAndCompany, width - 72, 2, roleSize, 600);\n    setFont(roleSize, 600);\n\n    roleLines.forEach(function (line, index) {\n      ctx.fillText(line, x + width \/ 2, y + 113 + index * 27);\n    });\n  }\n\n  function drawEventInformation() {\n    const x = 76;\n\n    ctx.save();\n    ctx.strokeStyle = \"#ec168c\";\n    ctx.lineWidth = 8;\n    ctx.lineCap = \"round\";\n    ctx.beginPath();\n    ctx.moveTo(x, 790);\n    ctx.lineTo(x, 1012);\n    ctx.stroke();\n    ctx.restore();\n\n    ctx.fillStyle = \"#ffffff\";\n    ctx.textAlign = \"left\";\n    ctx.textBaseline = \"alphabetic\";\n\n    const date = \"25 NOVEMBER 2026\";\n    const dateSize = fitFontSize(date, 360, 38, 28, 800);\n    setFont(dateSize, 800);\n    ctx.fillText(date, x + 30, 830);\n\n    setFont(35, 700);\n    ctx.fillText(\"13:00\u201318:30\", x + 30, 895);\n\n    setFont(29, 700);\n    ctx.fillText(\"MARIAS PLATZL HOTEL\", x + 30, 965);\n    ctx.fillText(\"MUNICH\", x + 30, 1004);\n  }\n\n  function drawCallToAction() {\n    const x = CTA_BOUNDS.x;\n    const y = CTA_BOUNDS.y;\n    const width = CTA_BOUNDS.width;\n    const height = CTA_BOUNDS.height;\n\n    const gradient = ctx.createLinearGradient(x, y, x + width, y);\n    gradient.addColorStop(0, \"#ff7139\");\n    gradient.addColorStop(1, \"#ec168c\");\n\n    ctx.save();\n    roundRectPath(ctx, x, y, width, height, 56);\n    ctx.fillStyle = gradient;\n    ctx.shadowColor = \"rgba(237,22,140,0.30)\";\n    ctx.shadowBlur = 25;\n    ctx.shadowOffsetY = 10;\n    ctx.fill();\n    ctx.restore();\n\n    ctx.fillStyle = \"#ffffff\";\n    ctx.textAlign = \"center\";\n    ctx.textBaseline = \"middle\";\n    setFont(48, 800);\n    ctx.fillText(\"JOIN NOW \u2192\", 540, y + height \/ 2 + 2);\n  }\n\n  function getCanvasPoint(event) {\n    const rect = canvas.getBoundingClientRect();\n\n    return {\n      x: (event.clientX - rect.left) * (canvas.width \/ rect.width),\n      y: (event.clientY - rect.top) * (canvas.height \/ rect.height)\n    };\n  }\n\n  function isInsideCallToAction(point) {\n    return (\n      point.x >= CTA_BOUNDS.x &&\n      point.x <= CTA_BOUNDS.x + CTA_BOUNDS.width &&\n      point.y >= CTA_BOUNDS.y &&\n      point.y <= CTA_BOUNDS.y + CTA_BOUNDS.height\n    );\n  }\n\n  function openEventPage() {\n    window.open(EVENT_URL, \"_blank\", \"noopener,noreferrer\");\n  }\n\n  function drawCard() {\n    ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n    if (backgroundReady) {\n      drawImageCover(ctx, backgroundImage, 0, 0, canvas.width, canvas.height);\n    } else {\n      const fallback = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);\n      fallback.addColorStop(0, \"#4a102b\");\n      fallback.addColorStop(1, \"#23083f\");\n      ctx.fillStyle = fallback;\n      ctx.fillRect(0, 0, canvas.width, canvas.height);\n    }\n\n    ctx.fillStyle = \"#ffffff\";\n    ctx.textAlign = \"left\";\n    ctx.textBaseline = \"alphabetic\";\n    setFont(61, 800);\n    ctx.fillText(\"MEET ME AT\", 70, 112);\n\n    const headerLine = ctx.createLinearGradient(70, 0, 440, 0);\n    headerLine.addColorStop(0, \"#f04bb5\");\n    headerLine.addColorStop(0.3, \"#ed168c\");\n    headerLine.addColorStop(1, \"#ff7139\");\n\n    ctx.save();\n    ctx.strokeStyle = headerLine;\n    ctx.lineWidth = 8;\n    ctx.lineCap = \"round\";\n    ctx.beginPath();\n    ctx.moveTo(70, 150);\n    ctx.lineTo(440, 150);\n    ctx.stroke();\n    ctx.restore();\n\n    if (logoReady) {\n      drawImageContain(ctx, logoImage, -35, 125, 650, 360);\n    }\n\n    drawPortraitCircle(773, 480, 270);\n    drawNameRibbon();\n    drawEventInformation();\n    drawCallToAction();\n  }\n\n  Object.keys(fields).forEach(function (key) {\n    if (key !== \"photo\") {\n      fields[key].addEventListener(\"input\", queueDraw);\n    }\n  });\n\n  canvas.addEventListener(\"mousemove\", function (event) {\n    canvas.style.cursor = isInsideCallToAction(getCanvasPoint(event))\n      ? \"pointer\"\n      : \"default\";\n  });\n\n  canvas.addEventListener(\"mouseleave\", function () {\n    canvas.style.cursor = \"default\";\n  });\n\n  canvas.addEventListener(\"click\", function (event) {\n    if (isInsideCallToAction(getCanvasPoint(event))) {\n      openEventPage();\n    }\n  });\n\n  canvas.addEventListener(\"keydown\", function (event) {\n    if (event.key === \"Enter\" || event.key === \" \") {\n      event.preventDefault();\n      openEventPage();\n    }\n  });\n\n  fields.photo.addEventListener(\"change\", function (event) {\n    const file = event.target.files && event.target.files[0];\n\n    if (!file) {\n      portraitImage = null;\n      queueDraw();\n      return;\n    }\n\n    if (!file.type.startsWith(\"image\/\")) {\n      window.alert(\"Please select an image file.\");\n      fields.photo.value = \"\";\n      return;\n    }\n\n    if (file.size > 10 * 1024 * 1024) {\n      window.alert(\"The image is larger than 10 MB. Please choose a smaller file.\");\n      fields.photo.value = \"\";\n      return;\n    }\n\n    const reader = new FileReader();\n\n    reader.onload = function (loadEvent) {\n      const image = new Image();\n\n      image.onload = function () {\n        portraitImage = image;\n        status.textContent = \"Portrait uploaded \u2713\";\n        queueDraw();\n      };\n\n      image.onerror = function () {\n        window.alert(\"The portrait could not be processed.\");\n      };\n\n      image.src = loadEvent.target.result;\n    };\n\n    reader.readAsDataURL(file);\n  });\n\n  document.getElementById(\"xg-reset\").addEventListener(\"click\", function () {\n    fields.firstName.value = \"\";\n    fields.lastName.value = \"\";\n    fields.role.value = \"\";\n    fields.company.value = \"\";\n    fields.photo.value = \"\";\n    portraitImage = null;\n    status.textContent = \"Your details have been reset.\";\n    queueDraw();\n  });\n\n  document.getElementById(\"xg-download\").addEventListener(\"click\", function () {\n    drawCard();\n\n    canvas.toBlob(function (blob) {\n      if (!blob) {\n        window.alert(\"The PNG file could not be created.\");\n        return;\n      }\n\n      const first = fields.firstName.value.trim() || \"xchange\";\n      const last = fields.lastName.value.trim() || \"setcard\";\n      const safeName = `${first}-${last}`\n        .toLowerCase()\n        .normalize(\"NFD\")\n        .replace(\/[\\u0300-\\u036f]\/g, \"\")\n        .replace(\/[^a-z0-9]+\/g, \"-\")\n        .replace(\/^-+|-+$\/g, \"\");\n\n      const objectUrl = URL.createObjectURL(blob);\n      const link = document.createElement(\"a\");\n      link.href = objectUrl;\n      link.download = `${safeName || \"xchange-setcard\"}-xchange-setcard.png`;\n      document.body.appendChild(link);\n      link.click();\n      link.remove();\n\n      window.setTimeout(function () {\n        URL.revokeObjectURL(objectUrl);\n      }, 1500);\n\n      status.textContent = \"Set card created \u2713\";\n    }, \"image\/png\");\n  });\n\n  if (document.fonts && document.fonts.ready) {\n    document.fonts.ready.then(queueDraw);\n  } else {\n    queueDraw();\n  }\n})();\n<\/script><\/div><\/div><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":7,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"100-width.php","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-10467","page","type-page","status-publish","hentry"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Xchange Set-Card Generator - Goldvertise<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/goldvertise.com\/en\/xchange-set-card-generator\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Xchange Set-Card Generator - Goldvertise\" \/>\n<meta property=\"og:url\" content=\"https:\/\/goldvertise.com\/en\/xchange-set-card-generator\/\" \/>\n<meta property=\"og:site_name\" content=\"Goldvertise\" \/>\n<meta property=\"article:modified_time\" content=\"2026-11-09T15:52:11+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/xchange-set-card-generator\\\/\",\"url\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/xchange-set-card-generator\\\/\",\"name\":\"Xchange Set-Card Generator - Goldvertise\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/#website\"},\"datePublished\":\"2026-11-09T13:47:38+00:00\",\"dateModified\":\"2026-11-09T15:52:11+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/xchange-set-card-generator\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/goldvertise.com\\\/en\\\/xchange-set-card-generator\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/xchange-set-card-generator\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Xchange Set-Card Generator\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/\",\"name\":\"Goldvertise\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/#organization\",\"name\":\"Goldvertise\",\"url\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/goldvertise.com\\\/wp-content\\\/uploads\\\/2025\\\/01\\\/Goldvertise_Logo_white_retina.png\",\"contentUrl\":\"https:\\\/\\\/goldvertise.com\\\/wp-content\\\/uploads\\\/2025\\\/01\\\/Goldvertise_Logo_white_retina.png\",\"width\":476,\"height\":80,\"caption\":\"Goldvertise\"},\"image\":{\"@id\":\"https:\\\/\\\/goldvertise.com\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Xchange Set-Card Generator - Goldvertise","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/goldvertise.com\/en\/xchange-set-card-generator\/","og_locale":"en_US","og_type":"article","og_title":"Xchange Set-Card Generator - Goldvertise","og_url":"https:\/\/goldvertise.com\/en\/xchange-set-card-generator\/","og_site_name":"Goldvertise","article_modified_time":"2026-11-09T15:52:11+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/goldvertise.com\/en\/xchange-set-card-generator\/","url":"https:\/\/goldvertise.com\/en\/xchange-set-card-generator\/","name":"Xchange Set-Card Generator - Goldvertise","isPartOf":{"@id":"https:\/\/goldvertise.com\/en\/#website"},"datePublished":"2026-11-09T13:47:38+00:00","dateModified":"2026-11-09T15:52:11+00:00","breadcrumb":{"@id":"https:\/\/goldvertise.com\/en\/xchange-set-card-generator\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/goldvertise.com\/en\/xchange-set-card-generator\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/goldvertise.com\/en\/xchange-set-card-generator\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/goldvertise.com\/en\/"},{"@type":"ListItem","position":2,"name":"Xchange Set-Card Generator"}]},{"@type":"WebSite","@id":"https:\/\/goldvertise.com\/en\/#website","url":"https:\/\/goldvertise.com\/en\/","name":"Goldvertise","description":"","publisher":{"@id":"https:\/\/goldvertise.com\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/goldvertise.com\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/goldvertise.com\/en\/#organization","name":"Goldvertise","url":"https:\/\/goldvertise.com\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/goldvertise.com\/en\/#\/schema\/logo\/image\/","url":"https:\/\/goldvertise.com\/wp-content\/uploads\/2025\/01\/Goldvertise_Logo_white_retina.png","contentUrl":"https:\/\/goldvertise.com\/wp-content\/uploads\/2025\/01\/Goldvertise_Logo_white_retina.png","width":476,"height":80,"caption":"Goldvertise"},"image":{"@id":"https:\/\/goldvertise.com\/en\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/goldvertise.com\/en\/wp-json\/wp\/v2\/pages\/10467","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/goldvertise.com\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/goldvertise.com\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/goldvertise.com\/en\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/goldvertise.com\/en\/wp-json\/wp\/v2\/comments?post=10467"}],"version-history":[{"count":20,"href":"https:\/\/goldvertise.com\/en\/wp-json\/wp\/v2\/pages\/10467\/revisions"}],"predecessor-version":[{"id":10510,"href":"https:\/\/goldvertise.com\/en\/wp-json\/wp\/v2\/pages\/10467\/revisions\/10510"}],"wp:attachment":[{"href":"https:\/\/goldvertise.com\/en\/wp-json\/wp\/v2\/media?parent=10467"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}