Tuesday, May 11, 2021

#1 2021-03-21 01:29:28 pm

akim
Member
Registered: 2010-04-04
Posts: 132

diagonal text with dirtyRect or BezierPath

I am trying to draw a diagonal text in a pdf using an attributed string's  AppKit's instance drawInRect. I am, however, unable to draw a rotated text using dirtyRect or BezierPath.

I would appreciate any suggestions or links that might address methods to accompish this task.

Online

 

#2 2021-03-22 03:24:06 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 1797

Re: diagonal text with dirtyRect or BezierPath

Hi,

As I see, you should use PDFPage class's draw method instead. Here is sample, written in Swift:

Applescript:


Abstract:
WatermarkPage is a PDFPage subclass that implements custom drawing.
*/

import Foundation
import PDFKit

/**
WatermarkPage subclasses PDFPage so that it can override the draw(with box: to context:) method.
This method is called by PDFDocument to draw the page into a PDFView. All custom drawing for a PDF
page should be done through this mechanism.

Custom drawing methods should always be thread-safe and call the super-class method. This is needed to draw the original PDFPage content. Custom drawing code can execute before or after this super-class call, though order matters! If your graphics run before the super-class call, they are drawn below the PDFPage content. Conversely, if your graphics run after the super-class call, they are drawn above the PDFPage.
*/

class WatermarkPage: PDFPage {

// 3. Override PDFPage custom draw
/// - Tag: OverrideDraw
override func draw(with box: PDFDisplayBox, to context: CGContext) {

// Draw original content
super.draw(with: box, to: context)

// Draw rotated overlay string
UIGraphicsPushContext(context)
context.saveGState()

let pageBounds = self.bounds(for: box)
context.translateBy(x: 0.0, y: pageBounds.size.height)
context.scaleBy(x: 1.0, y: -1.0)
context.rotate(by: CGFloat.pi / 4.0)

let string: NSString = "U s e r 3 1 4 1 5 9"
let attributes: [NSAttributedString.Key: Any] = [
NSAttributedString.Key.foregroundColor: #colorLiteral(red: 0.4980392157, green: 0.4980392157, blue: 0.4980392157, alpha: 0.5),
NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 64)
]

string.draw(at: CGPoint(x: 250, y: 40), withAttributes: attributes)

context.restoreGState()
UIGraphicsPopContext()

}
}

Here is view controller sample in Swift:

Applescript:


/*
Abstract:
ViewController is the main view controller of DocumentWatermark. It drives the
main PDFView display and loading of a PDFDocument.
*/

import UIKit
import PDFKit

/**
This ViewController initializes a PDFDocument, sets its delegate to self, and implements
the classForPage() delegate method. This declares that all instantiated PDFPages for
the presented document (through PDFView) should instantiate the subclass WatermarkPage instead.
This subclass, found in WatermarkPage.swift, implements custom drawing.

ViewController first loads a path to our Sample.pdf file through the application's
main bundle. This URL is then used to instantiate a PDFDocument. On success, the document
is assigned to our PDFView, which was setup in InterfaceBuilder.

Before document assignment, it is critical to assign our delegate, which is the ViewController
itself, so that classForPage() (a PDFDocumentDelgetate method) implements classForPage().
This method returns the PDFPage subclass used for custom drawing.
*/
class ViewController: UIViewController, PDFDocumentDelegate {

@IBOutlet weak var pdfView: PDFView?

/// - Tag: SetDelegate
override func viewDidLoad() {
super.viewDidLoad()

if let documentURL = Bundle.main.url(forResource: "Sample", withExtension: "pdf") {
if let document = PDFDocument(url: documentURL) {

// Center document on gray background
pdfView?.autoScales = true
pdfView?.backgroundColor = UIColor.lightGray

// 1. Set delegate
document.delegate = self
pdfView?.document = document
}
}
}

// 2. Return your custom PDFPage class
/// - Tag: ClassForPage
func classForPage() -> AnyClass {
return WatermarkPage.self
}

}

Last edited by KniazidisR (2021-03-22 03:38:28 am)


Model: MacBook Pro
OS X: Catalina 10.15.4
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#3 2021-03-22 10:55:19 pm

akim
Member
Registered: 2010-04-04
Posts: 132

Re: diagonal text with dirtyRect or BezierPath

How does your Swift code translate into a script that can be called or adopted into Applescript?

Online

 

#4 2021-03-23 01:20:04 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 1797

Re: diagonal text with dirtyRect or BezierPath

akim wrote:

How does your Swift code translate into a script that can be called or adopted into Applescript?


You said: at least some information, but you yourself do not start the code, and instead you want a ready-made solution now. I am running out of time, so I will build the code, possibly intermittently.

I would start with this skeleton where the handler drawWaterMarkOn corresponds to the first draw handler from Swift code. We will not need the second Swift code handler, since we will initiate the PDF document ourselves:

