跳至頁尾內容
移民指南

如何在 C# 中將 wkhtmltopdf 遷移到 IronPDF

wkhtmltopdf 是一款廣泛使用的工具,用於使用 Qt WebKit 將 HTML 文件轉換為 PDF。 儘管該專案因其命令列功能和免費許可而在開發者中廣受歡迎,但該專案現在存在著不容忽視的嚴重安全風險。 該庫已於 2016-2017 年正式棄用,並且存在一個嚴重漏洞(CVE-2022-35583),該漏洞至今仍未修復。

本指南提供了從 wkhtmltopdf 到 IronPDF 的完整遷移路徑,其中包含逐步說明、程式碼比較和實用範例,供需要從應用程式中消除此安全風險的專業 .NET 開發人員參考。

嚴重安全警告:CVE-2022-35583

wkhtmltopdf 包含一個永遠不會修復的嚴重安全漏洞:

問題嚴重程度地位
CVE-2022-35583評分:9.8/10未打補丁
SSRF漏洞基礎設施接管風險未打補丁
最後更新2016-2017
WebKit 版本2015 (Qt WebKit)過時的
CSS Grid 支持沒有任何破碎的
Flexbox 支援部分的破碎的
ES6+ JavaScript沒有任何破碎的

SSRF攻擊的工作原理

伺服器端請求偽造漏洞允許攻擊者存取內部服務、竊取憑證、掃描內部網絡,並透過精心建構的 HTML 程式碼竊取敏感資料:

<!-- Malicious HTML submitted to your PDF generator -->
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin"/>
<!-- Malicious HTML submitted to your PDF generator -->
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin"/>
HTML

當 wkhtmltopdf 渲染此 HTML 時,它會從伺服器的網路上下文中取得這些 URL,從而繞過防火牆和安全控制。

受影響的包裝庫

所有用於 wkhtmltopdf 的 .NET 封裝庫都存在這些漏洞:

包裝庫地位安全風險
DinkToPdf⚠️ 重要提示
旋轉⚠️ 重要提示
週二佩奇金⚠️ 重要提示
WkHtmlToPdf-DotNet⚠️ 重要提示
NReco.Pdf產生器使用 wkhtmltopdf⚠️ 重要提示

如果您使用這些庫中的任何一個,您將容易受到 CVE-2022-35583 的攻擊。

IronPDF 與 wkhtmltopdf:功能對比

了解架構差異有助於技術決策者評估遷移投資:

特徵wkhtmltopdfIronPDF
授權LGPLv3(免費)商業的
渲染引擎Qt WebKit(2015)當前鉻引擎
安全漏洞CVE-2022-35583,有重大未修復問題沒有已知的CVE
主動維護已棄坑,自 2017 年以來沒有實質更新。持續維護,定期發布新版本。
支援現代網路標準功能受限(flexbox 佈局損壞,不支援 CSS Grid)全力支持
整合與支援僅限社群論壇詳盡的文件和專門的支持
CSS Grid❌ 不支持✅ 全力支持
Flexbox⚠️ 破損✅ 全力支持
ES6+ JavaScript❌ 不支持✅ 全力支持
異步/等待❌ 不支持✅ 全力支持
PDF 處理❌ 不支持✅ 全力支持
數位簽名❌ 不支持✅ 全力支持
PDF/A 合規性❌ 不支持✅ 全力支持

快速入門:wkhtmltopdf 到 IronPDF 的遷移

透過這些基礎步驟,遷移工作可以立即開始。

步驟 1:刪除 wkhtmltopdf 軟體包和二進位文件

移除所有 wkhtmltopdf 封裝包:

# Remove wkhtmltopdf wrapper (whichever you're using)
dotnet remove package WkHtmlToPdf-DotNet
dotnet remove package DinkToPdf
dotnet remove package TuesPechkin
dotnet remove package Rotativa
dotnet remove package Rotativa.AspNetCore
dotnet remove package NReco.PdfGenerator

# Remove wkhtmltopdf binary from your deployment
# Delete wkhtmltopdf.exe, wkhtmltox.dll, etc.
# Remove wkhtmltopdf wrapper (whichever you're using)
dotnet remove package WkHtmlToPdf-DotNet
dotnet remove package DinkToPdf
dotnet remove package TuesPechkin
dotnet remove package Rotativa
dotnet remove package Rotativa.AspNetCore
dotnet remove package NReco.PdfGenerator

# Remove wkhtmltopdf binary from your deployment
# Delete wkhtmltopdf.exe, wkhtmltox.dll, etc.
SHELL

