From f36cce9a01c3f8d31ea830f50c359811f8b85fd8 Mon Sep 17 00:00:00 2001 From: CarterPerez-dev Date: Wed, 13 May 2026 03:04:36 -0400 Subject: [PATCH] feat(canary): docx template (binary blob + build helper) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 4 Task 4.1+4.2 — pure-Go OOXML template builder under backend/cmd/builddocxtemplate/. Run: go run ./cmd/builddocxtemplate \ -out ./internal/token/generators/docx/template/template.docx produces a minimal valid .docx (5 zip entries, mixed STORE/DEFLATE compression) whose word/footer2.xml carries an INCLUDEPICTURE field referencing the literal placeholder string HONEY_TRACK_URL with the \\d switch (forces fetch-on-open, no local cache). The committed template.docx is the embedded source for the Phase 4 docx generator (next commit). Mixed compression methods in the template make Task 4.3's TestGenerate_PreservesCompressionMethods a meaningful regression guard — a hardcode-DEFLATE generator would now mismatch on [Content_Types].xml + word/_rels/document.xml.rels (both STORE). cmd/builddocxtemplate is excluded from the production binary (separate cmd/ subdir from cmd/canary). --- .../backend/cmd/builddocxtemplate/main.go | 127 ++++++++++++++++++ .../generators/docx/template/template.docx | Bin 0 -> 2333 bytes 2 files changed, 127 insertions(+) create mode 100644 PROJECTS/beginner/canary-token-generator/backend/cmd/builddocxtemplate/main.go create mode 100644 PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/docx/template/template.docx diff --git a/PROJECTS/beginner/canary-token-generator/backend/cmd/builddocxtemplate/main.go b/PROJECTS/beginner/canary-token-generator/backend/cmd/builddocxtemplate/main.go new file mode 100644 index 00000000..5bfbba1d --- /dev/null +++ b/PROJECTS/beginner/canary-token-generator/backend/cmd/builddocxtemplate/main.go @@ -0,0 +1,127 @@ +// ©AngelaMos | 2026 +// main.go + +package main + +import ( + "archive/zip" + "flag" + "fmt" + "log" + "os" + "path/filepath" +) + +const ( + contentTypesXML = ` + + + + + + +` + + packageRelsXML = ` + + + +` + + documentXML = ` + + + Internal document - confidential. Do not redistribute. + + + + + + + +` + + documentRelsXML = ` + + + +` + + footer2XML = ` + + + + INCLUDEPICTURE "HONEY_TRACK_URL" \d \* MERGEFORMAT + + + +` + + pathContentTypes = "[Content_Types].xml" + pathPackageRels = "_rels/.rels" + pathDocument = "word/document.xml" + pathDocumentRels = "word/_rels/document.xml.rels" + pathFooter2 = "word/footer2.xml" + + dirPerm os.FileMode = 0o755 + filePerm os.FileMode = 0o644 +) + +type entry struct { + name string + body string + method uint16 +} + +func entries() []entry { + return []entry{ + {name: pathContentTypes, body: contentTypesXML, method: zip.Store}, + {name: pathPackageRels, body: packageRelsXML, method: zip.Deflate}, + {name: pathDocument, body: documentXML, method: zip.Deflate}, + {name: pathDocumentRels, body: documentRelsXML, method: zip.Store}, + {name: pathFooter2, body: footer2XML, method: zip.Deflate}, + } +} + +func main() { + out := flag.String("out", "", "output path for template.docx") + flag.Parse() + + if *out == "" { + log.Fatal("usage: builddocxtemplate -out ") + } + + if err := buildTemplate(*out); err != nil { + log.Fatalf("build docx template: %v", err) + } + + fmt.Printf("wrote %s\n", *out) +} + +func buildTemplate(out string) error { + if err := os.MkdirAll(filepath.Dir(out), dirPerm); err != nil { + return fmt.Errorf("mkdir parent: %w", err) + } + + f, err := os.OpenFile(out, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, filePerm) + if err != nil { + return fmt.Errorf("open output: %w", err) + } + defer f.Close() + + w := zip.NewWriter(f) + for _, e := range entries() { + hdr := &zip.FileHeader{Name: e.name, Method: e.method} + fw, cErr := w.CreateHeader(hdr) + if cErr != nil { + return fmt.Errorf("create %s: %w", e.name, cErr) + } + if _, wErr := fw.Write([]byte(e.body)); wErr != nil { + return fmt.Errorf("write %s: %w", e.name, wErr) + } + } + if err := w.Close(); err != nil { + return fmt.Errorf("close zip writer: %w", err) + } + return nil +} diff --git a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/docx/template/template.docx b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/docx/template/template.docx new file mode 100644 index 0000000000000000000000000000000000000000..be7405d170c57f0b5d50b559bec2b8a7304581bb GIT binary patch literal 2333 zcmWIWW@Zs#;9$T6!a$*D=lr~q)Vz}TkjjG8;#j?k+#DNwAfZr}T2!2wpJ%INsAr(0 zkeZjApOTrEZmSd;;-+h%q)=Rvn3s~6lb@Grt5lgcSOVl0rRx_YCTAz6r|KIS7?|lNL!6;o0&<3u9hZWF zjZ11;Vrfo^f@=lPfe^P8rREeXDL|bJvd&g1v7jI)GdZyY=vMu*yc9HxbfK2$0d<1a z6lY`>6l()rqNI<@5|AWrI}lp^fk9N1nUbmykXTgWmzWE5jDC53QHp*_esXCpFib&# ziN_=?uFX$N%S=v%>j#-uP?VpXT3iea``jEoxJ+(hW*(9+a5^O|Kfffk$cXwbffx*P zf{i{n?(Mh&yxBSSNpFa?VPaqa;Q(mj!%TkMz{D31icft|+!Xhm-{^P5K!D|fUHryd z^DNvO1-goPopy<}@^5(Ko0RxIDvo*Q{^bi5Iv(A+_@Xz>=Dp=X_0Zl!V*Y;{9Nwt9 z#Odxkt6$#FaZ84Mddta2Q$@}Po9_!M(&c(B)_rK6@am&$w|+|L`PIAWavaykd6$(; zcQ4y}<*LZ@`**#b)k(z!evdC7h|Ht5Y3?t}AvYoC1I z>Ob9bh53^=no>8yc{O@$E#=*r_t)*Sv1vM$w0Lu)lGKi*w&i)(#on&4KQG}L`%GZ7 z9-G?4C9kc%3!gI!pJEXA%s}9LSdpvEwbmP|%jAl`70z+4&6sumYff!Tx#T~2Nh!Ab zk3auroXwHE;gROElgoMT`mNmdLU`xvOY0`=+LjS_s%rDOlixBF4AY*Nx4bjuD%#?B zca80ed5<2*x_vYYc{MXcC9vmQdSbm5cUW%hmJ;u$TbJEF)U&tdG4FML=7|dR#~CG) z`&VV`R5N){u;IDFf{5z$hWNXutvJ87AJ6>rF?y=%^S=)ZavVf$C2!PM*_i!5Q@eGK zN3hWb`@G3-j(fhVP!gQ=UZ&v3hjORa6L@PZ*X)R`J^WEe``$LaW$(kpZzaX=+V5Gm z+a4GpkSJn_X*rSt$ORCYg(ONL1}P+w zjaBeWu~jPaOmPEedL;!&0ZF(ZhLk!k&{7Lh9-~;L56SI76BCQlQ%h`>kc$;yIclSi z?2KYaF~Fj?^`ROtyg?XuF(3d7ZzLm2`h2}VJBYM?|0z0scg@4MA>vAkGlP;=9j){5 z5OZ4Cb23+IhJIR`+TZU+dR%$?vC+!y|7#{*BZWy>EV1O@#rIrCAb$yf68+nG~77$9YPx^u?gQYZKMW6`yRlCp!Dt%9?2L(%`8L`oCj;)~#8c`mri!74M;i&4=x8 zOuJgV^H%unRNlg;VZU!blb^O^_xnCU9si^`O^;8e149IoVy7BB@Mi&rUO6}f8JR>F z5cN26Aq%R*MCP93F96X?8ga9pUP?L}nRe(1uP?Ujz6$l-H0eXxb!~+09oICyi literal 0 HcmV?d00001