Exemple #1
0
func main() {
	log.SetFlags(log.Lshortfile)

	// Process arguments
	if len(os.Args) != 2 {
		log.Fatalln("Usage: single [file.pdf]")
	}
	filename := os.Args[1]

	// open pdf document
	single, err := pdf.Open(filename)
	if err != nil {
		log.Fatalln(err)
	}
	defer single.Close()

	// create references to input pages
	catalog := single.Get(single.Root).(pdf.Dictionary)
	pages := getPages(single, catalog["Pages"].(pdf.ObjectReference))

	// output to A4
	paper_width := 595.224
	paper_height := 841.824

	// assume that all pages are the same size
	media_box_obj := pages[0].Object.(pdf.Dictionary)["MediaBox"]
	var media_box pdf.Array
	if media_box_obj == nil {
		// the first page inherits its MediaBox, therefore get it from the root
		pages_ref := catalog["Pages"].(pdf.ObjectReference)
		pages := single.Get(pages_ref)
		media_box = pages.(pdf.Dictionary)["MediaBox"].(pdf.Array)
	} else {
		media_box = media_box_obj.(pdf.Array)
	}

	var page_width float64
	switch typed := media_box[2].(type) {
	case pdf.Real:
		page_width = float64(typed)
	case pdf.Integer:
		page_width = float64(typed)
	default:
		panic(reflect.TypeOf(typed).Name())
	}

	var page_height float64
	switch typed := media_box[3].(type) {
	case pdf.Real:
		page_height = float64(typed)
	case pdf.Integer:
		page_height = float64(typed)
	default:
		panic(reflect.TypeOf(typed).Name())
	}

	num_pages := len(pages)

	// assuming that all the pages are the same size
	// the sum of the page areas must fit in the paper area
	// paper_area >= scale_factor² * num_pages * page_area
	paper_area := paper_width * paper_height
	page_area := page_width * page_height
	scale_factor := math.Sqrt(paper_area / float64(num_pages) / page_area)
	scaled_page_width := scale_factor * page_width
	nx := int(math.Ceil(paper_width / scaled_page_width))
	ny := num_pages / nx
	for (nx * ny) < num_pages {
		ny++
	}

	// adjust scale_factor to fit the new page count
	scale_factor_width := paper_width / float64(nx) / page_width
	scale_factor_height := paper_height / float64(ny) / page_height
	if scale_factor_width > scale_factor_height {
		scale_factor = scale_factor_height
	} else {
		scale_factor = scale_factor_width
	}

	xobjects := pdf.Dictionary{}
	stream := &bytes.Buffer{} // content stream for the single page

	// move to upper left
	fmt.Fprintf(stream, "1 0 0 1 %v %v cm ", 0, paper_height-(page_height*scale_factor))

	// if the pages won't fill up the paper, center them on the paper
	top_margin := (paper_height - (scale_factor * page_height * float64(ny))) / 2.0
	left_margin := (paper_width - (scale_factor * page_width * float64(nx))) / 2.0
	fmt.Fprintf(stream, "1 0 0 1 %v %v cm ", left_margin, -top_margin)

	// scale the pages
	fmt.Fprintf(stream, "%v 0 0 %v 0 0 cm ", scale_factor, scale_factor)

	for page_num, page := range pages {
		page := page.Object.(pdf.Dictionary)

		page["Type"] = pdf.Name("XObject")
		page["Subtype"] = pdf.Name("Form")
		page["BBox"] = media_box

		// consolidate the contents
		contents := []byte{}
		switch typed := page["Contents"].(type) {
		case pdf.ObjectReference:
			page_contents_obj := single.Get(typed)
			page_contents := page_contents_obj.(pdf.Stream)
			contents = page_contents.Stream
			page["Filter"] = page_contents.Dictionary["Filter"]
		case pdf.Array:
			if len(typed) == 1 {
				page_contents_obj := single.Get(typed[0].(pdf.ObjectReference))
				page_contents := page_contents_obj.(pdf.Stream)
				contents = page_contents.Stream
				page["Filter"] = page_contents.Dictionary["Filter"]
			} else {
				for _, page_contents_ref := range typed {
					page_contents_obj := single.Get(page_contents_ref.(pdf.ObjectReference))
					decoded, err := page_contents_obj.(pdf.Stream).Decode()
					if err != nil {
						log.Fatalln(err)
					}

					contents = append(contents, decoded...)
				}
			}
		default:
			panic(reflect.TypeOf(typed).Name())
		}

		// add the xobject to the pdf
		xobj_ref, err := single.Add(pdf.Stream{
			Dictionary: page,
			Stream:     contents,
		})
		if err != nil {
			log.Fatalln(err)
		}

		// draw the page
		page_name := fmt.Sprintf("Page%d", page_num)
		xobjects[pdf.Name(page_name)] = xobj_ref
		stream.WriteString("/" + page_name + " Do ")

		// draw rectangle around the page
		fmt.Fprintf(stream, "0 0 %v %v re S ", page_width, page_height)

		// move to where the next page goes
		if (page_num+1)%nx == 0 {
			// move to first page of next line of pages
			fmt.Fprintf(stream, "1 0 0 1 %v %v cm ", -page_width*float64(nx-1), -page_height)
		} else {
			// next page in same line
			fmt.Fprintf(stream, "1 0 0 1 %v %v cm ", page_width, 0)
		}
	}

	// Pages for single
	single_pages := pdf.Dictionary{
		pdf.Name("Type"): pdf.Name("Pages"),
	}
	single_pages_ref, err := single.Add(single_pages)
	if err != nil {
		log.Fatalln(err)
	}

	// content for single page
	contents := pdf.Stream{
		Stream: stream.Bytes(),
	}
	contents_ref, err := single.Add(contents)
	if err != nil {
		log.Fatalln(err)
	}

	// add page
	single_page := pdf.Dictionary{
		pdf.Name("Type"):   pdf.Name("Page"),
		pdf.Name("Parent"): single_pages_ref,
		pdf.Name("Resources"): pdf.Dictionary{
			pdf.Name("XObject"): xobjects,
		},
		pdf.Name("MediaBox"): pdf.Array{
			pdf.Integer(0),
			pdf.Integer(0),
			pdf.Real(paper_width),  // width
			pdf.Real(paper_height), // height
		},
		pdf.Name("Contents"): contents_ref,
	}
	single_page_ref, err := single.Add(single_page)
	if err != nil {
		log.Fatalln(err)
	}

	// update pages list
	single_pages["Kids"] = pdf.Array{single_page_ref}
	single_pages["Count"] = pdf.Integer(1)
	_, err = single.Add(pdf.IndirectObject{
		ObjectReference: single_pages_ref,
		Object:          single_pages,
	})
	if err != nil {
		log.Fatalln(err)
	}

	// catalog for single
	catalog["Pages"] = single_pages_ref
	_, err = single.Add(pdf.IndirectObject{
		ObjectReference: single.Root,
		Object:          catalog,
	})
	if err != nil {
		log.Fatalln(err)
	}

	// close files
	err = single.Save()
	if err != nil {
		log.Fatalln(err)
	}
}
Exemple #2
0
func main() {
	log.SetFlags(log.Lshortfile)

	binding := flag.String("binding", "chapbook", "Type of binding to generate {perfect, chapbook, none}. Default is chapbook.")
	flag.Parse()

	switch *binding {
	case "chapbook", "perfect", "none":
		// no-op
	default:
		usage()
	}

	// Process arguments
	if flag.NArg() != 1 {
		usage()
	}
	filename := flag.Arg(0)

	// open pdf document
	book, err := pdf.Open(filename)
	if err != nil {
		log.Fatalln(err)
	}
	defer book.Close()

	// get the pdf page references
	pagesRef := book.Get(book.Root).(pdf.Dictionary)["Pages"].(pdf.ObjectReference)
	pages := getPages(book, pagesRef)

	// assuming that all pages are the same size, figure out the
	// media box that will be the bbox of the xobject
	mediaBoxObj := pages[0]["MediaBox"]
	var mediaBox pdf.Array
	if mediaBoxObj == nil {
		// the first page inherits its MediaBox, therefore get it from the root
		pages := book.Get(pagesRef)
		mediaBox = pages.(pdf.Dictionary)["MediaBox"].(pdf.Array)
	} else {
		mediaBox = mediaBoxObj.(pdf.Array)
	}

	// change the pages to xobjects
	pageXobjects := []pdf.ObjectReference{}
	for _, page := range pages {
		page["Type"] = pdf.Name("XObject")
		page["Subtype"] = pdf.Name("Form")
		page["BBox"] = mediaBox

		// consolidate the contents into the xobject stream
		contents := []byte{}
		switch typed := page["Contents"].(type) {
		case pdf.ObjectReference:
			pageContents := book.Get(typed).(pdf.Stream)
			contents = pageContents.Stream
			page["Filter"] = pageContents.Dictionary["Filter"]
		case pdf.Array:
			if len(typed) == 1 {
				pageContents := book.Get(typed[0].(pdf.ObjectReference)).(pdf.Stream)
				contents = pageContents.Stream
				page["Filter"] = pageContents.Dictionary["Filter"]
			} else {
				for _, pageContentsRef := range typed {
					pageContents := book.Get(pageContentsRef.(pdf.ObjectReference)).(pdf.Stream)
					decoded, err := pageContents.Decode()
					if err != nil {
						log.Fatalln(err)
					}

					contents = append(contents, decoded...)
				}
			}
		default:
			panic(reflect.TypeOf(typed).Name())
		}
		delete(page, "Contents")

		// add the xobject to the pdf
		xobjRef, err := book.Add(pdf.Stream{
			Dictionary: page,
			Stream:     contents,
		})
		if err != nil {
			log.Fatalln(err)
		}
		pageXobjects = append(pageXobjects, xobjRef)
	}

	// figure out how many pages to layout for
	numDocumentPages := len(pages)
	numPagesToLayout := numDocumentPages
	switch *binding {
	case "perfect", "chapbook":
		if (numPagesToLayout % 4) != 0 {
			numPagesToLayout = numDocumentPages + (4 - (numDocumentPages % 4))
		}
	case "none":
		numDocumentPages++
		if (numPagesToLayout % 2) != 0 {
			numPagesToLayout++
		}
	}

	// layout on landscape version of page size
	paperHeight := toFloat64(mediaBox[3])      // same height as the original page
	paperWidth := toFloat64(mediaBox[2]) * 2.0 // twice the width of the original page

	// layout the pages
	layedOutPages := pdf.Array{}
	stream := &bytes.Buffer{}
	xobjects := pdf.Dictionary{}
	showPage := false
	flipNextPage := true
	for pageToLayout := 0; pageToLayout < numPagesToLayout; pageToLayout++ {
		var pageNum int
		switch *binding {
		case "perfect":
			// determine the real page number for perfect bound books
			pageNum = pageToLayout - 1
			if pageToLayout%4 == 0 {
				pageNum += 4
			}
		case "chapbook":
			// determine the real page number for chapbooks
			pageNum = pageToLayout / 2
			if pageToLayout%2 == 1 {
				pageNum = numPagesToLayout - pageNum - 1
			}
		case "none":
			pageNum = pageToLayout - 1
			flipNextPage = false
		default:
			log.Println("unhandled binding:", *binding)
			usage()
		}

		// only render non-blank pages
		if pageNum < numDocumentPages && pageNum >= 0 {
			fmt.Fprintf(stream, "q ")
			// horizontal offset for recto (odd) pages
			// this correctly handles 0 based indexes for 1 based page numbers
			if pageNum%2 == 0 {
				fmt.Fprintf(stream, "1 0 0 1 %v %v cm ", paperWidth/2.0, 0)
			}

			// render the page
			pageName := fmt.Sprintf("Page%d", pageNum)
			xobjects[pdf.Name(pageName)] = pageXobjects[pageNum]
			fmt.Fprintf(stream, "/%v Do Q ", pageName)
		}

		// emit layouts after drawing both pages
		if showPage {
			// content for book page
			contents := pdf.Stream{
				Stream: stream.Bytes(),
			}
			contentsRef, err := book.Add(contents)
			if err != nil {
				log.Fatalln(err)
			}

			// add page
			bookPage := pdf.Dictionary{
				pdf.Name("Type"):   pdf.Name("Page"),
				pdf.Name("Parent"): pagesRef,
				pdf.Name("Resources"): pdf.Dictionary{
					pdf.Name("XObject"): xobjects,
				},
				pdf.Name("Contents"): contentsRef,
			}
			bookPageRef, err := book.Add(bookPage)
			if err != nil {
				log.Fatalln(err)
			}
			layedOutPages = append(layedOutPages, bookPageRef)

			// reset the stream and xobjects
			stream = &bytes.Buffer{}
			xobjects = pdf.Dictionary{}

			// flip the next page over
			if flipNextPage {
				fmt.Fprintf(stream, "%f %f %f %f %v %v cm ",
					math.Cos(math.Pi),
					math.Sin(math.Pi),
					-math.Sin(math.Pi),
					math.Cos(math.Pi),
					paperWidth,
					paperHeight,
				)
			}
			flipNextPage = !flipNextPage
		}
		showPage = !showPage
	}

	// Page tree for book
	bookPages := pdf.Dictionary{
		"Type":  pdf.Name("Pages"),
		"Kids":  layedOutPages,
		"Count": pdf.Integer(len(layedOutPages)),
		"MediaBox": pdf.Array{
			pdf.Integer(0),
			pdf.Integer(0),
			pdf.Real(paperWidth),  // width
			pdf.Real(paperHeight), // height
		},
	}
	_, err = book.Add(pdf.IndirectObject{
		ObjectReference: pagesRef,
		Object:          bookPages,
	})
	if err != nil {
		log.Fatalln(err)
	}

	// save
	err = book.Save()
	if err != nil {
		log.Fatalln(err)
	}
}