步驟 2:安裝 IronPDF

# Add IronPDF (secure, modern alternative)
dotnet add package IronPdf
# Add IronPDF (secure, modern alternative)
dotnet add package IronPdf
SHELL

步驟 3:更新命名空間

將 wkhtmltopdf 命名空間替換為 IronPdf 命名空間:

// Before (wkhtmltopdf)
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;

// After (IronPDF)
using IronPdf;
// Before (wkhtmltopdf)
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;

// After (IronPDF)
using IronPdf;
$vbLabelText   $csharpLabel

步驟 4:初始化許可證

在應用程式啟動時新增許可證初始化:

IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
$vbLabelText   $csharpLabel

程式碼遷移範例

將 HTML 轉換為 PDF

最基本的操作揭示了這些 .NET PDF 方法之間的複雜性差異。

wkhtmltopdf 方法:

// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;

class Program
{
    static void Main()
    {
        var converter = new SynchronizedConverter(new PdfTools());
        var doc = new HtmlToPdfDocument()
        {
            GlobalSettings = {
                ColorMode = ColorMode.Color,
                方向 = Orientation.Portrait,
                PaperSize = PaperKind.A4
            },
            Objects = {
                new ObjectSettings()
                {
                    HtmlContent = "<h1>Hello World</h1><p>This is a PDF from HTML.</p>"
                }
            }
        };
        byte[] pdf = converter.Convert(doc);
        File.WriteAllBytes("output.pdf", pdf);
    }
}
// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;

class Program
{
    static void Main()
    {
        var converter = new SynchronizedConverter(new PdfTools());
        var doc = new HtmlToPdfDocument()
        {
            GlobalSettings = {
                ColorMode = ColorMode.Color,
                方向 = Orientation.Portrait,
                PaperSize = PaperKind.A4
            },
            Objects = {
                new ObjectSettings()
                {
                    HtmlContent = "<h1>Hello World</h1><p>This is a PDF from HTML.</p>"
                }
            }
        };
        byte[] pdf = converter.Convert(doc);
        File.WriteAllBytes("output.pdf", pdf);
    }
}
$vbLabelText   $csharpLabel

IronPDF 方法:

// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><p>This is a PDF from HTML.</p>");
        pdf.SaveAs("output.pdf");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><p>This is a PDF from HTML.</p>");
        pdf.SaveAs("output.pdf");
    }
}
$vbLabelText   $csharpLabel

wkhtmltopdf 需要使用PdfTools建立一個SynchronizedConverter ,使用GlobalSettingsObjects建構一個HtmlToPdfDocument ,設定ColorModeOrientationPaperSize等屬性,呼叫converter.Convert()取得原始位元組,然後使用File.WriteAllBytes()手動寫入檔案。

IronPDF 完全消除了這個步驟-建立一個ChromePdfRenderer ,呼叫RenderHtmlAsPdf() ,然後使用內建的SaveAs()方法。

如需更進階的 HTML 轉 PDF 場景,請參閱HTML 轉 PDF 轉換指南

將 URL 轉換為 PDF

URL 轉 PDF 轉換也呈現類似的複雜性模式。

wkhtmltopdf 方法:

// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;

class Program
{
    static void Main()
    {
        var converter = new SynchronizedConverter(new PdfTools());
        var doc = new HtmlToPdfDocument()
        {
            GlobalSettings = {
                ColorMode = ColorMode.Color,
                方向 = Orientation.Portrait,
                PaperSize = PaperKind.A4
            },
            Objects = {
                new ObjectSettings()
                {
                    Page = "https://www.example.com"
                }
            }
        };
        byte[] pdf = converter.Convert(doc);
        File.WriteAllBytes("webpage.pdf", pdf);
    }
}
// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;

class Program
{
    static void Main()
    {
        var converter = new SynchronizedConverter(new PdfTools());
        var doc = new HtmlToPdfDocument()
        {
            GlobalSettings = {
                ColorMode = ColorMode.Color,
                方向 = Orientation.Portrait,
                PaperSize = PaperKind.A4
            },
            Objects = {
                new ObjectSettings()
                {
                    Page = "https://www.example.com"
                }
            }
        };
        byte[] pdf = converter.Convert(doc);
        File.WriteAllBytes("webpage.pdf", pdf);
    }
}
$vbLabelText   $csharpLabel

IronPDF 方法:

// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
        pdf.SaveAs("webpage.pdf");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
        pdf.SaveAs("webpage.pdf");
    }
}
$vbLabelText   $csharpLabel

