Giter Club home page Giter Club logo

gopdf's Introduction

race result AG is always looking for smart, passionate and hard-working Golang and Javascript developers with both strong attention to the detail and an entrepreneurial approach to tasks.

We are a Germany-based company building technology for timing sports events such London Marathon, Challenge Roth, Tour Down Under and thousands of other races in more than 80 countries. Check out www.raceresult.com and karriere.raceresult.com for more information.

gopdf - Free PDF creator in pure Golang

Go code (golang) to create PDF documents with several layers of abstraction that allow both, easy placement of elements on pages and accessing lower layers to implement any type of PDF object / operator.

Does support Composite Fonts for full Unicode support.

Getting Started

The highest level of abstraction is provided by the gopdf package. Simply create a new Builder object, add pages and add elements to the pages:

package yours

import (
    "github.com/raceresult/gopdf"
    "github.com/raceresult/gopdf/types"
)

func TestExample1(t *testing.T) {
    // create new PDF Builder
    pb := gopdf.New()
    
    // use a built-in standard fontm
    f, err := pb.NewStandardFont(types.StandardFont_Helvetica, types.EncodingWinAnsi)
    if err != nil {
        t.Error(err)
        return
    }
    
    // add first page
    p := pb.NewPage(gopdf.GetStandardPageSize(gopdf.PageSizeA4, false))
    
    // add "hello world" element
	p.AddElement(&gopdf.TextElement{
		TextChunk: gopdf.TextChunk{
			Text:     "hello world",
			Font:     f,
			FontSize: 36,
		},
		Left:      gopdf.MM(105),
		Top:       gopdf.MM(100),
		TextAlign: gopdf.HorizontalAlignCenter,
	})
    
    // output
    bts, err := pb.Build()
    ...
}

More advanced: let's add an image, a rectangle and a text using a composite font. Using a composite font, any unicode character can be mapped to any glyph in one or more fonts. Non-composite fonts, on the contrary, only support 256 different characters, so in a world of UTF-8/Unicode, composite fonts are the only thing you want to use. Character encoding and subsetting (embed fonts reduced to only those characters you are using) is quite sophisticated but completely handled by the pdf package. When using Composite Fonts you only need to handover normal (UTF-8 encoded) Go strings.

package yours

import (
    "github.com/raceresult/gopdf/builder"
    "github.com/raceresult/gopdf/types"
)

func TestExample2(t *testing.T) {
    // create new PDF Builder
    pb := gopdf.New()
    
    // add first page
    p := pb.NewPage(gopdf.GetStandardPageSize(gopdf.PageSizeA4, false))
    
    // add image
    bts, err := ioutil.ReadFile("myImage.jpg")
    if err != nil {
        t.Error(err)
        return
    }
    img, err := pb.NewImage(bts)
    if err != nil {
        t.Error(err)
        return
    }
    p.AddElement(&gopdf.ImageElement{
        Width:  gopdf.MM(70),
        Height: gopdf.MM(70 * float64(img.Image.Height) / float64(img.Image.Width)),
        Left:   gopdf.MM(20),
        Top:    gopdf.MM(20),
        Img:    img,
    })
    
    // add rectangle
	p.AddElement(&gopdf.TextElement{
		TextChunk: gopdf.TextChunk{
			Text:         "hello world - 漢語",
			Font:         f,
			FontSize:     36,
			Color:        gopdf.NewColorRGB(200, 200, 200),
			OutlineColor: gopdf.NewColorRGB(10, 20, 10),
			OutlineWidth: gopdf.MM(0.5),
		},
		Left: gopdf.MM(20),
		Top:  gopdf.MM(100),
	})
    
    // add composite font
    bts, err = ioutil.ReadFile("arialuni.ttf")
    if err != nil {
        t.Error(err)
        return
    }
    f, err := pb.NewCompositeFont(bts)
    if err != nil {
        t.Error(err)
        return
    }
    
    // add text using composite font
    p.AddElement(&gopdf.TextElement{
        Text:         "hello world - 漢語",
        Font:         f,
        FontSize:     36,
        Left:         gopdf.MM(20),
        Top:          gopdf.MM(100),
        Color:        gopdf.ColorRGB{200, 200, 200},
        OutlineColor: gopdf.ColorRGB{10, 20, 10},
        OutlineWidth: gopdf.MM(0.5),
    })
    
    
    // output
    bts, err := pb.Build()
    ...
}

