I have this function generating files from html files and I can't seem to get a dynamic footer with page count to generate -> I need to be able to do this on the backend with Go/HTML so the javascript solutions don't work for me
Here's my code
{{define "Count"}}Invoice Footer{{end}}
{{define "invoiceFooter"}}
<foot>
<script>
var pdfInfo = {};
var x = document.location.search.substring(1).split('&');
for (var i in x) { var z = x[i].split('=',2); pdfInfo[z[0]] = unescape(z[1]); }
function getPdfInfo() {
var page = pdfInfo.page || 1;
var pageCount = pdfInfo.topage || 1;
document.getElementsByClassName('page').textContent = page;
document.getElementsByClassName('topage').textContent = pageCount;
}
</script>
</foot>
<body style="border:0; margin: 0;" onload="getPdfInfo()">
<table style="border-bottom: 1px solid black; width: 100%">
<tr>
<td class="--footer-center"></td>
<td style="text-align:center">
Page <span class="page"></span> of <span class="topage"></span>
</td>
</tr>
</table>
{{end}}
func generatePDF(data *cs.FileData) (pdfgBytes []byte, err error) {
var templ *template.Template
_, err = os.Getwd()
if err != nil {
return nil, err
}
tmplFiles, err := fs.ReadDir(templateDir, "templates")
if err != nil {
return nil, err
}
files := []string{}
for _, file := range tmplFiles {
if file.IsDir() {
continue
}
files = append(files, fmt.Sprintf("templates/%s", file.Name()))
}
// use Go's default HTML template generation tools to generate your HTML
if templ = template.Must(template.ParseFS(templateDir, files...)); err != nil {
return
}
// apply the parsed HTML template data and keep the result in a Buffer
var body bytes.Buffer
if err = templ.Execute(&body, data); err != nil {
return
}
// Create new PDF generator
pdfg, err := pdf.NewPDFGenerator()
if err != nil {
return
}
// read the HTML page as a PDF page
page := pdf.NewPageReader(bytes.NewReader(body.Bytes()))
// enable this if the HTML file contains local references such as images, CSS, etc.
page.EnableLocalFileAccess.Set(true)
//page := NewPage(`D:\body.html`)
page.FooterHTML.Set(`--footer-html`)
page.FooterCenter.Set("test")
page.FooterFontSize.Set(7)
//page.FooterRight.Set("[page]")
// add the page to your generator
pdfg.AddPage(page)
I've tried variations of all of these:
Medium Article
Github Issue
Stack Overflow
Related
I read this solution for resolve body data from a proxy.
Golang: how to read response body of ReverseProxy?
But I cannot read the body as a plain string, maybe the encoding is not right or other cryption.
My question is how to encode or transform the body to readable html string?
Currently I get:
n8�������♠�♠�A��b:J���g>-��ˤ���[���.....
Code example:
reverseProxy := httputil.NewSingleHostReverseProxy(url)
reverseProxy.ModifyResponse = func (resp *http.Response) (err error) {
var contentType = resp.Header.Get("Content-Type")
if strings.HasPrefix(contentType, "text/html") {
b, err := ioutil.ReadAll(resp.Body) //Read html
if err != nil {
return err
}
err = resp.Body.Close()
if err != nil {
return err
}
body := ioutil.NopCloser(bytes.NewReader(b))
resp.Body = body
resp.ContentLength = int64(len(b))
log.Printf(string(b))
}
return nil
}
I am trying to block CSS and images request in chromedp but I can't find proper details or code about that. if anyone know please msg me some reference code.
in puppeteer, I am using below code to block image and CSS.
await page.on('request', (req) => {
if (
req.resourceType() == 'stylesheet' ||
req.resourceType() == 'font' ||
req.resourceType() == 'image' ||
req.resourceType() == 'media'
) {
req.abort();
} else {
req.continue();
}
});
As per below github method, I tried but not work.
https://github.com/chromedp/chromedp/issues/260
package main
import (
"context"
"io/ioutil"
"os"
"time"
"github.com/chromedp/cdproto/cdp"
"github.com/chromedp/cdproto/fetch"
"github.com/chromedp/cdproto/network"
"github.com/chromedp/chromedp"
)
func main() {
dir, err := ioutil.TempDir("", "chromedp-example")
if err != nil {
panic(err)
}
defer os.RemoveAll(dir)
opts := append(chromedp.DefaultExecAllocatorOptions[:],
chromedp.DisableGPU,
chromedp.NoDefaultBrowserCheck,
chromedp.Flag("headless", false),
chromedp.Flag("ignore-certificate-errors", true),
chromedp.UserDataDir(dir),
)
allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
defer cancel()
// also set up a custom logger
taskCtx, cancel := chromedp.NewContext(allocCtx)
defer cancel()
// create a timeout
taskCtx, cancel = context.WithTimeout(taskCtx, 100*time.Second)
defer cancel()
// ensure that the browser process is started
if err := chromedp.Run(taskCtx); err != nil {
panic(err)
}
// listen network event
chromedp.ListenTarget(taskCtx, DisableImageLoad(taskCtx))
chromedp.Run(taskCtx,
network.Enable(),
fetch.Enable(),
chromedp.Navigate(`http://www.yahoo.com`),
chromedp.WaitVisible(`body`, chromedp.BySearch),
)
}
func DisableImageLoad(ctx context.Context) func(event interface{}) {
return func(event interface{}) {
switch ev := event.(type) {
case *fetch.EventRequestPaused:
go func() {
c := chromedp.FromContext(ctx)
ctx := cdp.WithExecutor(ctx, c.Target)
if ev.ResourceType == network.ResourceTypeImage {
fetch.FailRequest(ev.RequestID, network.ErrorReasonConnectionAborted).Do(ctx)
} else {
fetch.ContinueRequest(ev.RequestID).Do(ctx)
}
}()
}
}
}
I'm new to Go programming and I am writing a function to post into an alternate key as the primary key auto-increments using Go, Mysql, and template.HTML package.
After troubleshooting for the last few days I am loading a blank html page.
Any suggestions on best practice or ways I should troubleshoot in the future would be so appreciated.
Thank you so much for any help you are willing to offer!
I used linters and formatters to ensure that the code is correctly formatted, used print statements, and rebuilt the function and html content from scratch to narrow it down.
HTTP handler
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"text/template"
_ "github.com/go-sql-driver/mysql"
)
type watchInfo struct {
ID int
Brand string
}
var (
tpl *template.Template
// cnn, err = sql.Open("mysql", "root:root#tcp(db:3306)/appdb")
)
//function to connect to db
func dbConn() (db *sql.DB) {
dbDriver := "mysql"
dbUser := "root"
dbPass := "root"
dbName := "appdb"
db, err := sql.Open(dbDriver, dbUser+":"+dbPass+"#tcp"+"(db:3306)/"+dbName)
if err != nil {
fmt.Println("dbConn not work")
}
return db
}
func init() {
tpl = template.Must(template.ParseGlob("/go/templates/*"))
}
func main() {
http.HandleFunc("/", index)
http.HandleFunc("/upload", uploadWatchInfo)
http.ListenAndServe(":8080", nil)
}
//This function get watch id and brand using getWatch function and passes them to the gohtml template file
func index(w http.ResponseWriter, r *http.Request) {
db := dbConn()
defer db.Close()
scanWatches, err := db.Query("SELECT * FROM watches order by id")
if err != nil {
log.Fatal(err)
}
err = db.Ping()
if err != nil {
fmt.Println(err.Error())
}
watch := watchInfo{}
watchSlice := []watchInfo{}
for scanWatches.Next() {
var id int
var brand string
err = scanWatches.Scan(&id, &brand)
if err != nil {
fmt.Println("sW.Scan didn't work")
}
watch.ID = id
watch.Brand = brand
watchSlice = append(watchSlice, watch)
}
tpl.ExecuteTemplate(w, "pic.gohtml", watchSlice)
}
func uploadWatchInfo(w http.ResponseWriter, r *http.Request) {
db := dbConn()
defer db.Close()
if r.Method == "POST" {
brand := r.FormValue("brand")
insForm, err := db.Prepare("INSERT INTO watches(brand) VALUES(?)")
if err != nil {
panic(err.Error)
}
insForm.Exec(brand)
err = tpl.ExecuteTemplate(w, "upload.gohtml", watchInfo{Brand: brand})
if err != nil {
panic(err.Error)
}
}
}
//this is handling an error and can be called in other page functions
func HandleError(w http.ResponseWriter, err error) {
if err != nil {
fmt.Println("Index did not work. error in index")
}
}
HTML Template
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload page</title>
</head>
<body>
<h1>upload page</h1>
<form method="POST" action="insert">
<label> brand </label><input type="text" name="brand"/><br />
<input type="submit" value="Save brand"/>
</form>
Index
</body>
</html>
I Expect my handler to insert the string value into the database and print the confirmation in the terminal.
How do I can extract positional offset for specific node of already parsed HTML document? For example, for document <div>Hello, <b>World!</b></div> I want to be able to know that offset of World! is 15:21. Document may be changed while parsing.
I have a solution to render whole document with special marks, but it's really bad for performance. Any ideas?
package main
import (
"bytes"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
"log"
"strings"
)
func nodeIndexOffset(context *html.Node, node *html.Node) (int, int) {
if node.Type != html.TextNode {
node = node.FirstChild
}
originalData := node.Data
var buf bytes.Buffer
node.Data = "|start|" + originalData
_ = html.Render(&buf, context.FirstChild)
start := strings.Index(buf.String(), "|start|")
buf = bytes.Buffer{}
node.Data = originalData + "|end|"
_ = html.Render(&buf, context.FirstChild)
end := strings.Index(buf.String(), "|end|")
node.Data = originalData
return start, end
}
func main() {
s := "<div>Hello, <b>World!</b></div>"
var context html.Node
context = html.Node{
Type: html.ElementNode,
Data: "body",
DataAtom: atom.Body,
}
nodes, err := html.ParseFragment(strings.NewReader(s), &context)
if err != nil {
log.Fatal(err)
}
for _, node := range nodes {
context.AppendChild(node)
}
world := nodes[0].FirstChild.NextSibling.FirstChild
log.Println("target", world)
log.Println(nodeIndexOffset(&context, world))
}
Not an answer, but too long for a comment. The following could work to some extent:
Use a Tokenizer and step through each element one by one.
Wrap your input into a custom reader which records lines and
column offsets as the Tokenizer reads from it.
Query your custom reader for the position before and after calling Next()
to record the approximate position information you need.
This is a bit painful and not too accurate but probably the best you could do.
I come up with solution where we extend (please fix me if there's another way to do it) original HTML package with additional custom.go file with new exported function. This function is able to access unexported data property of Tokenizer, which holds exactly start and end position of current Node. We have to adjust positions after each buffer read. See globalBufDif.
I don't really like that I have to fork the package only to access couple of properties, but seems like this is a Go way.
func parseWithIndexes(p *parser) (map[*Node][2]int, error) {
// Iterate until EOF. Any other error will cause an early return.
var err error
var globalBufDif int
var prevEndBuf int
var tokenIndex [2]int
tokenMap := make(map[*Node][2]int)
for err != io.EOF {
// CDATA sections are allowed only in foreign content.
n := p.oe.top()
p.tokenizer.AllowCDATA(n != nil && n.Namespace != "")
t := p.top().FirstChild
for {
if t != nil && t.NextSibling != nil {
t = t.NextSibling
} else {
break
}
}
tokenMap[t] = tokenIndex
if prevEndBuf > p.tokenizer.data.end {
globalBufDif += prevEndBuf
}
prevEndBuf = p.tokenizer.data.end
// Read and parse the next token.
p.tokenizer.Next()
tokenIndex = [2]int{p.tokenizer.data.start + globalBufDif, p.tokenizer.data.end + globalBufDif}
p.tok = p.tokenizer.Token()
if p.tok.Type == ErrorToken {
err = p.tokenizer.Err()
if err != nil && err != io.EOF {
return tokenMap, err
}
}
p.parseCurrentToken()
}
return tokenMap, nil
}
// ParseFragmentWithIndexes parses a fragment of HTML and returns the nodes
// that were found. If the fragment is the InnerHTML for an existing element,
// pass that element in context.
func ParseFragmentWithIndexes(r io.Reader, context *Node) ([]*Node, map[*Node][2]int, error) {
contextTag := ""
if context != nil {
if context.Type != ElementNode {
return nil, nil, errors.New("html: ParseFragment of non-element Node")
}
// The next check isn't just context.DataAtom.String() == context.Data because
// it is valid to pass an element whose tag isn't a known atom. For example,
// DataAtom == 0 and Data = "tagfromthefuture" is perfectly consistent.
if context.DataAtom != a.Lookup([]byte(context.Data)) {
return nil, nil, fmt.Errorf("html: inconsistent Node: DataAtom=%q, Data=%q", context.DataAtom, context.Data)
}
contextTag = context.DataAtom.String()
}
p := &parser{
tokenizer: NewTokenizerFragment(r, contextTag),
doc: &Node{
Type: DocumentNode,
},
scripting: true,
fragment: true,
context: context,
}
root := &Node{
Type: ElementNode,
DataAtom: a.Html,
Data: a.Html.String(),
}
p.doc.AppendChild(root)
p.oe = nodeStack{root}
p.resetInsertionMode()
for n := context; n != nil; n = n.Parent {
if n.Type == ElementNode && n.DataAtom == a.Form {
p.form = n
break
}
}
tokenMap, err := parseWithIndexes(p)
if err != nil {
return nil, nil, err
}
parent := p.doc
if context != nil {
parent = root
}
var result []*Node
for c := parent.FirstChild; c != nil; {
next := c.NextSibling
parent.RemoveChild(c)
result = append(result, c)
c = next
}
return result, tokenMap, nil
}
I am trying to embed results from a Sql Query into an html table using GO as the back end. To iterate row results in Go, the Rows.Next() function is used. This works for printing to the console window, but not for a html table.
Here is my Go Code:
package main
// Database connection Code for http://play.golang.org/p/njPBsg0JjD
import (
"net/http"
"html/template"
"fmt"
"github.com/LukeMauldin/lodbc"
"github.com/jmoiron/sqlx"
"database/sql"
)
//declare database class
var db *sqlx.DB
type webdata struct {
Title string
Heading string
GridTitle string
ColumnHeading [9]string
RowData [9]string
NumOfRows *sql.Rows
}
//this is the function handler to handle '/mbconsole'
func ConsoleHandler(w http.ResponseWriter, r *http.Request) {
//declare an instance of webdata
var wdata webdata
//connect to database
//Set ODBC driver level
lodbc.SetODBCVersion(lodbc.ODBCVersion_3)
var err error
//connect to a Microsoft SQL Server
db, err = sqlx.Open("lodbc", "[connectionstring]")
if err == nil {
fmt.Println("Connection successful")
}else{
fmt.Println("SQL Connection error", err)
}
// Execute the queries
rows, err := db.Query("[Select ...]")
if err != nil {
panic(err.Error())
}
// Get column names
columns, err := rows.Columns()
if err != nil {
panic(err.Error())
}
// Make a slice for the values
values := make([]interface{}, len(columns))
// rows.Scan wants '[]interface{}' as an argument, so we must copy the
// references into such a slice
// See http://code.google.com/p/go-wiki/wiki/InterfaceSlice for details
scanArgs := make([]interface{}, len(values))
for i := range values {
scanArgs[i] = &values[i]
}
//fill table headings, the table returns 9 columns so I just hard coded it
for i:=0;i<9;i++ {
wdata.ColumnHeading[i] = columns[i]
}
wdata.NumOfRows = rows
// Fetch rows
for rows.Next() {
err = rows.Scan(scanArgs...)
if err != nil {
panic(err.Error())
}
// Print data
for i, value := range values {
switch value.(type) {
case nil:
wdata.RowData[i] = "NULL"
case []byte:
wdata.RowData[i] = string(value.([]byte))
default:
wdata.RowData[i] = fmt.Sprint(value)
}
}
}
wdata.Title = "Page Title"
wdata.Heading = "My View"
wdata.GridTitle = "My Grid Title"
//get the template the data will be loaded into
t1, err := template.ParseFiles("template.html")
if t1 == nil {
fmt.Println("File Not Found: ", err)
}
//load the template with data and display
terr := t1.Execute(w, &wdata)
if terr != nil {
fmt.Println("terr: ", terr)
}
db = db.Unsafe()
defer db.Close()
}
func main() {
http.HandleFunc("/",ConsoleHandler)
}
Here is my template.html
<html>
<head><title>{{.Title}}</title></head><body>
...
<h1>{{.Heading}}</h1>
<div id="gridviewcontainer">
<br />
<div id="gridtitle">
{{.GridTitle}}
</div>
<table id="gridtable">
<tr>{{range $ColumnIdx, $colheading := .ColumnHeading}}
<th>{{$colheading}}</th>{{end}}</tr>
<<!---This is what is causing the issue, .NumOfRows is not a valid field, must be array, channel, pipeline, or map -->
{{range $index, $rowval := .NumOfRows}}
<tr>
{{range $rowidx, $rowdat := .RowData}}<td>{{$rowdat}}</td>{{end}}
</tr>
{{endfor}}
</table>
...
</body>
</html>
I connect to the database correctly and using the "fmt" package I can print correctly. But I can't figure out how to loop through for number of rows retured in the html page. Is there a way to cast sql.Rows to a correct type or loop for a set integer number of times in html.
ps.
I tried using {{ $index := 3}}...{end}} in the html, but that didn't work
Any input would be greatly appreciated
At the start of a new row, insert a "NewRow" string to serve as a flag in the html. Then in the {{range $rowidx, $rowdat := .RowData}} loop add an if statement that ends and starts a new row if $rowdat == "NewRow"
I use this variant:
Go
func MainPageHandler(w http.ResponseWriter, r *http.Request) {
type User struct {
Name1 string
Name2 string
}
rows, err := database.Query("select .......;")
if err != nil {
log.Println(err)
}
defer rows.Close()
user_current := []User{}
for rows.Next() {
p := User{}
err := rows.Scan(&p.Name1, &p.Name2 )
if err != nil {
fmt.Println(err)
continue
}
user_current = append(user_current, p)
}
tmpl, _ := template.ParseFiles("main_page.html")
tmpl.Execute(w, user_current)
}
html
<table>
<thead><th>name1/th><th>name2</th></thead>
{{range . }}
<tr>
<td>{{.Name1}}</td>
<td>{{.Name2}}</td>
</tr>
{{end}}
</table>