Generate QR-Based E-Business Cards in ServiceNow with Arabic/English Support and Image Download
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-10-2025 04:53 AM
Use Case
Many organizations want to provide employees with digital business cards, especially for remote interactions or events. A QR-based e-card allows scanning and saving contact details instantly on mobile devices, bridging the gap between physical cards and modern convenience.
Overview
In this article, I’ll walk through how to build a complete E-Business Card generator in ServiceNow that:
Pulls user profile details automatically.
- Allow user to edit his information to be displayed on e-card.
Supports multilingual data (Arabic and English).
Generates a high-quality QR code with contact information (vCard).
Displays the card with a custom background.
Allows the user to download a high-resolution image of the card.
Ensures layout stability across devices and screen zoom levels.
Works well with Android and iOS contact scanning (including proper name parsing).
Solution Architecture
The solution includes:
A ServiceNow widget for form input and card preview.
- A portal page to display the widget in portal.
- A scoped custom table to stored the edited version of data.
- A background image add in Images[db_image] table.
Widget Code:
HTML:
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<div ng-class="{'rtl': isArabic}" ng-attr-dir="{{ isArabic ? 'rtl' : 'ltr' }}">
<!-- Form fields -->
<div class="form-box">
<div class="header-box">
{{labels.e_business_card}}
</div>
<div class="form-group">
<label>{{labels.arabic_name}}</label>
<input type="text" class="form-control" ng-model="card.arabic_name">
<label>{{labels.name}}</label>
<input type="text" class="form-control" ng-model="card.name">
<label>{{labels.arabic_title}}</label>
<input type="text" class="form-control" ng-model="card.arabic_title">
<label>{{labels.title}}</label>
<input type="text" class="form-control" ng-model="card.title">
<label>{{labels.phone}}</label>
<input type="tel" class="form-control" ng-model="card.phone">
<label>{{labels.email}}</label>
<input type="email" class="form-control" ng-model="card.email">
</div>
<!-- Generate QR -->
<div class="button-group">
<button class="btn btn-primary" ng-click="generateCard()">
{{labels.generateQR}}
</button>
</div>
</div>
<!-- E-card display -->
<div ng-if="dataSubmitted && data.qrUrl" class="ecard-preview">
<div id="ecard-download" class="ecard-container">
<img src="ecard_background_image.png" class="ecard-bg-img"> //replace with your image name
<div class="ecard-overlay">
<div class="ecard-info">
<h5 class="arabic-name">{{card.arabic_name}}</h5>
<h5 class="name">{{card.name}}</h5>
<p class="arabic-title">{{card.arabic_title}}</p>
<p class="title">{{card.title}}</p>
<p class="phone">{{card.phone}}</p>
<p class="email">{{card.email}}</p>
<img id="qr-img-render" src="{{data.qrUrl}}" class="qr-img" alt="QR Code">
</div>
</div>
</div>
<!-- Download button -->
<button class="btn btn-success" ng-click="downloadCard()" style="margin-top: 20px;">
{{labels.downloadQR}}
</button>
</div>
</div>
CSS:
.header-box {
width: 100%;
margin-bottom: 20px;
padding: 16px;
background: whitesmoke !important;
color: #4d4d4f;
font-size: 24px;
text-align: left;
font-weight: bold;
border-radius: 6px;
box-shadow: 0 0 5px rgba(0,0,0,0.1);
}
/* Form box */
.form-box {
position: relative; /* Enables absolute children if needed */
margin: 20px auto;
padding: 20px 25px;
border: 1px solid #ddd;
border-radius: 10px;
background-color: #fff;
box-shadow: 0 0px 5px rgba(0,0,0,0.1);
max-width: 90%; /* Responsive width */
width: 100%;
box-sizing: border-box;
}
/* Label styling */
.form-group label {
display: block;
font-weight: bold;
margin-top: 15px;
margin-bottom: 5px;
color: #4d4d4f;
}
/* Input styling */
.form-control {
width: 100%;
padding: 10px 12px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 14px;
}
/* Button styling (optional tweak) */
.button-group {
text-align: left;
margin-top: 21px;
}
.button-group .btn {
padding: 10px 20px;
font-size: 16px;
}
.ecard-preview {
text-align: center;
margin-top: 20px;
font-family: 'Segoe UI', 'Tahoma', 'Arial', sans-serif;
}
.ecard-container {
position: relative;
width: 350px;
height: 600px;
margin: 0 auto;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 0 5px #ccc;
}
.ecard-bg-img {
position: absolute;
width: 100%;
height: 100%;
top: 0; left: 0;
z-index: 0;
}
.ecard-overlay {
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
z-index: 1;
}
.ecard-info {
position: relative;
width: 100%;
height: 100%;
z-index: 2;
}
.ecard-info h5, .ecard-info p {
position: absolute;
left: 50%;
transform: translateX(-50%);
white-space: nowrap;
/*overflow: hidden;*/
text-overflow: ellipsis;
}
.arabic-name { top: 23%; font-size: 15px; color: #b45533; }
.name { top: 26%; font-size: 15px; color: #b45533; }
.arabic-title { top: 30%; font-size: 12px; color: #000; }
.title { top: 32.5%; font-size: 12px; color: #000; }
.phone { top: 42%; font-size: 11px; color: #000; }
.email { top: 50%; font-size: 11px; color: #000; }
.qr-img { position: absolute; top: 56%; left: 50%; transform: translateX(-50%); width: 140px; height: 140px; }
Server Script:
(function() {
var userID = gs.getUserID();
if (!input) {
var user = new GlideRecord('sys_user');
if (user.get(userID)) {
data.name = user.name.toString();
data.arabic_name = user.u_arabic_name ? user.u_arabic_name.toString() : '';
data.arabic_title = user.title.toString();
data.phone = user.mobile_phone.toString();
data.email = user.email.toString();
// Detect language preference for client
data.isArabic = gs.getSession().getLanguage() == 'ar';
//gs.addErrorMessage("Add: "+data.isArabic);
// Provide label translations
data.labels = data.isArabic ? {
arabic_name: "الاسم",
name: "Name",
arabic_title: "الوظيفة",
title: "Job Title",
phone: "الهاتف / Phone",
email: "البريد الإلكتروني / Email",
generateQR: "توليد رمز الاستجابة السريعة",
scanToSave: "امسح للحفظ في جهات الاتصال",
downloadQR: "تحميل رمز الاستجابة السريعة",
e_business_card: "بطاقة عمل إلكترونية"
} : {
arabic_name: "الاسم",
name: "Name",
arabic_title: "الوظيفة",
title: "Job Title",
phone: "Phone / الهاتف",
email: "Email / البريد الإلكتروني",
generateQR: "Generate QR",
scanToSave: "Scan to Save Contact",
downloadQR: "Download E-Card",
e_business_card: "E-Business Card"
};
}
return;
}
if (input.action === 'save') {
var gr = new GlideRecord('x_1234_ebusiness_card'); // Replace with your actual table name
gr.initialize();
gr.name = input.name;
gr.arabic_name = input.arabic_name;
gr.title = input.arabic_title;
gr.u_eng_title = input.title;
gr.phone = input.phone;
gr.email = input.email;
gr.user = userID;
gr.unique_id = 'id-' + gs.generateGUID();
gr.insert();
// Build vCard string with + prefixed phone
var isArabic = gs.getSession().getLanguage() == 'ar';
// Build vCard string based on language
var vcard;
var companyName = "NCW";
if (isArabic) {
var fullName = input.arabic_name;
var nameParts = fullName.trim().split(" ");
var givenName = nameParts.slice(0, 2).join(" ");
var familyName = nameParts.slice(2).join(" ");
vcard =
'BEGIN:VCARD\n' +
'VERSION:3.0\n' +
'FN:' + fullName + '\n' +
'N:' + familyName + ';' + givenName + ';;;\n' +
'TITLE:' + input.arabic_title + '\n' +
'ORG:' + companyName + '\n' +
'TEL:+' + input.phone + '\n' +
'EMAIL:' + input.email + '\n' +
'END:VCARD';
} else {
var fullName = input.name || "";
var nameParts = fullName.trim().split(" ");
var givenName = nameParts[0] || "";
var familyName = nameParts.slice(1).join(" ") || "";
vcard =
'BEGIN:VCARD\n' +
'VERSION:3.0\n' +
'FN:' + fullName + '\n' +
'N:' + familyName + ';' + givenName + ';;;\n' + // 👈 Added
'TITLE:' + input.title + '\n' +
'ORG:' + companyName + '\n' +
'TEL:+' + input.phone + '\n' +
'EMAIL:' + input.email + '\n' +
'END:VCARD';
}
// var vcard =
// 'BEGIN:VCARD\n' +
// 'VERSION:3.0\n' +
// 'FN:' + input.name + '\n' +
// 'TITLE:' + input.arabic_title + '\n' +
// 'TEL:+' + input.phone + '\n' +
// 'EMAIL:' + input.email + '\n' +
// 'END:VCARD';
data.qrUrl = 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&color=180-85-51&data=' +
encodeURIComponent(vcard);
return {
qrUrl: data.qrUrl
};
}
})();
Client Controller:
function($scope) {
$scope.card = {
name: $scope.data.name,
arabic_name: $scope.data.arabic_name,
arabic_title: $scope.data.arabic_title,
title: $scope.data.title,
phone: $scope.data.phone,
email: $scope.data.email
};
$scope.isArabic = $scope.data.isArabic;
$scope.labels = $scope.data.labels;
$scope.dataSubmitted = false;
$scope.generateCard = function() {
const nameFilled = $scope.card.name.trim() || "";
const arabicNameFilled = $scope.card.arabic_name.trim() || "";
const phoneFilled = $scope.card.phone.trim();
const emailFilled = $scope.card.email.trim();
const messages = $scope.isArabic ? {
name: "يرجى إدخال الاسم أو الاسم العربي.",
phone: "يرجى إدخال رقم الهاتف.",
email: "يرجى إدخال البريد الإلكتروني."
} : {
name: "Please enter either name or Arabic name.",
phone: "Please enter phone number.",
email: "Please enter email address."
};
if (!nameFilled && !arabicNameFilled) {
alert(messages.name);
return;
}
if (!phoneFilled) {
alert(messages.phone);
return;
}
if (!emailFilled) {
alert(messages.email);
return;
}
// Ensure '+' prefix
// if ($scope.card.phone && !$scope.card.phone.startsWith('+')) {
// $scope.card.phone = '+' + $scope.card.phone;
// }
$scope.server.get({
action: "save",
name: $scope.card.name,
arabic_name: $scope.card.arabic_name,
arabic_title: $scope.card.arabic_title,
title: $scope.card.title,
phone: $scope.card.phone,
email: $scope.card.email
}).then(function(response) {
$scope.data.qrUrl = response.data.qrUrl;
$scope.dataSubmitted = true;
});
};
$scope.downloadCard = function() {
const el = document.getElementById("ecard-download");
// const scale = window.devicePixelRatio || 1;
html2canvas(el, {
useCORS: true,
allowTaint: true,
backgroundColor: null,
scale: 4, // Fixed high scale for sharp image
width: el.offsetWidth,
height: el.offsetHeight,
windowWidth: el.scrollWidth,
windowHeight: el.scrollHeight
}).then(function(canvas) {
// Manually upscale the canvas
const finalCanvas = document.createElement('canvas');
finalCanvas.width = canvas.width;
finalCanvas.height = canvas.height;
const ctx = finalCanvas.getContext('2d');
ctx.drawImage(canvas, 0, 0);
const link = document.createElement("a");
link.download = "ecard.png";
link.href = finalCanvas.toDataURL("image/png", 1.0);
link.click();
});
};
}
- Labels:
-
Cost Management (ITSM)