using IronPdf;
// Disable local disk access or cross-origin requests
Installation.EnableWebSecurity = true;
// Instantiate Renderer
var renderer = new ChromePdfRenderer();
// Create a PDF from a HTML string using C#
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1>");
// Export to a file or Stream
pdf.SaveAs("output.pdf");
// Advanced Example with HTML Assets
// Load external html assets: Images, CSS and JavaScript.
// An optional BasePath 'C:\site\assets\' is set as the file location to load assets from
var myAdvancedPdf = renderer.RenderHtmlAsPdf("<img src='icons/iron.png'>", @"C:\site\assets\");
myAdvancedPdf.SaveAs("html-with-assets.pdf");
Migracja zSyncfusion PDFFramework do IronPDF przekształca przepływ pracy generacji PDF z opartego na współrzędnych API grafiki zawartego w dużej Suite na samodzielną bibliotekę skoncentrowaną na HTML/CSS z nowoczesnym silnikiem renderującym Chromium. Ten przewodnik oferuje pełną, szczegółową ścieżkę migracji eliminującą licencjonowanie tylko na suite, złożone wymogi dotyczące wdrożenia i pozycjonowanie oparte na współrzędnych.
Dlaczego migrować zSyncfusion PDFdo IronPDF
ZrozumienieSyncfusion PDFFramework
Syncfusion PDF Framework to kompleksowa biblioteka, która zapewnia szeroki zakres funkcjonalności do tworzenia, edytowania i zabezpieczania dokumentów PDF używając C#. Jest częścią Essential Studio firmy Syncfusion, które zawiera ponad tysiąc komponentów na wielu platformach.
Biblioteka PDF nie może być zakupiona jako pojedynczy komponent.Syncfusionoferuje obecnie poziom "Document SDK", który obejmuje biblioteki PDF, Word, Excel i PowerPoint bez komponentów UI, obok pełnej Suite Essential Studio. Zespoły, które potrzebują tylko funkcjonalności PDF, muszą wziąć minimum Document SDK, co może być uciążliwe, kiedy inne biblioteki dokumentów nie są używane.
Problem z licencjonowaniem pakietu
Model licencyjny firmySyncfusionstwarza poważne wyzwania dla zespołów, które potrzebują wyłącznie funkcji związanych z plikami PDF:
Brak zakupu na bibliotekę: PDF jest zintegrowany z Document SDK (PDF + Word + Excel + PowerPoint) lub pełnym Essential Studio — nie może być licencjonowany samodzielnie
Ograniczenia licencji społecznościowej: Darmowa wersja wymaga rocznego obrotu < $1M, ≤ 5 deweloperów I ≤ 10 łącznie pracowników, plus maksymalna kwota 3M $ na zewnętrzny kapitał
Złożone licencjonowanie wdrożeń: Różne licencje dla wdrożeń w sieci, na desktopie, na serwerze
Wymagana coroczna odnowa: Model subskrypcyjny z kosztami rocznymi (minimalny okres 1 rok)
Ceny za dewelopera: Koszty rosną liniowo wraz z rozmiarem zespołu
Nadmierna pakietowość: Dokument SDK wciąga zależności Word/Excel/PowerPoint, nawet jeśli potrzebujesz tylko PDF; pełne Essential Studio dodaje ponad 1000 komponentów UI na wierzch
PorównanieSyncfusion PDFvs IronPDF
Aspekt
Syncfusion PDF
IronPDF
Model zakupu
Pakiet Dokument SDK lub pełne Essential Studio (brak SKU na bibliotekę)
Samodzielny
Licencjonowanie
Złożone warstwy
Prosty dla każdego programisty
Limit społeczności
<$1M revenue, ≤5 devs, ≤10 employees, ≤$3M outside capital
Bezpłatna wersja próbna, a następnie licencja
Wdrożenie
Różne rodzaje licencji
Jedna licencja obejmuje wszystkie
Styl API
Grafika oparta na współrzędnych + konwerter HTML
HTML/CSS-najpierw
Silnik HTML
Blink (oddzielne NuGets dla Windows/Linux/Mac)
Bundled Chromium
Obsługa CSS
Nowoczesny (Blink) — zbliżona do parytetu z Chromium
Pełna obsługa CSS3/flexbox/grid
Zależności
Wiele pakietów
Pojedynczy NuGet
Wymóg pakietu
Tak (Dokument SDK lub Essential Studio)
Nie
Skup się na pliku PDF
Ogólne; część większego pakietu
Wąski; Skupione na PDF
IronPDF oferuje bardziej skoncentrowane podejście, oferując swoje możliwości PDF jako samodzielny produkt. Ta różnica wyraźnie wpływa na rozważania kosztów i łatwość integracji.
Dla zespołów przyjmujących nowoczesne .NET, samodzielne licencjonowanie i podejścieHTML/CSS-najpierww IronPDF zapewnia elastyczność bez zależności od pakietu.
// Must register before anySyncfusioncalls
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("YOUR-SYNCFUSION-KEY");
// Must register before anySyncfusioncalls
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("YOUR-SYNCFUSION-KEY");
' Must register before any Syncfusion calls
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("YOUR-SYNCFUSION-KEY")
$vbLabelText $csharpLabel
IronPDF:
// One-time at startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
// One-time at startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
' One-time at startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY"
$vbLabelText $csharpLabel
Kompletna dokumentacija API
Zmiany w przestrzeni nazw
// Before:SyncfusionPDF
using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;
using Syncfusion.Pdf.Parsing;
using Syncfusion.HtmlConverter;
using Syncfusion.Drawing;
// After: IronPDF
using IronPdf;
using IronPdf.Rendering;
// Before:SyncfusionPDF
using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;
using Syncfusion.Pdf.Parsing;
using Syncfusion.HtmlConverter;
using Syncfusion.Drawing;
// After: IronPDF
using IronPdf;
using IronPdf.Rendering;
Imports IronPdf
Imports IronPdf.Rendering
$vbLabelText $csharpLabel
Mapowania podstawowych interfejsów API
Syncfusion
IronPDF
PdfDocument
ChromePdfRenderer
PdfLoadedDocument
PdfDocument.FromFile()
HtmlToPdfConverter
ChromePdfRenderer
graphics.DrawString()
Elementy tekstowe HTML
graphics.DrawImage()
<img> tag
PdfGrid
HTML
PdfStandardFont
CSS font-family
PdfBrushes.Black
CSS color: black
document.Security
pdf.SecuritySettings
PdfTextExtractor
pdf.ExtractAllText()
ImportPageRange()
PdfDocument.Merge()
document.Save(stream)
pdf.SaveAs(path)
document.Close(true)
Nie jest potrzebne
Przykłady migracji kodu
Przykład 1: Konwersja HTML/URL do PDF
Before (Syncfusion PDF):
// NuGet: Install-Package Syncfusion.Pdf.Net.Core
using Syncfusion.HtmlConverter;
using Syncfusion.Pdf;
using System.IO;
class Program
{
static void Main()
{
// Initialize HTML to PDF converter
HtmlToPdfConverter htmlConverter = new HtmlToPdfConverter();
// Convert URL to PDF
PdfDocument document = htmlConverter.Convert("https://www.example.com");
// Save the document
FileStream fileStream = new FileStream("Output.pdf", FileMode.Create);
document.Save(fileStream);
document.Close(true);
fileStream.Close();
}
}
// NuGet: Install-Package Syncfusion.Pdf.Net.Core
using Syncfusion.HtmlConverter;
using Syncfusion.Pdf;
using System.IO;
class Program
{
static void Main()
{
// Initialize HTML to PDF converter
HtmlToPdfConverter htmlConverter = new HtmlToPdfConverter();
// Convert URL to PDF
PdfDocument document = htmlConverter.Convert("https://www.example.com");
// Save the document
FileStream fileStream = new FileStream("Output.pdf", FileMode.Create);
document.Save(fileStream);
document.Close(true);
fileStream.Close();
}
}
Imports Syncfusion.HtmlConverter
Imports Syncfusion.Pdf
Imports System.IO
Module Program
Sub Main()
' Initialize HTML to PDF converter
Dim htmlConverter As New HtmlToPdfConverter()
' Convert URL to PDF
Dim document As PdfDocument = htmlConverter.Convert("https://www.example.com")
' Save the document
Dim fileStream As New FileStream("Output.pdf", FileMode.Create)
document.Save(fileStream)
document.Close(True)
fileStream.Close()
End Sub
End Module
$vbLabelText $csharpLabel
Po (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main()
{
// Create a PDF from a URL
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
// Save the PDF
pdf.SaveAs("Output.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main()
{
// Create a PDF from a URL
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
// Save the PDF
pdf.SaveAs("Output.pdf");
}
}
Imports IronPdf
Class Program
Shared Sub Main()
' Create a PDF from a URL
Dim renderer = New ChromePdfRenderer()
Dim pdf = renderer.RenderUrlAsPdf("https://www.example.com")
' Save the PDF
pdf.SaveAs("Output.pdf")
End Sub
End Class
$vbLabelText $csharpLabel
Ten przykład demonstruje podstawowe różnice w API.Syncfusion PDFwymaga instancji HtmlToPdfConverter, wywołania Convert(), które zwraca PdfDocument, następnie ręcznego utworzenia FileStream, zapisania i zamknięcia zarówno dokumentu, jak i strumienia.
IronPDF używa ChromePdfRenderer z RenderUrlAsPdf() w zaledwie trzech liniach kodu. Brak zarządzania FileStream, brak wywołań Close() —IronPDF automatycznie obsługuje czyszczenie. Kompleksowe przykłady można znaleźć w dokumentacji dotyczącej konwersji HTML do PDF.
Przykład 2: Tworzenie PDF z tekstu
Before (Syncfusion PDF):
// NuGet: Install-Package Syncfusion.Pdf.Net.Core
using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;
using Syncfusion.Drawing;
using System.IO;
class Program
{
static void Main()
{
// Create a new PDF document
PdfDocument document = new PdfDocument();
// Add a page
PdfPage page = document.Pages.Add();
// Create a font
PdfFont font = new PdfStandardFont(PdfFontFamily.Helvetica, 12);
// Draw text
page.Graphics.DrawString("Hello, World!", font, PdfBrushes.Black, new PointF(10, 10));
// Save the document
FileStream fileStream = new FileStream("Output.pdf", FileMode.Create);
document.Save(fileStream);
document.Close(true);
fileStream.Close();
}
}
// NuGet: Install-Package Syncfusion.Pdf.Net.Core
using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;
using Syncfusion.Drawing;
using System.IO;
class Program
{
static void Main()
{
// Create a new PDF document
PdfDocument document = new PdfDocument();
// Add a page
PdfPage page = document.Pages.Add();
// Create a font
PdfFont font = new PdfStandardFont(PdfFontFamily.Helvetica, 12);
// Draw text
page.Graphics.DrawString("Hello, World!", font, PdfBrushes.Black, new PointF(10, 10));
// Save the document
FileStream fileStream = new FileStream("Output.pdf", FileMode.Create);
document.Save(fileStream);
document.Close(true);
fileStream.Close();
}
}
Imports Syncfusion.Pdf
Imports Syncfusion.Pdf.Graphics
Imports Syncfusion.Drawing
Imports System.IO
Module Program
Sub Main()
' Create a new PDF document
Dim document As New PdfDocument()
' Add a page
Dim page As PdfPage = document.Pages.Add()
' Create a font
Dim font As PdfFont = New PdfStandardFont(PdfFontFamily.Helvetica, 12)
' Draw text
page.Graphics.DrawString("Hello, World!", font, PdfBrushes.Black, New PointF(10, 10))
' Save the document
Dim fileStream As New FileStream("Output.pdf", FileMode.Create)
document.Save(fileStream)
document.Close(True)
fileStream.Close()
End Sub
End Module
$vbLabelText $csharpLabel
Po (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
class Program
{
static void Main()
{
// Create a PDF from HTML string
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello, World!</h1>");
// Save the document
pdf.SaveAs("Output.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
class Program
{
static void Main()
{
// Create a PDF from HTML string
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello, World!</h1>");
// Save the document
pdf.SaveAs("Output.pdf");
}
}
Imports IronPdf
Imports IronPdf.Rendering
Class Program
Shared Sub Main()
' Create a PDF from HTML string
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf("<h1>Hello, World!</h1>")
' Save the document
pdf.SaveAs("Output.pdf")
End Sub
End Class
$vbLabelText $csharpLabel
Syncfusion PDF używa modelu graficznego opartego na współrzędnych. Tworzysz PdfDocument, dodajesz PdfPage, tworzysz PdfFont z PdfFontFamily.Helvetica, a następnie wywołujesz page.Graphics.DrawString() z wyraźnymi współrzędnymi (new PointF(10, 10)), czcionką i pędzlem (PdfBrushes.Black). Na koniec, zarządzasz utworzeniem i zniszczeniem FileStream.
IronPDF stosuje podejście oparte na HTML/CSS. Instead of coordinates, you write <h1>Hello, World!</h1> and let CSS handle positioning, fonts, and colors. Takie podejście jest prostsze, łatwiejsze w utrzymaniu i wykorzystuje umiejętności, które programiści już posiadają. Dowiedz się więcej z naszych samouczków.
Przykład 3: Łączenie dokumentów PDF
Before (Syncfusion PDF):
// NuGet: Install-Package Syncfusion.Pdf.Net.Core
using Syncfusion.Pdf;
using Syncfusion.Pdf.Parsing;
using System.IO;
class Program
{
static void Main()
{
// Load the first PDF document
FileStream stream1 = new FileStream("Document1.pdf", FileMode.Open, FileAccess.Read);
PdfLoadedDocument loadedDocument1 = new PdfLoadedDocument(stream1);
// Load the second PDF document
FileStream stream2 = new FileStream("Document2.pdf", FileMode.Open, FileAccess.Read);
PdfLoadedDocument loadedDocument2 = new PdfLoadedDocument(stream2);
// Merge the documents
PdfDocument finalDocument = new PdfDocument();
finalDocument.ImportPageRange(loadedDocument1, 0, loadedDocument1.Pages.Count - 1);
finalDocument.ImportPageRange(loadedDocument2, 0, loadedDocument2.Pages.Count - 1);
// Save the merged document
FileStream outputStream = new FileStream("Merged.pdf", FileMode.Create);
finalDocument.Save(outputStream);
// Close all documents
finalDocument.Close(true);
loadedDocument1.Close(true);
loadedDocument2.Close(true);
stream1.Close();
stream2.Close();
outputStream.Close();
}
}
// NuGet: Install-Package Syncfusion.Pdf.Net.Core
using Syncfusion.Pdf;
using Syncfusion.Pdf.Parsing;
using System.IO;
class Program
{
static void Main()
{
// Load the first PDF document
FileStream stream1 = new FileStream("Document1.pdf", FileMode.Open, FileAccess.Read);
PdfLoadedDocument loadedDocument1 = new PdfLoadedDocument(stream1);
// Load the second PDF document
FileStream stream2 = new FileStream("Document2.pdf", FileMode.Open, FileAccess.Read);
PdfLoadedDocument loadedDocument2 = new PdfLoadedDocument(stream2);
// Merge the documents
PdfDocument finalDocument = new PdfDocument();
finalDocument.ImportPageRange(loadedDocument1, 0, loadedDocument1.Pages.Count - 1);
finalDocument.ImportPageRange(loadedDocument2, 0, loadedDocument2.Pages.Count - 1);
// Save the merged document
FileStream outputStream = new FileStream("Merged.pdf", FileMode.Create);
finalDocument.Save(outputStream);
// Close all documents
finalDocument.Close(true);
loadedDocument1.Close(true);
loadedDocument2.Close(true);
stream1.Close();
stream2.Close();
outputStream.Close();
}
}
Imports Syncfusion.Pdf
Imports Syncfusion.Pdf.Parsing
Imports System.IO
Class Program
Shared Sub Main()
' Load the first PDF document
Dim stream1 As New FileStream("Document1.pdf", FileMode.Open, FileAccess.Read)
Dim loadedDocument1 As New PdfLoadedDocument(stream1)
' Load the second PDF document
Dim stream2 As New FileStream("Document2.pdf", FileMode.Open, FileAccess.Read)
Dim loadedDocument2 As New PdfLoadedDocument(stream2)
' Merge the documents
Dim finalDocument As New PdfDocument()
finalDocument.ImportPageRange(loadedDocument1, 0, loadedDocument1.Pages.Count - 1)
finalDocument.ImportPageRange(loadedDocument2, 0, loadedDocument2.Pages.Count - 1)
' Save the merged document
Dim outputStream As New FileStream("Merged.pdf", FileMode.Create)
finalDocument.Save(outputStream)
' Close all documents
finalDocument.Close(True)
loadedDocument1.Close(True)
loadedDocument2.Close(True)
stream1.Close()
stream2.Close()
outputStream.Close()
End Sub
End Class
$vbLabelText $csharpLabel
Po (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using System.Collections.Generic;
class Program
{
static void Main()
{
// Load PDF documents
var pdf1 = PdfDocument.FromFile("Document1.pdf");
var pdf2 = PdfDocument.FromFile("Document2.pdf");
// Merge PDFs
var merged = PdfDocument.Merge(new List<PdfDocument> { pdf1, pdf2 });
// Save the merged document
merged.SaveAs("Merged.pdf");
}
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System.Collections.Generic;
class Program
{
static void Main()
{
// Load PDF documents
var pdf1 = PdfDocument.FromFile("Document1.pdf");
var pdf2 = PdfDocument.FromFile("Document2.pdf");
// Merge PDFs
var merged = PdfDocument.Merge(new List<PdfDocument> { pdf1, pdf2 });
// Save the merged document
merged.SaveAs("Merged.pdf");
}
}
Imports IronPdf
Imports System.Collections.Generic
Class Program
Shared Sub Main()
' Load PDF documents
Dim pdf1 = PdfDocument.FromFile("Document1.pdf")
Dim pdf2 = PdfDocument.FromFile("Document2.pdf")
' Merge PDFs
Dim merged = PdfDocument.Merge(New List(Of PdfDocument) From {pdf1, pdf2})
' Save the merged document
merged.SaveAs("Merged.pdf")
End Sub
End Class
$vbLabelText $csharpLabel
Różnica w łączeniu plików PDF jest ogromna.Syncfusion PDFwymaga utworzenia obiektów FileStream dla każdego dokumentu wejściowego, załadowania ich jako PdfLoadedDocument, utworzenia nowego PdfDocument, wywołania ImportPageRange() ze startowymi i końcowymi indeksami dla każdego źródła, utworzenia wyjścia FileStream, a następnie zamknięcia sześciu oddzielnych obiektów (finalDocument, loadedDocument1, loadedDocument2, stream1, stream2, outputStream).
IronPDF używa PdfDocument.FromFile() do załadowania każdego PDF i statycznej metody PdfDocument.Merge(), która przyjmuje listę dokumentów. Bez zarządzania strumieniem, bez ręcznego obliczania zakresu stron, bez wątpliwych przypadków.
Kluczowe różnice w filozofii API
Podejście oparte na współrzędnych a podejście "HTML/CSS-First"
Syncfusion PDF wykorzystuje model graficzny oparty na współrzędnych, odziedziczony po tradycyjnych bibliotekach PDF:
// Syncfusion: Podręcznik positioning
page.Graphics.DrawString("Text", font, PdfBrushes.Black, new PointF(100, 200));
page.Graphics.DrawRectangle(brush, new RectangleF(50, 50, 200, 100));
// Syncfusion: Podręcznik positioning
page.Graphics.DrawString("Text", font, PdfBrushes.Black, new PointF(100, 200));
page.Graphics.DrawRectangle(brush, new RectangleF(50, 50, 200, 100));
' Syncfusion: Podręcznik positioning
page.Graphics.DrawString("Text", font, PdfBrushes.Black, New PointF(100, 200))
page.Graphics.DrawRectangle(brush, New RectangleF(50, 50, 200, 100))
$vbLabelText $csharpLabel
IronPDF wykorzystuje HTML/CSS do tworzenia układu:
// IronPDF: CSS-based positioning
var html = @"
<div style='margin: 50px; padding: 20px; border: 1px solid black;'>
<p style='color: black;'>Text</p>
</div>";
var pdf = renderer.RenderHtmlAsPdf(html);
// IronPDF: CSS-based positioning
var html = @"
<div style='margin: 50px; padding: 20px; border: 1px solid black;'>
<p style='color: black;'>Text</p>
</div>";
var pdf = renderer.RenderHtmlAsPdf(html);
' IronPDF: CSS-based positioning
Dim html As String = "
<div style='margin: 50px; padding: 20px; border: 1px solid black;'>
<p style='color: black;'>Text</p>
</div>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
$vbLabelText $csharpLabel
Podejście oparte na HTML/CSS jest bardziej intuicyjne dla programistów stron internetowych, łatwiejsze w utrzymaniu i zapewnia spójne wyniki na stronach o różnych rozmiarach.
Zarządzanie strumieniem a automatyczne czyszczenie
Syncfusion PDF wymaga jawnego usuwania strumieni i dokumentów:
' Syncfusion: Podręcznik cleanup
Dim fileStream As New FileStream("Output.pdf", FileMode.Create)
document.Save(fileStream)
document.Close(True)
fileStream.Close()
Aktualizacja importów przestrzeni nazw (using Syncfusion.Pdf; → using IronPdf;)
Zastąpienie Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense() z IronPdf.License.LicenseKey = "..."
Zastąpienie HtmlToPdfConverter.Convert() z ChromePdfRenderer.RenderUrlAsPdf() lub RenderHtmlAsPdf()
Zastąpienie PdfDocument + Pages.Add() + Graphics.DrawString() z ChromePdfRenderer.RenderHtmlAsPdf()
Zastąpienie PdfLoadedDocument z PdfDocument.FromFile()
Zastąpienie ImportPageRange() z PdfDocument.Merge()
Zastąpienie document.Save(stream) z pdf.SaveAs(path)
Usunięcie wszystkich wywołań document.Close(true) i stream.Close()
Zastąpienie PdfGrid z elementami HTML
Zastąpienie PdfStandardFont z CSS font-family
Zastąpienie PdfBrushes z właściwościami CSS color
Testowanie
Wizualne porównanie pliku PDF
Sprawdź ulepszenia w renderowaniu CSS (flexbox i grid już działają)
Wyodrębnianie tekstu testowego
Testowanie łączenia i dzielenia
Porównanie wydajności
Zwróć uwagęSyncfusion jest zastrzeżonym znakiem towarowym odpowiedniego właściciela. Ta strona nie jest powiązana z firmą Syncfusion, nie jest przez nią promowana ani sponsorowana. Wszystkie nazwy produktów, logo i marki są własnością ich odpowiednich właścicieli. Porównania mają charakter wyłącznie informacyjny i odzwierciedlają informacje dostępne publicznie w momencie pisania.
Curtis Chau posiada tytuł licencjata z informatyki (Uniwersytet Carleton) i specjalizuje się w front-endowym rozwoju, z ekspertką w Node.js, TypeScript, JavaScript i React. Pasjonuje się tworzeniem intuicyjnych i estetycznie przyjemnych interfejsów użytkownika, Curtis cieszy się pracą z nowoczesnymi frameworkami i tworzeniem dobrze zorganizowanych, atrakcyjnych wizualnie podrę...