This way, the builder supports the following element types with various attributes:

  • images (supports jpg, png, gif, bmp)
  • lines
  • rectangles (filled or unfilled)
  • texts
  • textboxes (text with max width, opt. max height, word wrap, ..)

Advanced: Add your own functionality

The following section describes how to go one level deeper and add custom functionality that is not yet provided by the builder package.

First of all, a quick overview of the PDF file format: PDF files are a set of "objects" that are referenced by other objects. For example, the root is the "Document Catalog" which references to the "Page Tree" which references to the individual page objects. Each page references to resources like images or fonts, and also to a "Content Stream" that contains the commands to draw text, images, or graphical elements.

The content stream is a list of "Operators" with "Operands", you could also call them function calls. Some functions change a state, for example set the drawing position or change the drawing color, others actually draw the text/image/line/...

Please take a look at the PDF Reference manual. You may have to use it look up the functions you need to use for your needs.

Let's assume you need a custom function to draw a cubic Bézier curve. The operators for this are:

  • "m": move to position
  • "c": add bezier curve to current path line
  • "S": stroke the path line.

Most operators have been implemented in the package "pdf" (even if there are no elements in the builder package using them). In this example, the functions needed are:

func (q *Page) Path_m(x, y float64)

func (q *Page) Path_c(x1, y1, x2, y2, x3, y3 float64)

func (q *Page) Path_S()

You can easily create your own element type for the Builder, it only needs to fulfill the Element interface:

type Element interface {
    Build(page *pdf.Page)
}

For example:

type BezierCurve struct {
    X, Y Length
    X1, Y1, X2, Y2, X3, Y3 Length
}

func (q *BezierCurve) Build(page *pdf.Page) error {
    page.Path_m(q.X.Pt(), q.Y.Pt())
    page.Path_c(q.X1.Pt(), q.Y1.Pt(), q.X2.Pt(), q.Y2.Pt(), q.X3.Pt(), q.Y3.Pt())
    page.Path_S()
	return nil
}

An instance of BezierCurve can now be added to a page using the AddElement method.

If you need to use operators that are not implemented in the pdf package, you can use the general function AddCommand:

func (q *Page) AddCommand(operator string, args ...types.Object)

If, however, the operator is implemented, please use the associated function to avoid unexpected behavior. In order to minimize the size of the PDF file, the pdf package keeps tracking of the current text and graphics state and ignores function call, that would not change the state, for example:

func (q *Page) TextState_Tc(charSpace float64) {
    if q.textState.Tc == charSpace {
        return
    }
    q.textState.Tc = charSpace
    
    q.AddCommand("Tc", types.Int(charSpace))
}

If you would call AddCommand("Tc", ...) instead of TextState_Tc, the internal textState would not be updated and you may see unexpected behavior later in your program.


Installation

To install GoPDF, use go get:

go get github.com/raceresult/gopdf

Staying up to date

To update GoPDF to the latest version, use go get -u github.com/raceresult/gopdf.


Supported go versions

We currently support the most recent major Go versions from 1.16 onward.


Contributing

Please feel free to submit issues, fork the repository and send pull requests!

When submitting an issue, we ask that you please include a complete test function that demonstrates the issue.


License

This project is licensed under the terms of the MIT license.

gopdf's People

Contributors

soenkep83 avatar

Stargazers

Lord of Scripts ™ avatar Joakim Uddholm avatar Philipp Speck avatar ekis avatar  avatar Hashem Aldhaheri avatar  avatar Quinn McPhail avatar c avatar Bethuel Mmbaga avatar Esword618 avatar

Watchers

James Cloos avatar Sascha Voth avatar Martha Augsburger avatar Gabriel Nelle avatar  avatar

gopdf's Issues

Paragraphs and other features

For weeksI have been trying to integrate Maroto/v2 into my application to produce a PDF document, het I always get stumped by some Maroto limitation, or rather, design flaws.

And now I found this. So a few questions before I junk weeks of work...

  • How does your library handle paragraphs? One doesn't always have single lines.
  • Support for header & footer
  • How do I retrieve a (dynamic?) reference to the current page number in order to output internal document references such as Table of Contents of List of Images?
  • Is it possible to output horizontal lines? Special symbols?
  • Can I mix multiple styles in a paragraph such as Bold, Italic, Underline or perhaps even font change (for example to use Courier for code references)?

I noticed one can add Elements which makes for extensibility. However, I noticed an Element is based on a Page rather than a sub-page element, like a quotation block, etc.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.