wkhtmltopdf 使用ObjectSettings中的Page屬性來指定 URL,這需要相同的文件建構模式。 IronPDF 提供了一個專門的RenderUrlAsPdf()方法,可以清楚地表達意圖。

查看指向 PDF 文件的 URL ,以了解身份驗證和自訂標頭選項。

自訂設定:包含頁面配置的 HTML 文件

配置方向、邊距和紙張尺寸需要不同的方法。

wkhtmltopdf 方法:

// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;

class Program
{
    static void Main()
    {
        var converter = new SynchronizedConverter(new PdfTools());
        var doc = new HtmlToPdfDocument()
        {
            GlobalSettings = {
                ColorMode = ColorMode.Color,
                方向 = Orientation.Landscape,
                PaperSize = PaperKind.A4,
                Margins = new MarginSettings() { Top = 10, Bottom = 10, Left = 10, Right = 10 }
            },
            Objects = {
                new ObjectSettings()
                {
                    Page = "input.html",
                    WebSettings = { DefaultEncoding = "utf-8" }
                }
            }
        };
        byte[] pdf = converter.Convert(doc);
        File.WriteAllBytes("custom-output.pdf", pdf);
    }
}
// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;

class Program
{
    static void Main()
    {
        var converter = new SynchronizedConverter(new PdfTools());
        var doc = new HtmlToPdfDocument()
        {
            GlobalSettings = {
                ColorMode = ColorMode.Color,
                方向 = Orientation.Landscape,
                PaperSize = PaperKind.A4,
                Margins = new MarginSettings() { Top = 10, Bottom = 10, Left = 10, Right = 10 }
            },
            Objects = {
                new ObjectSettings()
                {
                    Page = "input.html",
                    WebSettings = { DefaultEncoding = "utf-8" }
                }
            }
        };
        byte[] pdf = converter.Convert(doc);
        File.WriteAllBytes("custom-output.pdf", pdf);
    }
}
$vbLabelText   $csharpLabel

IronPDF 方法:

// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape;
        renderer.RenderingOptions.MarginTop = 10;
        renderer.RenderingOptions.MarginBottom = 10;
        renderer.RenderingOptions.MarginLeft = 10;
        renderer.RenderingOptions.MarginRight = 10;
        renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;

        var pdf = renderer.RenderHtmlFileAsPdf("input.html");
        pdf.SaveAs("custom-output.pdf");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape;
        renderer.RenderingOptions.MarginTop = 10;
        renderer.RenderingOptions.MarginBottom = 10;
        renderer.RenderingOptions.MarginLeft = 10;
        renderer.RenderingOptions.MarginRight = 10;
        renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;

        var pdf = renderer.RenderHtmlFileAsPdf("input.html");
        pdf.SaveAs("custom-output.pdf");
    }
}
$vbLabelText   $csharpLabel

wkhtmltopdf 將設定嵌套在GlobalSettingsObjects中,其中MarginSettings是一個單獨的物件。 IronPDF 提供直接的RenderingOptions屬性,其名稱清晰明了,例如PaperOrientationMarginTopPaperSize

wkhtmltopdf API 到 IronPDF 映射參考

此映射透過顯示直接的 API 等效項來加速遷移:

CLI 到 IronPDF 映射

wkhtmltopdf CLI 選項IronPDF當量筆記
wkhtmltopdf input.html output.pdfrenderer.RenderHtmlFileAsPdf()文件轉換為 PDF
wkhtmltopdf URL output.pdfrenderer.RenderUrlAsPdf()PDF檔案的URL
--page-size A4RenderingOptions.PaperSize = PdfPaperSize.A4紙張尺寸
--page-size LetterRenderingOptions.PaperSize = PdfPaperSize.Letter美國信紙
--orientation LandscapeRenderingOptions.PaperOrientation = Landscape方向
--margin-top 10mmRenderingOptions.MarginTop = 10邊距(毫米)
--margin-bottom 10mmRenderingOptions.MarginBottom = 10
--margin-left 10mmRenderingOptions.MarginLeft = 10
--margin-right 10mmRenderingOptions.MarginRight = 10
--header-html header.htmlRenderingOptions.HtmlHeaderHTML 頭部
--footer-center "[page]"{page}佔位符頁碼
--footer-center "[toPage]"{total-pages}佔位符總頁數
--enable-javascript預設啟用JavaScript
--javascript-delay 500RenderingOptions.WaitFor.RenderDelay = 500JS 延遲
--dpi 300RenderingOptions.Dpi = 300DPI設定
--grayscaleRenderingOptions.GrayScale = true灰階

