Skip to content

Commit

Permalink
#2: Implement Text demo
Browse files Browse the repository at this point in the history
  • Loading branch information
broadwaylamb committed Jan 8, 2019
1 parent a26b979 commit 8216d69
Show file tree
Hide file tree
Showing 3 changed files with 286 additions and 1 deletion.
261 changes: 261 additions & 0 deletions Demo/Demo.playground/Pages/Text.xcplaygroundpage/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
/*:
# Text

**Text** is a program which show various way of text showing.

([Original example that uses LibHaru](https://github.com/libharu/libharu/wiki/Examples#text_democ))
*/
import SwiftyHaru
import func Foundation.tan
/*:
Firstly, we need to define some helper functions.
*/
let sampleTextShort = "ABCabc123"
let sampleTextLong = "abcdefgABCDEFG123!#$%&+-@?"
let sampleTextPhrase = "The quick brown fox jumps over the lazy dog."

func showStripePattern(in context: DrawingContext, x: Float, y: Float) {

context.strokeColor = #colorLiteral(red: 0, green: 0, blue: 0.5, alpha: 1)
context.lineWidth = 1

for iy in stride(from: 0 as Float, to: 50, by: 3) {
context.stroke(
Path()
.moving(toX: x, y: y + iy)
.appendingLine(toX: x + context.textWidth(for: sampleTextShort), y: y + iy)
)
}

context.lineWidth = 2.5
}

func showDescription(in context: DrawingContext, x: Float, y: Float, text: String) throws {
let fontSize = context.fontSize
let fillColor = context.fillColor

context.fillColor = .black
context.textRenderingMode = .fill
context.fontSize = 10
try context.show(text: text, atX: x, y: y - 12)

context.fontSize = fontSize
context.fillColor = fillColor
}
/*:
Then we setup our document.
*/
let document = PDFDocument()
let pageTitle = "Text Demo"

try document.setCompressionMode(to: .all)

