< iOS samples

ElementBuilderTest - Swift

Illustrates how to use PDFNet page writing API, how to embed fonts and images and how to copy graphical elements from one page to another.

//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2017 by PDFTron Systems Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

import PDFNet
import Foundation

func runElementBuilderTest() -> Int {
    return autoreleasepool {
        var ret: Int = 0
        
        
        do {
            try PTPDFNet.catchException {
                let doc: PTPDFDoc = PTPDFDoc()

                let eb: PTElementBuilder = PTElementBuilder()   // ElementBuilder is used to build new Element objects
                let writer: PTElementWriter = PTElementWriter() // ElementWriter is used to write Elements to the page
                
                var element: PTElement
                var gstate: PTGState
                
                // Start a new page ------------------------------------
                var page: PTPage? = doc.pageCreate(PTPDFRect(x1: 0, y1: 0, x2: 612, y2: 794))
                
                writer.writerBegin(with: page, placement: e_ptoverlay, page_coord_sys: true, compress: true)    // begin writing to the page
                
                // Create an Image that can be reused in the document or on the same page.
                let img: PTImage = PTImage.create(doc.getSDFDoc(), filename: Bundle.main.path(forResource: "peppers", ofType: "jpg"))
                
                element = eb.createImage(withMatrix: img, mtx: PTMatrix2D(a: Double(img.getWidth() / 2), b: -145, c: 20, d: Double(img.getHeight() / 2), h: 200, v: 150))
                writer.writePlacedElement(element)
                
                gstate = element.getGState()   // use the same image (just change its matrix)
                gstate.setTransform(200, b: 0, c: 0, d: 300, h: 50, v: 450)
                writer.writePlacedElement(element)
                
                // use the same image again (just change its matrix).
                writer.writePlacedElement(eb.createImage(withCornerAndScale: img, x: 300, y: 600, hscale: 200, vscale: -150))
                
                writer.end()    // save changes to the current page
                doc.pagePushBack(page)
                
                // Start a new page ------------------------------------
                // Construct and draw a path object using different styles
                page = doc.pageCreate(PTPDFRect(x1: 0, y1: 0, x2: 612, y2: 794))
                
                writer.writerBegin(with: page, placement: e_ptoverlay, page_coord_sys: true, compress: true)    // begin writing to the page
                eb.reset(PTGState())    // Reset the GState to default
                
                eb.pathBegin()  // start constructing the path
                eb.move(to: 306, y: 396)
                eb.curve(to: 681, cy1: 771, cx2: 399.75, cy2: 864.75, x2: 306, y2: 771)
                eb.curve(to: 212.25, cy1: 864.75, cx2: -69, cy2: 771, x2: 306, y2: 396)
                eb.closePath()
                element = eb.pathEnd()  // the path is now finished
                element.setPathFill(true)    // the path should be filled
                
                // Set the path color space and color
                gstate = element.getGState()
                gstate.setFill(PTColorSpace.createDeviceCMYK())
                gstate.setFillColor(with: PTColorPt(x: 1, y: 0, z: 0, w: 0))   // cyan
                gstate.setTransform(0.5, b: 0, c: 0, d: 0.5, h: -20, v: 300)
                writer.writePlacedElement(element)
                
                // Draw the same path using a different stroke color
                element.setPathStroke(true)   // this path is should be filled and stroked
                gstate.setFillColor(with: PTColorPt(x: 0, y: 0, z: 1, w: 0))    // yellow
                gstate.setStroke(PTColorSpace.createDeviceRGB())
                gstate.setStrokeColor(with: PTColorPt(x: 1, y: 0, z: 0, w: 0))  // red
                gstate.setTransform(0.5, b: 0, c: 0, d: 0.5, h: 280, v: 300)
                gstate.setLineWidth(20)
                writer.writePlacedElement(element)
                
                // Draw the same path with with a given dash pattern
                element.setPathFill(false)  // this path is should be only stroked
                gstate.setStrokeColor(with: PTColorPt(x: 0, y: 0, z: 1, w: 0))  // blue
                gstate.setTransform(0.5, b: 0, c: 0, d: 0.5, h: 280, v: 0)
                let dash_pattern = NSMutableArray(capacity: 1)
                dash_pattern.add(30.0)
                gstate.setDashPattern(dash_pattern, phase: 0)
                writer.writePlacedElement(element)

                // Use the path as a clipping path
                writer.write(eb.createGroupBegin())
                // Save the graphics state
                // Start constructing the new path (the old path was lost when we created
                // a new Element using CreateGroupBegin()).
                eb.pathBegin()
                eb.move(to: 306, y: 396)
                eb.curve(to: 681, cy1: 771, cx2: 399.75, cy2: 864.75, x2: 306, y2: 771)
                eb.curve(to: 212.25, cy1: 864.75, cx2: -69, cy2: 771, x2: 306, y2: 396)
                eb.closePath()
                element = eb.pathEnd()  // path is now constructed
                element.setPathClip(true) // this path is a clipping path
                element.setPathStroke(true)   // this path should be filled and stroked
                gstate = element.getGState()
                gstate.setTransform(0.5, b: 0, c: 0, d: 0.5, h: -20, v: 0)
                
                writer.write(element)
                
                writer.write(eb.createImage(withCornerAndScale: img, x: 100, y: 300, hscale: 400, vscale: 600))
                
                writer.write(eb.createGroupEnd())   // Restore the graphics state
                
                writer.end()    // save changes to the current page
                doc.pagePushBack(page)
                
                // Start a new page ------------------------------------
                page = doc.pageCreate(PTPDFRect(x1: 0, y1: 0, x2: 612, y2: 794))
                
                writer.writerBegin(with: page, placement: e_ptoverlay, page_coord_sys: true, compress: true)    // begin writing to this page
                eb.reset(PTGState())    // Reset the GState to default
                
                // Begin writing a block of text
                element = eb.createTextBegin(with: PTFont.create(doc.getSDFDoc(), type: e_pttimes_roman, embed: false), font_sz: 12)
                writer.write(element)
                
                element = eb.createTextRun("Hello World!")
                element.setTextMatrix(10, b: 0, c: 0, d: 10, h: 0, v: 600)
                element.getGState().setLeading(15)  // Set the spacing between lines
                writer.write(element)
                
                writer.write(eb.createTextNewLine()) // New line
                
                element = eb.createTextRun("Hello World!")
                gstate = element.getGState()
                gstate.setTextRenderMode(e_ptstroke_text)
                gstate.setCharSpacing(-1.25)
                gstate.setWordSpacing(-1.25)
                writer.write(element)
                
                writer.write(eb.createTextNewLine()) // New line
                
                element = eb.createTextRun("Hello World!")
                gstate = element.getGState()
                gstate.setCharSpacing(0)
                gstate.setWordSpacing(0)
                gstate.setLineWidth(3)
                gstate.setTextRenderMode(e_ptfill_stroke_text)
                gstate.setStroke(PTColorSpace.createDeviceRGB())
                gstate.setStrokeColor(with: PTColorPt(x: 1, y: 0, z: 0, w: 0))  // red
                gstate.setFill(PTColorSpace.createDeviceCMYK())
                gstate.setFillColor(with: PTColorPt(x: 1, y: 0, z: 0, w: 0))    // cyan
                writer.write(element)
                
                writer.write(eb.createTextNewLine())    // New line
                
                // Set text as a clipping path to the image.
                element = eb.createTextRun("Hello World!")
                gstate = element.getGState()
                gstate.setTextRenderMode(e_ptclip_text)
                writer.write(element)
                
                // Finish the block of text
                writer.write(eb.createTextEnd())
                
                // Draw an image that will be clipped by the above text
                writer.write(eb.createImage(withCornerAndScale: img, x: 10, y: 100, hscale: 1300, vscale: 720))

                writer.end()    // save changes to the current page
                doc.pagePushBack(page)

                // Start a new page ------------------------------------
                //
                // The example illustrates how to embed the external font in a PDF document.
                // The example also shows how ElementReader can be used to copy and modify
                // Elements between pages.
                
                let reader: PTElementReader = PTElementReader()

                // Start reading Elements from the last page. We will copy all Elements to
                // a new page but will modify the font associated with text.
                reader.begin(doc.getPage(UInt32(doc.getPageCount())))
                
                page = doc.pageCreate(PTPDFRect(x1: 0, y1: 0, x2: 1300, y2: 794))
                
                writer.writerBegin(with: page, placement: e_ptoverlay, page_coord_sys: true, compress: true)    // begin writing to this page
                eb.reset(PTGState())    // Reset the GState to default
                
                // Embed an external font in the document.
                let font = PTFont.createTrueTypeFont(doc.getSDFDoc(), font_path: Bundle.main.path(forResource: "font", ofType: "ttf"), embed: true, subset: true)
                
                while let element = reader.next() {
                    if element.getType().rawValue == e_pttext.rawValue {
                        element.getGState().setFont(font, font_sz: 12)
                    }
                    writer.write(element)
                }
                
                reader.end()
                writer.end()    // save changes to the current page
                
                doc.pagePushBack(page)
                
                // Start a new page ------------------------------------
                //
                // The example illustrates how to embed the external font in a PDF document.
                // The example also shows how ElementReader can be used to copy and modify
                // Elements between pages.
                
                // Start reading Elements from the last page. We will copy all Elements to
                // a new page but will modify the font associated with text.
                reader.begin(doc.getPage(UInt32(doc.getPageCount())))

                page = doc.pageCreate(PTPDFRect(x1: 0, y1: 0, x2: 1300, y2: 794))

                writer.writerBegin(with: page, placement: e_ptoverlay, page_coord_sys: true, compress: true)    // begin writing to this page
                eb.reset(PTGState())    // Reset the GState to default

                // Embed an external font in the document.
                let font2 = PTFont.createType1Font(doc.getSDFDoc(), font_path: Bundle.main.path(forResource: "Misc-Fixed", ofType: "pfa"), embed: true)

                while let element = reader.next() {
                    if element.getType().rawValue == e_pttext.rawValue {
                        element.getGState().setFont(font2, font_sz: 12)
                    }
                    writer.write(element)
                }

                reader.end()
                writer.end()    // save changes to the current page
                doc.pagePushBack(page)

                // Start a new page ------------------------------------
                page = doc.pageCreate(PTPDFRect(x1: 0, y1: 0, x2: 612, y2: 792))

                writer.writerBegin(with: page, placement: e_ptoverlay, page_coord_sys: true, compress: true)    // begin writing to this page
                eb.reset(PTGState())    // Reset the GState to default

                // Begin writing a block of text
                element = eb.createTextBegin(with: PTFont.create(doc.getSDFDoc(), type: e_pttimes_roman, embed: false), font_sz: 12)
                element.setTextMatrix(1.5, b: 0, c: 0, d: 1.5, h: 50, v: 600)
                element.getGState().setLeading(15)    // Set the spacing between lines
                writer.write(element)

                let para = """
                    A PDF text object consists of operators that can show \
                    text strings, move the text position, and set text state and certain \
                    other parameters. In addition, there are three parameters that are \
                    defined only within a text object and do not persist from one text \
                    object to the next: Tm, the text matrix, Tlm, the text line matrix, \
                    Trm, the text rendering matrix, actually just an intermediate result \
                    that combines the effects of text state parameters, the text matrix \
                    (Tm), and the current transformation matrix
                    """
                
                let para_end = para.count
                var text_run = 0
                
                let para_width: Double = 300    // paragraph width is 300 units
                var cur_width: Double = 0
                
                while text_run < para_end {
                    var text_run_end: Int = (para as NSString).range(of: " ", options: .literal, range: NSRange(location: text_run, length: para.count - text_run)).location
                    
                    if text_run_end == NSNotFound {
                        text_run_end = para_end - 1
                    }
                    let str: String = (para as NSString).substring(with: NSRange(location: text_run, length: text_run_end - text_run + 1))
                    element = eb.createTextRun(str)
                    if cur_width + element.getTextLength() < para_width {
                        writer.write(element)
                        cur_width = cur_width + element.getTextLength()
                    }
                    else {
                        writer.write(eb.createTextNewLine())    // New line
                        element = eb.createTextRun(str)
                        cur_width = element.getTextLength()
                        writer.write(element)
                    }
                    
                    text_run = text_run_end + 1
                }
                
                // -----------------------------------------------------------------------
                // The following code snippet illustrates how to adjust spacing between
                // characters (text runs).
                element = eb.createTextNewLine()
                writer.write(element)   // Skip 2 lines
                writer.write(element)
                
                writer.write(eb.createTextRun("An example of space adjustments between inter-characters:"))
                writer.write(eb.createTextNewLine())
                
                // Write string "AWAY" without space adjustments between characters.
                element = eb.createTextRun("AWAY")
                writer.write(element)
                
                writer.write(eb.createTextNewLine())
                
                // Write string "AWAY" with space adjustments between characters.
                element = eb.createTextRun("A")
                writer.write(element)
                
                element = eb.createTextRun("W")
                element.setPosAdjustment(140)
                writer.write(element)
                
                element = eb.createTextRun("A")
                element.setPosAdjustment(140)
                writer.write(element)
                
                element = eb.createTextRun("Y again")
                element.setPosAdjustment(115)
                writer.write(element)
                
                // Draw the same strings using direct content output...
                writer.flush()  // flush pending Element writing operations.
                
                // You can also write page content directly to the content stream using
                // ElementWriter.WriteString(...) and ElementWriter.WriteBuffer(...) methods.
                // Note that if you are planning to use these functions you need to be familiar
                // with PDF page content operators (see Appendix A in PDF Reference Manual).
                // Because it is easy to make mistakes during direct output we recommend that
                // you use ElementBuilder and Element interface instead.
                
                writer.write("T* T* ")  // Skip 2 lines
                writer.write("(Direct output to PDF page content stream:) Tj  T* ")
                writer.write("(AWAY) Tj T* ")
                writer.write("[(A)140(W)140(A)115(Y again)] TJ ")
                // Finish the block of text
                writer.write(eb.createTextEnd())
                
                writer.end()    // save changes to the current page
                doc.pagePushBack(page)
                
                // Start a new page ------------------------------------
                
                // Image Masks
                //
                // In the opaque imaging model, images mark all areas they occupy on the page as
                // if with opaque paint. All portions of the image, whether black, white, gray,
                // or color, completely obscure any marks that may previously have existed in the
                // same place on the page.
                // In the graphic arts industry and page layout applications, however, it is common
                // to crop or 'mask out' the background of an image and then place the masked image
                // on a different background, allowing the existing background to show through the
                // masked areas. This sample illustrates how to use image masks.
                
                page = doc.pageCreate(PTPDFRect(x1: 0, y1: 0, x2: 612, y2: 792))
                writer.writerBegin(with: page, placement: e_ptoverlay, page_coord_sys: true, compress: true)    // begin writing to this page
                
                // Create the Image Mask
                let imgf = PTMappedFile(filename: Bundle.main.path(forResource: "imagemask", ofType: "dat"))
                let mask_read = PTFilterReader(filter: imgf)
                
                let device_gray = PTColorSpace.createDeviceGray()
                let mask: PTImage = PTImage.create(withStreamAndFormat: doc.getSDFDoc(), image_data: mask_read, width: 64, height: 64, bpc: 1, color_space: device_gray, input_format: e_ptascii_hex)
                
                mask.getSDFObj().putBool("ImageMask", value: true)
                element = eb.createRect(0, y: 0, width: 612, height: 794)
                
                element.setPathStroke(false)
                element.setPathFill(true)
                element.getGState().setFill(device_gray)
                element.getGState().setFillColor(with: PTColorPt(x: 0.8, y: 0, z: 0, w: 0))
                writer.writePlacedElement(element)
                
                element = eb.createImage(withMatrix: mask, mtx: PTMatrix2D(a: 200, b: 0, c: 0, d: -200, h: 40, v: 680))
                element.getGState().setFillColor(with: PTColorPt(x: 0.1, y: 0, z: 0, w: 0))
                writer.writePlacedElement(element)
                
                element.getGState().setFill(PTColorSpace.createDeviceRGB())
                element.getGState().setFillColor(with: PTColorPt(x: 1, y: 0, z: 0, w: 0))
                element = eb.createImage(withMatrix: mask, mtx: PTMatrix2D(a: 200, b: 0, c: 0, d: -200, h: 320, v: 680))
                writer.writePlacedElement(element)
                
                element.getGState().setFillColor(with: PTColorPt(x: 0, y: 1, z: 0, w: 0))
                element = eb.createImage(withMatrix: mask, mtx: PTMatrix2D(a: 200, b: 0, c: 0, d: -200, h: 40, v: 380))
                writer.writePlacedElement(element)
                
                do {
                    // This sample illustrates Explicit Masking.
                    let img: PTImage = PTImage.create(withFile: doc.getSDFDoc(), filename: Bundle.main.path(forResource: "peppers", ofType: "jpg"), encoder_hints: PTObj())
                    
                    // mask is the explicit mask for the primary (base) image
                    img.setMaskWith(mask)
                    element = eb.createImage(withMatrix: img, mtx: PTMatrix2D(a: 200, b: 0, c: 0, d: -200, h: 320, v: 380))
                    writer.writePlacedElement(element)
                }
                
                writer.end()    // save changes to the current page
                doc.pagePushBack(page)
                
                // Transparency sample ----------------------------------
                
                // Start a new page -------------------------------------
                page = doc.pageCreate(PTPDFRect(x1: 0, y1: 0, x2: 612, y2: 792))
                writer.writerBegin(with: page, placement: e_ptoverlay, page_coord_sys: true, compress: true)    // begin writing to this page
                eb.reset(PTGState())    // Reset the GState to default
                
                // Write some transparent text at the bottom of the page.
                element = eb.createTextBegin(with: PTFont.create(doc.getSDFDoc(), type: e_pttimes_roman, embed: false), font_sz: 100)
                
                // Set the text knockout attribute. Text knockout must be set outside of
                // the text group.
                gstate = element.getGState()
                gstate.setTextKnockout(false)
                gstate.setBlendMode(e_ptbl_difference)
                writer.write(element)
                
                element = eb.createTextRun("Transparency")
                element.setTextMatrix(1, b: 0, c: 0, d: 1, h: 30, v: 30)
                gstate = element.getGState()
                gstate.setFill(PTColorSpace.createDeviceCMYK())
                gstate.setFillColor(with: PTColorPt(x: 1, y: 0, z: 0, w: 0))
                
                gstate.setFillOpacity(0.5)
                writer.write(element)
                
                // Write the same text on top the old; shifted by 3 points
                element.setTextMatrix(1, b: 0, c: 0, d: 1, h: 33, v: 33)
                gstate.setFillColor(with: PTColorPt(x: 0, y: 1, z: 0, w: 0))
                gstate.setFillOpacity(0.5)
                
                writer.write(element)
                writer.write(eb.createTextEnd())
                
                // Draw three overlapping transparent circles.
                eb.pathBegin()  // start constructing the path
                eb.move(to: 459.223, y: 505.646)
                eb.curve(to: 459.223, cy1: 415.841, cx2: 389.85, cy2: 343.04, x2: 304.273, y2: 343.04)
                eb.curve(to: 218.697, cy1: 343.04, cx2: 149.324, cy2: 415.841, x2: 149.324, y2: 505.646)
                eb.curve(to: 149.324, cy1: 595.45, cx2: 218.697, cy2: 668.25, x2: 304.273, y2: 668.25)
                eb.curve(to: 389.85, cy1: 668.25, cx2: 459.223, cy2: 595.45, x2: 459.223, y2: 505.646)
                element = eb.pathEnd()
                element.setPathFill(true)
                
                gstate = element.getGState()
                gstate.setFill(PTColorSpace.createDeviceRGB())
                gstate.setFillColor(with: PTColorPt(x: 0, y: 0, z: 1, w: 0))    // Blue Circle
                
                gstate.setBlendMode(e_ptbl_normal)
                gstate.setFillOpacity(0.5)
                writer.write(element)
                
                // Translate relative to the Blue Circle
                gstate.setTransform(1, b: 0, c: 0, d: 1, h: 113, v: -185)
                gstate.setFillColor(with: PTColorPt(x: 0, y: 1, z: 0, w: 0))    // Green Circle
                gstate.setFillOpacity(0.5)
                writer.write(element)
                
                // Translate relative to the Green Circle
                gstate.setTransform(1, b: 0, c: 0, d: 1, h: -220, v: 0)
                gstate.setFillColor(with: PTColorPt(x: 1, y: 0, z: 0, w: 0))    // Red Circle
                gstate.setFillOpacity(0.5)
                writer.write(element)
                
                writer.end()    // save changes to the current page
                doc.pagePushBack(page)
                
                // End page ------------------------------------
                
                doc.save(toFile: URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]).appendingPathComponent("element_builder.pdf").path, flags: e_ptremove_unused.rawValue)
                
                // doc.Save((output_path + "element_builder.pdf").c_str(), Doc::e_ptlinearized, NULL);

                print("Done. Result saved in element_builder.pdf...")
            }
        } catch let e as NSError {
            print("Uncaught exception: \(e)")
            ret = 1
        }
        
        return ret
    }
}