C# 封裝器 API 映射

wkhtmltopdf 包裝器IronPDF筆記
SynchronizedConverterChromePdfRenderer主渲染器
HtmlToPdfDocumentRenderingOptions配置
GlobalSettings.Outpdf.SaveAs()輸出檔案
GlobalSettings.PaperSizeRenderingOptions.PaperSize紙張尺寸
GlobalSettings.OrientationRenderingOptions.PaperOrientation方向
GlobalSettings.MarginsRenderingOptions.Margin*個人利潤
ObjectSettings.PageRenderHtmlFileAsPdf()文件輸入
ObjectSettings.HtmlContentRenderHtmlAsPdf()HTML字串
converter.Convert(doc)renderer.RenderHtmlAsPdf()產生 PDF

佔位符語法遷移

wkhtmltopdf佔位符IronPDF佔位符
[page]{page}
[toPage]{total-pages}
[date]{date}
[time]{time}
[title]{html-title}
[url]{url}

常見遷移問題及解決方案

問題 1:頁首/頁尾佔位符語法

wkhtmltopdf:使用方括號語法,如[page][toPage]

解決方案:更新 IronPDF 的花括號佔位符:

// Before (wkhtmltopdf)
FooterSettings = { Left = "Page [page] of [toPage]" }

// After (IronPDF)
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = "<div style='text-align:left;'>Page {page} of {total-pages}</div>",
    MaxHeight = 25
};
// Before (wkhtmltopdf)
FooterSettings = { Left = "Page [page] of [toPage]" }

// After (IronPDF)
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = "<div style='text-align:left;'>Page {page} of {total-pages}</div>",
    MaxHeight = 25
};
$vbLabelText   $csharpLabel

問題 2:JavaScript 延遲配置

wkhtmltopdf:使用JavascriptDelay屬性,但可靠性有限。

解決方案: IronPDF 提供多種選擇:

renderer.RenderingOptions.EnableJavaScript = true;

// Option 1: Fixed delay
renderer.RenderingOptions.WaitFor.RenderDelay(500);

// Option 2: Wait for specific element (more reliable)
renderer.RenderingOptions.WaitFor.HtmlElementById("content-loaded");

// Option 3: Wait for JavaScript condition
renderer.RenderingOptions.WaitFor.JavaScript("window.renderComplete === true");
renderer.RenderingOptions.EnableJavaScript = true;

// Option 1: Fixed delay
renderer.RenderingOptions.WaitFor.RenderDelay(500);

// Option 2: Wait for specific element (more reliable)
renderer.RenderingOptions.WaitFor.HtmlElementById("content-loaded");

// Option 3: Wait for JavaScript condition
renderer.RenderingOptions.WaitFor.JavaScript("window.renderComplete === true");
$vbLabelText   $csharpLabel

問題 3:現代 CSS 無法渲染

症狀: wkhtmltopdf 中 CSS Grid 和 Flexbox 佈局渲染不正確。

解決方案: IronPDF 的 Chromium 引擎可以正確處理現代 CSS:

// This CSS now works with IronPDF
var html = @"
<style>
    .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
    .flex { display: flex; justify-content: space-between; align-items: center; }
</style>
<div class='grid'>
    <div>Column 1</div>
    <div>Column 2</div>
    <div>Column 3</div>
</div>";

var pdf = renderer.RenderHtmlAsPdf(html);
// This CSS now works with IronPDF
var html = @"
<style>
    .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
    .flex { display: flex; justify-content: space-between; align-items: center; }
</style>
<div class='grid'>
    <div>Column 1</div>
    <div>Column 2</div>
    <div>Column 3</div>
</div>";

var pdf = renderer.RenderHtmlAsPdf(html);
$vbLabelText   $csharpLabel

問題 4:同步渲染與非同步渲染

wkhtmltopdf:包裝器是同步的,會阻塞執行緒。

解決方案: IronPDF 支援非同步渲染:

public async Task<byte[]> GeneratePdfAsync(string html)
{
    var renderer = new ChromePdfRenderer();
    var pdf = await renderer.RenderHtmlAsPdfAsync(html);
    return pdf.BinaryData;
}
public async Task<byte[]> GeneratePdfAsync(string html)
{
    var renderer = new ChromePdfRenderer();
    var pdf = await renderer.RenderHtmlAsPdfAsync(html);
    return pdf.BinaryData;
}
$vbLabelText   $csharpLabel