try document.addPage() { context in

/*:
Let's draw the grid for convenience.
*/
let horizontalLabels = sequence(first: 50, next: { $0 + 50 }).lazy.map(String.init)
let verticalLabels = sequence(first: 10, next: { $0 + 10 }).lazy.map(String.init)

let labels = Grid.Labels(top: Grid.LabelParameters(sequence: "" + horizontalLabels,
offset: Vector(dx: 0, dy: -6)),
bottom: Grid.LabelParameters(sequence: "" + horizontalLabels,
offset: Vector(dx: 0, dy: 6)),
left: Grid.LabelParameters(sequence: "" + verticalLabels,
frequency: 1,
offset: Vector(dx: 6, dy: 0)))

let serifs = Grid.Serifs(top: .default,
bottom: .default,
left: Grid.SerifParameters(frequency: 1),
right: nil)

let grid = Grid(width: context.page.width,
height: context.page.height,
labels: labels,
serifs: serifs)
try context.draw(grid, position: .zero)

/*:
Let's print the title of the page:
*/

context.fontSize = 24
let textWidth = context.textWidth(for: pageTitle)
try context.show(text: pageTitle, atX: (context.page.width - textWidth) / 2, y: context.page.height - 50)
/*:
We are now ready to place some text samples.
First, let's play with font size.
*/
var textPosition = Point(x: 60, y: context.page.height - 60)

for fontSize in sequence(first: 8 as Float, next: { $0 * 1.5 }) {

if fontSize >= 60 {
break
}

context.fontSize = fontSize
textPosition.y -= 5 + fontSize

// Measure the number of characters which included in the page
let utf8Length = try context
.measureText(sampleTextLong, width: context.page.width - 120, wordwrap: false).utf8Length

try context.show(text: String(sampleTextLong.utf8.prefix(utf8Length))!, atPosition: textPosition)

// Print the description
textPosition.y -= 10
context.fontSize = 8
try context.show(text: String(format: "Fontsize=%.0f", fontSize), atPosition: textPosition)
}
/*:
Then let's experiment with font color.
*/
context.fontSize = 8
textPosition.y -= 30
try context.show(text: "Font color", atPosition: textPosition)

context.fontSize = 18
textPosition.y -= 20
for (i, char) in sampleTextLong.enumerated() {
let char = String(char)
let red = Float(i) / Float(sampleTextLong.utf8.count)
let green = 1 - red
context.fillColor = Color(red: red, green: green, blue: 0)!
try context.show(text: char, atPosition: textPosition)
textPosition.x += context.textWidth(for: char)
}

textPosition.x = 60
textPosition.y -= 25
for (i, char) in sampleTextLong.enumerated() {
let char = String(char)
let red = Float(i) / Float(sampleTextLong.utf8.count)
let blue = 1 - red
context.fillColor = Color(red: red, green: 0, blue: blue)!
try context.show(text: char, atPosition: textPosition)
textPosition.x += context.textWidth(for: char)
}

textPosition.x = 60
textPosition.y -= 25
for (i, char) in sampleTextLong.enumerated() {
let char = String(char)
let blue = Float(i) / Float(sampleTextLong.utf8.count)
let green = 1 - blue
context.fillColor = Color(red: 0, green: green, blue: blue)!
try context.show(text: char, atPosition: textPosition)
textPosition.x += context.textWidth(for: char)
}
/*:
Explore different font rendering modes:
*/
let ypos: Float = 450

context.fontSize = 32
context.fillColor = #colorLiteral(red: 0.5, green: 0.5, blue: 0, alpha: 1)
context.lineWidth

try showDescription(in: context, x: 60, y: ypos, text: "textRenderingMode = .fill")
context.textRenderingMode = .fill
try context.show(text: sampleTextShort, atX: 60, y: ypos)

try showDescription(in: context, x: 60, y: ypos - 50, text: "textRenderingMode = .stroke")
context.textRenderingMode = .stroke
try context.show(text: sampleTextShort, atX: 60, y: ypos - 50)

try showDescription(in: context, x: 60, y: ypos - 100, text: "textRenderingMode = .fillThenStroke")
context.textRenderingMode = .fillThenStroke
try context.show(text: sampleTextShort, atX: 60, y: ypos - 100)

try showDescription(in: context, x: 60, y: ypos - 150, text: "textRenderingMode = .fillClipping")
try context.withNewGState {
context.textRenderingMode = .fillClipping
try context.show(text: sampleTextShort, atX: 60, y: ypos - 150)
showStripePattern(in: context, x: 60, y: ypos - 150)
}

try showDescription(in: context, x: 60, y: ypos - 200, text: "textRenderingMode = .strokeClipping")
try context.withNewGState {
context.textRenderingMode = .strokeClipping
try context.show(text: sampleTextShort, atX: 60, y: ypos - 200)
showStripePattern(in: context, x: 60, y: ypos - 200)
}

try showDescription(in: context, x: 60, y: ypos - 250, text: "textRenderingMode = .fillStrokeClipping")
try context.withNewGState {
context.textRenderingMode = .fillStrokeClipping
try context.show(text: sampleTextShort, atX: 60, y: ypos - 250)
showStripePattern(in: context, x: 60, y: ypos - 250)
}

/*:
Reset text attributes and try to transform text in different ways:
*/
context.textRenderingMode = .fill
context.fillColor = .black
context.fontSize = 30

try showDescription(in: context, x: 320, y: ypos - 60, text: "Rotating text")
let rotatedTextPosition = Point(x: 330, y: ypos - 60)
let rotatedTextMatrix = AffineTransform(rotationAngle: .pi / 6) *
AffineTransform(translationX: rotatedTextPosition.x, y: rotatedTextPosition.y)
try context.show(text: sampleTextShort,
atPosition: rotatedTextPosition,
textMatrix: rotatedTextMatrix)

try showDescription(in: context, x: 320, y: ypos - 120, text: "Skewing text")
let skewedTextPosition = Point(x: 330, y: ypos - 120)
let skewedTextMatrix = AffineTransform(a: 1, b: tan(.pi / 18),
c: tan(.pi / 9), d: 1,
tx: skewedTextPosition.x, ty: skewedTextPosition.y)
try context.show(text: sampleTextShort,
atPosition: skewedTextPosition,
textMatrix: skewedTextMatrix)

try showDescription(in: context, x: 320, y: ypos - 175, text: "Scaling text (X direction)")
let scaledXTextPosition = Point(x: 320, y: ypos - 175)
let scaledXTextMatrix = AffineTransform(scaleX: 1.5, y: 1) *
AffineTransform(translationX: scaledXTextPosition.x, y: scaledXTextPosition.y)
try context.show(text: sampleTextShort,
atPosition: scaledXTextPosition,
textMatrix: scaledXTextMatrix)

try showDescription(in: context, x: 320, y: ypos - 250, text: "Scaling text (Y direction)")
let scaledYTextPosition = Point(x: 320, y: ypos - 250)
let scaledYTextMatrix = AffineTransform(scaleX: 1, y: 2) *
AffineTransform(translationX: scaledYTextPosition.x, y: scaledYTextPosition.y)
try context.show(text: sampleTextShort,
atPosition: scaledYTextPosition,
textMatrix: scaledYTextMatrix)

/*:
Alter character spacing and word spacing:
*/

try showDescription(in: context, x: 60, y: 140, text: "char-spacing 0")
try showDescription(in: context, x: 60, y: 100, text: "char-spacing 1.5")
try showDescription(in: context, x: 60, y: 60, text: "char-spacing 1.5, word-spacing 2.5")

context.fontSize = 20
context.fillColor = #colorLiteral(red: 0.1, green: 0.3, blue: 0.1, alpha: 1)

context.characterSpacing = 0
try context.show(text: sampleTextPhrase, atX: 60, y: 140)

context.characterSpacing = 1.5
try context.show(text: sampleTextPhrase, atX: 60, y: 100)

context.wordSpacing = 2.5
try context.show(text: sampleTextPhrase, atX: 60, y: 60)
}

