惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

宝玉的分享
宝玉的分享
Recent Commits to openclaw:main
Recent Commits to openclaw:main
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
罗磊的独立博客
V
Visual Studio Blog
爱范儿
爱范儿
H
Help Net Security
J
Java Code Geeks
I
InfoQ
Recent Announcements
Recent Announcements
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Recorded Future
Recorded Future
Jina AI
Jina AI
Microsoft Security Blog
Microsoft Security Blog
WordPress大学
WordPress大学
GbyAI
GbyAI
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Y
Y Combinator Blog
Google DeepMind News
Google DeepMind News
Scott Helme
Scott Helme
S
SegmentFault 最新的问题
S
Securelist
L
LINUX DO - 热门话题
Cyberwarzone
Cyberwarzone
C
Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
G
Google Developers Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 叶小钗
T
The Blog of Author Tim Ferriss
博客园_首页
B
Blog
F
Fortinet All Blogs
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
S
Secure Thoughts
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Forbes - Security
Forbes - Security
S
Security @ Cisco Blogs
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Schneier on Security
Project Zero
Project Zero
Martin Fowler
Martin Fowler
C
Cybersecurity and Infrastructure Security Agency CISA
N
Netflix TechBlog - Medium
N
News and Events Feed by Topic

Inside Nutrient

A guide to the invisible work behind documents Introducing Nutrient Documents for Salesforce: Native document generation and signing Document AI vs. traditional OCR: Choosing between OCR, AI, and hybrid pipelines PDF SDK compliance and security evaluation checklist for enterprise teams (2026) Invariant Corp replaces paper processes with Nutrient Workflow and scales without limits What is process mapping? A complete guide Nutrient vs. Conga Composer for Salesforce document generation (2026) Document routing: How to automate document distribution The CTO’s AI playbook: Why accountability architecture beats orchestration Compliance workflow automation: Why built-in compliance is table stakes Workflow diagrams: Examples, symbols, and how to build one that actually runs Digital forms: Replace paper forms with automated workflows Approval workflow software: How to automate approvals Why document-centric automation is different The CEO’s AI playbook: Why decision architecture beats model selection Nutrient SDK product updates for Q1 2026 PDF redaction verification: How to prove sensitive data is permanently removed What is a VPAT? The complete guide to accessibility conformance reports What is PDF/UA? The accessible PDF standard explained Salesforce eSignatures: Generate, sign, and track documents in one flow Online document viewer: Options, tradeoffs, and how to embed one Document viewer for web apps: React, Vue, Angular (2026) Best document viewers in 2026: A buyer’s guide How to edit a PDF in Python: Add text, images, and annotations Nutrient advances Workflow platform with agentic AI for enterprise-grade speed and consistency in document-heavy operations How to create a Salesforce quote template from opportunity data The business case for accessibility: Five ways it drives enterprise value Python PDF library comparison (2026): 7 libraries for developers Why your AI agent hallucinates PDF table data PDF.js limitations: When to upgrade to a commercial PDF SDK How Subject scaled 5× with Nutrient’s PDF SDK without rebuilding its document layer I replaced our sales training with an AI coach that runs in Slack — here’s what broke Redirecting to: https://securitybuzz.com/cybersecurity-news/why-enterprise-permissions-are-ais-most-dangerous-inheritance/ Nutrient .NET SDK vs. iText Core: Complete comparison for .NET developers DocuVieware: Support’s most frequently asked setup questions Introducing Nutrient Workflow How to convert PDF to Word in C# (.NET) When email and spreadsheets stop working: Work order approval workflows for field teams on the move Compliance with confidence: Why document-centric automation is the foundation of your mission Nutrient expands AI Assistant, automating multistep document workflows inside any application What is document generation? A developer’s guide to PDF generation Document Converter data flow and how real-time watermarks skip the queue PDF/UA compliance guide: Requirements, standards, and best practices Computers still can’t understand you How Athena Intelligence built AI agents for regulated enterprises with Nutrient’s document infrastructure How to convert HTML to PDF (2026): 4 methods from browser print to SDK How to build a document extraction pipeline with Nutrient Vision API OCR vs. intelligent document processing: Choosing the right document extraction engine Beyond OCR: How document intelligence eliminates manual processing in regulated industries Nutrient vs. IronPDF: Complete comparison for .NET developers Nutrient vs. Aspose.PDF: Complete comparison for .NET developers Redirecting to: https://fortune.com/2026/02/19/openclaw-who-is-peter-steinberger-openai-sam-altman-anthropic-moltbook/ Lufthansa Systems uses Nutrient to deliver reliable, scalable PDF rendering for pilots worldwide Nutrient vs. Syncfusion: Complete comparison for .NET developers React’s useTransition: The hook you’re probably using wrong First City Monument Bank streamlines banking processes with Nutrient Workflow Redirecting to: https://www.sdcexec.com/warehousing/automation/article/22957364/nutrient-workflow-automation-the-missing-link-in-supply-chain-efficiency The complete guide to digital signatures: PAdES, CAdES, and XAdES explained Nutrient Python SDK: Production-grade document processing for Python Introducing agentic document editing for web applications with AI Assistant Nutrient vs. QuestPDF: Complete comparison for .NET developers How we fixed the GdPicture license expiration (and what to do if you’re affected) Red team security testing with agentic AI The future of healthcare document automation Best healthcare workflow software compared Nutrient SDK product updates for Q4 2025 How Harvey scaled legal document workflows 50 percent MoM without rebuilding infrastructure HIPAA-compliant document management in hospitals How we optimized rendering performance while handling thousands of annotations in React — Part 2 Automated PII removal with Nutrient API Redirecting to: https://www.devopsdigest.com/2026-low-code-no-code-predictions Redirecting to: https://www.kmworld.com/Articles/Editorial/ViewPoints/Leaders-predict-AI-to-continue-permeating-all-aspects-of-KM-in-2026-172594.aspx What are deep agents and how do they solve complex problems? Whipping up document magic: Your easy-bake recipe for Vue and Nutrient Web SDK 🧁 What I’ve learned about product iteration planning while building SDKs Passwordless document signing: Three-layer security guide New zip folder functionality streamlines file management in Document Automation Server The keyboard shortcuts playbook: Taking control of keyboard events in Nutrient Web SDK From experienced engineer to AI beginner: My unexpected journey AI-assisted manual testing: Handling Safari’s PDF rendering and UI quirks How we optimized rendering performance while handling thousands of annotations in React — Part 1 Nutrient announces new executive hires to accelerate next phase of growth High performance UI using web workers Automate document conversion at scale with Python and Nutrient DCS From curiosity to PLG (and AI): My journey to understanding product-led growth Prost to progress: One year as Nutrient Pigeon usage at Nutrient: Bridging native SDKs to Flutter Modernizing CI build servers: How to migrate from Chef to Ansible Unix man pages: AI-friendly documentation since 1971 Consistent hashing for even load distribution Best AI redaction APIs: Complete comparison guide for 2025 Why AI document redaction matters for modern security From coding to coordinating: How AI transformed my workflow What is intelligent document processing (IDP)? A complete guide Enterprise PDF SDKs: Best PSPDFKit (now Nutrient) alternatives Nutrient SDK product updates for Q3 2025 GdPicture support best practices Redacting sensitive data with Nutrient AI redaction API How AI is transforming the customer experience at Nutrient: From instant answers to intelligent support How manual QA uses PR testing between releases
How to keep a 20-year-old SDK up to date
Olivier Houssin · 2025-12-08 · via Inside Nutrient

