| Safe Haskell | Safe-Inferred |
|---|---|
| Language | Haskell2010 |
Graphics.PDF.Documentation
Description
Quick documentation for the PDF library.
For detailed examples, download the tar.gz package from Hackage and look at the test.hs in folder Test.
Synopsis
Creating a document
When you create a document, you must give some information for the PDF file like the author, the default size (the pages can use different sizes if specified) and if the document is compressed.
So, a standard way to start a PDF document is with:
main :: IO()
main = do
let rect = PDFRect 0 0 600 400
runPdf "demo.pdf" (standardDocInfo { author=toPDFString "alpheccar", compressed = False}) rect $ do
myDocument
where myDocument is generating the pages and is a value of the PDF monad.
Adding pages
You can add pages and specify a hierarchical structure for the pages. This hierarchy is optional. Here is an example of how you could add some pages and specify the table of contents:
myDocument ::addPageNothingnewSection(toPDFString"Section") Nothing Nothing $ donewSection(toPDFString"Subsection") Nothing Nothing $ do createPageContent page1
when you use addPage you can specify a different size for the page or use the document's default one.
In newSection, the two Maybe options are used to style the entry in the PDF table of contents.
There are other functions to add pages with transitions.
Creating the page content
To create content for a page, you have to use a page reference with drawWithPage.
drawWithPage is using a Draw monad value.
Element of the Draw monad are built with geometry, text and color primitives.
createPageContent ::PDFReferencePDFPage-> Draw () createPageContent page =drawWithPagepage $ dostrokeColorredsetWidth0.5stroke$Rectangle10 0 200 300
Text
Text is complex. You can use the low level PDFText to create a text in the Draw monad. For instance:
textText ::PDFFont->PDFString->Draw() textText f t = dodrawText$ dosetFontftextStart10 200.0leading$getHeightfrenderModeFillTextdisplayTexttstartNewLinedisplayText$toPDFString"Another little test"
It gives a detailed control on the position of characters and lines but it is too much work.
The library is thus supporting a higher level typesetting system with paragraph styles.
Displaying a formatted text is done with displayFormattedText and using a typesetting monad value:
displayFormattedText(Rectangle(10 :+ 0) (110 :+ 300))NormalParaNormal$ doparagraph$ dotxt$ "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor "txt$ "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud "txt$ "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute "txt$ "irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "txt$ "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia "txt$ "deserunt mollit anim id est laborum."
The text will be formatted using the NormalPara paragraph style and the Normal style for sentences.
NormalPara is part of an algebraic data type defining some vertical styles (from file test.hs):
data MyVertStyles = NormalPara
| CirclePara
| BluePara !PDFFloat
and Normal is part of another algebraic data typec (from file test.hs):
data MyParaStyles = Normal
| Bold
| Crazy
| SuperCrazy [Int] [PDFFloat]
| DebugStyle
| RedRectStyle
| BlueStyle
The library is coming with standard styles StandardParagraphStyle and StandardStyle.
Custom styles must be instances of some classes. A ComparableStyle to allow the typesetting algorithm to decide when to group
different characters in a span of the same style.
A Style class used for sentence style. And a ParagraphStyle to group together the paragraph style and the sentence
style that can be used in this paragraph.
Why the ComparableStyle is used instead of the class Eq ? A style is containing information
used for the font (size etc ...) but it can also contain additional information used by styling function (a styling
function may draw a decoration). In that latter case, the additional information is changing the look of the sentence
but not its layout : the font size is not changed. So, from a text point of view, the PDF text is drawn using the same
attributes. But the additional decoration on top of it is changing.
So, ComparableStyle is used to compare the font settings of a style.
The ParagraphStyle is used to change the geometry of the paragraph (the paragraph can be typeset using
a circle as shape for instance). This style is also used to style the bounding box.
The other attributes like distance between two lines etc ... are controlled in the typesetting monad.
setParaStyle(BluePara 0)setFirstPassTolerance500unstyledGlue6 0.33 0paragraph$ dotxt$ "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
Inside a paragraph, it is possible to change the line style and create new paragraphs:
paragraph$ dotxt$ "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor"setStyleBoldtxt$ " incididunt ut labore et dolore magna aliqua. "forceNewLine
When charts are created, it is often useful to be able to display captions, labels etc ... The position
of the box containing the text is relative to some specific points in the drawing. To ease with this use-case, an
additional function is provided : drawTextBox
MonadStyle
The typesetting is similar to the TeX one with kern, glues and boxes. So, it means that any drawing
can be used as a letter since any drawing can be contained in a box. The operators to draw boxes, glues are
part of the MonadStyle monad. The Draw value can be transformed into a box with mkDrawBox.
The paragraph and the typesetting monad are instances of this class. So, boxes, glues, kerns can be used in horizontal mode (paragraph) or vertical mode (typesetting monad).
Geometry
Building shapes inside the draw monad is easy. For instance:
strokeColorredstroke$Rectangle0 (200 :+ 100)fillColorbluefill$Ellipse100 100 300 200fillAndStroke$RoundRectangle32 32 200 200 600 400
you can also create paths.
In addition to color, other attributes can be changed:
withNewContext$ dosetWidth2setDash$DashPattern[3] 0stroke$Rectangle0 (200 :+ 100)
withNewContext is saving and restoring the settings.
Shapes can be filled with shading patterns:
paintWithShading(RadialShading0 0 50 0 0 600 (Rgb1 0 0) (Rgb0 0 1)) (addShape$Rectangle0 (300 :+ 300))paintWithShading(AxialShading300 300 600 400 (Rgb1 0 0) (Rgb0 0 1)) (addShape$Ellipse300 300 600 400)
Note that in above example, addShape is used. You can't use stroke or fill. You are just adding a shape to a path.
More complex patterns can also be used to fill the shapes. In below example we are filling shapes with a complex
drawing defined with a Draw monad value.
patternTest ::PDFReferencePDFPage->createUncoloredTiling0 0 100 50 100 50ConstantSpacingpattern cp <-createColoredTiling0 0 100 50 100 50ConstantSpacingcpatterndrawWithPagepage $ dostrokeColorgreensetUncoloredFillPatternp (Rgb1 0 0)fillAndStroke$Ellipse0 0 300 300setColoredFillPatterncpfillAndStroke$Ellipse300 300 600 400 where pattern = dostroke(Ellipse0 0 100 50)cpattern= dostrokeColor(Rgb0 0 1)stroke(Ellipse0 0 100 50)
X Form
You can share an object between different pages of a document. It helps reducing the size of the
document is the shared drawing is big. An object can be a Draw monad value. But it can be a JPEG picture too.
r <-createPDFXForm0 0 200 200 lineStyledrawWithPagepage6 $ dodrawXObjectr
in the above example, lineStyle is a Draw() value.
Image
It is possible to embed JPEG images in the document.
testImage ::JpegFile->PDFReferencePDFPage->createPDFJpegjpgfdrawWithPagepage $ dowithNewContext$ dosetFillAlpha0.4drawXObjectjpgwithNewContext$ doapplyMatrix$rotate(Degree 20)applyMatrix$translate(200 :+ 200)applyMatrix$scale2 2drawXObjectjpg
The JpegFile value must be created in the IO monad with:
Right jpg <- readJpegFile "logo.jpg"
Alternatively, jpegs can be compiled into your code. After converting a jpeg to a data URL, a JpegFile can be created with:
let Right jpg = readJpegDataURL "data:image/jpeg;base64,........."
The haskell code is just extracting the size of the image from the file. The image is not decoded.
Annotations
A pdf page can contain several kind of annotations like links, notes etc ... For instance, to define and display a link:
newAnnotation(URLLink(toPDFString"Go to my blog") [0,0,200,100] "http://www.alpheccar.org" True)
Warning
The PDF format is full of extensions. Depending on the viewer that you use some extensions may not be supported. It is always a good thing to test on a few viewers if you use complex features.
Mobile viewers (tablets and phones) are generally focusing on a more portable and more restricted set of features. So, you may not be able to display you document on a mobile device if you use complex features.
So, I repeat : test.