fix wrong git ignore
This commit is contained in:
126
internal/pdf/annotator/circle.go
Normal file
126
internal/pdf/annotator/circle.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package annotator
|
||||
|
||||
import (
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
|
||||
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream/draw"
|
||||
pdfcore "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
|
||||
pdf "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
|
||||
)
|
||||
|
||||
type CircleAnnotationDef struct {
|
||||
X float64
|
||||
Y float64
|
||||
Width float64
|
||||
Height float64
|
||||
FillEnabled bool // Show fill?
|
||||
FillColor *pdf.PdfColorDeviceRGB
|
||||
BorderEnabled bool // Show border?
|
||||
BorderWidth float64
|
||||
BorderColor *pdf.PdfColorDeviceRGB
|
||||
Opacity float64 // Alpha value (0-1).
|
||||
}
|
||||
|
||||
// Creates a circle/ellipse annotation object with appearance stream that can be added to page PDF annotations.
|
||||
func CreateCircleAnnotation(circDef CircleAnnotationDef) (*pdf.PdfAnnotation, error) {
|
||||
circAnnotation := pdf.NewPdfAnnotationCircle()
|
||||
|
||||
if circDef.BorderEnabled {
|
||||
r, g, b := circDef.BorderColor.R(), circDef.BorderColor.G(), circDef.BorderColor.B()
|
||||
circAnnotation.C = pdfcore.MakeArrayFromFloats([]float64{r, g, b})
|
||||
bs := pdf.NewBorderStyle()
|
||||
bs.SetBorderWidth(circDef.BorderWidth)
|
||||
circAnnotation.BS = bs.ToPdfObject()
|
||||
}
|
||||
|
||||
if circDef.FillEnabled {
|
||||
r, g, b := circDef.FillColor.R(), circDef.FillColor.G(), circDef.FillColor.B()
|
||||
circAnnotation.IC = pdfcore.MakeArrayFromFloats([]float64{r, g, b})
|
||||
} else {
|
||||
circAnnotation.IC = pdfcore.MakeArrayFromIntegers([]int{}) // No fill.
|
||||
}
|
||||
|
||||
if circDef.Opacity < 1.0 {
|
||||
circAnnotation.CA = pdfcore.MakeFloat(circDef.Opacity)
|
||||
}
|
||||
|
||||
// Make the appearance stream (for uniform appearance).
|
||||
apDict, bbox, err := makeCircleAnnotationAppearanceStream(circDef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
circAnnotation.AP = apDict
|
||||
circAnnotation.Rect = pdfcore.MakeArrayFromFloats([]float64{bbox.Llx, bbox.Lly, bbox.Urx, bbox.Ury})
|
||||
|
||||
return circAnnotation.PdfAnnotation, nil
|
||||
|
||||
}
|
||||
|
||||
func makeCircleAnnotationAppearanceStream(circDef CircleAnnotationDef) (*pdfcore.PdfObjectDictionary, *pdf.PdfRectangle, error) {
|
||||
form := pdf.NewXObjectForm()
|
||||
form.Resources = pdf.NewPdfPageResources()
|
||||
|
||||
gsName := ""
|
||||
if circDef.Opacity < 1.0 {
|
||||
// Create graphics state with right opacity.
|
||||
gsState := pdfcore.MakeDict()
|
||||
gsState.Set("ca", pdfcore.MakeFloat(circDef.Opacity))
|
||||
gsState.Set("CA", pdfcore.MakeFloat(circDef.Opacity))
|
||||
err := form.Resources.AddExtGState("gs1", gsState)
|
||||
if err != nil {
|
||||
common.Log.Debug("Unable to add extgstate gs1")
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
gsName = "gs1"
|
||||
}
|
||||
|
||||
content, localBbox, globalBbox, err := drawPdfCircle(circDef, gsName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = form.SetContentStream(content, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Local bounding box for the XObject Form.
|
||||
form.BBox = localBbox.ToPdfObject()
|
||||
|
||||
apDict := pdfcore.MakeDict()
|
||||
apDict.Set("N", form.ToPdfObject())
|
||||
|
||||
return apDict, globalBbox, nil
|
||||
}
|
||||
|
||||
func drawPdfCircle(circDef CircleAnnotationDef, gsName string) ([]byte, *pdf.PdfRectangle, *pdf.PdfRectangle, error) {
|
||||
// The annotation is drawn locally in a relative coordinate system with 0,0 as the origin rather than an offset.
|
||||
circle := draw.Circle{
|
||||
X: circDef.X,
|
||||
Y: circDef.Y,
|
||||
Width: circDef.Width,
|
||||
Height: circDef.Height,
|
||||
FillEnabled: circDef.FillEnabled,
|
||||
FillColor: circDef.FillColor,
|
||||
BorderEnabled: circDef.BorderEnabled,
|
||||
BorderWidth: circDef.BorderWidth,
|
||||
BorderColor: circDef.BorderColor,
|
||||
Opacity: circDef.Opacity,
|
||||
}
|
||||
|
||||
content, localBbox, err := circle.Draw(gsName)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Bounding box - global page coordinate system (with offset).
|
||||
globalBbox := &pdf.PdfRectangle{}
|
||||
globalBbox.Llx = circDef.X + localBbox.Llx
|
||||
globalBbox.Lly = circDef.Y + localBbox.Lly
|
||||
globalBbox.Urx = circDef.X + localBbox.Urx
|
||||
globalBbox.Ury = circDef.Y + localBbox.Ury
|
||||
|
||||
return content, localBbox, globalBbox, nil
|
||||
}
|
||||
133
internal/pdf/annotator/line.go
Normal file
133
internal/pdf/annotator/line.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package annotator
|
||||
|
||||
import (
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream/draw"
|
||||
pdfcore "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
|
||||
pdf "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
|
||||
)
|
||||
|
||||
// Defines a line between point 1 (X1,Y1) and point 2 (X2,Y2). The line ending styles can be none (regular line),
|
||||
// or arrows at either end. The line also has a specified width, color and opacity.
|
||||
type LineAnnotationDef struct {
|
||||
X1 float64
|
||||
Y1 float64
|
||||
X2 float64
|
||||
Y2 float64
|
||||
LineColor *pdf.PdfColorDeviceRGB
|
||||
Opacity float64 // Alpha value (0-1).
|
||||
LineWidth float64
|
||||
LineEndingStyle1 draw.LineEndingStyle // Line ending style of point 1.
|
||||
LineEndingStyle2 draw.LineEndingStyle // Line ending style of point 2.
|
||||
}
|
||||
|
||||
// Creates a line annotation object that can be added to page PDF annotations.
|
||||
func CreateLineAnnotation(lineDef LineAnnotationDef) (*pdf.PdfAnnotation, error) {
|
||||
// Line annotation.
|
||||
lineAnnotation := pdf.NewPdfAnnotationLine()
|
||||
|
||||
// Line endpoint locations.
|
||||
lineAnnotation.L = pdfcore.MakeArrayFromFloats([]float64{lineDef.X1, lineDef.Y1, lineDef.X2, lineDef.Y2})
|
||||
|
||||
// Line endings.
|
||||
le1 := pdfcore.MakeName("None")
|
||||
if lineDef.LineEndingStyle1 == draw.LineEndingStyleArrow {
|
||||
le1 = pdfcore.MakeName("ClosedArrow")
|
||||
}
|
||||
le2 := pdfcore.MakeName("None")
|
||||
if lineDef.LineEndingStyle2 == draw.LineEndingStyleArrow {
|
||||
le2 = pdfcore.MakeName("ClosedArrow")
|
||||
}
|
||||
lineAnnotation.LE = pdfcore.MakeArray(le1, le2)
|
||||
|
||||
// Opacity.
|
||||
if lineDef.Opacity < 1.0 {
|
||||
lineAnnotation.CA = pdfcore.MakeFloat(lineDef.Opacity)
|
||||
}
|
||||
|
||||
r, g, b := lineDef.LineColor.R(), lineDef.LineColor.G(), lineDef.LineColor.B()
|
||||
lineAnnotation.IC = pdfcore.MakeArrayFromFloats([]float64{r, g, b}) // fill color of line endings, rgb 0-1.
|
||||
lineAnnotation.C = pdfcore.MakeArrayFromFloats([]float64{r, g, b}) // line color, rgb 0-1.
|
||||
bs := pdf.NewBorderStyle()
|
||||
bs.SetBorderWidth(lineDef.LineWidth) // Line width: 3 points.
|
||||
lineAnnotation.BS = bs.ToPdfObject()
|
||||
|
||||
// Make the appearance stream (for uniform appearance).
|
||||
apDict, bbox, err := makeLineAnnotationAppearanceStream(lineDef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lineAnnotation.AP = apDict
|
||||
|
||||
// The rect specifies the location and dimensions of the annotation. Technically if the annotation could not
|
||||
// be displayed if it goes outside these bounds, although rarely enforced.
|
||||
lineAnnotation.Rect = pdfcore.MakeArrayFromFloats([]float64{bbox.Llx, bbox.Lly, bbox.Urx, bbox.Ury})
|
||||
|
||||
return lineAnnotation.PdfAnnotation, nil
|
||||
}
|
||||
|
||||
func makeLineAnnotationAppearanceStream(lineDef LineAnnotationDef) (*pdfcore.PdfObjectDictionary, *pdf.PdfRectangle, error) {
|
||||
form := pdf.NewXObjectForm()
|
||||
form.Resources = pdf.NewPdfPageResources()
|
||||
|
||||
gsName := ""
|
||||
if lineDef.Opacity < 1.0 {
|
||||
// Create graphics state with right opacity.
|
||||
gsState := pdfcore.MakeDict()
|
||||
gsState.Set("ca", pdfcore.MakeFloat(lineDef.Opacity))
|
||||
err := form.Resources.AddExtGState("gs1", gsState)
|
||||
if err != nil {
|
||||
common.Log.Debug("Unable to add extgstate gs1")
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
gsName = "gs1"
|
||||
}
|
||||
|
||||
content, localBbox, globalBbox, err := drawPdfLine(lineDef, gsName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = form.SetContentStream(content, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Local bounding box for the XObject Form.
|
||||
form.BBox = localBbox.ToPdfObject()
|
||||
|
||||
apDict := pdfcore.MakeDict()
|
||||
apDict.Set("N", form.ToPdfObject())
|
||||
|
||||
return apDict, globalBbox, nil
|
||||
}
|
||||
|
||||
func drawPdfLine(lineDef LineAnnotationDef, gsName string) ([]byte, *pdf.PdfRectangle, *pdf.PdfRectangle, error) {
|
||||
// The annotation is drawn locally in a relative coordinate system with 0,0 as the origin rather than an offset.
|
||||
line := draw.Line{
|
||||
X1: 0,
|
||||
Y1: 0,
|
||||
X2: lineDef.X2 - lineDef.X1,
|
||||
Y2: lineDef.Y2 - lineDef.Y1,
|
||||
LineColor: lineDef.LineColor,
|
||||
Opacity: lineDef.Opacity,
|
||||
LineWidth: lineDef.LineWidth,
|
||||
LineEndingStyle1: lineDef.LineEndingStyle1,
|
||||
LineEndingStyle2: lineDef.LineEndingStyle2,
|
||||
}
|
||||
|
||||
content, localBbox, err := line.Draw(gsName)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Bounding box - global page coordinate system (with offset).
|
||||
globalBbox := &pdf.PdfRectangle{}
|
||||
globalBbox.Llx = lineDef.X1 + localBbox.Llx
|
||||
globalBbox.Lly = lineDef.Y1 + localBbox.Lly
|
||||
globalBbox.Urx = lineDef.X1 + localBbox.Urx
|
||||
globalBbox.Ury = lineDef.Y1 + localBbox.Ury
|
||||
|
||||
return content, localBbox, globalBbox, nil
|
||||
}
|
||||
129
internal/pdf/annotator/rectangle.go
Normal file
129
internal/pdf/annotator/rectangle.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package annotator
|
||||
|
||||
import (
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/common"
|
||||
|
||||
"gitea.tecamino.com/paadi/pdfmerge/internal/pdf/contentstream/draw"
|
||||
pdfcore "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/core"
|
||||
pdf "gitea.tecamino.com/paadi/pdfmerge/internal/pdf/model"
|
||||
)
|
||||
|
||||
// A rectangle defined with a specified Width and Height and a lower left corner at (X,Y). The rectangle can
|
||||
// optionally have a border and a filling color.
|
||||
// The Width/Height includes the border (if any specified).
|
||||
type RectangleAnnotationDef struct {
|
||||
X float64
|
||||
Y float64
|
||||
Width float64
|
||||
Height float64
|
||||
FillEnabled bool // Show fill?
|
||||
FillColor *pdf.PdfColorDeviceRGB
|
||||
BorderEnabled bool // Show border?
|
||||
BorderWidth float64
|
||||
BorderColor *pdf.PdfColorDeviceRGB
|
||||
Opacity float64 // Alpha value (0-1).
|
||||
}
|
||||
|
||||
// Creates a rectangle annotation object that can be added to page PDF annotations.
|
||||
func CreateRectangleAnnotation(rectDef RectangleAnnotationDef) (*pdf.PdfAnnotation, error) {
|
||||
rectAnnotation := pdf.NewPdfAnnotationSquare()
|
||||
|
||||
if rectDef.BorderEnabled {
|
||||
r, g, b := rectDef.BorderColor.R(), rectDef.BorderColor.G(), rectDef.BorderColor.B()
|
||||
rectAnnotation.C = pdfcore.MakeArrayFromFloats([]float64{r, g, b})
|
||||
bs := pdf.NewBorderStyle()
|
||||
bs.SetBorderWidth(rectDef.BorderWidth)
|
||||
rectAnnotation.BS = bs.ToPdfObject()
|
||||
}
|
||||
|
||||
if rectDef.FillEnabled {
|
||||
r, g, b := rectDef.FillColor.R(), rectDef.FillColor.G(), rectDef.FillColor.B()
|
||||
rectAnnotation.IC = pdfcore.MakeArrayFromFloats([]float64{r, g, b})
|
||||
} else {
|
||||
rectAnnotation.IC = pdfcore.MakeArrayFromIntegers([]int{}) // No fill.
|
||||
}
|
||||
|
||||
if rectDef.Opacity < 1.0 {
|
||||
rectAnnotation.CA = pdfcore.MakeFloat(rectDef.Opacity)
|
||||
}
|
||||
|
||||
// Make the appearance stream (for uniform appearance).
|
||||
apDict, bbox, err := makeRectangleAnnotationAppearanceStream(rectDef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rectAnnotation.AP = apDict
|
||||
rectAnnotation.Rect = pdfcore.MakeArrayFromFloats([]float64{bbox.Llx, bbox.Lly, bbox.Urx, bbox.Ury})
|
||||
|
||||
return rectAnnotation.PdfAnnotation, nil
|
||||
|
||||
}
|
||||
|
||||
func makeRectangleAnnotationAppearanceStream(rectDef RectangleAnnotationDef) (*pdfcore.PdfObjectDictionary, *pdf.PdfRectangle, error) {
|
||||
form := pdf.NewXObjectForm()
|
||||
form.Resources = pdf.NewPdfPageResources()
|
||||
|
||||
gsName := ""
|
||||
if rectDef.Opacity < 1.0 {
|
||||
// Create graphics state with right opacity.
|
||||
gsState := pdfcore.MakeDict()
|
||||
gsState.Set("ca", pdfcore.MakeFloat(rectDef.Opacity))
|
||||
gsState.Set("CA", pdfcore.MakeFloat(rectDef.Opacity))
|
||||
err := form.Resources.AddExtGState("gs1", gsState)
|
||||
if err != nil {
|
||||
common.Log.Debug("Unable to add extgstate gs1")
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
gsName = "gs1"
|
||||
}
|
||||
|
||||
content, localBbox, globalBbox, err := drawPdfRectangle(rectDef, gsName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = form.SetContentStream(content, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Local bounding box for the XObject Form.
|
||||
form.BBox = localBbox.ToPdfObject()
|
||||
|
||||
apDict := pdfcore.MakeDict()
|
||||
apDict.Set("N", form.ToPdfObject())
|
||||
|
||||
return apDict, globalBbox, nil
|
||||
}
|
||||
|
||||
func drawPdfRectangle(rectDef RectangleAnnotationDef, gsName string) ([]byte, *pdf.PdfRectangle, *pdf.PdfRectangle, error) {
|
||||
// The annotation is drawn locally in a relative coordinate system with 0,0 as the origin rather than an offset.
|
||||
rect := draw.Rectangle{
|
||||
X: 0,
|
||||
Y: 0,
|
||||
Width: rectDef.Width,
|
||||
Height: rectDef.Height,
|
||||
FillEnabled: rectDef.FillEnabled,
|
||||
FillColor: rectDef.FillColor,
|
||||
BorderEnabled: rectDef.BorderEnabled,
|
||||
BorderWidth: 2 * rectDef.BorderWidth,
|
||||
BorderColor: rectDef.BorderColor,
|
||||
Opacity: rectDef.Opacity,
|
||||
}
|
||||
|
||||
content, localBbox, err := rect.Draw(gsName)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Bounding box - global page coordinate system (with offset).
|
||||
globalBbox := &pdf.PdfRectangle{}
|
||||
globalBbox.Llx = rectDef.X + localBbox.Llx
|
||||
globalBbox.Lly = rectDef.Y + localBbox.Lly
|
||||
globalBbox.Urx = rectDef.X + localBbox.Urx
|
||||
globalBbox.Ury = rectDef.Y + localBbox.Ury
|
||||
|
||||
return content, localBbox, globalBbox, nil
|
||||
}
|
||||
Reference in New Issue
Block a user