<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: James Moberg</title>
    <description>The latest articles on Forem by James Moberg (@gamesover).</description>
    <link>https://forem.com/gamesover</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F30374%2Fc2352eb5-9f39-42ae-b262-f063fa7dda8a.jpg</url>
      <title>Forem: James Moberg</title>
      <link>https://forem.com/gamesover</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gamesover"/>
    <language>en</language>
    <item>
      <title>Moving to MyCFML.com</title>
      <dc:creator>James Moberg</dc:creator>
      <pubDate>Fri, 06 Jun 2025 02:50:47 +0000</pubDate>
      <link>https://forem.com/gamesover/moving-to-mycfmlcom-2978</link>
      <guid>https://forem.com/gamesover/moving-to-mycfmlcom-2978</guid>
      <description>&lt;p&gt;Blogging on the dev.to platform was a nice change from &lt;a href="https://www.tumblr.com/blog/gamesover2600" rel="noopener noreferrer"&gt;Tumblr&lt;/a&gt;. I really liked that I could embed my &lt;a href="https://github.com/JamoCA" rel="noopener noreferrer"&gt;Github&lt;/a&gt; and &lt;a href="https://gist.github.com/JamoCA" rel="noopener noreferrer"&gt;gists&lt;/a&gt; to display and share code... but I wanted to do more (ie, interactive examples, command line/custom JAR demos, etc).&lt;/p&gt;

&lt;p&gt;I recently saw one of my blog posts &lt;a href="https://www.linkedin.com/posts/jamesmoberg_hack-wkhtmltopdf-pdf-to-enable-adobe-acrobat-activity-7249486444040175616-jWXh" rel="noopener noreferrer"&gt;get some attention&lt;/a&gt; on &lt;a href="https://www.linkedin.com/in/jamesmoberg/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and received an email. The image associated with my article was displayed as the DEV logo with text indicating "image no longer exists". This broken image can't be a good sign. I checked, and other articles as recent as 7 months old that I shared on LinkedIn also display the branded-DevTo fallback image. #smh&lt;/p&gt;

&lt;p&gt;I also want better tools. I've been a ColdFusion developer for almost 30 years, and I should have stopped depending on external third parties to host my content a long time ago. Over time, I've witnessed many other ColdFusion-hosted websites migrate to WordPress or some other static "serverless" platform. As a result, I've decided to do the reverse and focus solely on building my content, more reputation and my code and do it my way... on &lt;a href="https://www.mycfml.com/" rel="noopener noreferrer"&gt;MyCFML&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Yup, MyCFML is a 100% CFML-powered website. I considered adding one of the old-school early 90's website badges, but I didn't want to avoid increasing the target vector potential from wanna-be hackers and script-kiddies. Join me over there. I'm still working on adding the ability to comment. I wrote a commenting module many years ago and would rather spend time modernizing it than using a third-party.&lt;/p&gt;

