From 052d1a79aafe3f04078a2716f8e77d4340308383 Mon Sep 17 00:00:00 2001 From: Aum Patel <150163017+Aum-Patel1234@users.noreply.github.com> Date: Sat, 28 Feb 2026 18:34:54 +0530 Subject: [PATCH] feat(render): add PDF renderer and tests (#4491) * render: add PDF renderer and tests * render: update PDF renderer copyright header * test: add PDF rendering tests including 204 No Content in context_test.go --- context.go | 6 ++++++ context_test.go | 27 +++++++++++++++++++++++++++ render/pdf.go | 26 ++++++++++++++++++++++++++ render/render.go | 1 + render/render_test.go | 16 ++++++++++++++++ 5 files changed, 76 insertions(+) create mode 100644 render/pdf.go diff --git a/context.go b/context.go index 92fb3704..5174033e 100644 --- a/context.go +++ b/context.go @@ -1224,6 +1224,12 @@ func (c *Context) XML(code int, obj any) { c.Render(code, render.XML{Data: obj}) } +// PDF writes the given PDF binary data into the response body. +// It also sets the Content-Type as "application/pdf". +func (c *Context) PDF(code int, data []byte) { + c.Render(code, render.PDF{Data: data}) +} + // YAML serializes the given struct as YAML into the response body. func (c *Context) YAML(code int, obj any) { c.Render(code, render.YAML{Data: obj}) diff --git a/context_test.go b/context_test.go index 44db7475..ef60379d 100644 --- a/context_test.go +++ b/context_test.go @@ -1320,6 +1320,33 @@ func TestContextRenderNoContentXML(t *testing.T) { assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type")) } +// TestContextRenderPDF tests that the response is serialized as PDF +// and Content-Type is set to application/pdf +func TestContextRenderPDF(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + data := []byte("%Test pdf content") + c.PDF(http.StatusCreated, data) + + assert.Equal(t, http.StatusCreated, w.Code) + assert.Equal(t, data, w.Body.Bytes()) + assert.Equal(t, "application/pdf", w.Header().Get("Content-Type")) +} + +// Tests that no PDF is rendered if code is 204 +func TestContextRenderNoContentPDF(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + data := []byte("%Test pdf content") + c.PDF(http.StatusNoContent, data) + + assert.Equal(t, http.StatusNoContent, w.Code) + assert.Empty(t, w.Body.Bytes()) + assert.Equal(t, "application/pdf", w.Header().Get("Content-Type")) +} + // TestContextRenderString tests that the response is returned // with Content-Type set to text/plain func TestContextRenderString(t *testing.T) { diff --git a/render/pdf.go b/render/pdf.go new file mode 100644 index 00000000..04dcc1f5 --- /dev/null +++ b/render/pdf.go @@ -0,0 +1,26 @@ +// Copyright 2026 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package render + +import "net/http" + +// PDF contains the given PDF binary data. +type PDF struct { + Data []byte +} + +var pdfContentType = []string{"application/pdf"} + +// Render (PDF) writes PDF data with custom ContentType. +func (r PDF) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + _, err := w.Write(r.Data) + return err +} + +// WriteContentType (PDF) writes PDF ContentType for response. +func (r PDF) WriteContentType(w http.ResponseWriter) { + writeContentType(w, pdfContentType) +} diff --git a/render/render.go b/render/render.go index 4bdcfa23..28bc0f5d 100644 --- a/render/render.go +++ b/render/render.go @@ -31,6 +31,7 @@ var ( _ Render = (*AsciiJSON)(nil) _ Render = (*ProtoBuf)(nil) _ Render = (*TOML)(nil) + _ Render = (*PDF)(nil) ) func writeContentType(w http.ResponseWriter, value []string) { diff --git a/render/render_test.go b/render/render_test.go index b48ab3d3..f63878b9 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -466,6 +466,22 @@ func TestRenderXMLError(t *testing.T) { assert.Contains(t, err.Error(), "xml: unsupported type: chan int") } +func TestRenderPDF(t *testing.T) { + w := httptest.NewRecorder() + data := []byte("%Test pdf content") + + pdf := PDF{data} + + pdf.WriteContentType(w) + assert.Equal(t, "application/pdf", w.Header().Get("Content-Type")) + + err := pdf.Render(w) + require.NoError(t, err) + + assert.Equal(t, data, w.Body.Bytes()) + assert.Equal(t, "application/pdf", w.Header().Get("Content-Type")) +} + func TestRenderRedirect(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "/test-redirect", nil) require.NoError(t, err)