/*:
We need to save our document.
*/
document.display()
/*:
[Previous page](@previous) • **[Table of contents](Table%20of%20contents)** • [Next page](@next)
*/
3 changes: 2 additions & 1 deletion Demo/Demo.playground/contents.xcplayground
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='6.0' target-platform='macos' display-mode='rendered'>
<playground version='6.0' target-platform='macos' display-mode='raw'>
<pages>
<page name='Table of contents'/>
<page name='Fonts'/>
<page name='Lines'/>
<page name='Text'/>
<page name='Arcs'/>
<page name='TrueType Fonts'/>
<page name='Grid Sheet'/>
Expand Down
23 changes: 23 additions & 0 deletions Sources/SwiftyHaru/Graphics/Geometry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,17 @@ public struct Point: Hashable {
public static func +(lhs: Point, rhs: Vector) -> Point {
return Point(x: lhs.x + rhs.dx, y: lhs.y + rhs.dy)
}

/// Translates the `lhs` point by the specified `rhs` vector and stores the result in `lhs`.
///
/// - Parameters:
/// - lhs: The point to translate.
/// - rhs: The difference vector.
@inlinable
public static func +=(lhs: inout Point, rhs: Vector) {
lhs.x += rhs.dx
lhs.y += rhs.dy
}

/// Translates the `lhs` point by negation of the specified `rhs` vector.
///
Expand All @@ -112,6 +123,18 @@ public struct Point: Hashable {
return Point(x: lhs.x - rhs.dx, y: lhs.y - rhs.dy)
}

/// Translates the `lhs` point by negation of the specified `rhs` vector and stores the result
/// in `lhs`.
///
/// - Parameters:
/// - lhs: The point to translate.
/// - rhs: The difference vector.
@inlinable
public static func -=(lhs: inout Point, rhs: Vector) {
lhs.x -= rhs.dx
lhs.y -= rhs.dy
}

/// Returns the vector that needs to be added to `rhs` to get `lhs`.
///
/// - Parameters:
Expand Down

0 comments on commit 8216d69

Please sign in to comment.