Maintaining and evolving a codebase over decades forces you to reconcile legacy assumptions with modern expectations. What started as our Windows-centric .NET Framework library wrapping GDI+ has, over the course of 20 years, become a cross-platform document SDK powering web, desktop, and embedded experiences for PDFs and Office documents. That journey required unbundling platform entanglement, replacing obsolete dependencies, building abstraction layers, and riding the wave of evolving managed runtimes and user interface (UI) platforms.

This post walks through that path: from untethering from GDI+, to adopting Skia for rendering, abstracting native runtime dependencies (including Win32), migrating the managed surface from .NET Framework to .NET Core/.NET, and finally, the pursuit of truly cross-platform viewers via MAUI. Along the way — driven by customer demand for Linux and web deployments — we’ve balanced compatibility, performance, and developer ergonomics, because keeping a 20-year-old SDK healthy isn’t about rewriting everything from scratch; it’s about applying the right incremental evolution at the right layers.

From Windows graphics to cross-platform rendering

Achieving cross-platform compatibility meant replacing the Windows-locked graphics layer with a portable rendering engine that could perform consistently across all target platforms.

A bit of history: GDI and GDI+

Our original .NET SDK was deeply rooted in the Windows graphics stack, specifically GDI+ (Graphics Device Interface Plus). GDI+ is the successor to the original GDI — Microsoft’s legacy system for representing graphical objects and sending them to output devices like screens and printers. It exposed drawing primitives, text rendering, and image manipulation through a C++ class-based API and was the default for new Windows applications when it replaced the more primitive GDI. Over time, while still supported for backward compatibility, newer rendering layers such as Direct2D and hardware-accelerated pipelines became preferred for modern workloads. Relying on GDI+ implicitly tethered the SDK to Windows and limited its ability to scale beyond that ecosystem.

