Skip to content

Commit

Permalink
examples: add PDF export and TOC generation examples
Browse files Browse the repository at this point in the history
This only works on Windows as it uses OLE to drive MS Word, but
it allows offloading complex functionality to the actual MS Word
instead of implementing a Word renderer.

Fixes unidoc#185
  • Loading branch information
tbaliance committed Jun 4, 2018
1 parent 9471100 commit a78d579
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 0 deletions.
61 changes: 61 additions & 0 deletions _examples/document/pdf-export-ole/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2017 Baliance. All rights reserved.
package main

import (
"log"
"os"
"path/filepath"

"baliance.com/gooxml/document"
"github.com/go-ole/go-ole/oleutil"
)

// NOTE: This example can only run on Windows and requires that Word be installed.

func main() {
doc := document.New()

para := doc.AddParagraph()
run := para.AddRun()
para.SetStyle("Title")
run.AddText("Simple Document Formatting")

para = doc.AddParagraph()
para.SetStyle("Heading1")
run = para.AddRun()
run.AddText("Some Heading Text")

para = doc.AddParagraph()
para.SetStyle("Heading2")
run = para.AddRun()
run.AddText("Some Heading Text")
doc.SaveToFile("simple.docx")

cwd, _ := os.Getwd()
ConvertToPDF(filepath.Join(cwd, "simple.docx"), filepath.Join(cwd, "simple.pdf"))
}

// ConvertToPDF uses go-ole to convert a docx to a PDF using the Word application
func ConvertToPDF(source, destination string) {
ole.CoInitialize(0)
defer ole.CoUninitialize()

iunk, err := oleutil.CreateObject("Word.Application")
if err != nil {
log.Fatalf("error creating Word object: %s", err)
}

word := iunk.MustQueryInterface(ole.IID_IDispatch)
defer word.Release()

// opening then saving works due to the call to doc.Settings.SetUpdateFieldsOnOpen(true) above

docs := oleutil.MustGetProperty(word, "Documents").ToIDispatch()
wordDoc := oleutil.MustCallMethod(docs, "Open", source).ToIDispatch()

// file format constant comes from https://msdn.microsoft.com/en-us/vba/word-vba/articles/wdsaveformat-enumeration-word
const wdFormatPDF = 17
oleutil.MustCallMethod(wordDoc, "SaveAs2", destination, wdFormatPDF)
oleutil.MustCallMethod(wordDoc, "Close")
oleutil.MustCallMethod(word, "Quit")
}
95 changes: 95 additions & 0 deletions _examples/document/toc-generation-ole/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2017 Baliance. All rights reserved.
package main

import (
"log"
"os"
"path/filepath"

"baliance.com/gooxml/document"
"baliance.com/gooxml/measurement"
"baliance.com/gooxml/schema/soo/wml"

"github.com/go-ole/go-ole/oleutil"
)

// NOTE: This example can only run on Windows and requires that Word be installed.

var lorem = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin lobortis, lectus dictum feugiat tempus, sem neque finibus enim, sed eleifend sem nunc ac diam. Vestibulum tempus sagittis elementum`

func main() {
doc := document.New()

// Force the TOC to update upon opening the document
doc.Settings.SetUpdateFieldsOnOpen(true)

// Add a TOC
doc.AddParagraph().AddRun().AddField(document.FieldTOC)
// followed by a page break
doc.AddParagraph().Properties().AddSection(wml.ST_SectionMarkNextPage)

nd := doc.Numbering.AddDefinition()
for i := 0; i < 9; i++ {
lvl := nd.AddLevel()
lvl.SetFormat(wml.ST_NumberFormatNone)
lvl.SetAlignment(wml.ST_JcLeft)
if i%2 == 0 {
lvl.SetFormat(wml.ST_NumberFormatBullet)
lvl.RunProperties().SetFontFamily("Symbol")
lvl.SetText("")
}
lvl.Properties().SetLeftIndent(0.5 * measurement.Distance(i) * measurement.Inch)
}

// and finally paragraphs at different heading levels
for i := 0; i < 4; i++ {
para := doc.AddParagraph()
para.SetNumberingDefinition(nd)
para.Properties().SetHeadingLevel(1)
para.AddRun().AddText("First Level")

doc.AddParagraph().AddRun().AddText(lorem)
for i := 0; i < 3; i++ {
para := doc.AddParagraph()
para.SetNumberingDefinition(nd)
para.Properties().SetHeadingLevel(2)
para.AddRun().AddText("Second Level")
doc.AddParagraph().AddRun().AddText(lorem)

para = doc.AddParagraph()
para.SetNumberingDefinition(nd)
para.Properties().SetHeadingLevel(3)
para.AddRun().AddText("Third Level")
doc.AddParagraph().AddRun().AddText(lorem)
}
}
doc.SaveToFile("toc.docx")

cwd, _ := os.Getwd()
UpdateFields(filepath.Join(cwd, "toc.docx"))
}

// UpdateFields uses go-ole to convert a docx to a PDF using the Word application
func UpdateFields(source string) {
ole.CoInitialize(0)
defer ole.CoUninitialize()

iunk, err := oleutil.CreateObject("Word.Application")
if err != nil {
log.Fatalf("error creating Word object: %s", err)
}
defer iunk.Release()

word := iunk.MustQueryInterface(ole.IID_IDispatch)
defer word.Release()

docs := oleutil.MustGetProperty(word, "Documents").ToIDispatch()
defer docs.Release()
wordDoc := oleutil.MustCallMethod(docs, "Open", source).ToIDispatch()
defer wordDoc.Release()

const wdFormatXMLDocument = 12
oleutil.MustCallMethod(wordDoc, "SaveAs2", source, wdFormatXMLDocument)
oleutil.MustCallMethod(wordDoc, "Close")
oleutil.MustCallMethod(word, "Quit")
}

0 comments on commit a78d579

Please sign in to comment.