package report import ( "archive/zip" "bytes" "encoding/base64" "encoding/xml" "fmt" "io" "net/http" "path/filepath" "strings" ) // Relationships maps the pkg rId to the actual media file type Relationships struct { XMLName xml.Name `xml:"Relationships"` Rel []struct { Id string `xml:"Id,attr"` Target string `xml:"Target,attr"` } `xml:"Relationship"` } func ServeDOCXAsHTML(w http.ResponseWriter, docxPath string) { r, err := zip.OpenReader(docxPath) if err != nil { http.Error(w, "Failed to open DOCX archive", http.StatusInternalServerError) return } defer r.Close() relsMap := make(map[string]string) for _, f := range r.File { if f.Name == "word/_rels/document.xml.rels" { rc, _ := f.Open() var rels Relationships xml.NewDecoder(rc).Decode(&rels) rc.Close() for _, rel := range rels.Rel { relsMap[rel.Id] = rel.Target } break } } mediaMap := make(map[string]string) for _, f := range r.File { if strings.HasPrefix(f.Name, "word/media/") { rc, _ := f.Open() data, _ := io.ReadAll(rc) rc.Close() ext := strings.TrimPrefix(filepath.Ext(f.Name), ".") if ext == "jpeg" || ext == "jpg" { ext = "jpeg" } b64 := base64.StdEncoding.EncodeToString(data) mediaMap[f.Name] = fmt.Sprintf("data:image/%s;base64,%s", ext, b64) } } var htmlOutput bytes.Buffer var inParagraph bool for _, f := range r.File { if f.Name == "word/document.xml" { rc, _ := f.Open() decoder := xml.NewDecoder(rc) for { token, err := decoder.Token() if err != nil { break } switch se := token.(type) { case xml.StartElement: if se.Name.Local == "p" { htmlOutput.WriteString("

") inParagraph = true } if se.Name.Local == "t" { var text string decoder.DecodeElement(&text, &se) htmlOutput.WriteString(text) } if se.Name.Local == "blip" { for _, attr := range se.Attr { if attr.Name.Local == "embed" { targetPath := relsMap[attr.Value] fullMediaPath := "word/" + targetPath if b64URI, exists := mediaMap[fullMediaPath]; exists { imgTag := fmt.Sprintf(`

`, b64URI) htmlOutput.WriteString(imgTag) } } } } case xml.EndElement: if se.Name.Local == "p" && inParagraph { htmlOutput.WriteString("

\n") inParagraph = false } } } rc.Close() break } } w.Write(htmlOutput.Bytes()) }