That tight coupling shaped everything; drawing, layout, and even some higher-level document rendering logic expected the semantics and behaviors of GDI+, making any cross-platform ambitions brittle without a fundamental change.

Untying from GDI+: Choosing Skia

To become truly cross-platform, the first major step was to untie the SDK from GDI+. Microsoft’s own investment patterns (e.g. pushing toward newer graphics primitives and the increasing stagnation around GDI+ portability) and internal signals — like preference for Skia in areas adjacent to our renderer — made it clear the future lay elsewhere. We decided to migrate all existing surface drawing calls — more than 70 public API entry points — from GDI+ to Skia, which we were already using in parts of our PDF renderer, giving us confidence in its stability. Skia is an open source, high-performance 2D graphics engine originally developed by Google. It’s designed for cross-platform consistency and powers rendering in Chrome, Flutter, and many other projects.

The migration involved a substantial refactor. APIs that implicitly assumed GDI+ state, coordinate system quirks, and resource lifetimes had to be rethought. But Skia brought advantages that justified the effort:

  1. True cross-platform rendering — Without platform-specific fallbacks
  2. Consistent look and behavior — Across devices, because Skia implements its own backend logic rather than relying on fragmentary native toolkits
  3. Modern performance characteristics — With GPU acceleration where available, and well-tested fallbacks on constrained environments

The switch also future-proofed the SDK: Skia’s ecosystem includes ports to emerging targets (including WebAssembly through integration projects), which opened doors we didn’t have when we were locked to GDI+.

Native runtimes, Win32 dependencies, and platform abstraction

The rendering layer wasn’t the only Windows dependency. Performance-sensitive operations relied on native C++ code tightly coupled to Win32 APIs, requiring a careful abstraction approach to enable cross-platform execution.

Why we relied on C++ runtime libraries

