4
Makefile
4
Makefile
@@ -34,8 +34,8 @@ deps:
|
|||||||
|
|
||||||
.PHONY: pdf
|
.PHONY: pdf
|
||||||
pdf: build deps
|
pdf: build deps
|
||||||
@echo "Generating PDF with headless Chrome..."
|
@echo "Generating PDF by rendering each page and merging..."
|
||||||
@$(GO) run ./cmd/pdf --in $(OUT_DIR)/print.html --out $(PDF)
|
@$(GO) run ./cmd/pdfbook --pages $(OUT_DIR) --order pages.yaml --out $(PDF)
|
||||||
|
|
||||||
.PHONY: pdf-2up
|
.PHONY: pdf-2up
|
||||||
pdf-2up: build deps
|
pdf-2up: build deps
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Heading Now";
|
font-family: "Heading Now";
|
||||||
src: url("/assets/font/HeadingNow-95Medium.otf");
|
src: url("../font/HeadingNow-95Medium.otf");
|
||||||
}
|
}
|
||||||
|
|
||||||
.ytmnd-1 {
|
.ytmnd-1 {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -27,152 +26,6 @@ func floatToIn(v float64) string {
|
|||||||
return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.4fin", v), "0"), ".")
|
return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.4fin", v), "0"), ".")
|
||||||
}
|
}
|
||||||
|
|
||||||
func min(a, b int) int {
|
|
||||||
if a < b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractAndScopeCSS extracts <style> blocks from HTML content and scopes them to a page-specific selector
|
|
||||||
func extractAndScopeCSS(content string, pageID string) (string, string) {
|
|
||||||
styleRegex := regexp.MustCompile(`(?s)<style[^>]*>(.*?)</style>`)
|
|
||||||
matches := styleRegex.FindAllStringSubmatch(content, -1)
|
|
||||||
|
|
||||||
var scopedCSS strings.Builder
|
|
||||||
contentWithoutCSS := content
|
|
||||||
|
|
||||||
// Remove all <style> blocks from content
|
|
||||||
contentWithoutCSS = styleRegex.ReplaceAllString(contentWithoutCSS, "")
|
|
||||||
|
|
||||||
// Process each CSS block and scope it
|
|
||||||
for _, match := range matches {
|
|
||||||
cssContent := strings.TrimSpace(match[1])
|
|
||||||
if cssContent == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scope the CSS rules to the page
|
|
||||||
scopedCSS.WriteString(scopeCSS(cssContent, pageID))
|
|
||||||
scopedCSS.WriteString("\n\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return contentWithoutCSS, scopedCSS.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// scopeCSS wraps CSS rules with a page-specific selector
|
|
||||||
func scopeCSS(css string, pageID string) string {
|
|
||||||
// Simple CSS scoping - wrap all rules with the page selector
|
|
||||||
lines := strings.Split(css, "\n")
|
|
||||||
var scopedLines []string
|
|
||||||
var inRule bool
|
|
||||||
var ruleBuffer strings.Builder
|
|
||||||
|
|
||||||
for _, line := range lines {
|
|
||||||
trimmed := strings.TrimSpace(line)
|
|
||||||
|
|
||||||
// Skip comments and empty lines
|
|
||||||
if trimmed == "" || strings.HasPrefix(trimmed, "/*") {
|
|
||||||
scopedLines = append(scopedLines, line)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle @-rules (like @font-face, @media) - don't scope these
|
|
||||||
if strings.HasPrefix(trimmed, "@") {
|
|
||||||
if inRule {
|
|
||||||
// Finish current rule first
|
|
||||||
scopedLines = append(scopedLines, scopeRuleBuffer(ruleBuffer.String(), pageID))
|
|
||||||
ruleBuffer.Reset()
|
|
||||||
inRule = false
|
|
||||||
}
|
|
||||||
scopedLines = append(scopedLines, line)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect start of CSS rule
|
|
||||||
if strings.Contains(trimmed, "{") && !inRule {
|
|
||||||
inRule = true
|
|
||||||
ruleBuffer.WriteString(line)
|
|
||||||
ruleBuffer.WriteString("\n")
|
|
||||||
} else if inRule {
|
|
||||||
ruleBuffer.WriteString(line)
|
|
||||||
ruleBuffer.WriteString("\n")
|
|
||||||
|
|
||||||
// Check if rule ends
|
|
||||||
if strings.Contains(trimmed, "}") {
|
|
||||||
scopedLines = append(scopedLines, scopeRuleBuffer(ruleBuffer.String(), pageID))
|
|
||||||
ruleBuffer.Reset()
|
|
||||||
inRule = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Standalone line - might be a selector
|
|
||||||
if strings.Contains(trimmed, "{") {
|
|
||||||
ruleBuffer.WriteString(line)
|
|
||||||
ruleBuffer.WriteString("\n")
|
|
||||||
inRule = true
|
|
||||||
} else {
|
|
||||||
scopedLines = append(scopedLines, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle any remaining rule
|
|
||||||
if inRule && ruleBuffer.Len() > 0 {
|
|
||||||
scopedLines = append(scopedLines, scopeRuleBuffer(ruleBuffer.String(), pageID))
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(scopedLines, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func scopeRuleBuffer(rule string, pageID string) string {
|
|
||||||
lines := strings.Split(strings.TrimSpace(rule), "\n")
|
|
||||||
if len(lines) == 0 {
|
|
||||||
return rule
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the selector line (first line before {)
|
|
||||||
for i, line := range lines {
|
|
||||||
if strings.Contains(line, "{") {
|
|
||||||
// Extract selector part
|
|
||||||
parts := strings.Split(line, "{")
|
|
||||||
if len(parts) >= 2 {
|
|
||||||
selector := strings.TrimSpace(parts[0])
|
|
||||||
rest := "{" + strings.Join(parts[1:], "{")
|
|
||||||
|
|
||||||
// Scope the selector
|
|
||||||
scopedSelector := scopeSelector(selector, pageID)
|
|
||||||
lines[i] = scopedSelector + " " + rest
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(lines, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func scopeSelector(selector string, pageID string) string {
|
|
||||||
// Split multiple selectors by comma
|
|
||||||
selectors := strings.Split(selector, ",")
|
|
||||||
var scopedSelectors []string
|
|
||||||
|
|
||||||
for _, sel := range selectors {
|
|
||||||
sel = strings.TrimSpace(sel)
|
|
||||||
if sel == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't scope selectors that already include the page ID or are body/html
|
|
||||||
if strings.Contains(sel, "#"+pageID) || sel == "body" || sel == "html" {
|
|
||||||
scopedSelectors = append(scopedSelectors, sel)
|
|
||||||
} else {
|
|
||||||
// Scope to page
|
|
||||||
scopedSelectors = append(scopedSelectors, "#"+pageID+" "+sel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(scopedSelectors, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
type PageData struct {
|
type PageData struct {
|
||||||
Title string
|
Title string
|
||||||
Content template.HTML
|
Content template.HTML
|
||||||
@@ -184,9 +37,7 @@ type IndexPage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PrintPage struct {
|
type PrintPage struct {
|
||||||
Content template.HTML
|
Content template.HTML
|
||||||
PageID string
|
|
||||||
ScopedCSS template.HTML
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TwoUpSheet struct {
|
type TwoUpSheet struct {
|
||||||
@@ -309,52 +160,11 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build print.html containing all pages one after another for PDF
|
// Prepare pages for aggregate printing (2-up, etc.)
|
||||||
printTplPath := filepath.Join(templatesDir, "print.gohtml")
|
|
||||||
printTpl := template.Must(template.New("print").Parse(mustReadFile(printTplPath)))
|
|
||||||
var printPages []PrintPage
|
var printPages []PrintPage
|
||||||
var allScopedCSS strings.Builder
|
|
||||||
|
|
||||||
for _, pagePath := range pageFiles {
|
for _, pagePath := range pageFiles {
|
||||||
content := strings.TrimSpace(mustReadFile(pagePath))
|
content := strings.TrimSpace(mustReadFile(pagePath))
|
||||||
|
printPages = append(printPages, PrintPage{Content: template.HTML(content)})
|
||||||
// Generate unique page ID based on filename
|
|
||||||
base := filepath.Base(pagePath)
|
|
||||||
pageID := strings.TrimSuffix(base, filepath.Ext(base)) + "-page"
|
|
||||||
|
|
||||||
// Extract and scope CSS
|
|
||||||
contentWithoutCSS, scopedCSS := extractAndScopeCSS(content, pageID)
|
|
||||||
|
|
||||||
// Collect all scoped CSS
|
|
||||||
if scopedCSS != "" {
|
|
||||||
allScopedCSS.WriteString(scopedCSS)
|
|
||||||
allScopedCSS.WriteString("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
printPages = append(printPages, PrintPage{
|
|
||||||
Content: template.HTML(contentWithoutCSS),
|
|
||||||
PageID: pageID,
|
|
||||||
ScopedCSS: template.HTML(scopedCSS),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
var outPrint bytes.Buffer
|
|
||||||
if err := printTpl.Execute(&outPrint, map[string]any{
|
|
||||||
"Pages": printPages,
|
|
||||||
"PageSizeAttr": pageSizeStyleAttr(config.PageWidthIn, config.PageHeightIn),
|
|
||||||
}); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inject scoped CSS into the output
|
|
||||||
cssToInject := allScopedCSS.String()
|
|
||||||
finalOutput := strings.ReplaceAll(
|
|
||||||
outPrint.String(),
|
|
||||||
"PLACEHOLDER_FOR_SCOPED_CSS",
|
|
||||||
cssToInject,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := writeFile(filepath.Join(outDir, "print.html"), []byte(finalOutput)); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build 2-up print HTML (letter pages with two half-letter pages side by side)
|
// Build 2-up print HTML (letter pages with two half-letter pages side by side)
|
||||||
@@ -371,20 +181,10 @@ func main() {
|
|||||||
print2TplPath := filepath.Join(templatesDir, "print_2up.gohtml")
|
print2TplPath := filepath.Join(templatesDir, "print_2up.gohtml")
|
||||||
print2Tpl := template.Must(template.New("print2up").Parse(mustReadFile(print2TplPath)))
|
print2Tpl := template.Must(template.New("print2up").Parse(mustReadFile(print2TplPath)))
|
||||||
var outPrint2 bytes.Buffer
|
var outPrint2 bytes.Buffer
|
||||||
if err := print2Tpl.Execute(&outPrint2, map[string]any{
|
if err := print2Tpl.Execute(&outPrint2, map[string]any{"Sheets": sheets}); err != nil {
|
||||||
"Sheets": sheets,
|
|
||||||
}); err != nil {
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
if err := writeFile(filepath.Join(outDir, "print_2up.html"), outPrint2.Bytes()); err != nil {
|
||||||
// Inject scoped CSS into the 2-up output
|
|
||||||
finalOutput2Up := strings.ReplaceAll(
|
|
||||||
outPrint2.String(),
|
|
||||||
"PLACEHOLDER_FOR_SCOPED_CSS",
|
|
||||||
cssToInject,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := writeFile(filepath.Join(outDir, "print_2up.html"), []byte(finalOutput2Up)); err != nil {
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
go.mod
17
go.mod
@@ -1,11 +1,14 @@
|
|||||||
module smartbar
|
module smartbar
|
||||||
|
|
||||||
go 1.21
|
go 1.23.0
|
||||||
|
|
||||||
|
toolchain go1.24.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732
|
github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732
|
||||||
github.com/chromedp/chromedp v0.9.5
|
github.com/chromedp/chromedp v0.9.5
|
||||||
github.com/fsnotify/fsnotify v1.9.0
|
github.com/fsnotify/fsnotify v1.9.0
|
||||||
|
github.com/pdfcpu/pdfcpu v0.11.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -13,7 +16,17 @@ require (
|
|||||||
github.com/gobwas/httphead v0.1.0 // indirect
|
github.com/gobwas/httphead v0.1.0 // indirect
|
||||||
github.com/gobwas/pool v0.2.1 // indirect
|
github.com/gobwas/pool v0.2.1 // indirect
|
||||||
github.com/gobwas/ws v1.3.2 // indirect
|
github.com/gobwas/ws v1.3.2 // indirect
|
||||||
|
github.com/hhrutter/lzw v1.0.0 // indirect
|
||||||
|
github.com/hhrutter/pkcs7 v0.2.0 // indirect
|
||||||
|
github.com/hhrutter/tiff v1.0.2 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
golang.org/x/sys v0.16.0 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
golang.org/x/crypto v0.38.0 // indirect
|
||||||
|
golang.org/x/image v0.27.0 // indirect
|
||||||
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
|
golang.org/x/text v0.25.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
28
go.sum
28
go.sum
@@ -12,14 +12,40 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
|||||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
|
github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
|
||||||
github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||||
|
github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0=
|
||||||
|
github.com/hhrutter/lzw v1.0.0/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo=
|
||||||
|
github.com/hhrutter/pkcs7 v0.2.0 h1:i4HN2XMbGQpZRnKBLsUwO3dSckzgX142TNqY/KfXg+I=
|
||||||
|
github.com/hhrutter/pkcs7 v0.2.0/go.mod h1:aEzKz0+ZAlz7YaEMY47jDHL14hVWD6iXt0AgqgAvWgE=
|
||||||
|
github.com/hhrutter/tiff v1.0.2 h1:7H3FQQpKu/i5WaSChoD1nnJbGx4MxU5TlNqqpxw55z8=
|
||||||
|
github.com/hhrutter/tiff v1.0.2/go.mod h1:pcOeuK5loFUE7Y/WnzGw20YxUdnqjY1P0Jlcieb/cCw=
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
|
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
|
||||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||||
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
||||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||||
|
github.com/pdfcpu/pdfcpu v0.11.0 h1:mL18Y3hSHzSezmnrzA21TqlayBOXuAx7BUzzZyroLGM=
|
||||||
|
github.com/pdfcpu/pdfcpu v0.11.0/go.mod h1:F1ca4GIVFdPtmgvIdvXAycAm88noyNxZwzr9CpTy+Mw=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||||
|
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||||
|
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
|
||||||
|
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||||
|
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
background:
|
background:
|
||||||
linear-gradient(rgba(0,0,0,0.35), rgba(0,0,0,0.35)),
|
linear-gradient(rgba(0,0,0,0.35), rgba(0,0,0,0.35)),
|
||||||
url("/assets/img/cover.jpg") no-repeat center center;
|
url("assets/img/cover.jpg") no-repeat center center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<style>
|
<style>
|
||||||
#hack-the-planet-page {
|
#hack-the-planet-page {
|
||||||
background: url("/assets/img/hack-the-planet.jpg") no-repeat center center;
|
background: url("assets/img/hack-the-planet.jpg") no-repeat center center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<style>
|
<style>
|
||||||
#wtf-page {
|
#wtf-page {
|
||||||
background: url("/assets/img/wtf.png") no-repeat center center;
|
background: url("assets/img/wtf.png") no-repeat center center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
|
||||||
font: 8.75pt/1.40 Tahoma, sans-serif;
|
font: 8.75pt/1.40 Tahoma, sans-serif;
|
||||||
@@ -133,4 +133,4 @@
|
|||||||
<h3>TRENT SNEEK</h3>
|
<h3>TRENT SNEEK</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</style>
|
</div>
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>Print</title>
|
<title>Print</title>
|
||||||
<link rel="stylesheet" href="assets/css/base.css" />
|
<link rel="stylesheet" href="assets/css/base.css" />
|
||||||
|
<link rel="stylesheet" href="assets/css/smartbar.css" />
|
||||||
<style>
|
<style>
|
||||||
/* Ensure a clean print with no gaps between pages */
|
/* Ensure a clean print with no gaps between pages */
|
||||||
@media screen { body { background: #eeeeee; } }
|
@media screen { body { background: #eeeeee; } }
|
||||||
@@ -13,17 +14,15 @@
|
|||||||
@media print {
|
@media print {
|
||||||
html, body { width: auto !important; height: auto !important; overflow: visible !important; margin: 0 !important; padding: 0 !important; background: white !important; }
|
html, body { width: auto !important; height: auto !important; overflow: visible !important; margin: 0 !important; padding: 0 !important; background: white !important; }
|
||||||
}
|
}
|
||||||
/* Each .page-container is a fixed-size sheet */
|
/* Each #page is a fixed-size sheet */
|
||||||
.page-container { width: var(--page-w, 5.5in); height: var(--page-h, 8.5in); background: white; box-shadow: none !important; overflow: hidden; }
|
#page { width: var(--page-w, 5.5in); height: var(--page-h, 8.5in); background: white; box-shadow: none !important; overflow: hidden; }
|
||||||
.page-container { page-break-after: always; break-after: page; }
|
#page { page-break-after: always; break-after: page; }
|
||||||
.page-container:last-child { page-break-after: auto; break-after: auto; }
|
#page:last-child { page-break-after: auto; break-after: auto; }
|
||||||
|
|
||||||
PLACEHOLDER_FOR_SCOPED_CSS
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body {{ .PageSizeAttr }}>
|
<body {{ .PageSizeAttr }}>
|
||||||
{{ range .Pages }}
|
{{ range .Pages }}
|
||||||
<div class="page-container" id="{{ .PageID }}">{{ .Content }}</div>
|
<div id="page">{{ .Content }}</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -14,18 +14,16 @@
|
|||||||
/* Each .sheet is one letter-sized page */
|
/* Each .sheet is one letter-sized page */
|
||||||
.sheet { display: flex; gap: 0; margin: 0; padding: 0; width: 11in; height: 8.5in; }
|
.sheet { display: flex; gap: 0; margin: 0; padding: 0; width: 11in; height: 8.5in; }
|
||||||
.cell { width: 5.5in; height: 8.5in; overflow: hidden; }
|
.cell { width: 5.5in; height: 8.5in; overflow: hidden; }
|
||||||
.cell > .page-content { width: 5.5in; height: 8.5in; box-shadow: none !important; }
|
.cell > #page { width: 5.5in; height: 8.5in; box-shadow: none !important; }
|
||||||
.sheet { page-break-after: always; break-after: page; }
|
.sheet { page-break-after: always; break-after: page; }
|
||||||
.sheet:last-child { page-break-after: auto; break-after: auto; }
|
.sheet:last-child { page-break-after: auto; break-after: auto; }
|
||||||
|
|
||||||
PLACEHOLDER_FOR_SCOPED_CSS
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{ range .Sheets }}
|
{{ range .Sheets }}
|
||||||
<div class="sheet">
|
<div class="sheet">
|
||||||
<div class="cell"><div class="page-content" id="{{ .Left.PageID }}">{{ .Left.Content }}</div></div>
|
<div class="cell"><div id="page">{{ .Left.Content }}</div></div>
|
||||||
{{ if .Right }}<div class="cell"><div class="page-content" id="{{ .Right.PageID }}">{{ .Right.Content }}</div></div>{{ end }}
|
{{ if .Right }}<div class="cell"><div id="page">{{ .Right.Content }}</div></div>{{ end }}
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user