&lt;p&gt;Want to contact me? I'm active on X/Twitter (&lt;a href="https://x.com/gamesover" rel="noopener noreferrer"&gt;gamesover&lt;/a&gt;), &lt;a href="https://www.linkedin.com/in/jamesmoberg/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and through my company website, &lt;a href="https://www.sunstarmedia.com/" rel="noopener noreferrer"&gt;SunStar Media&lt;/a&gt;. I'm also a member on various CF-related Slack &amp;amp; Facebook groups, but don't really participate in those forums much. Later!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using ColdFusion and Xpdf to extract PDF metadata</title>
      <dc:creator>James Moberg</dc:creator>
      <pubDate>Mon, 02 Jun 2025 22:55:41 +0000</pubDate>
      <link>https://forem.com/gamesover/using-coldfusion-and-xpdf-to-extract-pdf-metadata-4pe3</link>
      <guid>https://forem.com/gamesover/using-coldfusion-and-xpdf-to-extract-pdf-metadata-4pe3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article "&lt;a href="https://www.mycfml.com/articles/using-coldfusion-and-xpdf-to-extract-pdf-metadata/" rel="noopener noreferrer"&gt;Using ColdFusion and Xpdf to extract PDF metadata&lt;/a&gt;" has been moved to myCFML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.xpdfreader.com/" rel="noopener noreferrer"&gt;Xpdf&lt;/a&gt; is an open source projects that includes a PDF viewer, but it also includes a collection of command line tools for Linux, Windows and Mac that can perform some helpful functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;xpdf:&lt;/strong&gt; PDF viewer (click for a screenshot)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pdftotext:&lt;/strong&gt; converts PDF to text
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pdftops:&lt;/strong&gt; converts PDF to PostScript
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pdftoppm:&lt;/strong&gt; converts PDF pages to netpbm (PPM/PGM/PBM) image files
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pdftopng:&lt;/strong&gt; converts PDF pages to PNG image files
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pdftohtml:&lt;/strong&gt; converts PDF to HTML
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pdfinfo:&lt;/strong&gt; extracts PDF metadata
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pdfimages:&lt;/strong&gt; extracts raw images from PDF files
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pdffonts:&lt;/strong&gt; lists fonts used in PDF files
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pdfdetach:&lt;/strong&gt; extracts attached files from PDF files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Can ColdFusion already do some of this? Of course it can, but I am always exploring alternative options and have to occasionally perform some process intensive operations outside the context of potential CF timeouts, threads and java heap limitations.  I've encountered some issues in the past where ColdFusion will evaluate &lt;a href="https://cfdocs.org/ispdffile" rel="noopener noreferrer"&gt;isPDFFile&lt;/a&gt; as &lt;code&gt;TRUE&lt;/code&gt; when reading a non-Acrobat-or-CF-generated PDF, but then decide that it's not really a PDF file and throw a CF error when using &lt;a href="https://cfdocs.org/cfpdf" rel="noopener noreferrer"&gt;CFPDF&lt;/a&gt; to read the same PDF (using &lt;code&gt;action="getInfo"&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;When it comes to metadata, I haven't entirely decided if I'm a purist regarding returned values.  For example, CFPDF returns "created" and "modified" as a string formatted like "&lt;code&gt;D:20250324103702-07'00&lt;/code&gt;'". It's probably consistent with how the metadata is stored in the PDF file, but fails IMHO as it's not a valid date format and requires additional parsing in order to be useful. (It does appear to retain timezone info. That's nice, I guess.) CFPDF also returns a boolean rotation flags and page sizes for every page as separate arrays. If you attempt to pass &lt;code&gt;pages="1"&lt;/code&gt; in hopes of minimizing the response, a hard error is thrown as this argument is not allowed.  It appears that metadata for every page is the one and only option.&lt;/p&gt;

&lt;p&gt;Recently when using CFPDF to personalize an existing single-page cover PDF by adding a watermark, I needed to know both the dimensions &amp;amp; rotation of the preexisting PDF so I could generate a PDF (using &lt;a href="https://wkhtmltopdf.org/" rel="noopener noreferrer"&gt;WKHTMLTOPDF&lt;/a&gt;) with the correct watermark placement. I decided to use Xpdf's pdfinfo.exe to extract this information primarily so that the output would be consistent regardless of which version of CFML platform is used. It's definitely possible that the future &lt;code&gt;CFPDF action="getinfo"&lt;/code&gt; option may be updated to return different data in the name of progress/modernity. I also wanted dates to be dates, numeric values to be numeric, boolean to be boolean and for "rotation" to be calculated and the width/height to be converted to inches. (The "points" unit is nice, but I prefer to use "in" with WKHTMLTOPDF for CSS absolute positioning of elements and defining the width/height output of the PDF.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Go Deeper
&lt;/h2&gt;

&lt;p&gt;To view the metadata and access CFML source code, check out this post on MyCFML.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mycfml.com/articles/using-coldfusion-and-xpdf-to-extract-pdf-metadata/" rel="noopener noreferrer"&gt;https://www.mycfml.com/articles/using-coldfusion-and-xpdf-to-extract-pdf-metadata/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>coldfusion</category>
      <category>commandline</category>
    </item>
    <item>
      <title>Support Unlimited Forwarded Email Messages via API (and ColdFusion)</title>
      <dc:creator>James Moberg</dc:creator>
      <pubDate>Fri, 09 May 2025 01:35:26 +0000</pubDate>
      <link>https://forem.com/gamesover/support-unlimited-forwarded-email-messages-via-api-and-coldfusion-1meb</link>
      <guid>https://forem.com/gamesover/support-unlimited-forwarded-email-messages-via-api-and-coldfusion-1meb</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article &lt;a href="https://www.mycfml.com/articles/support-unlimited-forwarded-email-messages-via-api-and-coldfusion/" rel="noopener noreferrer"&gt;Support Unlimited Forwarded Email Messages via API (and ColdFusion) - Forward Email Webhook and Taffy API&lt;/a&gt; has been moved to myCFML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;To access to the source code for the Taffy API endpoint, check out the post on the new &lt;a href="https://www.mycfml.com/articles/support-unlimited-forwarded-email-messages-via-api-and-coldfusion/" rel="noopener noreferrer"&gt;MyCFML website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>coldfusion</category>
    </item>
    <item>
      <title>CF_Timer Revisited</title>
      <dc:creator>James Moberg</dc:creator>
      <pubDate>Thu, 01 May 2025 19:21:42 +0000</pubDate>
      <link>https://forem.com/gamesover/cftimer-revisited-43lm</link>
      <guid>https://forem.com/gamesover/cftimer-revisited-43lm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I posted an update to my ColdFusion CF_Timer custom tag on my new &lt;a href="https://www.mycfml.com/articles/cf-timer-revisited/" rel="noopener noreferrer"&gt;myCFML blog&lt;/a&gt; with the same title.  It has an &lt;code&gt;allowedIPs&lt;/code&gt; option so that it can be safely integrated without displayed data to users that don't need to see it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After I posted the initial CFTag back in 2022, a fix was requested on Adobe's tracker, &lt;a href="https://tracker.adobe.com/#/view/CF-4214324" rel="noopener noreferrer"&gt;CF-4214324&lt;/a&gt; to "to decouple cftimer from admin debug output feature", but nothing has changed.&lt;/p&gt;

&lt;p&gt;Check out the article on myCFML:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mycfml.com/articles/cf-timer-revisited/" rel="noopener noreferrer"&gt;https://www.mycfml.com/articles/cf-timer-revisited/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>coldfusion</category>
    </item>
    <item>
      <title>Ben &amp; Ryan Show Guest Appearance &amp; myCFML</title>
      <dc:creator>James Moberg</dc:creator>
      <pubDate>Thu, 24 Apr 2025 22:04:12 +0000</pubDate>
      <link>https://forem.com/gamesover/ryan-ben-show-guest-appearance-mycfml-phg</link>
      <guid>https://forem.com/gamesover/ryan-ben-show-guest-appearance-mycfml-phg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article "&lt;a href="https://www.mycfml.com/articles/ben-and-ryan-show-guest-appearance-coldfusion-custom-udfs/" rel="noopener noreferrer"&gt;Ben &amp;amp; Ryan Show Guest Appearance - ColdFusion Custom UDFs&lt;/a&gt;" has been moved to myCFML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was recently invited to be a guest on the "&lt;a href="https://www.youtube.com/playlist?list=PLFOfpcEBWaV6hc3F429uOC20Wtv-MrtzO" rel="noopener noreferrer"&gt;Ben &amp;amp; Ryan Show&lt;/a&gt;" podcast to share some custom ColdFusion UDFs. The episode just aired on Tuesday, 2025-04-22. This served as motivation to launch a new CFML resource that I'd been discussing internally for the past couple of years with my business partner... introducing &lt;a href="https://www.mycfml.com/" rel="noopener noreferrer"&gt;myCFML.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/YpEtEUo2epk"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I'll still keep blogging on DEV.to, tweeting on &lt;a href="https://x.com/gamesover" rel="noopener noreferrer"&gt;X&lt;/a&gt; and cross-posting on &lt;a href="https://www.linkedin.com/in/jamesmoberg/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, but I wanted to create a new home dedicated to CFML where I could continue to blog, post code examples that could be tested and have additional resources dedicated to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recommended web application developer utilities &amp;amp; services&lt;/li&gt;
&lt;li&gt;Third-party command-line software (to extend CFML)&lt;/li&gt;
&lt;li&gt;Third-party java libraries&lt;/li&gt;
&lt;li&gt;Stats comparing versions and CFML vs non-CFML approaches&lt;/li&gt;
&lt;li&gt;References to lost CFML resources &lt;em&gt;(only available via archive.org)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I haven't built in subscriptions or commenting modules as I will be adding expanded account features &amp;amp; functionality that go further than regular blogs.&lt;/p&gt;

</description>
      <category>coldfusion</category>
    </item>
    <item>
      <title>PDF Generation, Bloat and Optimization</title>
      <dc:creator>James Moberg</dc:creator>
      <pubDate>Thu, 10 Apr 2025 00:05:43 +0000</pubDate>
      <link>https://forem.com/gamesover/pdf-generation-bloat-and-optimization-2118</link>
      <guid>https://forem.com/gamesover/pdf-generation-bloat-and-optimization-2118</guid>
      <description>&lt;h1&gt;
  
  
  The State of myCFML PDF Generation
&lt;/h1&gt;

&lt;p&gt;My current comparison is with CFDocument on the deprecated CF2016 Developer Edition. I'm planning on performing tests with CF2021 &amp;amp; CF2023 soon and will perform this on my personal developer workstation using &lt;a href="https://www.ortussolutions.com/products/commandbox" rel="noopener noreferrer"&gt;CommandBox&lt;/a&gt; and a testing framework that I'm in the process of developing. I have not personally downloaded or used CF2025 yet&lt;sup&gt;1&lt;/sup&gt;. I believe that it may be using the same iText library as CF10. &lt;em&gt;(Can anyone confirm this?)&lt;/em&gt;  I also haven't compared with Lucee 5.3+ &lt;a href="https://docs.lucee.org/recipes/pdf-engine-flying-saucer.html" rel="noopener noreferrer"&gt;Flying Saucer&lt;/a&gt; implementation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This will be a part of a series as I intend on performing a lot more tests  as well as sharing tips that we've learned on how to improve overall PDF quality, performance and file size.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Technologies
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://cfdocs.org/cfdocument" rel="noopener noreferrer"&gt;CFDocument&lt;/a&gt; Built-in Adobe ColdFusion function that creates PDF output from a text block containing CFML and HTML. (NOTE: Some ACF overhead may occur due to adding the "Adobe ColdFusion Developer/Trial Edition - Not for Production Use" watermark.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://cfdocs.org/cfhtmltopdf" rel="noopener noreferrer"&gt;CFHTMLtoPDF&lt;/a&gt; Built-in Adobe ColdFusion function that creates PDFs from HTML using a WebKit based rendering engine. Some ACF overhead may occur due to adding the "Adobe ColdFusion Developer/Trial Edition - Not for Production Use" watermark. I tried testing this on a CF2016 Standard production server, but got the error message "No Service manager is available.". (We don't use CFHTMLTOPDF, so it may be disabled at the CFAdmin level.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://wkhtmltopdf.org/" rel="noopener noreferrer"&gt;WKHTMLTOPDF&lt;/a&gt; (LGPLv3; portable) tends to be faster and generate smaller PDFs. It can also run concurrently and generate PDFs in the background without using a ColdFusion thread or impacting the Java heap memory. &lt;a href="https://wkhtmltopdf.org/downloads.html" rel="noopener noreferrer"&gt;Download&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.ghostscript.com/" rel="noopener noreferrer"&gt;Ghostscript&lt;/a&gt; (GNU GPL Affero license; portable) is an interpreter for the PostScript(R) language and PDF files. It runs on various embedded operating systems and platforms including Windows, macOS, the wide variety of Unix and Unix-like platforms, and VMS systems. &lt;a href="https://www.ghostscript.com/releases/gsdnld.html" rel="noopener noreferrer"&gt;Download&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Test an empty page with only a "Hello World" H1 header
&lt;/h2&gt;

&lt;p&gt;There's a huge differences in the generation file size and the Ghostscript optimization doesn't make much of a difference.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;Generation&lt;/th&gt;
&lt;th&gt;FileSize&lt;/th&gt;
&lt;th&gt;Ghostscript Duration&lt;/th&gt;
&lt;th&gt;Ghostscript Filesize&lt;/th&gt;
&lt;th&gt;Percent Smaller&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CF2016&lt;/td&gt;
&lt;td&gt;CFDocument&lt;/td&gt;
&lt;td&gt;486 ms&lt;/td&gt;
&lt;td&gt;40,837&lt;/td&gt;
&lt;td&gt;10 ms&lt;/td&gt;
&lt;td&gt;40,068&lt;/td&gt;
&lt;td&gt;1.89%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CF2016&lt;/td&gt;
&lt;td&gt;CFHTMLTOPDF&lt;/td&gt;
&lt;td&gt;1286 ms&lt;/td&gt;
&lt;td&gt;77,179&lt;/td&gt;
&lt;td&gt;9 ms&lt;/td&gt;
&lt;td&gt;98,953&lt;/td&gt;
&lt;td&gt;-28.20%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CF2016&lt;/td&gt;
&lt;td&gt;WKHTMLTOPDF&lt;/td&gt;
&lt;td&gt;223 ms&lt;/td&gt;
&lt;td&gt;7,539&lt;/td&gt;
&lt;td&gt;9 ms&lt;/td&gt;
&lt;td&gt;5,167&lt;/td&gt;
&lt;td&gt;31.47%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello World&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Tests with a locally-downloaded &lt;a href="https://www.nasa.gov/wp-content/uploads/2025/04/54438297406-927b72555e-o.jpg" rel="noopener noreferrer"&gt;4.7mb NASA JPG&lt;/a&gt; image rendered at 400px wide.
&lt;/h2&gt;

&lt;p&gt;The difference regarding file size is a much wider gap here for CFDocument. Using Ghostscript to optimize results in a much smaller CFDocument.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;Generation&lt;/th&gt;
&lt;th&gt;FileSize&lt;/th&gt;
&lt;th&gt;Ghostscript Duration&lt;/th&gt;
&lt;th&gt;Ghostscript Filesize&lt;/th&gt;
&lt;th&gt;Percent Smaller&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CF2016&lt;/td&gt;
&lt;td&gt;CFDocument&lt;/td&gt;
&lt;td&gt;8,046 ms&lt;/td&gt;
&lt;td&gt;24,311,787&lt;/td&gt;
&lt;td&gt;50 ms&lt;/td&gt;
&lt;td&gt;74,478&lt;/td&gt;
&lt;td&gt;99.693%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CF2016&lt;/td&gt;
&lt;td&gt;CFHTMLTOPDF&lt;/td&gt;
&lt;td&gt;2,256 ms&lt;/td&gt;
&lt;td&gt;120,258&lt;/td&gt;
&lt;td&gt;1017 ms&lt;/td&gt;
&lt;td&gt;136,211&lt;/td&gt;
&lt;td&gt;-11.71%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CF2016&lt;/td&gt;
&lt;td&gt;WKHTMLTOPDF&lt;/td&gt;
&lt;td&gt;1,638 ms&lt;/td&gt;
&lt;td&gt;492,115&lt;/td&gt;
&lt;td&gt;45 ms&lt;/td&gt;
&lt;td&gt;21,012&lt;/td&gt;
&lt;td&gt;95.73%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I also ran this test script on a production server for CFDocument to eliminate the watermark and the filesize was consistent 24mb, but the Ghostscript result was 47,028 (36.86% smaller if no watermark exists) and still twice as large as WKHTMLTOPDF.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello World&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/tempdirectory/images_yyyymmddHHnnss.jpg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"400"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"248"&lt;/span&gt; &lt;span class="na"&gt;border=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;sup id="fn1"&gt;1&lt;/sup&gt; I haven't personally interacted with CF2025 beyond accessing it online via &lt;a href="https://cffiddle.org/" rel="noopener noreferrer"&gt;CFFiddle&lt;/a&gt;. I haven't had any time to read the terms and do not wish to be legally bound by any rules that I may not be fully understand. Back to text&lt;/p&gt;
&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/JamoCA/b957c34cddea38f4bd2d777b41e348ac" rel="noopener noreferrer"&gt;https://gist.github.com/JamoCA/b957c34cddea38f4bd2d777b41e348ac&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



</description>
      <category>pdf</category>
      <category>cfml</category>
      <category>coldfusion</category>
    </item>
    <item>
      <title>CFFrankenstein: Choosing to use EXEs instead of CFML BIFs</title>
      <dc:creator>James Moberg</dc:creator>
      <pubDate>Thu, 27 Mar 2025 16:29:13 +0000</pubDate>
      <link>https://forem.com/gamesover/cffrankenstein-choosing-to-use-exes-instead-of-cfml-bifs-4394</link>
      <guid>https://forem.com/gamesover/cffrankenstein-choosing-to-use-exes-instead-of-cfml-bifs-4394</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article "&lt;a href="https://www.mycfml.com/articles/cffrankenstein-choosing-to-use-exes-instead-of-cfml-bifs/" rel="noopener noreferrer"&gt;CFFrankenstein: Choosing to use EXEs instead of CFML BIFs&lt;/a&gt;" has been moved to myCFML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I &lt;strong&gt;choose&lt;/strong&gt; to use a lot of third-party Windows command line programs as part of our ColdFusion/CFML stack. Much of the functionality could be considered duplicated since official built-in functions (BIFs) already exist, but I perceive it as a "customer-controlled functions that always return reliable results regardless of CFML platform and/or version". (I've shared most of these programs back in 2020 article entitled &lt;a href="https://dev.to/gamesover/supporting-coldfusion-with-command-line-programs-2plj"&gt;Supporting ColdFusion with Command Line Programs&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;In the upcoming months, I'll be modernizing and expanding the functionality of my existing CFCs &amp;amp; UDFs that leverage these executables and share them with the ColdFusion/CFML community at-large. (I recently shared a &lt;a href="https://www.mycfml.com/articles/coldfusion-wrapper-for-zint-barcode-generator/" rel="noopener noreferrer"&gt;getQRSVG UDF&lt;/a&gt; using Zint.) &lt;strong&gt;Comment and let me know which you'd like to see first.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;7-Zip&lt;/strong&gt;: We started using this before CF8 added ZIP functionality. This is still faster, supports more options and can operate in the background without a CFThread.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bulk Rename Command&lt;/strong&gt;: This approach was soo much faster when it came to bulk renaming files. Without this, we'd have to recursively use CFDirectory, loop and use CFile to rename files and pray that it doesn't time-out.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CURL&lt;/strong&gt;: We've had to use this for MLS RETS access because CFHTTP basic authentication wouldn't reauthenticate as needed.  We also use it to fetch large files in the background, since the file doesn't have to be fully loaded into the Java Heap memory when downloaded. (I also found a way to stream a remote file to be saved locally without having to load it into memory.) We've been using a &lt;a href="https://taffy.io/" rel="noopener noreferrer"&gt;Taffy API&lt;/a&gt; endpoint that leverages CURL to perform 3rdparty DNS look-ups to bind the request to an external WAF IP when validating SSL certificates. &lt;em&gt;(Why should I pay for a service that I can easily build myself... I'm a developer, right?)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;FastCopy&lt;/strong&gt;: This program is no longer has a free license, but we've been using the previously open-sourced version to sync files from a mail server's inbox folder via a UNC path. This approach was significantly faster than using similar CFML logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GhostScript&lt;/strong&gt;: We use this only because we still use CFDocument and this EXE enables us to reduce the bloated filesize of the generated PDFs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GraphicsMagic&lt;/strong&gt;: We looked for an alternative because CFImage wasn't able to read some iPhone 4 JPGs. We read an Etsy blog article entitled &lt;a href="https://www.etsy.com/codeascraft/batch-processing-millions-of-images" rel="noopener noreferrer"&gt;Batch Processing Millions and Millions of Images&lt;/a&gt; and   determined that we needed that same level of performance. After integration, my business partner thought it wasn't working because CFImage was previously adding 2-8 seconds to the processing request whereas GM was nearly instantaneous.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Kid3&lt;/strong&gt;: This is an area that Adobe has no existing support even though Adobe has commercial products that work with audio &amp;amp; video. This EXE enables us to manipulate artist, title, album, track, copyright data and artwork to MP3s via the embedded ID3 metadata.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PDFtk/qPdf/xPdf&lt;/strong&gt;: These are three different PDF-related libraries that provide somewhat similar functionality. We were kinda forced to use these because sometimes &lt;code&gt;isPDFFile()&lt;/code&gt; will return true, but CFPDF will throw an error and claim that it's not a PDF. It's also convenient to have always have PDF-related functionality when spinning up a CFML server using &lt;a href="https://www.ortussolutions.com/products/commandbox" rel="noopener noreferrer"&gt;CommandBox&lt;/a&gt; without having to manually install additional modules.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;S3Express&lt;/strong&gt;: For a digital on-demand web application, we were using built-in S3 services, but it was too slow (110 seconds). Even using the AmazonS3 CLI EXE was too slow (40 seconds). S3Express performed the same task in 16 seconds. After discovering this, how could we keep using the built-in features? (NOTE: I haven't retested this since C10. Has there been any improvements?)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;West Wind DeleteFiles&lt;/strong&gt;: We use CRON (not CFSchedule) to reliably perform tasks and one of those is to delete temp files. "DeleteFiles" enables us to blindly bulk delete any files older than &lt;code&gt;x&lt;/code&gt; days. This approach operates entirely separate from our CFML servers, has zero impact on CFML performance and "just works".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;WKHTMLTOPDF&lt;/strong&gt;: This EXE offers more advanced HTML/CSS support than CFDocument, but may have issues if accessing 3rd-party web content. (In a single use case, I've had to use Puppeteer due to client-side javascript feature detection rendering issues.) This EXE is faster and provides smaller, consistent output since we started using it with CF9. It also enables us to generate PDFs when spinning up a CFML server using &lt;a href="https://www.ortussolutions.com/products/commandbox" rel="noopener noreferrer"&gt;CommandBox&lt;/a&gt; without having to manually install any additional modules.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zint&lt;/strong&gt;: Need a barcode? This is the standard. I recently shared a &lt;a href="https://www.mycfml.com/articles/coldfusion-wrapper-for-zint-barcode-generator/" rel="noopener noreferrer"&gt;UDF wrapper&lt;/a&gt; that leverages Zint to generate responsive QR code SVGs without creating physical files. The SVG text can then be embedded inline on webpages and PDFs (er, using WKHTMLTOPDF. I don't believe SVG images work with CFDocument.) I've used some java libraries that can generate QR codes, but they were limited in terms of options regarding color/size and output formats (ie, only JPG or PNG).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Comment and let me know which you'd like to see first.
&lt;/h2&gt;

</description>
      <category>architecture</category>
      <category>cli</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Using ColdFusion to Generate Pre-Signed Wasabi Download URL</title>
      <dc:creator>James Moberg</dc:creator>
      <pubDate>Thu, 20 Mar 2025 15:45:41 +0000</pubDate>
      <link>https://forem.com/gamesover/using-coldfusion-to-generate-pre-signed-wasabi-download-url-1fe0</link>
      <guid>https://forem.com/gamesover/using-coldfusion-to-generate-pre-signed-wasabi-download-url-1fe0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article "&lt;a href="https://www.mycfml.com/articles/using-coldfusion-to-generate-pre-signed-wasabi-url/" rel="noopener noreferrer"&gt;Using ColdFusion to Generate Pre-Signed Wasabi Download URL&lt;/a&gt;" has been moved to myCFML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There was an internal decision to use &lt;a href="https://wasabi.com/" rel="noopener noreferrer"&gt;Wasabi Cloud Storage&lt;/a&gt; instead of &lt;a href="https://aws.amazon.com/pm/serv-s3/" rel="noopener noreferrer"&gt;Amazon S3&lt;/a&gt; and I needed to use ColdFusion to generate a pre-signed URL to allow access to AI-generated content for a limited time.  I had used the &lt;a href="https://gist.github.com/Leigh-/a2798584b79fd9072605a4cc7ff60df4" rel="noopener noreferrer"&gt;Sv4Util.cfc&lt;/a&gt; and &lt;a href="https://github.com/jcberquist/aws-cfml" rel="noopener noreferrer"&gt;aws-cfml&lt;/a&gt; libraries before with Amazon and thought it was just as simple, but I got confused somewhere along the way and it just wasn't working.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.wasabi.com/v1/docs/how-do-i-generate-pre-signed-urls-for-temporary-access-with-wasabi" rel="noopener noreferrer"&gt;Wasabi documention&lt;/a&gt; listed several approaches to generate a valid pre-signed URL...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the AWS CLI&lt;/li&gt;
&lt;li&gt;Using the AWS Tools for Powershell&lt;/li&gt;
&lt;li&gt;Using the S3 Browser&lt;/li&gt;
&lt;li&gt;Using Wasabi Explorer&lt;/li&gt;
&lt;li&gt;Using pre-signed S3 URLs for temporary, automated access in your application code

&lt;ul&gt;
&lt;li&gt;Python and Boto3&lt;/li&gt;
&lt;li&gt;aws-sdk for Nodejs&lt;/li&gt;
&lt;li&gt;AWS SDK for PHP (V2)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;... but none of these solutions were very helpful for my environment and I didn't want to have to fallback to using the command line.&lt;/p&gt;

&lt;p&gt;I thought it'd be an easy function for AI to generate, but the results still weren't working.&lt;/p&gt;

&lt;p&gt;After some additional searches on Google, I came across an Amazon API reference regarding &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html" rel="noopener noreferrer"&gt;Authenticating Requests: Using Query Parameters (AWS Signature Version 4)&lt;/a&gt; and it outlined a step-by-step approach with detail instructions, detailed descriptions and static example (with example output). &lt;em&gt;Whenever I'm working with a third-party API, I always look for basic CURL examples so that I can see all the explicit settings and this example was perfect.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I was able to quickly debug &amp;amp; identify the issues that caused the calculation to be wrong.  After finding out how to do it right, I took another look at the aws-cfml library and shared the &lt;a href="https://gist.github.com/JamoCA/d2d38c5eaacba400decb21010124b159" rel="noopener noreferrer"&gt;cfml example&lt;/a&gt; with Wasabi (but I don't think they'll update their webpage to include links to AWS documentation or CFML examples.)&lt;/p&gt;

&lt;p&gt;While I'm not currently using this &lt;strong&gt;generateS3PresignedUrl&lt;/strong&gt; UDF in production, I thought I'd share it in case other developers can benefit from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/JamoCA/bbdb652e4390898ea27eee489923ede3" rel="noopener noreferrer"&gt;https://gist.github.com/JamoCA/bbdb652e4390898ea27eee489923ede3&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here's an example using the &lt;strong&gt;aws-cfml&lt;/strong&gt; library.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/JamoCA/d2d38c5eaacba400decb21010124b159" rel="noopener noreferrer"&gt;https://gist.github.com/JamoCA/d2d38c5eaacba400decb21010124b159&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


</description>
      <category>aws</category>
      <category>s3</category>
      <category>coldfusion</category>
      <category>cfml</category>
    </item>
    <item>
      <title>ColdFusion getQRSVG() UDF - Returns responsive SVG source</title>
      <dc:creator>James Moberg</dc:creator>
      <pubDate>Thu, 13 Mar 2025 17:07:44 +0000</pubDate>
      <link>https://forem.com/gamesover/coldfusion-getqrsvg-returns-responsive-svg-source-37hf</link>
      <guid>https://forem.com/gamesover/coldfusion-getqrsvg-returns-responsive-svg-source-37hf</guid>
      <description>&lt;p&gt;In a project I'm working on, I needed to generate a QR code and display it within an HTML file that is converted to a PDF using &lt;a href="https://gist.github.com/JamoCA/74e556e7d4f1a715a41d" rel="noopener noreferrer"&gt;WKHTMLTOPDF&lt;/a&gt;. I didn't want to rely upon physical, statically-hosted SVG image files and wished to embed it. With WKTHMLTOPDF, I could use javascript to generate the QR code, but that requires adding a time delay and I didn't want to negatively impact processing time (even though WKHTMLTOPDF is faster than CFDocument.)&lt;/p&gt;

&lt;p&gt;I already have a &lt;a href="https://www.mycfml.com/articles/coldfusion-wrapper-for-zint-barcode-generator/" rel="noopener noreferrer"&gt;ColdFusion wrapper for Zint Barcode Generator&lt;/a&gt; function, so I just needed to write a CFML wrapper to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use generateZint() to create a temporary SVG file&lt;/li&gt;
&lt;li&gt;read the temp file data&lt;/li&gt;
&lt;li&gt;remove XML &amp;amp; HTML doctype headers&lt;/li&gt;
&lt;li&gt;modify the SVG XML source to "make it responsive"&lt;/li&gt;
&lt;li&gt;delete the temp file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here it is. I hope that this helps some CFML developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  UPDATE (8 Hours later)
&lt;/h2&gt;

&lt;p&gt;I've updated the UDF so that it isn't dependent on the generateZint() UDF and so it outputs the SVG text directly to the console... thus skipping writing, reading &amp;amp; deleting. The process is now only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use zint.exe to capture SVG output&lt;/li&gt;
&lt;li&gt;remove XML &amp;amp; HTML doctype headers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I didn't notice much difference in terms of performance, but I consider it a win anytime I can reduce the amount of I/O calls to the file system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/JamoCA/fbbd2599102216448ada8e9f85d40b9c" rel="noopener noreferrer"&gt;https://gist.github.com/JamoCA/fbbd2599102216448ada8e9f85d40b9c&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Backporting New ColdFusion 2025 Function listGetDuplicates()</title>
      <dc:creator>James Moberg</dc:creator>
      <pubDate>Tue, 11 Mar 2025 19:37:20 +0000</pubDate>
      <link>https://forem.com/gamesover/backporting-new-coldfusion-2025-function-listgetduplicates-i9n</link>
      <guid>https://forem.com/gamesover/backporting-new-coldfusion-2025-function-listgetduplicates-i9n</guid>
      <description>&lt;p&gt;I saw a post on Linkedin regarding the "&lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7305117341355249665?utm_source=share&amp;amp;utm_medium=member_desktop&amp;amp;rcm=ACoAAAAOuAoB086d9j5t_iO1uJIz1zWpUNfOIUA" rel="noopener noreferrer"&gt;Unlocking the Power of listGetDuplicates&lt;/a&gt;". I like unlocking power, so I checked it out.&lt;/p&gt;

&lt;p&gt;The new Adobe ColdFusion 2025 &lt;a href="https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-functions/functions-l/listgetduplicates.html" rel="noopener noreferrer"&gt;listgetduplicates&lt;/a&gt; function accepts 4 parameters: &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;delimiter&lt;/code&gt;, &lt;code&gt;ignoreCase&lt;/code&gt; &amp;amp; &lt;code&gt;includeEmptyFields&lt;/code&gt;.  A &lt;strong&gt;List&lt;/strong&gt; string is required, and the default &lt;strong&gt;delimiter&lt;/strong&gt; is a comma. I'm not sure what the other 2 parameters default to. The values must be boolean, so one could assume that it's &lt;code&gt;false&lt;/code&gt; since they added language to describe what happens "if true". (Documentation should be explicit, and much of Adobe's documentation doesn't include the default values of function properties &amp;amp; arguments.)&lt;/p&gt;

&lt;p&gt;None of our CFML projects have been migrated to ColdFusion 2025 yet. (This will require lots of testing as we use many third-party JAVA libraries). We've been using the "anti-duplicate" version of this function since 2008, &lt;a href="http://www.cflib.org/udf/listGetDistinctValues" rel="noopener noreferrer"&gt;listGetDistinctValues&lt;/a&gt; UDF from &lt;a href="https://cflib.org/" rel="noopener noreferrer"&gt;CFLib.org&lt;/a&gt;.  This function doesn't support "case" or automatically trim values. (I've previously extended this function to accept &amp;amp; return arrays.)&lt;/p&gt;

&lt;p&gt;I wondered how difficult it would be to port this new function to an old, deprecated version of ColdFusion... like 2016. Built-in functions like &lt;code&gt;structnew("casesensitive")&lt;/code&gt; didn't exist, so that method can't be used. My workaround was to use java's &lt;a href="https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html" rel="noopener noreferrer"&gt;&lt;code&gt;hashCode()&lt;/code&gt;&lt;/a&gt; function to quickly calculate a unique-ish signed integer to aid in identify character case.&lt;/p&gt;

&lt;p&gt;Additional support for the following has been added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;list&lt;/strong&gt;: Accept a list &lt;strong&gt;or an array&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;trimValues&lt;/strong&gt;: Boolean to ensure values are properly trimmed to avoid false duplicates. (default: true)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;returnFirstMatch&lt;/strong&gt;: Boolean to return the first match and retain original sort order (default: false)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;returnDelimiter&lt;/strong&gt;: Character(s) returned list elements (default: the original "delimiter")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;returnAsArray&lt;/strong&gt;: Return the list elements as an array (default: false)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/JamoCA/103625ffdf9688b810485183db9b648f" rel="noopener noreferrer"&gt;https://gist.github.com/JamoCA/103625ffdf9688b810485183db9b648f&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>isValidHexString ColdFusion UDF</title>
      <dc:creator>James Moberg</dc:creator>
      <pubDate>Fri, 07 Mar 2025 17:06:13 +0000</pubDate>
      <link>https://forem.com/gamesover/isvalidhexstring-coldfusion-udf-1cge</link>
      <guid>https://forem.com/gamesover/isvalidhexstring-coldfusion-udf-1cge</guid>
      <description>&lt;p&gt;While working on my ColdFusion &lt;a href="https://github.com/JamoCA/colorTools" rel="noopener noreferrer"&gt;colorTools CFC&lt;/a&gt;, I wondered if &lt;a href="https://cfdocs.org/isvalid" rel="noopener noreferrer"&gt;isValid()&lt;/a&gt; supported a "hex" type since "guid" &amp;amp; "UUID" both rely on validating hexadecimal numbers... and the answer is no.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: I've added some new features to colorTools, but will write about them next week after I add some demo &amp;amp; test scripts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Validating hex strings isn't difficult, but some values may be prefixed with "#" or "0x".  Hex is also used for many other things beyond color codes, but I wanted to optionally &amp;amp; explicitly test for 6 character string values when accepting hex color values.  I believe that this UDF covers all the requirements. (If it doesn't, let me know.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/JamoCA/c4079d7c813b0ad3d1a28cfc7551c355" rel="noopener noreferrer"&gt;https://gist.github.com/JamoCA/c4079d7c813b0ad3d1a28cfc7551c355&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


</description>
      <category>cfml</category>
      <category>coldfusion</category>
    </item>
    <item>
      <title>ColdFusion colorTools CFC (In Progress)</title>
      <dc:creator>James Moberg</dc:creator>
      <pubDate>Thu, 27 Feb 2025 15:34:25 +0000</pubDate>
      <link>https://forem.com/gamesover/coldfusion-colortools-cfc-in-progress-3i1h</link>
      <guid>https://forem.com/gamesover/coldfusion-colortools-cfc-in-progress-3i1h</guid>
      <description>&lt;p&gt;While working on some internal graphs to represent daily Fastly WAF/CDN statistics, I realized that I needed to generate some additional random colors. I started to use the &lt;a href="https://cflib.org/udf/returnRandomHEXColors" rel="noopener noreferrer"&gt;returnRandomHEXColors&lt;/a&gt; UDF from &lt;a href="https://cflib.org/" rel="noopener noreferrer"&gt;CFLib&lt;/a&gt;.  Out of the first 10 colors that were generated, the first 5 looked very similar and one of them was white and didn't contrast very well on a white background.&lt;/p&gt;

&lt;p&gt;My initial thought was to create a single UDF, but it wasn't that simple as the function required additional support for max luminance &amp;amp; min contrast. I didn't want to have to remember to keep 3 &lt;a href="https://helpx.adobe.com/coldfusion/developing-applications/building-blocks-of-coldfusion-applications/writing-and-calling-user-defined-functions/creating-user-defined-functions.html" rel="noopener noreferrer"&gt;UDF&lt;/a&gt;s together in order for them to retain functionality. I also was already performing HEX-to-RGB conversion when using HEX colors with the &lt;a href="https://www.chartjs.org/" rel="noopener noreferrer"&gt;Chart.js&lt;/a&gt; javascript library, so it seemed natural to include these related functions to the CFC so I wouldn't have to "repeat myself".&lt;/p&gt;

&lt;h2&gt;
  
  
  makeWCAGChartColor
&lt;/h2&gt;

&lt;p&gt;My approach, which could very well be overkill, was to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;compare against an array of existing colors (to prevent duplication)&lt;/li&gt;
&lt;li&gt;identify last color used and ensure the new color meets a WCAG minimum contrast requirement (contrast min: 4.5)&lt;/li&gt;
&lt;li&gt;calculate luminance to ensure the result is not too bright (luminance max: 0.7)&lt;/li&gt;
&lt;li&gt;provide a fallback in case a color can't be determined after 100 attempts&lt;/li&gt;
&lt;li&gt;return color value as uppercase HEX

&lt;ul&gt;
&lt;li&gt;Better readability and scannability&lt;/li&gt;
&lt;li&gt;Wider adoption in professional style guides and tools&lt;/li&gt;
&lt;li&gt;Consistency with historical and team-based workflows&lt;/li&gt;
&lt;li&gt;It didn't need to look "modern", just be consistent&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Here's what default usage source currently looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cfscript"&gt;&lt;code&gt;&lt;span class="nx"&gt;colorTools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;colorTools&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;lte&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;colorTools.makeWCAGChartColor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;writeoutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;div style="display:inline-block; width:100px; height:50px; background-color:##'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="s1"&gt;'; border:1px solid ##000; margin:5px; text-align:center; line-height:50px; font-family:Arial, sans-serif; font-size:14px; color:##fff; text-shadow:0 0 2px ##000;"&amp;gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;/div&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;... and here's the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmn6s7b9gx2zhnfhelqhm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmn6s7b9gx2zhnfhelqhm.png" alt="25 randomly generated colors with minimum contrast between each" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've added some other public helper functions too, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;calculateContrast&lt;/li&gt;
&lt;li&gt;calculateLuminance&lt;/li&gt;
&lt;li&gt;hexToRGB&lt;/li&gt;
&lt;li&gt;rgbToHex&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I included the &lt;a href="https://colorbrewer2.org/" rel="noopener noreferrer"&gt;ColorBrewer&lt;/a&gt; palette and all named &lt;a href="https://en.wikipedia.org/wiki/X11_color_names" rel="noopener noreferrer"&gt;W3C X11 colors&lt;/a&gt; and these values can be retrieved via the &lt;code&gt;getVariable()&lt;/code&gt; method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;colorTools.getVariable("colorBrewerPalette") &lt;code&gt;// returns a struct of named palettes with array of colors&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;colorTools.getVariable("w3cx11") &lt;code&gt;// returns a struct of named colors&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More updates and functionality will follow soon.  Now that I have my extra colors, I need to get back to integrating the interactive Chart.js graphs.&lt;/p&gt;

&lt;p&gt;Feel free to contact me if there's a nagging color issue that has always bugged you or if you have any ideas for future features. (I'm going to personally review the projects that we've developed during the last 25 years to identify other color-related features that would be useful.)&lt;/p&gt;

&lt;p&gt;Here's the Github repository (I'm not using a gist for this one.):&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/JamoCA" rel="noopener noreferrer"&gt;
        JamoCA
      &lt;/a&gt; / &lt;a href="https://github.com/JamoCA/colorTools" rel="noopener noreferrer"&gt;
        colorTools
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      CFML function for color generation and manipulation
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;colorTools&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;CFML function for color generation and manipulation&lt;/p&gt;

&lt;p&gt;NOTE: This CFC is still in the early stages of development. More functions and better documentation will be coming in the future.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;colorTools = new colorTools()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;makeWCAGChartColor(&lt;em&gt;makeUnique&lt;/em&gt;, &lt;em&gt;usedHexColors&lt;/em&gt;, &lt;em&gt;fallbackHexColor&lt;/em&gt;, &lt;em&gt;minContrast&lt;/em&gt;, &lt;em&gt;maxLuminance&lt;/em&gt;, &lt;em&gt;returnAs&lt;/em&gt;)&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Returns a random WCAG HEX color with minimum contrast and maximum luminance filters with option to make unique and ensure that next color isn't too similiar to previously generated color.&lt;/p&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;for (i = 1; i lte 25; i += 1) {
    color = colorTools.makeWCAGChartColor();
    writeoutput('&amp;lt;div style="display:inline-block; width:100px; height:50px; background-color:##' &amp;amp; color &amp;amp; '; border:1px solid ##000; margin:5px; text-align:center; line-height:50px; font-family:Arial, sans-serif; font-size:14px; color:##fff; text-shadow:0 0 2px ##000;"&amp;gt;' &amp;amp; color &amp;amp; '&amp;lt;/div&amp;gt;');
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;calculateContast(color1, color2)&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Description &amp;amp; example coming soon.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;calculateLuminance(&lt;em&gt;r&lt;/em&gt;, &lt;em&gt;g&lt;/em&gt;, &lt;em&gt;b&lt;/em&gt;, &lt;em&gt;color&lt;/em&gt;)&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Description &amp;amp; example coming soon.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;hexToRGB(hexColor, &lt;em&gt;returnAs&lt;/em&gt;)&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Description &amp;amp; example coming soon.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;rgbToHex(r,&lt;/h2&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/JamoCA/colorTools" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>cfml</category>
      <category>coldfusion</category>
    </item>
  </channel>
</rss>
