From 7f4e3b8f1392806de83aed76507cb02c13212838 Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Thu, 30 May 2019 13:52:05 +0300 Subject: [PATCH 1/4] Update page resources Font dictionary when applying license information (#5) * Make PdfObjectDictionary Merge method chainable * Update page resources Font dictionary when applying license information * Add license font to the page resources only when it does not exist * Update hash for split test after verification --- core/primitives.go | 5 ++++- internal/e2etest/split_test.go | 4 ++-- model/writer.go | 12 +++++++----- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/core/primitives.go b/core/primitives.go index 439d6c22..d2e8ca81 100644 --- a/core/primitives.go +++ b/core/primitives.go @@ -605,13 +605,16 @@ func (array *PdfObjectArray) GetAsFloat64Slice() ([]float64, error) { } // Merge merges in key/values from another dictionary. Overwriting if has same keys. -func (d *PdfObjectDictionary) Merge(another *PdfObjectDictionary) { +// The mutated dictionary (d) is returned in order to allow method chaining. +func (d *PdfObjectDictionary) Merge(another *PdfObjectDictionary) *PdfObjectDictionary { if another != nil { for _, key := range another.Keys() { val := another.Get(key) d.Set(key, val) } } + + return d } // String returns a string describing `d`. diff --git a/internal/e2etest/split_test.go b/internal/e2etest/split_test.go index f471a96d..b49cf435 100644 --- a/internal/e2etest/split_test.go +++ b/internal/e2etest/split_test.go @@ -30,8 +30,8 @@ var ( // knownHashes defines a list of known output hashes to ensure that the output is constant. // If there is a change in hash need to find out why and update only if the change is accepted. var knownHashes = map[string]string{ - "bf7c9d5dabc7e7ec2fc0cf9db2d9c8e7aa456fca.pdf": "f7891d491fa9f20ed2975dd28961c205", - "371dce2c2720581a3eef3f123e5741dd3566ef87.pdf": "4a25934226b6b64e5d95d571260b1f01", + "bf7c9d5dabc7e7ec2fc0cf9db2d9c8e7aa456fca.pdf": "fdd638603c6f655babbc90358de66107", + "371dce2c2720581a3eef3f123e5741dd3566ef87.pdf": "4c5356ac623a96004d80315f24613fff", "e815311526b50036db6e89c54af2b9626edecf30.pdf": "97dcfdde59a2f3a6eb105d0c31ebd3fb", "3bf64014e0c9e4a56f1a9363f1b34fd707bd9fa0.pdf": "6f310c9fdd44d49766d3cc32d3053b89", "004feecd47e2da4f2ed5cdbbf4791a77dd59ce20.pdf": "309a072a97d0566aa3f85edae504bb53", diff --git a/model/writer.go b/model/writer.go index 5999fb59..192120f0 100644 --- a/model/writer.go +++ b/model/writer.go @@ -532,11 +532,11 @@ func (w *PdfWriter) addObjects(obj core.PdfObject) error { // AddPage adds a page to the PDF file. The new page should be an indirect object. func (w *PdfWriter) AddPage(page *PdfPage) error { + procPage(page) obj := page.ToPdfObject() common.Log.Trace("==========") common.Log.Trace("Appending to page list %T", obj) - procPage(page) pageObj, ok := obj.(*core.PdfIndirectObject) if !ok { @@ -627,14 +627,16 @@ func procPage(p *PdfPage) { return } - // Add font as needed. - f := DefaultFont() - p.Resources.SetFontByName("UF1", f.ToPdfObject()) + // Add font, if needed. + fontName := core.PdfObjectName("UF1") + if !p.Resources.HasFontByName(fontName) { + p.Resources.SetFontByName(fontName, DefaultFont().ToPdfObject()) + } var ops []string ops = append(ops, "q") ops = append(ops, "BT") - ops = append(ops, "/UF1 14 Tf") + ops = append(ops, fmt.Sprintf("/%s 14 Tf", fontName.String())) ops = append(ops, "1 0 0 rg") ops = append(ops, "10 10 Td") s := "Unlicensed UniDoc - Get a license on https://unidoc.io" From 1dcde7d76e4e9567b560440aeb31908a6174d5e3 Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Fri, 31 May 2019 01:01:56 +0300 Subject: [PATCH 2/4] Add support for vertical alignment of styled paragraphs inside table cells (#69) --- creator/styled_paragraph.go | 43 ++++++++++++++++++++++ creator/styled_paragraph_test.go | 63 ++++++++++++++++++++++++++++++++ creator/table.go | 33 +++++++++++++++-- 3 files changed, 135 insertions(+), 4 deletions(-) diff --git a/creator/styled_paragraph.go b/creator/styled_paragraph.go index fa39c0f4..1359194e 100644 --- a/creator/styled_paragraph.go +++ b/creator/styled_paragraph.go @@ -226,6 +226,49 @@ func (p *StyledParagraph) Height() float64 { return height } +// getLineHeight returns both the capheight and the font size based height of +// the line with the specified index. +func (p *StyledParagraph) getLineHeight(idx int) (capHeight, height float64) { + if p.lines == nil || len(p.lines) == 0 { + p.wrapText() + } + if idx < 0 || idx > len(p.lines)-1 { + common.Log.Debug("ERROR: invalid paragraph line index %d. Returning 0, 0", idx) + return 0, 0 + } + + line := p.lines[idx] + for _, chunk := range line { + descriptor, err := chunk.Style.Font.GetFontDescriptor() + if err != nil { + common.Log.Debug("ERROR: Unable to get font descriptor") + } + + var fontCapHeight float64 + if descriptor != nil { + if fontCapHeight, err = descriptor.GetCapHeight(); err != nil { + common.Log.Debug("ERROR: Unable to get font CapHeight: %v", err) + } + } + if int(fontCapHeight) <= 0 { + common.Log.Debug("WARN: CapHeight not available - setting to 1000") + fontCapHeight = 1000 + } + + h := fontCapHeight / 1000.0 * chunk.Style.FontSize * p.lineHeight + if h > capHeight { + capHeight = h + } + + h = p.lineHeight * chunk.Style.FontSize + if h > height { + height = h + } + } + + return capHeight, height +} + // getTextWidth calculates the text width as if all in one line (not taking // wrapping into account). func (p *StyledParagraph) getTextWidth() float64 { diff --git a/creator/styled_paragraph_test.go b/creator/styled_paragraph_test.go index d82acc54..9829b4b1 100644 --- a/creator/styled_paragraph_test.go +++ b/creator/styled_paragraph_test.go @@ -798,3 +798,66 @@ func TestStyledLinkRotation(t *testing.T) { t.Fatalf("Fail: %v\n", err) } } + +func TestStyledParagraphTableVerticalAlignment(t *testing.T) { + c := New() + + fontRegular := newStandard14Font(t, model.CourierName) + + createTable := func(c *Creator, text string, align CellVerticalAlignment, fontSize float64) { + textStyle := c.NewTextStyle() + textStyle.Font = fontRegular + textStyle.FontSize = fontSize + + table := c.NewTable(1) + table.SetMargins(0, 0, 5, 5) + + cell := table.NewCell() + sp := c.NewStyledParagraph() + textChunk := sp.Append(text) + textChunk.Style = textStyle + + cell.SetVerticalAlignment(align) + cell.SetContent(sp) + cell.SetBorder(CellBorderSideAll, CellBorderStyleSingle, 1) + + if err := c.Draw(table); err != nil { + t.Fatalf("Error drawing: %v", err) + } + } + + chunks := []string{ + "TR", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lacus viverra vitae congue eu consequat. Cras adipiscing enim eu turpis. Lectus magna fringilla urna porttitor. Condimentum id venenatis a condimentum. Quis ipsum suspendisse ultrices gravida dictum fusce. In fermentum posuere urna nec tincidunt.", + } + + alignments := []struct { + Label string + Alignment CellVerticalAlignment + }{ + {"Top alignment", CellVerticalAlignmentTop}, + {"Middle alignment", CellVerticalAlignmentMiddle}, + {"Bottom alignment", CellVerticalAlignmentBottom}, + } + + for _, chunk := range chunks { + for _, alignment := range alignments { + c.NewPage() + + sp := c.NewStyledParagraph() + sp.Append(alignment.Label).Style.FontSize = 16 + sp.SetMargins(0, 0, 0, 5) + + if err := c.Draw(sp); err != nil { + t.Fatalf("Error drawing: %v", err) + } + + for i := 4; i <= 20; i += 2 { + createTable(c, chunk, alignment.Alignment, float64(i)) + } + } + } + + // Write output file. + testWriteAndRender(t, c, "styled_paragraph_table_vertical_align.pdf") +} diff --git a/creator/table.go b/creator/table.go index 585f2cd6..4e5aa843 100644 --- a/creator/table.go +++ b/creator/table.go @@ -516,8 +516,10 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, } if cell.content != nil { - // content width. - cw := cell.content.Width() + cw := cell.content.Width() // content width. + ch := cell.content.Height() // content height. + vertOffset := 0.0 + switch t := cell.content.(type) { case *Paragraph: if t.enableWrap { @@ -527,6 +529,26 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, if t.enableWrap { cw = t.getMaxLineWidth() / 1000.0 } + + // Calculate the height of the paragraph. + lineCapHeight, lineHeight := t.getLineHeight(0) + if len(t.lines) == 1 { + ch = lineCapHeight + } else { + ch = ch - lineHeight + lineCapHeight + } + + // Account for the top offset the paragraph adds. + vertOffset = lineCapHeight - t.defaultStyle.FontSize*t.lineHeight + + switch cell.verticalAlignment { + case CellVerticalAlignmentTop: + // Add a bit of space from the top border of the cell. + vertOffset += lineCapHeight * 0.5 + case CellVerticalAlignmentBottom: + // Add a bit of space from the bottom border of the cell. + vertOffset -= lineCapHeight * 0.5 + } case *Table: cw = w case *List: @@ -553,8 +575,9 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, } } + ctx.Y += vertOffset + // Account for vertical alignment. - ch := cell.content.Height() // content height. switch cell.verticalAlignment { case CellVerticalAlignmentTop: // Default: do nothing. @@ -567,7 +590,7 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, case CellVerticalAlignmentBottom: if h > ch { ctx.Y = ctx.Y + h - ch - ctx.Height = ch + ctx.Height = h } } @@ -575,6 +598,8 @@ func (table *Table) GeneratePageBlocks(ctx DrawContext) ([]*Block, DrawContext, if err != nil { common.Log.Debug("ERROR: %v", err) } + + ctx.Y -= vertOffset } ctx.Y += h From 33945e77798a31cfb044a8541a7d5e8cda5c21da Mon Sep 17 00:00:00 2001 From: Gunnsteinn Hall Date: Sun, 2 Jun 2019 11:16:00 +0000 Subject: [PATCH 3/4] Prepare for release. Update version.go and README. Fix testcase . --- README.md | 46 ++++++++++++++++++++---------------------- common/version.go | 8 ++++---- model/appender_test.go | 4 +++- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 7c6bd3c8..5788bdee 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,46 @@ # UniPDF - PDF for Go -[UniDoc](http://unidoc.io)'s UniPDF is a powerful PDF library for Go (golang) with capabilities for -creating and processing PDF files. The library is written and supported by -the [FoxyUtils.com](https://foxyutils.com) website, where the library is used to power -many of the PDF services offered. +[UniDoc](http://unidoc.io)'s unipdf (formerly unidoc) is a powerful PDF library for Go (golang) with capabilities for +creating and reading, processing PDF files. The library is written and supported by +[FoxyUtils.com](https://foxyutils.com), where the library is used to power many of its services. [![Build Status](https://app.wercker.com/status/22b50db125a6d376080f3f0c80d085fa/s/master "wercker status")](https://app.wercker.com/project/bykey/22b50db125a6d376080f3f0c80d085fa) +[![GitHub (pre-)release](https://img.shields.io/github/release/unidoc/unipdf/all.svg)](https://github.com/unidoc/unipdf/releases) [![License: AGPL v3](https://img.shields.io/badge/License-Dual%20AGPL%20v3/Commercial-blue.svg)](https://www.gnu.org/licenses/agpl-3.0) [![Go Report Card](https://goreportcard.com/badge/github.com/unidoc/unipdf)](https://goreportcard.com/report/github.com/unidoc/unipdf) [![GoDoc](https://godoc.org/github.com/unidoc/unipdf?status.svg)](https://godoc.org/github.com/unidoc/unipdf) ## News -- unidoc is being renamed to unipdf and will be maintained under https://github.com/unidoc/unipdf -- The old repository will remain under https://github.com/unidoc/unidoc for backwards compatibility and will be read-only. +- unidoc has being renamed to unipdf and is maintained under https://github.com/unidoc/unipdf +- The old repository remains under https://github.com/unidoc/unidoc for backwards compatibility and will be read-only. All development will be under the unipdf repository. -- The initial release of unipdf v3.0.0 will be compliant with Go modules from the start. - +- The initial release of unipdf v3.0.0 is compatible with Go modules from the start. ## Features -unipdf has a powerful set of features both for reading, processing and writing PDF. -The following list describes some key features: +unipdf has multiple features for creating and modifying PDF: - [x] [Create PDF reports](https://github.com/unidoc/unipdf-examples/blob/v3/report/pdf_report.go) -- [x] [Create PDF invoices](https://unidoc.io/news/simple-invoices) +- [x] [Invoice creation](https://unidoc.io/news/simple-invoices) - [x] Advanced table generation in the creator with subtable support - [x] Paragraph in creator handling multiple styles within the same paragraph -- [x] Table of contents automatically generated -- [x] Text extraction significantly improved in quality and foundation in place for vectorized (position-based) text extraction (XY) -- [x] Image extraction with coordinates - [x] [Merge PDF pages](https://github.com/unidoc/unipdf-examples/blob/v3/pages/pdf_merge.go) -- [x] Merge page contents -- [x] [Split PDF pages and change page order](https://github.com/unidoc/unipdf-examples/blob/v3/pages/pdf_split.go) +- [x] [Split PDF pages](https://github.com/unidoc/unipdf-examples/blob/v3/pages/pdf_split.go) and change page order - [x] [Rotate pages](https://github.com/unidoc/unipdf-examples/blob/v3/pages/pdf_rotate.go) - [x] [Extract text from PDF files](https://github.com/unidoc/unipdf-examples/blob/v3/text/pdf_extract_text.go) -- [x] Extract images -- [x] Add images to pages -- [x] [Compress and optimize PDF output](https://github.com/unidoc/unipdf-examples/blob/v3/compress/pdf_optimize.g) -- [x] [Draw watermark on PDF files](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_watermark_image.go) +- [x] [Extract images](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_extract_images.go) with coordinates +- [x] [Images to PDF](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_images_to_pdf.go) +- [x] [Add images to pages](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_add_image_to_page.go) +- [x] [Compress and optimize PDF](https://github.com/unidoc/unipdf-examples/blob/v3/compress/pdf_optimize.go) +- [x] [Watermark PDF files](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_watermark_image.go) - [x] Advanced page manipulation (blocks/templates) - [x] Load PDF templates and modify -- [x] [Flatten forms and generate appearance streams](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_flatten.go) -- [x] [Fill out forms and FDF merging](https://github.com/unidoc/unipdf-examples/tree/v3/forms) -- [x] [FDF merge](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_fill_fdf_merge.go) and [form filling via JSON data](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_fill_json.go) - [x] [Form creation](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_add.go) +- [x] [Fill and flatten forms](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_flatten.go) +- [x] [Fill out forms](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_fill_json.go) and [FDF merging](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_fill_fdf_merge.go) - [x] [Unlock PDF files / remove password](https://github.com/unidoc/unipdf-examples/blob/v3/security/pdf_unlock.go) - [x] [Protect PDF files with a password](https://github.com/unidoc/unipdf-examples/blob/v3/security/pdf_protect.go) - [x] [Digital signing validation and signing](https://github.com/unidoc/unipdf-examples/tree/v3/signatures) - [x] CCITTFaxDecode decoding and encoding support -- [x] Append mode ## Installation With modules: @@ -55,6 +48,11 @@ With modules: go get github.com/unidoc/unipdf/v3 ~~~ +With GOPATH: +~~~ +go get github.com/unidoc/unipdf/... +~~~ + ## How can I convince myself and my boss to buy unipdf rather using a free alternative? diff --git a/common/version.go b/common/version.go index f3e5f4ca..0a66bb3f 100644 --- a/common/version.go +++ b/common/version.go @@ -11,12 +11,12 @@ import ( ) const releaseYear = 2019 -const releaseMonth = 4 -const releaseDay = 20 -const releaseHour = 23 +const releaseMonth = 6 +const releaseDay = 2 +const releaseHour = 11 const releaseMin = 30 // Version holds version information, when bumping this make sure to bump the released at stamp also. -const Version = "3.0.0-rc.1" +const Version = "3.0.1" var ReleasedAt = time.Date(releaseYear, releaseMonth, releaseDay, releaseHour, releaseMin, 0, 0, time.UTC) diff --git a/model/appender_test.go b/model/appender_test.go index 1e4e6104..9af5938b 100644 --- a/model/appender_test.go +++ b/model/appender_test.go @@ -66,6 +66,8 @@ func TestAppenderNoop(t *testing.T) { appender, err := model.NewPdfAppender(reader) require.NoError(t, err) + model.SetPdfProducer("UniPDF") + model.SetPdfCreator("UniDoc UniPDF") err = appender.WriteToFile(tempFile("appender_noop.pdf")) if err != nil { t.Errorf("Fail: %v\n", err) @@ -123,7 +125,7 @@ func TestAppenderNoop(t *testing.T) { 3: core.XrefObject{ObjectNumber: 3, XType: 0, Offset: 178}, 4: core.XrefObject{ObjectNumber: 4, XType: 0, Offset: 457}, 5: core.XrefObject{ObjectNumber: 5, XType: 0, Offset: 740}, - 6: core.XrefObject{ObjectNumber: 6, XType: 0, Offset: 860}, + 6: core.XrefObject{ObjectNumber: 6, XType: 0, Offset: 802}, } require.Equal(t, expected, xrefs.ObjectMap) } From 45bef6de2617131a5322f94f0adc43a6aafd7938 Mon Sep 17 00:00:00 2001 From: Gunnsteinn Hall Date: Sun, 2 Jun 2019 15:06:28 +0000 Subject: [PATCH 4/4] Update README.md --- README.md | 71 ++++++++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 5788bdee..414ad8f2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # UniPDF - PDF for Go -[UniDoc](http://unidoc.io)'s unipdf (formerly unidoc) is a powerful PDF library for Go (golang) with capabilities for +[UniDoc](http://unidoc.io)'s UniPDF (formerly unidoc) is a PDF library for Go (golang) with capabilities for creating and reading, processing PDF files. The library is written and supported by [FoxyUtils.com](https://foxyutils.com), where the library is used to power many of its services. @@ -10,37 +10,41 @@ creating and reading, processing PDF files. The library is written and supported [![Go Report Card](https://goreportcard.com/badge/github.com/unidoc/unipdf)](https://goreportcard.com/report/github.com/unidoc/unipdf) [![GoDoc](https://godoc.org/github.com/unidoc/unipdf?status.svg)](https://godoc.org/github.com/unidoc/unipdf) -## News -- unidoc has being renamed to unipdf and is maintained under https://github.com/unidoc/unipdf -- The old repository remains under https://github.com/unidoc/unidoc for backwards compatibility and will be read-only. -All development will be under the unipdf repository. -- The initial release of unipdf v3.0.0 is compatible with Go modules from the start. - ## Features -unipdf has multiple features for creating and modifying PDF: -- [x] [Create PDF reports](https://github.com/unidoc/unipdf-examples/blob/v3/report/pdf_report.go) -- [x] [Invoice creation](https://unidoc.io/news/simple-invoices) -- [x] Advanced table generation in the creator with subtable support -- [x] Paragraph in creator handling multiple styles within the same paragraph -- [x] [Merge PDF pages](https://github.com/unidoc/unipdf-examples/blob/v3/pages/pdf_merge.go) -- [x] [Split PDF pages](https://github.com/unidoc/unipdf-examples/blob/v3/pages/pdf_split.go) and change page order -- [x] [Rotate pages](https://github.com/unidoc/unipdf-examples/blob/v3/pages/pdf_rotate.go) -- [x] [Extract text from PDF files](https://github.com/unidoc/unipdf-examples/blob/v3/text/pdf_extract_text.go) -- [x] [Extract images](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_extract_images.go) with coordinates -- [x] [Images to PDF](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_images_to_pdf.go) -- [x] [Add images to pages](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_add_image_to_page.go) -- [x] [Compress and optimize PDF](https://github.com/unidoc/unipdf-examples/blob/v3/compress/pdf_optimize.go) -- [x] [Watermark PDF files](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_watermark_image.go) -- [x] Advanced page manipulation (blocks/templates) -- [x] Load PDF templates and modify -- [x] [Form creation](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_add.go) -- [x] [Fill and flatten forms](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_flatten.go) -- [x] [Fill out forms](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_fill_json.go) and [FDF merging](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_fill_fdf_merge.go) -- [x] [Unlock PDF files / remove password](https://github.com/unidoc/unipdf-examples/blob/v3/security/pdf_unlock.go) -- [x] [Protect PDF files with a password](https://github.com/unidoc/unipdf-examples/blob/v3/security/pdf_protect.go) -- [x] [Digital signing validation and signing](https://github.com/unidoc/unipdf-examples/tree/v3/signatures) -- [x] CCITTFaxDecode decoding and encoding support +- [Create PDF reports](https://github.com/unidoc/unipdf-examples/blob/v3/report/pdf_report.go) +- [Invoice creation](https://unidoc.io/news/simple-invoices) +- Advanced table generation in the creator with subtable support +- Paragraph in creator handling multiple styles within the same paragraph +- [Merge PDF pages](https://github.com/unidoc/unipdf-examples/blob/v3/pages/pdf_merge.go) +- [Split PDF pages](https://github.com/unidoc/unipdf-examples/blob/v3/pages/pdf_split.go) and change page order +- [Rotate pages](https://github.com/unidoc/unipdf-examples/blob/v3/pages/pdf_rotate.go) +- [Extract text from PDF files](https://github.com/unidoc/unipdf-examples/blob/v3/text/pdf_extract_text.go) +- [Extract images](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_extract_images.go) with coordinates +- [Images to PDF](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_images_to_pdf.go) +- [Add images to pages](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_add_image_to_page.go) +- [Compress and optimize PDF](https://github.com/unidoc/unipdf-examples/blob/v3/compress/pdf_optimize.go) +- [Watermark PDF files](https://github.com/unidoc/unipdf-examples/blob/v3/image/pdf_watermark_image.go) +- Advanced page manipulation (blocks/templates) +- Load PDF templates and modify +- [Form creation](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_add.go) +- [Fill and flatten forms](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_flatten.go) +- [Fill out forms](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_fill_json.go) and [FDF merging](https://github.com/unidoc/unipdf-examples/blob/v3/forms/pdf_form_fill_fdf_merge.go) +- [Unlock PDF files / remove password](https://github.com/unidoc/unipdf-examples/blob/v3/security/pdf_unlock.go) +- [Protect PDF files with a password](https://github.com/unidoc/unipdf-examples/blob/v3/security/pdf_protect.go) +- [Digital signing validation and signing](https://github.com/unidoc/unipdf-examples/tree/v3/signatures) +- CCITTFaxDecode decoding and encoding support + +Multiple examples are provided in our example repository https://github.com/unidoc/unidoc-examples +as well as [documented examples](https://unidoc.io/examples) on our website. + +Contact us if you need any specific examples. + +## News +- unidoc has been renamed to unipdf and is maintained under https://github.com/unidoc/unipdf +- The old repository remains under https://github.com/unidoc/unidoc for backwards compatibility and will be read-only. +All development is under the unipdf repository. +- The initial release of unipdf v3.0.0 is compatible with Go modules from the start. ## Installation With modules: @@ -65,13 +69,6 @@ Security. We take security very seriously and we restrict access to github.com/ The profits are invested back into making unipdf better. We want to make the best possible product and in order to do that we need the best people to contribute. A large fraction of the profits made goes back into developing unipdf. That way we have been able to get many excellent people to work and contribute to unipdf that would not be able to contribute their work for free. -## Examples - -Multiple examples are provided in our example repository https://github.com/unidoc/unidoc-examples -as well as [documented examples](https://unidoc.io/examples) on our website. - -Contact us if you need any specific examples. - ## Contributing [![CLA assistant](https://cla-assistant.io/readme/badge/unidoc/unipdf)](https://cla-assistant.io/unidoc/unipdf)