Compare commits
12 Commits
ec3dbbaf3a
...
chore/mini
| Author | SHA1 | Date | |
|---|---|---|---|
| b59c939e3e | |||
| 3a30c7a6f4 | |||
| bdab11b100 | |||
| d3f6f0e946 | |||
| f05c4bbcc7 | |||
| d2ac081d5e | |||
| 39af776d00 | |||
| 15130938ce | |||
| 05ac24254c | |||
| 72a245610a | |||
| 7040909625 | |||
| ec9426b59c |
55
.drone.yml
Normal file
55
.drone.yml
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: kubernetes
|
||||||
|
name: build-clis
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build
|
||||||
|
image: golang:1.17
|
||||||
|
commands:
|
||||||
|
- make build-cross-clis
|
||||||
|
- name: upload
|
||||||
|
image: plugins/s3
|
||||||
|
settings:
|
||||||
|
bucket: covergen
|
||||||
|
source: dist/*
|
||||||
|
target: /dist/
|
||||||
|
strip_prefix: dist/
|
||||||
|
acl: readonly
|
||||||
|
|
||||||
|
path_style: true
|
||||||
|
endpoint: https://s3.blacknova.io
|
||||||
|
access_key:
|
||||||
|
from_secret: minio_access_key_id
|
||||||
|
secret_key:
|
||||||
|
from_secret: minio_secret_access_key
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: kubernetes
|
||||||
|
name: build-wasm
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build
|
||||||
|
image: golang:1.17
|
||||||
|
environment:
|
||||||
|
GOOS: js
|
||||||
|
GOARCH: wasm
|
||||||
|
commands:
|
||||||
|
- make wasm
|
||||||
|
- name: upload
|
||||||
|
image: plugins/s3
|
||||||
|
settings:
|
||||||
|
bucket: covergen
|
||||||
|
source: assets/**/*
|
||||||
|
target: /
|
||||||
|
strip_prefix: assets/
|
||||||
|
acl: readonly
|
||||||
|
|
||||||
|
path_style: true
|
||||||
|
endpoint: https://s3.blacknova.io
|
||||||
|
access_key:
|
||||||
|
from_secret: minio_access_key_id
|
||||||
|
secret_key:
|
||||||
|
from_secret: minio_secret_access_key
|
||||||
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
covergen
|
covergen
|
||||||
!covergen/
|
!covergen/
|
||||||
*.pdf
|
*.pdf
|
||||||
|
assets/*.wasm
|
||||||
|
dist/
|
||||||
8
Makefile
8
Makefile
@@ -1,3 +1,9 @@
|
|||||||
|
.PHONY: build-cross-clis
|
||||||
|
build-cross-clis:
|
||||||
|
rm -rf ./dist && mkdir ./dist
|
||||||
|
GOOS=linux GOARCH=amd64 go build -o dist/covergen.linux-amd64 ./cmd/covergen
|
||||||
|
GOOS=darwin GOARCH=amd64 go build -o dist/covergen.darwin-amd64 ./cmd/covergen
|
||||||
|
|
||||||
|
.PHONY: wasm
|
||||||
wasm:
|
wasm:
|
||||||
GOOS=js GOARCH=wasm go build -o assets/covergen.wasm ./cmd/wasm/main.go
|
GOOS=js GOARCH=wasm go build -o assets/covergen.wasm ./cmd/wasm/main.go
|
||||||
.PHONY: wasm
|
|
||||||
|
|||||||
Binary file not shown.
@@ -9,7 +9,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
function makeCover(args) {
|
function makeCover(args) {
|
||||||
const result = window.covergen(args);
|
const result = window.generateCover(args);
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
throw result.error;
|
throw result.error;
|
||||||
}
|
}
|
||||||
@@ -17,19 +17,44 @@
|
|||||||
return new File([result], 'cover.pdf', {type: 'application/pdf'});
|
return new File([result], 'cover.pdf', {type: 'application/pdf'});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeSplitCover(args) {
|
||||||
|
const result = window.generateSplitCover(args);
|
||||||
|
if (result.error) {
|
||||||
|
throw result.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.fromEntries(['front', 'back'].map((side) => [side, new File([result[side]], `${side}.pdf`, {type: 'application/pdf'})]));
|
||||||
|
}
|
||||||
|
|
||||||
function letsfuckinggo() {
|
function letsfuckinggo() {
|
||||||
document.getElementById('cover').src = window.URL.createObjectURL(makeCover({
|
const covers = makeSplitCover({
|
||||||
customer: document.getElementById('customer').value,
|
customer: document.getElementById('customer').value,
|
||||||
number: document.getElementById('number').value,
|
number: document.getElementById('number').value,
|
||||||
}));
|
numberPrefix: document.getElementById('prefix').value,
|
||||||
|
hlColor: document.getElementById('color').value,
|
||||||
|
})
|
||||||
|
|
||||||
|
document.getElementById('front').src = window.URL.createObjectURL(covers.front);
|
||||||
|
document.getElementById('back').src = window.URL.createObjectURL(covers.back);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
html, body, #cover {
|
html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.covers {
|
||||||
|
height: 100%;
|
||||||
|
width: 90%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.covers iframe {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -39,15 +64,30 @@
|
|||||||
<textarea id="customer"></textarea>
|
<textarea id="customer"></textarea>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
Prefix:
|
||||||
|
<input id="prefix" type="text" value="offerte">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>
|
<label>
|
||||||
Number:
|
Number:
|
||||||
<input id="number" type="text">
|
<input id="number" type="text">
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
Color:
|
||||||
|
<input id="color" type="color" value="#FF69B4">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button onclick="letsfuckinggo()">Fuck it!</button>
|
<button onclick="letsfuckinggo()">Fuck it!</button>
|
||||||
</div>
|
</div>
|
||||||
<iframe id="cover"></iframe>
|
<div class="covers">
|
||||||
|
<iframe id="front"></iframe>
|
||||||
|
<iframe id="back"></iframe>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -26,6 +26,7 @@ var customer = pflag.StringP("customer", "c", "", "Customer name for cover")
|
|||||||
var numberPrefix = pflag.String("number-prefix", "offerte", "Prefix to use for number")
|
var numberPrefix = pflag.String("number-prefix", "offerte", "Prefix to use for number")
|
||||||
var color = pflag.String("color", "random", "Selects a color to use for the grid highlight. Valid choices: ['random', 'red', 'blue', 'yellow', 'green']")
|
var color = pflag.String("color", "random", "Selects a color to use for the grid highlight. Valid choices: ['random', 'red', 'blue', 'yellow', 'green']")
|
||||||
var output = pflag.StringP("output", "o", "cover.pdf", "File to output to")
|
var output = pflag.StringP("output", "o", "cover.pdf", "File to output to")
|
||||||
|
var output2 = pflag.StringP("output-back", "b", "", "Output front and back separately")
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).With().Timestamp().Logger()
|
log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).With().Timestamp().Logger()
|
||||||
@@ -69,13 +70,15 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
*customer = strings.ReplaceAll(*customer, "\\n", "\n")
|
*customer = strings.ReplaceAll(*customer, "\\n", "\n")
|
||||||
|
settings := covergen.CoverSettings{
|
||||||
pdf, err := covergen.GenerateInvoice(covergen.CoverSettings{
|
|
||||||
Number: *number,
|
Number: *number,
|
||||||
NumberPrefix: *numberPrefix,
|
NumberPrefix: *numberPrefix,
|
||||||
CustomerName: *customer,
|
CustomerName: *customer,
|
||||||
HLColor: chosenColor,
|
HLColor: chosenColor,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if *output2 == "" {
|
||||||
|
pdf, err := covergen.GenerateInvoice(settings)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Failed to render invoice")
|
log.Fatal().Err(err).Msg("Failed to render invoice")
|
||||||
@@ -84,4 +87,22 @@ func main() {
|
|||||||
if err := pdf.OutputFileAndClose(*output); err != nil {
|
if err := pdf.OutputFileAndClose(*output); err != nil {
|
||||||
log.Fatal().Err(err).Msg("Failed to write invoice to disk")
|
log.Fatal().Err(err).Msg("Failed to write invoice to disk")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
front, err := covergen.GenerateFrontCover(settings)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to render front")
|
||||||
|
}
|
||||||
|
if err := front.OutputFileAndClose(*output); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to write front to disk")
|
||||||
|
}
|
||||||
|
|
||||||
|
back, err := covergen.GenerateBackCover(settings)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to render back")
|
||||||
|
}
|
||||||
|
if err := back.OutputFileAndClose(*output2); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to write back to disk")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,11 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
|
|
||||||
|
"github.com/go-pdf/fpdf"
|
||||||
"github.com/thegrumpylion/jsref"
|
"github.com/thegrumpylion/jsref"
|
||||||
|
|
||||||
"covergen/pkg/covergen"
|
"covergen/pkg/covergen"
|
||||||
@@ -72,6 +71,18 @@ func settingsFromValue(arg js.Value) (*covergen.CoverSettings, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pdfToJs(pdf *fpdf.Fpdf) (js.Value, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := pdf.Output(&buf); err != nil {
|
||||||
|
return js.Null(), fmt.Errorf("failed to write pdf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := buf.Bytes()
|
||||||
|
ta := js.Global().Get("Uint8Array").New(len(s))
|
||||||
|
js.CopyBytesToJS(ta, s)
|
||||||
|
return ta, nil
|
||||||
|
}
|
||||||
|
|
||||||
func generateCover(this js.Value, args []js.Value) interface{} {
|
func generateCover(this js.Value, args []js.Value) interface{} {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -98,21 +109,61 @@ func generateCover(this js.Value, args []js.Value) interface{} {
|
|||||||
return jsmap{"error": err.Error()}
|
return jsmap{"error": err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
jsBytes, err := pdfToJs(pdf)
|
||||||
if err = pdf.Output(&buf); err != nil {
|
if err != nil {
|
||||||
|
return jsmap{"error": err.Error()}
|
||||||
|
}
|
||||||
|
return jsBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateSplitCover(this js.Value, args []js.Value) interface{} {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Println("recovered", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if len(args) != 1 {
|
||||||
|
return jsmap{"error": "missing argument"}
|
||||||
|
}
|
||||||
|
|
||||||
|
arg := args[0]
|
||||||
|
if arg.Type() != js.TypeObject {
|
||||||
|
return jsmap{"error": "expected object"}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings, err := settingsFromValue(arg)
|
||||||
|
if err != nil {
|
||||||
return jsmap{"error": err.Error()}
|
return jsmap{"error": err.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
s := buf.Bytes()
|
front, err := covergen.GenerateFrontCover(*settings)
|
||||||
sum := sha256.Sum256(s)
|
if err != nil {
|
||||||
fmt.Println("shasum", hex.EncodeToString(sum[:]))
|
return jsmap{"error": fmt.Sprintf("failed to render front: %s", err.Error())}
|
||||||
|
}
|
||||||
|
|
||||||
ta := js.Global().Get("Uint8Array").New(len(s))
|
back, err := covergen.GenerateBackCover(*settings)
|
||||||
js.CopyBytesToJS(ta, s)
|
if err != nil {
|
||||||
return ta
|
return jsmap{"error": fmt.Sprintf("failed to render back: %s", err.Error())}
|
||||||
|
}
|
||||||
|
|
||||||
|
frontJSBytes, err := pdfToJs(front)
|
||||||
|
if err != nil {
|
||||||
|
return jsmap{"error": fmt.Sprintf("failed to render front: %s", err.Error())}
|
||||||
|
}
|
||||||
|
backJSBytes, err := pdfToJs(back)
|
||||||
|
if err != nil {
|
||||||
|
return jsmap{"error": fmt.Sprintf("failed to render back: %s", err.Error())}
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsmap{
|
||||||
|
"front": frontJSBytes,
|
||||||
|
"back": backJSBytes,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
js.Global().Set("covergen", js.FuncOf(generateCover))
|
js.Global().Set("generateCover", js.FuncOf(generateCover))
|
||||||
|
js.Global().Set("generateSplitCover", js.FuncOf(generateSplitCover))
|
||||||
<-make(chan bool)
|
<-make(chan bool)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-pdf/fpdf"
|
"github.com/go-pdf/fpdf"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const margin = 20
|
const margin = 20
|
||||||
@@ -44,17 +43,58 @@ type CoverSettings struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GenerateInvoice(settings CoverSettings) (*fpdf.Fpdf, error) {
|
func GenerateInvoice(settings CoverSettings) (*fpdf.Fpdf, error) {
|
||||||
|
pdf, err := GenerateFrontCover(settings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pdf.AddPage()
|
||||||
|
drawCustomerName(pdf, settings)
|
||||||
|
|
||||||
|
if pdf.Err() {
|
||||||
|
return nil, pdf.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
return pdf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateFrontCover(settings CoverSettings) (*fpdf.Fpdf, error) {
|
||||||
|
pdf, err := generateBaseInvoice(settings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
drawInvoiceNumber(pdf, settings)
|
||||||
|
drawMiwebbLink(pdf)
|
||||||
|
|
||||||
|
if pdf.Err() {
|
||||||
|
return nil, pdf.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
return pdf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateBackCover(settings CoverSettings) (*fpdf.Fpdf, error) {
|
||||||
|
pdf, err := generateBaseInvoice(settings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pdf, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateBaseInvoice(settings CoverSettings) (*fpdf.Fpdf, error) {
|
||||||
pdf := fpdf.New("P", scaleUnit, "A4", "")
|
pdf := fpdf.New("P", scaleUnit, "A4", "")
|
||||||
|
|
||||||
if err := addEmbeddedFont(pdf, "Mark Medium", "", "Mark-Medium"); err != nil {
|
if err := addEmbeddedFont(pdf, "Mark Medium", "", "Mark-Medium"); err != nil {
|
||||||
log.Fatal().Err(err).Msg("Error adding font")
|
return nil, fmt.Errorf("error adding font: %w", err)
|
||||||
}
|
}
|
||||||
if err := addEmbeddedFont(pdf, "Mark Light", "", "Mark-Light"); err != nil {
|
if err := addEmbeddedFont(pdf, "Mark Light", "", "Mark-Light"); err != nil {
|
||||||
log.Fatal().Err(err).Msg("Error adding font")
|
return nil, fmt.Errorf("error adding font: %w", err)
|
||||||
}
|
}
|
||||||
miwebbLogo, err := addEmbeddedLogo(pdf, miwebbLogoName, miwebbLogoPath)
|
miwebbLogo, err := addEmbeddedLogo(pdf, miwebbLogoName, "miwebb.white.png")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Error adding logo")
|
return nil, fmt.Errorf("error adding logo: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pdf.SetMargins(margin, margin, margin)
|
pdf.SetMargins(margin, margin, margin)
|
||||||
@@ -67,21 +107,16 @@ func GenerateInvoice(settings CoverSettings) (*fpdf.Fpdf, error) {
|
|||||||
pdf.Image(miwebbLogoName, margin-5, margin, miwebbLogo.Width()*logoScale, miwebbLogo.Height()*logoScale, false, "", 0, "")
|
pdf.Image(miwebbLogoName, margin-5, margin, miwebbLogo.Width()*logoScale, miwebbLogo.Height()*logoScale, false, "", 0, "")
|
||||||
})
|
})
|
||||||
|
|
||||||
pdf.AddPage()
|
|
||||||
|
|
||||||
drawCustomerName(pdf, settings)
|
|
||||||
drawInvoiceNumber(pdf, settings)
|
|
||||||
drawMiwebbLink(pdf)
|
|
||||||
|
|
||||||
pdf.AddPage()
|
pdf.AddPage()
|
||||||
drawCustomerName(pdf, settings)
|
drawCustomerName(pdf, settings)
|
||||||
|
|
||||||
if pdf.Err() {
|
if pdf.Err() {
|
||||||
return nil, pdf.Error()
|
return nil, fmt.Errorf("error generating invoice: %w", pdf.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return pdf, nil
|
return pdf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func drawInvoiceNumber(pdf *fpdf.Fpdf, settings CoverSettings) {
|
func drawInvoiceNumber(pdf *fpdf.Fpdf, settings CoverSettings) {
|
||||||
_, pageHeight := pdf.GetPageSize()
|
_, pageHeight := pdf.GetPageSize()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user