For some of the most performance-sensitive and low-level operations — especially small, hot API calls or CPU-intensive document processing tasks — the managed layer (C# code running on .NET’s runtime) wasn’t sufficient. We built and maintained runtime libraries in C++ that encapsulated optimized implementations, often exposing functionality with heavy dependency on the Win32 API. The Win32 API (also called the Windows API) is the core set of interfaces Microsoft exposes for interacting with the operating system, including GUI/window management, drawing primitives, file system access, and threading. Historically, high-performance GUI applications and system services on Windows were built atop Win32 directly or via thin layers, giving fine-grained control but encoding platform assumptions deeply into the code.

These native runtimes packaged performance-critical code — optimized math routines, memory helpers, and system integrations — that weren’t feasible or efficient in managed C#. Historically they leaned heavily on Win32, which gave us the low-level control and predictable behavior we needed on Windows, but also deeply tied those implementations to that platform.

Abstracting the platform

The combination of deeply embedded Win32 calls and C++ runtime dependencies presented the next portability hurdle. To break the Windows ownership while preserving performance, we designed a small abstraction framework around the native runtime layer. Its goals were:

  • Detect and encapsulate platform-specifics — e.g. Win32 windowing or file handles behind a uniform interface
  • Provide alternative implementations — Or shims on non-Windows platforms without leaking Windows idioms upward
  • Allow incremental porting — Existing consumers of the runtime libraries could continue calling the same APIs while the underlying implementations diverged per OS

With this abstraction in place, the native runtimes were ported and compiled for Linux, macOS, ARM, and even WebAssembly, allowing the same core engine to run in environments far from its original Windows-only roots. The payoff: The SDK could ship on any platform, and in some cases (like the web) run in constrained sandboxes. For example, Nutrient Web SDK leverages this portability to render Office documents inside a browser without any Office dependency — relying on the ported native runtimes to drive rendering logic that used to assume Windows.

Migrating the managed surface: .NET Framework to .NET Core and beyond

Decoupling the managed code from Windows-specific APIs was the next major frontier. Early versions of the SDK were built against the full .NET Framework, which exposed many Windows-first APIs (e.g. WinForms, certain registry or configuration helpers, and some interop assumptions) that didn’t exist or behaved differently on .NET Core. Our first cross-platform managed runtime target was .NET Core 3.1, which, while stable at the time, lacked many of the higher-level framework conveniences developers had grown used to, making the migration both a technical and conditioning challenge.

Key efforts included:

  • Identifying and isolating Windows-only calls — e.g. parts of WinForms hosting, legacy threading models, or P/Invoke patterns that presumed certain behaviors, and replacing or abstracting them
  • Rewriting compatibility layers — Where no direct substitution existed, using conditional compilation and interface-driven design so that fallbacks could be swapped depending on the runtime
  • Adapting to the evolving .NET SDK — As .NET improved, particularly with .NET 6 and 7 and the quality/performance investments in MAUI and the underlying runtime, the surface area for portability increased, and we were able to revisit earlier compromises

Thanks to those improvements in the broader .NET ecosystem, we also successfully ported our Windows-specific viewers, including WinForms and WPF-based components, to newer SDKs, preserving the familiar experiences for Windows users while enabling a parallel path forward.

What’s next: Bringing the viewer everywhere with .NET MAUI

WinForms and WPF remain, as of today, largely Windows-centric. To offer our document viewer component (GdViewer) consistently across all platforms — not just desktop Windows — we initiated a port onto .NET MAUI, which is Microsoft’s unified framework for building native cross-platform applications targeting Windows, macOS, iOS, and Android from a single C#/.NET codebase. It extends the ideas from Xamarin.Forms with tighter integration into modern .NET SDKs, single project support, and improved tooling.

Advantages of MAUI include:

  • Single project targeting multiple OSes — With shared UI definitions and access to native controls
  • Native performance and look and feel — Due to its handlers/mapper architecture
  • Improved tooling and hot reload — Compared to previous cross-platform attempts, accelerating developer feedback loops
  • Convergence with the modern .NET ecosystem — Benefiting from performance and quality improvements baked into .NET 8 and beyond

However, the community is still navigating some growing pains. There are lingering concerns around memory leaks in long-running MAUI applications, which can surface as sluggishness or stability issues if not carefully diagnosed and mitigated — problems that persist even into 2025 and require disciplined patterns and tooling to resolve. Discussions among practitioners reflect a cautious optimism: The framework’s current state shows meaningful maturity from earlier rocky transition periods, but edge cases — especially for UI-heavy or complex app scenarios — still prompt questions about viability and best practices.

Choosing the right evolution path

Looking back, the modernization of the SDK wasn’t a single big rewrite; it was a series of deliberate decouplings and migrations, each with its own criteria:

  • Rendering dependency — Replace platform-locked graphics (GDI+) with a portable engine (Skia) when cross-platform reach is a priority
  • Native performance hotspots — Encapsulate and abstract native runtime logic so that optimized code can evolve per platform without leaking assumptions
  • Managed runtime drift — Layer Windows-specific .NET APIs behind interfaces to allow swapping implementations as the underlying framework evolves
  • UI/viewer delivery — Move from Windows-bound UI frameworks to cross-platform ones (MAUI) once the ecosystem has stabilized sufficiently to support your feature set and performance needs

Each step preserved existing investments while opening new deployment targets; the guiding principle was always isolate, abstract, then iterate.

Putting it to work at Nutrient

At Nutrient, this evolution has tangible downstream effects. Our SDKs — such as the .NET SDK and the Web SDK — now share core rendering logic despite running in environments as different as desktop Windows, browsers (via WebAssembly), and mobile devices. The layered portability means that our engineers can build viewers and document pipelines once and confidently deploy them across clients, reducing duplication, surface area for bugs, and maintenance overhead. The approach also aligns with our broader philosophy: push complexity into well-tested layers so product teams can focus on experience rather than plumbing.

Add document viewing, editing, and processing to your .NET applications with Nutrient’s battle-tested, cross-platform SDK.

Conclusion

A 20-year-old codebase doesn’t become modern by erasing its past; it becomes resilient by gradually replacing brittle assumptions with well-bounded abstractions. Untying from GDI+ with Skia, encapsulating Win32 and native runtime logic behind portable facades, migrating the managed surface through .NET’s evolution, and embracing MAUI for cross-platform delivery — each of these moves was a vote for longevity over short-term convenience.

The next time a legacy dependency limits where you ship or how fast you iterate, ask yourself: Can the platform be abstracted? Can the contract be preserved while the implementation modernizes? Keeping a long-lived SDK up to date isn’t about chasing the newest shiny thing; it’s about choosing the right inflection points and letting the system evolve in layers. Automate fragmentation away, and let your SDK serve future platforms without rewriting its soul.

Building cross-platform document features? Nutrient’s SDKs handle the complexity so you can focus on your application — whether you’re targeting .NET, web, or mobile.