Skip to content

Instantly share code, notes, and snippets.

@JamoCA
Last active March 14, 2025 01:14
Show Gist options
  • Save JamoCA/fbbd2599102216448ada8e9f85d40b9c to your computer and use it in GitHub Desktop.
Save JamoCA/fbbd2599102216448ada8e9f85d40b9c to your computer and use it in GitHub Desktop.
<cfscript>
/*
* getQRSVG UDF
* @displayname getQRSVG
* @Dependency Requires Zint executable from https://zint.org.uk/
* @Dependency_Documentation https://zint.org.uk/manual/chapter/4
* @author James Moberg http://sunstarmedia.com, @sunstarmedia
* @version 1
* @lastUpdate 2025-03-13
* @gist https://gist.github.com/JamoCA/fbbd2599102216448ada8e9f85d40b9c
* @blog https://dev.to/gamesover/coldfusion-getqrsvg-returns-responsive-svg-source-37hf
* @twitter https://x.com/gamesover/status/1900234390921502840
* @LinkedIn https://www.linkedin.com/posts/jamesmoberg_coldfusion-getqrsvg-udf-returns-responsive-activity-7306001304919650305-CcRp
* @param String data The data to encode.
* @param String exePath The filepath to the Zint executable.
*/
string function getQRSVG(required string data, string exePath="") hint="I create a temp SVG file and return the SVG source code (requires zint.exe)" {
local.sanitizedData = tostring(arguments.data).replaceAll("""", "\""");
local.cmd = "-b 58 --scale=8 --direct --filetype=svg -d ""#local.sanitizedData#""";
try {
cfexecute(name="#arguments.exePath#", arguments="#local.cmd#", timeout="90", variable="local.svg");
} catch (any error){
throw(message="getQRSVG UDF: Zint.exe error");
}
if (!find("<?xml", local.svg) || !find("<!", local.svg)){
throw(message="getQRSVG UDF: Zint.exe generated invalid SVG QR Code.");
}
local.svg = replace(local.svg, "<desc>Zint Generated Symbol</desc>", "<desc>#encodeforxml(arguments.data)#</desc>");
// replace fixed width/height to make responsive
local.wResults = refindnocase("width\s*=\s*""(\d+)+", local.svg, 1, true);
local.w = val(local.wResults.match[2]);
local.hResults = refindnocase("height\s*=\s*""(\d+)+", local.svg, 1, true);
local.h = val(local.hResults.match[2]);
local.svg = local.svg.replaceAll('width="\d+" height="\d+"', 'viewbox="0 0 #val(local.w)# #val(local.h)#" style="width:100%; display:block;"');
// remove xml header & html doctype
local.svg = local.svg.replaceAll("\<\?xml.*?\>", "").replaceAll("\s*\<\!.*?\>", "");
return trim(local.svg);
}
</cfscript>
<cfscript>
/**
* generateZint UDF
* @displayname generateZint
* @Dependency Requires Zint executable from https://zint.org.uk/
* @Dependency_Documentation https://zint.org.uk/manual/chapter/4
* @author James Moberg http://sunstarmedia.com, @sunstarmedia
* @version 1
* @lastUpdate 2024-10-30 16:00 Pacific
* @gist https://gist.github.com/JamoCA/fbbd2599102216448ada8e9f85d40b9c
* @blog https://dev.to/gamesover/coldfusion-wrapper-for-zint-barcode-generator-15mc
* @twitter https://x.com/gamesover/status/1851768390760960101
* @LinkedIn https://www.linkedin.com/posts/jamesmoberg_coldfusion-activity-7257534479445962752-aeoL
* @param String filepath The path and file name to use when storing the generated file. (These params can also be defined in "options".)
* @param String barcodeType The barcode type. Normally a integer, but also accepts "QR". (https://zint.org.uk/manual/chapter/4#selecting-barcode-type)
* @param String data The data to encode.
* @param String exePath The filepath to the Zint executable.
* @param struct options All of the configuration data. Some very basic defaults are used if nothing is passed.
*/
struct function generateZint(string filepath="", string barcodeType=58, string data="", string exePath="", struct options={}) {
local.timestart = gettickcount();
local.response = [
"success": false
,"duration": 0
,"filePath": ""
,"cmd": ""
,"output": ""
,"errors": []
];
local.options = duplicate(arguments.options);
// reset some primary values if found in options & validate
if (local.options.keyexists("exePath")){
arguments.exePath = local.options.exePath;
structdelete(local.options, "exePath");
}
if (!fileexists(arguments.exePath)){
arrayappend(local.response.errors, "exePath is required.");
}
if (local.options.keyexists("o")){
arguments.filePath = local.options.o;
structdelete(local.options, "o");
}else if (local.options.keyexists("filePath")){
arguments.filePath = local.options.filePath;
structdelete(local.options, "filePath");
}
if (!len(arguments.filepath)){
arrayappend(local.response.errors, "filePath is required.");
}
if (local.options.keyexists("b")){
arguments.barcodeType = local.options.b;
structdelete(local.options, "b");
} else if (local.options.keyexists("barcodeType")){
arguments.barcodeType = local.options.barcodeType;
structdelete(local.options, "barcodeType");
}
arguments.barcodeType = (arguments.barcodeType eq "qr") ? 58 : arguments.barcodeType;
if (!val(arguments.barcodeType)){
arrayappend(local.response.errors, "barcodeType is required.");
}
if (local.options.keyexists("d")){
arguments.data = local.options.d;
structdelete(local.options, "d");
} else if (local.options.keyexists("data")){
arguments.data = local.options.data;
structdelete(local.options, "data");
}
if (!issimplevalue(arguments.data) || !len(arguments.data)){
arrayappend(local.response.errors, "data is required.");
}
// populate options with some default setting (if empty)
if (!structcount(local.options)){
// if QR code, set default options for barcode type 58
if (arguments.barcodeType eq 58){
local.options = [
"scale": javacast("int", 12)
,"fg": javacast("string", "000000")
,"bg": "ffffff"
,"nobackground": true
,"whitesp": javacast("int", 2)
,"vwhitesp": javacast("int", 2)
];
// default scale
} else {
local.options = [
"scale": javacast("int", 5)
];
}
}
local.cmd = [
"-o ""#arguments.filePath#"""
,"-b #javacast("int", val(arguments.barcodeType))#"
];
if (structcount(local.options)){
for (local.p in local.options){
local.v = trim(local.options[local.p]);
if (refindnocase("^([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$", local.v)){
local.v = javacast("string", local.v); // hex
} else if (isvalid("integer", local.v)) {
local.v = javacast("int", local.v); // integers
} else if (refind(local.v, "[:space:]")){
local.v = """#local.v#"""; // values with spaces
}
if (local.p eq "nobackground"){
if (isvalid("boolean", local.v) && local.v){
arrayappend(local.cmd, "--nobackground");
}
} else if (len(local.p) gt 1){
local.s = len(trim(local.v)) ? "--#local.p#=#local.v#" : "--#local.p#";
arrayappend(local.cmd, local.s);
} else if (len(local.p) eq 1){
local.s = len(trim(local.v)) ? "-#local.p# #local.v#" : "-#local.p#";
arrayappend(local.cmd, local.s);
}
}
}
arrayappend(local.cmd, "-d ""#replace(arguments.data, """", "\""", "all")#""");
local.response["cmd"] = arraytolist(local.cmd, " ");
if (!arraylen(local.response.errors)){
cfexecute(name="#arguments.exePath#", arguments="#local.response.cmd#", timeout="90", variable="local.response.output");
local.response.success = fileexists(arguments.filePath);
local.response.filePath = fileexists(arguments.filePath) ? arguments.filePath : "";
}
local.response.duration = javacast("int", gettickcount() - local.timestart);
return local.response;
}
/* Example
testImageName = "this-is-a-test.svg";
options = [
"filePath": "D:\www\#testImageName#"
,"data": "This is a test"
,"exePath": "C:\zint\zint.exe"
];
zintData = request.generateZint(options=options);
writeoutput('<img src="/#testImageName#" style="width:250px; height:auto;">');
writedump(var=zintData, label="zintData");
*/
</cfscript>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment