summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKurt Jung <kurt.w.jung@code.google.com>2014-09-16 22:38:49 -0400
committerKurt Jung <kurt.w.jung@code.google.com>2014-09-16 22:38:49 -0400
commitcbbfe21f94ea53f0800feaf4e9c06c038ddc1e6e (patch)
tree952009ff7b8098e3ffc10c96fe88887e7a78ffa4
parent9f16b52334238f446dbafcbe06ffff0637403361 (diff)
Added layer functionality. This allows content to be placed into layers, the visibility of which can be controlled from the document reader.
-rw-r--r--def.go1
-rw-r--r--fpdf.go9
-rw-r--r--fpdf_test.go39
-rw-r--r--layer.go123
4 files changed, 171 insertions, 1 deletions
diff --git a/def.go b/def.go
index 422eb1c..6d12af3 100644
--- a/def.go
+++ b/def.go
@@ -197,6 +197,7 @@ type Fpdf struct {
transformNest int // Number of active transformation contexts
err error // Set if error occurs during life cycle of instance
protect protectType // document protection structure
+ layer layerRecType // manages optional layers in document
colorFlag bool // indicates whether fill and text colors are different
color struct { // Composite values of colors
draw, fill, text clrType
diff --git a/fpdf.go b/fpdf.go
index 20705b8..5fd3f63 100644
--- a/fpdf.go
+++ b/fpdf.go
@@ -173,6 +173,7 @@ func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType)
f.gradientList = append(f.gradientList, gradientType{}) // gradientList[0] is unused
// Set default PDF version number
f.pdfVersion = "1.3"
+ f.layerInit()
return
}
@@ -2346,6 +2347,7 @@ func (f *Fpdf) beginpage(orientationStr string, size SizeType) {
}
func (f *Fpdf) endpage() {
+ f.EndLayer()
f.state = 1
}
@@ -3028,7 +3030,8 @@ func (f *Fpdf) putresourcedict() {
}
f.out(">>")
}
-
+ // Layers
+ f.layerPutResourceDict()
}
func (f *Fpdf) putBlendModes() {
@@ -3072,6 +3075,7 @@ func (f *Fpdf) putresources() {
if f.err != nil {
return
}
+ f.layerPutLayers()
f.putBlendModes()
f.putGradients()
f.putfonts()
@@ -3148,6 +3152,8 @@ func (f *Fpdf) putcatalog() {
f.outf("/Outlines %d 0 R", f.outlineRoot)
f.out("/PageMode /UseOutlines")
}
+ // Layers
+ f.layerPutCatalog()
}
func (f *Fpdf) putheader() {
@@ -3224,6 +3230,7 @@ func (f *Fpdf) enddoc() {
if f.err != nil {
return
}
+ f.layerEndDoc()
f.putheader()
f.putpages()
f.putresources()
diff --git a/fpdf_test.go b/fpdf_test.go
index 0da5e73..964985d 100644
--- a/fpdf_test.go
+++ b/fpdf_test.go
@@ -1246,3 +1246,42 @@ func ExampleFpdf_tutorial25() {
// Output:
// Successfully generated pdf/tutorial25.pdf
}
+
+// This example demonstrates document layers. The initial visibility of a layer
+// is specified with the second parameter to AddLayer(). The layer list
+// displayed by the document reader allows layer visibility to be controlled
+// interactively.
+func ExampleFpdf_tutorial26() {
+
+ pdf := gofpdf.New("P", "mm", "A4", "")
+ pdf.AddPage()
+ pdf.SetFont("Arial", "", 15)
+ pdf.Write(8, "This line doesn't belong to any layer.\n")
+
+ // Define layers
+ l1 := pdf.AddLayer("Layer 1", true)
+ l2 := pdf.AddLayer("Layer 2", true)
+
+ // Open layer pane in PDF viewer
+ pdf.OpenLayerPane()
+
+ // First layer
+ pdf.BeginLayer(l1)
+ pdf.Write(8, "This line belongs to layer 1.\n")
+ pdf.EndLayer()
+
+ // Second layer
+ pdf.BeginLayer(l2)
+ pdf.Write(8, "This line belongs to layer 2.\n")
+ pdf.EndLayer()
+
+ // First layer again
+ pdf.BeginLayer(l1)
+ pdf.Write(8, "This line belongs to layer 1 again.\n")
+ pdf.EndLayer()
+
+ pdf.OutputAndClose(docWriter(pdf, 26))
+
+ // Output:
+ // Successfully generated pdf/tutorial26.pdf
+}
diff --git a/layer.go b/layer.go
new file mode 100644
index 0000000..d567e37
--- /dev/null
+++ b/layer.go
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2014 Kurt Jung (Gmail: kurt.w.jung)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package gofpdf
+
+// Routines in this file are translated from
+// http://www.fpdf.org/en/script/script97.php
+
+type layerType struct {
+ name string
+ visible bool
+ objNum int // object number
+}
+
+type layerRecType struct {
+ list []layerType
+ currentLayer int
+ openLayerPane bool
+}
+
+func (f *Fpdf) layerInit() {
+ f.layer.list = make([]layerType, 0)
+ f.layer.currentLayer = -1
+ f.layer.openLayerPane = false
+}
+
+// AddLayer defines a layer that can be shown or hidden when the document is
+// displayed. name specifies the layer name that the document reader will
+// display in the layer list. visible specifies whether the layer will be
+// initially visible. The return value is an integer ID that is used in a call
+// to BeginLayer().
+//
+// Layers are demonstrated in tutorial 26.
+func (f *Fpdf) AddLayer(name string, visible bool) (layerID int) {
+ layerID = len(f.layer.list)
+ f.layer.list = append(f.layer.list, layerType{name: name, visible: visible})
+ return
+}
+
+// BeginLayer is called to begin adding content to the specified layer. All
+// content added to the page between a call to BeginLayer and a call to
+// EndLayer is added to the layer specified by id. See AddLayer for more
+// details.
+func (f *Fpdf) BeginLayer(id int) {
+ f.EndLayer()
+ if id >= 0 && id < len(f.layer.list) {
+ f.outf("/OC /OC%d BDC", id)
+ f.layer.currentLayer = id
+ }
+}
+
+// EndLayer is called to stop adding content to the currently active layer. See
+// BeginLayer for more details.
+func (f *Fpdf) EndLayer() {
+ if f.layer.currentLayer >= 0 {
+ f.out("EMC")
+ f.layer.currentLayer = -1
+ }
+}
+
+// OpenLayerPane advises the document reader to open the layer pane when the
+// document is initially displayed.
+func (f *Fpdf) OpenLayerPane() {
+ f.layer.openLayerPane = true
+}
+
+func (f *Fpdf) layerEndDoc() {
+ if len(f.layer.list) > 0 {
+ if f.pdfVersion < "1.5" {
+ f.pdfVersion = "1.5"
+ }
+ }
+}
+
+func (f *Fpdf) layerPutLayers() {
+ for j, l := range f.layer.list {
+ f.newobj()
+ f.layer.list[j].objNum = f.n
+ f.outf("<</Type /OCG /Name %s>>", f.textstring(utf8toutf16(l.name)))
+ f.out("endobj")
+ }
+}
+
+func (f *Fpdf) layerPutResourceDict() {
+ if len(f.layer.list) > 0 {
+ f.out("/Properties <<")
+ for j, layer := range f.layer.list {
+ f.outf("/OC%d %d 0 R", j, layer.objNum)
+ }
+ f.out(">>")
+ }
+
+}
+
+func (f *Fpdf) layerPutCatalog() {
+ if len(f.layer.list) > 0 {
+ onStr := ""
+ offStr := ""
+ for _, layer := range f.layer.list {
+ onStr += sprintf("%d 0 R ", layer.objNum)
+ if !layer.visible {
+ offStr += sprintf("%d 0 R ", layer.objNum)
+ }
+ }
+ f.outf("/OCProperties <</OCGs [%s] /D <</OFF [%s] /Order [%s]>>>>", onStr, offStr, onStr)
+ if f.layer.openLayerPane {
+ f.out("/PageMode /UseOC")
+ }
+ }
+}