Applescript:


use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "Quartz"
use framework "QuartzCore"
property mediabox : a reference to current application's kPDFDisplayBoxMediaBox
property |PI| : pi

-- Choose PDF, indicate destination path
set aPDF to choose file of type "pdf"
set destPosixPath to (POSIX path of (path to desktop folder)) & "WaterMarked.pdf"
-- Get PDF document
set aURL to (current application's |NSURL|'s fileURLWithPath:(POSIX path of aPDF))
set aPDFdoc to current application's PDFDocument's alloc()'s initWithURL:aURL
-- Count pages
set pCount to aPDFdoc's pageCount()

-- Process the pages
repeat with i from 0 to (pCount - 1)
   set pdfPage to (aPDFdoc's pageAtIndex:i)
   (my drawWaterMarkOn:pdfPage dpi:72)
end repeat

-- set outNSURL to current application's |NSURL|'s fileURLWithPath:destPosixPath
-- aPDFdoc's writeToURL:outNSURL


----------------------- The Handler -----------------------------------
on drawWaterMarkOn:pdfPage dpi:theDPI
   -- Swift: let string: NSString = "U s e r 3 1 4 1 5 9"
   set watermarkText to current application's NSString's stringWithString:"U s e r 3 1 4 1 5 9"
   set scale to theDPI / 72
   -- Swift: let pageBounds = self.bounds(for: box)
   set pageBounds to (pdfPage's boundsForBox:mediabox)
   set pageWidth to current application's NSWidth(pageBounds)
   set pageHeight to current application's NSHeight(pageBounds)
   set pixelWidth to (pageWidth * theDPI / 72) div 1
   set pixelHeight to (pageHeight * theDPI / 72) div 1
   -- make bitmaps
   set theImageRep to (current application's NSPDFImageRep's imageRepWithData:(pdfPage's dataRepresentation()))
   set newRep to (current application's NSBitmapImageRep's alloc()'s initWithBitmapDataPlanes:(missing value) pixelsWide:pixelWidth pixelsHigh:pixelHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:yes isPlanar:false colorSpaceName:(current application's NSDeviceRGBColorSpace) bytesPerRow:0 bitsPerPixel:32)
   -- Swift: context.saveGState()
   current application's NSGraphicsContext's saveGraphicsState()
   -- Swift: UIGraphicsPushContext(context)
   (current application's NSGraphicsContext's setCurrentContext:(current application's NSGraphicsContext's graphicsContextWithBitmapImageRep:newRep))
   -- Swift: super.draw(with: box, to: context)
   (theImageRep's drawInRect:{origin:{x:0, y:0}, |size|:{width:pixelWidth, height:pixelHeight}} fromRect:(current application's NSZeroRect) operation:(current application's NSCompositeSourceOver) fraction:1.0 respectFlipped:false hints:(missing value))
   (current application's NSGraphicsContext's graphicsContextWithBitmapImageRep:newRep)'s rotateBy:(pi / 4.0)
end drawWaterMarkOn:dpi:

Last edited by KniazidisR (2021-03-23 01:58:53 am)


Model: MacBook Pro
OS X: Catalina 10.15.4
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#5 2021-03-23 09:53:25 am

akim
Member
Registered: 2010-04-04
Posts: 132

Re: diagonal text with dirtyRect or BezierPath

KniazidisR,
Thanks for your assistance. As I have never roated an image, I cannot debug the error the script throws at its  last line of its handler

Applescript:

(current application's NSGraphicsContext's graphicsContextWithBitmapImageRep:newRep)'s rotateBy:(pi / 4.0)

with

-[NSBitmapGraphicsContext rotateBy:]: unrecognized selector sent to instance 0x600003d40ba0



I cannot find any reference to rotateBy using applecript and frameworks Quartz or Foundation.

1. Might you have ideas on a method to overcome this error?
If not, my research of Rotation in Foundation appears to indicate a need for an affine matrix  to transform.
2. Do you know of a method to transform an affine matrix in this type of script, or do you prefer another method?

Online

 

#6 2021-03-23 06:08:52 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6628

Re: diagonal text with dirtyRect or BezierPath

I suspect you need to use an affine transform, something like this:

Applescript:

use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "Quartz"
use framework "QuartzCore"

set theDpi to 72
set aPDF to choose file of type "pdf"
set destPosixPath to (POSIX path of (path to desktop folder)) & "WaterMarked.pdf"
-- prepare text stuff
set watermarkText to current application's NSString's stringWithString:"U s e r 3 1 4 1 5 9"
set theFont to current application's NSFont's fontWithName:"Helvetica" |size|:100
set theColor to current application's NSColor's blackColor()
set styleDict to current application's NSDictionary's dictionaryWithObjects:{theColor, theFont} forKeys:{current application's NSForegroundColorAttributeName, current application's NSFontAttributeName}
-- Get PDF stuff
set aURL to (current application's |NSURL|'s fileURLWithPath:(POSIX path of aPDF))
set aPDFdoc to current application's PDFDocument's alloc()'s initWithURL:aURL
set pCount to aPDFdoc's pageCount()
repeat with i from 0 to (pCount - 1)
   set thisPage to (aPDFdoc's pageAtIndex:i)
   set pageSize to (thisPage's boundsForBox:(current application's kPDFDisplayBoxMediaBox))
   set pageWidth to current application's NSWidth(pageSize)
   set pageHeight to current application's NSHeight(pageSize)
   set pixelWidth to (pageWidth * theDpi / 72) div 1
   set pixelHeight to (pageHeight * theDpi / 72) div 1
   -- make bitmaps
   set theImageRep to (current application's NSPDFImageRep's imageRepWithData:(thisPage's dataRepresentation()))
   set newRep to (current application's NSBitmapImageRep's alloc()'s initWithBitmapDataPlanes:(missing value) pixelsWide:pixelWidth pixelsHigh:pixelHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:yes isPlanar:false colorSpaceName:(current application's NSDeviceRGBColorSpace) bytesPerRow:0 bitsPerPixel:32)
   -- prepare transform
   set theTransform to current application's NSAffineTransform's transform()
   (theTransform's rotateByDegrees:45)
   -- do drawing
   current application's NSGraphicsContext's saveGraphicsState()
   (current application's NSGraphicsContext's setCurrentContext:(current application's NSGraphicsContext's graphicsContextWithBitmapImageRep:newRep))
   (theImageRep's drawInRect:{origin:{x:0, y:0}, |size|:{width:pixelWidth, height:pixelHeight}} fromRect:(current application's NSZeroRect) operation:(current application's NSCompositeSourceOver) fraction:1.0 respectFlipped:false hints:(missing value))
   theTransform's concat() -- applies transform
   (watermarkText's drawAtPoint:{0, 0} withAttributes:styleDict) -- point needs to allow for transform
   current application's NSGraphicsContext's restoreGraphicsState()
   -- make PDF page from bitmap
   (newRep's setSize:{pageWidth, pageHeight})
   set theData to newRep's TIFFRepresentation()
   set theImage to (current application's NSImage's alloc()'s initWithData:theData)
   set newPage to (current application's PDFPage's alloc()'s initWithImage:theImage)
   -- modify PDF doc
   (aPDFdoc's removePageAtIndex:i)
   (aPDFdoc's insertPage:newPage atIndex:i)
end repeat
-- save
set outNSURL to current application's |NSURL|'s fileURLWithPath:destPosixPath
aPDFdoc's writeToURL:outNSURL

But honestly, I'd use one of the many apps that can do this. They avoid needing to rasterize, for starters.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#7 2021-03-23 11:50:57 pm

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 1797

Re: diagonal text with dirtyRect or BezierPath

Thanks for your help, Shane Stanley. I would have had to deal with this NSAffineTransform for a long time.

I tried to adjust the color to gray here, and to set alpha to 0.5. As I understand it, I have achieved transparency.

Applescript:


-- prepare text stuff
set watermarkText to current application's NSString's stringWithString:" U s e r 3 1 4 1 5 9"
set theFont to current application's NSFont's fontWithName:"Helvetica" |size|:80
set theColor to current application's NSColor's grayColor()
set theColor to theColor's colorWithAlphaComponent:0.5
set styleDict to current application's NSDictionary's dictionaryWithObjects:{theColor, theFont} forKeys:{current application's NSForegroundColorAttributeName, current application's NSFontAttributeName}

I have 1 question.
You are talking about third-party applications: "They avoid needing to rasterize, for starters". Is it because they draw watermark on the focused view?

Last edited by KniazidisR (2021-03-24 12:09:15 am)


Model: MacBook Pro
OS X: Catalina 10.15.4
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#8 2021-03-24 12:11:25 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6628

Re: diagonal text with dirtyRect or BezierPath

KniazidisR wrote:

1) I tried to adjust the color to gray here, and to set alpha to 1/10. As I understand it, I have not achieved transparency. Is it possible to do this?



You'd need:

Applescript:

set theColor to theColor's colorWithAlphaComponent:0.1

Is it because they draw watermark on the focused view?



No, it's because they can add the text directly as an element of the PDF itself.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#9 2021-03-25 10:24:49 pm

akim
Member
Registered: 2010-04-04
Posts: 132

Re: diagonal text with dirtyRect or BezierPath

Thanks for your great insights on

the colors colorWithAlphaComponent 
NSAffineTransform's transform.

You have greatly increased my understanding of a Quartz method of generating graphics and overlaying text.

With the success of your script, I now note that the font in the output version is more blurred and less sharp, when compared with the font found in the original pdf document.
What might be some possibilities that reduce the font's sharpness?
Might the loss of font sharpness arise from any of the following:
1.Tiff representation
2.NSBitmapImageRepresentation
3.Some other issue


Thanks for your help.

Online

 

#10 2021-03-25 11:56:05 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6628

Re: diagonal text with dirtyRect or BezierPath

The sharpness will depend in part on the resolution you set. But you're making bitmaps, and rotating text. See my earlier comment: I'd use one of the many apps that can do this.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#11 2021-03-27 09:40:09 am

akim
Member
Registered: 2010-04-04
Posts: 132

Re: diagonal text with dirtyRect or BezierPath

My original pdf font Helvetica size 14 was transformed into a bolder and fuzzier-looking distortion.
My rotated font however appeared sharp without any apparent distortion.

Would not the opposite occur were bitmap rotation the cause of the font fuzziness?
Is it possible that the overlying rotated bitmap blurred the underlying bitmap font and that some other property was invoked to increase the boldness of that underlying font?

The applescript command:

Applescript:

set newNSBitmapImageRep to (current application's NSBitmapImageRep's alloc()'s initWithBitmapDataPlanes:(missing value) pixelsWide:pixelWidth pixelsHigh:pixelHeight bitsPerSample:8 samplesPerPixel:4 hasAlpha:yes isPlanar:false colorSpaceName:(current application's NSDeviceRGBColorSpace) bytesPerRow:0 bitsPerPixel:32)

returned:

NSBitmapImageRep 0x600003e06b80 Size={612, 792} ColorSpace=Device RGB colorspace BPS=8 BPP=32 Pixels=612x792 Alpha=YES Planar=NO Format=0 CurrentBacking=nil (faulting)


1. What NSBitmapImageRep' parameter can be altered or added to increase font sharpness or resolution in the  bitmap image representation in which the original pdf was created?
2. What other Quartz object or parameter can be added to increase font sharpness or resolution in the original pdf font?
3. What property, if any, can be changed to allow greater penetration of the original document's underlying font through the overlying bitmap?
4. What free programs do you recommend to circumvent this bitmap problem?


I greatly appreciate your continual insights.

Last edited by akim (2021-03-27 10:46:29 am)

Online

 

#12 2021-03-27 07:33:27 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6628

Re: diagonal text with dirtyRect or BezierPath

Change the line:

Applescript:

set theDpi to 72

I can't recommend any particular app.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#13 2021-03-28 10:48:27 am

akim
Member
Registered: 2010-04-04
Posts: 132

Re: diagonal text with dirtyRect or BezierPath

Shane,
Thanks for your direction.

The variable, theDpi, already been set to 72, based upon your prior example. When I increased the size of theDPI, such as to 600, the clarity of the original PDF font increased back to its original clarity. Font resolution solved!
The page size however also grew. Ugh!
Reducing theDPI to 36 had the opposite effect of blurring the font and shrinking the page size. Worse!

What method might I use to increase the resulting quality of the Helvetica 14 font in the original document, without increasing the page size?

Online

 

#14 2021-03-28 04:26:14 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6628

Re: diagonal text with dirtyRect or BezierPath

akim wrote:

What method might I use to increase the resulting quality of the Helvetica 14 font in the original document, without increasing the page size?



Using this approach, you can't. You need to find an app that modifies the PDF without rasterizing it.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#15 2021-03-28 06:31:48 pm

akim
Member
Registered: 2010-04-04
Posts: 132

Re: diagonal text with dirtyRect or BezierPath

Is anti-aliasing available for this method, and if so, might it reduce the fuzziness in the rasterized font?

Online

 

#16 2021-03-28 06:38:15 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6628

Re: diagonal text with dirtyRect or BezierPath

No and no.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#17 2021-03-29 04:59:33 pm

akim
Member
Registered: 2010-04-04
Posts: 132

Re: diagonal text with dirtyRect or BezierPath

I have set dots per inch to 300, rotated Transform by -45 degrees, scaled its x and y axes by 4, and translated its origin leftward by 300 and upward by 500.

Applescript:

set theTransform to current application's NSAffineTransform's transform()
   (theTransform's rotateByDegrees:-45)
   (theTransform's scaleXBy:4 yBy:4)
   (theTransform's translateXBy:(-300.0) yBy:500.0)

This has increased the original font sharpness.
As increasing  the number of dots per inch variable, theDPI, to 300 enlarged the overall page size, I  reduced the page size  back to its original, or the size of a standard pdf, by employing the Print function, scaling to fit, and then saving to pdf.

What might be Applescript methods to perform the above print-related activity to:
1. Reduce the page size back to its original, or to a  standard pdf size?
2. Scale it to fit its dimensions?
3. Save it to a pdf?

Online

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)