wkhtmltopdf 遷移清單

遷移前任務

審核您的程式碼庫,找出所有 wkhtmltopdf 的使用情況:

# Find all wkhtmltopdf references
grep -r "WkHtmlToPdfDotNet\|DinkToPdf\|TuesPechkin\|Rotativa" --include="*.cs" .
grep -r "wkhtmltopdf" --include="*.yml" --include="*.yaml" --include="Dockerfile" .
# Find all wkhtmltopdf references
grep -r "WkHtmlToPdfDotNet\|DinkToPdf\|TuesPechkin\|Rotativa" --include="*.cs" .
grep -r "wkhtmltopdf" --include="*.yml" --include="*.yaml" --include="Dockerfile" .
SHELL

尋找並記錄 wkhtmltopdf 二進位檔案以便刪除。 記錄目前設定(紙張大小、頁邊距、頁首/頁尾)。

程式碼更新任務

  1. 刪除所有 wkhtmltopdf 包裝 NuGet 套件
  2. 刪除 wkhtmltopdf 二進位(wkhtmltopdf.exe、wkhtmltox.dll)
  3. 安裝 IronPdf NuGet 套件
  4. 將命名空間導入從WkHtmlToPdfDotNet更新為IronPdf
  5. SynchronizedConverter替換為ChromePdfRenderer
  6. HtmlToPdfDocument模式轉換為直接渲染方法
  7. GlobalSettings配置更新為RenderingOptions
  8. 將邊距配置從MarginSettings轉換為單獨的屬性
  9. 更新佔位符語法( [page]{page}[toPage]{total-pages}
  10. 在啟動時加入 IronPDF 許可證初始化

遷移後測試

遷移完成後,請確認以下幾個面向:

  • PDF 輸出的視覺對比(隨著現代 CSS 支援的應用,效果應該會更好)
  • 驗證現代 CSS 渲染(CSS Grid 和 Flexbox 現在可以正常工作)
  • 測試大量使用 JavaScript 的頁面
  • 安全性掃描以確認沒有殘留的 wkhtmltopdf 二進位。
  • 用於效能比較的負載測試

安全驗證

# Scan for any remaining wkhtmltopdf artifacts
find /var/www/ -name "*wkhtmlto*" 2>/dev/null
find /usr/local/bin/ -name "*wkhtmlto*" 2>/dev/null
docker images | grep wkhtmltopdf

# Check if any process is still using it
ps aux | grep wkhtmltopdf
# Scan for any remaining wkhtmltopdf artifacts
find /var/www/ -name "*wkhtmlto*" 2>/dev/null
find /usr/local/bin/ -name "*wkhtmlto*" 2>/dev/null
docker images | grep wkhtmltopdf

# Check if any process is still using it
ps aux | grep wkhtmltopdf
SHELL

遷移到 IronPDF 的主要優勢

從 wkhtmltopdf 遷移到 IronPDF 可以帶來以下幾個關鍵優勢:

安全性: CVE-2022-35583 和所有 wkhtmltopdf 漏洞均已消除。 IronPDF 沒有已知的 CVE 漏洞,並定期收到安全性更新。

現代渲染引擎: IronPDF 使用最新的 Chromium 引擎,確保完全支援 CSS3、CSS Grid、Flexbox 和 ES6+ JavaScript。 現代框架能夠正確渲染。

簡化 API:直接渲染方法取代文件建構模式。 內建的SaveAs()方法無需手動處理位元組。

擴充功能: PDF 操作、數位簽章、PDF/A 合規性、浮水印和合併/分割操作是 wkhtmltopdf 無法提供的內建功能。

積極開發:隨著 .NET 10 和 C# 14 的普及,IronPDF 將持續更新,確保與目前和未來的 .NET 版本相容。

非同步支援:透過原生 async/await 支持,防止高負載 Web 應用程式中的執行緒阻塞。

柯蒂斯·週
技術撰稿人

Curtis Chau擁有卡爾頓大學電腦科學學士學位,專長於前端開發,精通Node.js、TypeScript、JavaScript和React。他熱衷於打造直覺美觀的使用者介面,喜歡使用現代框架,並擅長撰寫結構清晰、視覺效果出色的使用者手冊。

除了開發工作之外,柯蒂斯對物聯網 (IoT) 也抱有濃厚的興趣,致力於探索硬體和軟體整合的創新方法。閒暇時,他喜歡玩遊戲和製作 Discord 機器人,將他對科技的熱愛與創造力結合。