Mac-Pasteboard

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN


0.007		2014-03-08	T. R. Wyant
   No changes since 0.006_02.

0.006_02	2014-03-02	T. R. Wyant
   Notify users of intent to remove the configuration prompt (about
     installing pbtool) and simply install it by default. The -y and -n
     options will remain, with their present function.

0.006_01	2014-03-01	T. R. Wyant
  Serialize access of tests to clipboard. This involves an increase in
    the required version of Test::More, to 0.96.

0.006		2013-11-18	T. R. Wyant
  No changes since 0.005_01.

0.005_01	2013-11-16	T. R. Wyant
  Fix compile errors under Xcode 5.0.2.

0.005		2013-05-11	T. R. Wyant
  No changes since 0.004_01.

README  view on Meta::CPAN

Mac-Pasteboard is Copyright (C) 2008, 2011-2026 by Thomas R. Wyant, III

DESCRIPTION

This XS module accesses Mac OS X pasteboards, which can be thought of as
clipboards with bells and whistles. System-defined pasteboards can be
accessed, and user-defined pasteboards can be created and accessed. Each
pasteboard can contain multiple items of data, and each item can contain
multiple 'flavors.'

NOTE that Mac OS X appears to restrict pasteboard access to processes
that are logged in interactively. Ssh sessions and cron jobs can not
create the requisite pasteboard handles, giving coreFoundationUnknownErr
(-4960).

Flavors (Apple's technical term) correspond more or less to MIME types,

README  view on Meta::CPAN

as the case may be. For instance, 'public.utf16-plain-text' conforms to
'public.plain-text', which in turn conforms to 'public.text'. But none
of them conforms to 'public.image'.  Conformance is transitive, so
'public.utf16-plain-text' also conforms to 'public.plain-text', and so
on.

This module makes all these features available to the user, but is (I
hope!) organized in such a way that the user who does not wish to deal
with them need not do so, since defaults are provided to cover what the
author suspects to be the most common case: manipulating plain text on
the system clipboard.

The programming interface is object-oriented. Each object represents a
pasteboard, with the default being the system clipboard. Item ID is an
attribute of the object rather than a method argument, with the default
(undef) being special-cased to write to item ID 1, but read from the
most recent ID. Flavor _is_ an argument, but defaults to
'com.apple.traditional-mac-plain-text', which is the flavor used by the
'pbcopy' and 'pbpaste' executables provided with Mac OS X. A couple
convenience subroutines (pbcopy() and pbpaste()) are provided to make
things even simpler, and are exported by default. Also exported on
demand are various manifest constants: pasteboard names, flavor and
status flags, and pasteboard-related error codes.

eg/README  view on Meta::CPAN


This is the file you are currently looking at.

droplet

    This is the source for an Apple Script droplet which passes the full
    path names of any files dropped on it to a Perl script. To make this
    into an Apple Script application bundle, open the Script Editor (in
    /Applications/AppleScript until OS 10.10 Yosemite, where it became
    /Applications/Utilities/Script Editor). Then run eg/droplet onto the
    clipboard by (e.g.)

     $ pbcopy <eg/droplet

    Then paste the source into the Script Editor and save it as an
    application bundle. The Mac::Pasteboard::Droplet documentation gives
    the details on how to inject your Perl script into the application
    bundle.

save_image

    This script saves an image on the system clipboard to a file. The
    file name may be given on the command line; it defaults to
    'clipboard'. If there is a file name extension associated with the
    image's flavor, that extension is tacked onto the end of the file
    name. For example, if the clipboard contains flavor 'com.apple.pict'
    and this script is run, the output file will be 'clipboard.pict'
    unless the user specifies otherwise. If there is more than one image
    on the pasteboard, all will be dumped. If there is no image on the
    pasteboard, the script dies with a semi-appropriate message.

eg/save_image  view on Meta::CPAN

#!/usr/local/bin/perl

use strict;
use warnings;

use Mac::Pasteboard;

my $file = shift @ARGV || 'clipboard';

my $pb = Mac::Pasteboard->new ();

my @img = $pb->paste_all ('public.image')
    or die "No data conforming to 'public.image' found.\n";

foreach my $item (@img) {
    my $tags = $pb->flavor_tags ($item->{flavor});
    my $fn = $tags->{extension} ? "$file.$tags->{extension}" : $file;
    warn "Creating $fn\n";

lib/Mac/Pasteboard.pm  view on Meta::CPAN

    return;
}

# Autoload methods go after =cut, and are processed by the autosplit program.

1;
__END__

=head1 NAME

Mac::Pasteboard - Manipulate Mac OS X clipboards/pasteboards.

=head1 SYNOPSIS

To acquire text from the system clipboard, replacing it with your own:

  use Mac::Pasteboard;
  my $old_text = pbpaste();
  pbcopy ("Hello, sailor!\n");

or equivalently, using the object-oriented interface,

  use Mac::Pasteboard;
  my $pb = Mac::Pasteboard->new ();
  my $old_text = $pb->paste ();

lib/Mac/Pasteboard.pm  view on Meta::CPAN


B<Some> taint support was added in version C<0.015_01>. Specifically, if
you are running with taint support turned on, data off the pasteboard
will be tainted, and an attempt to create a pasteboard with a tainted
name will result in an exception. More such will be added if it seems
warranted.

=head1 DESCRIPTION

This XS module accesses Mac OS X pasteboards, which can be thought of as
clipboards with bells and whistles. Under Mac OS X, the system clipboard
is simply a special case of a pasteboard. In the following
documentation, 'clipboard' refers to the system clipboard, and
'pasteboard' refers to pasteboards in general.

This module uses the Pasteboard interface, which was introduced in Mac
OS 10.3 (a.k.a. 'Panther'), so it requires Mac OS 10.3 or better to run.

The simple case of placing plain text onto and reading it from the
system clipboard is accomplished by subroutines pbcopy() and pbpaste()
respectively. These correspond roughly to the command-line executables
of the same name, and are exported by default. If this is all you are
interested in, you can stop reading here. The rest of this section
describes the bells and whistles associated with a Mac OS X pasteboard.

A Mac OS X pasteboard contains zero or more data items, each of which is
capable of holding one or more flavors of data. The system defines a
couple pasteboards, including the system clipboard, named
'com.apple.pasteboard.clipboard'. The system clipboard is the default
taken if new() is called without arguments.

Data items are identified by an item id which is provided by the creator
of the item, and which (the documentation says) should only be
interpreted by the creator. Item flavors may be duplicated between items
but not within items. The item L<id|/id> is an attribute of
the Mac::Pasteboard object, with the default chosen so that you should
not need to worry about it unless you explicitly want more than one item
on a pasteboard.

lib/Mac/Pasteboard.pm  view on Meta::CPAN

and true for failure.

The following methods are provided:

=head2 new

 $pb = Mac::Pasteboard->new( $name )

This method creates a new pasteboard object, connected to the pasteboard
of the given name, creating the pasteboard if necessary. If called with
no argument, you get the system clipboard, a.k.a.
L</kPasteboardClipboard>, a.k.a.  C<'com.apple.pasteboard.clipboard'>.
Passing undef to new() is B<not> equivalent to calling it with no
arguments at all, since undef is the encoding for
L</kPasteboardUniqueName>.

If running with taint checking enabled, a tainted pasteboard name will
cause an exception.

Note that an error in creating a new pasteboard B<will> cause an
exception, since the L<fatal|/fatal> attribute defaults to 1.
If you want to get a status back, you will need to call

lib/Mac/Pasteboard.pm  view on Meta::CPAN

If running with taint checking enabled, the C<{data}> value will be
tainted.

The L</SEE ALSO> section has a link to the I<Uniform Type Identifiers
Overview>, which deals with the notion of type conformance.

=head2 pbcopy

 pbcopy( $data, $flavor, $flags )

This convenience subroutine (B<not> method) clears the system clipboard
and then copies the given data to it. All three arguments are optional
(the prototype being C<(;$$$)>. If C<$data> is undef, the value of C<$_>
is used.  If C<$flavor> is C<undef>, the L<default
flavor|/defaultFlavor> is used. If C<$flags> is C<undef>,
L<kPasteboardFlavorNoFlags|/kPasteboardFlavorNoFlags> is used.

In other words, this subroutine is more-or-less equivalent to the
C<pbcopy> executable.

=head2 pbcopy_find

lib/Mac/Pasteboard.pm  view on Meta::CPAN

This convenience subroutine (B<not> method) returns the default data
flavor for the 'find' pasteboard. if the argument is defined and not
C<''>, the argument becomes the new default flavor and the old default
flavor is returned.

=head2 pbpaste

 ( $data, $flags ) = pbpaste( $flavor )

This convenience subroutine (B<not> method) retrieves the given flavor
of data from the system clipboard, and its associated flavor flags. The
flavor is optional, the default being the
L<default flavor|/defaultFlavor>. If the given flavor is not found
C<undef> is returned for C<$data>.

The functionality is equivalent to calling C<paste()> on an object whose
L<id|/id> attribute is C<undef>.

If called in scalar context, you get C<$data>.

In other words, this subroutine is more-or-less equivalent to the

lib/Mac/Pasteboard.pm  view on Meta::CPAN


This name may not be the name you used to create the
pasteboard, even if you used one of the built-in names. But unless you
created the pasteboard using name kPasteboardUniqueName, the name will
be equivalent. That is,

 my $pb1 = Mac::Pasteboard->new();
 my $pb2 = Mac::Pasteboard->new(
     $pb1->get('name'));

gives two handles to the same clipboard.

=head2 requested_name

This read-only string attribute reports the name passed to C<new()>.

=head2 status

This dualvar attribute contains the status of the last operation. You
can set this with an integer; the dualvar will be generated.

lib/Mac/Pasteboard.pm  view on Meta::CPAN

available to the process that placed it on the pasteboard.

Oddly enough, the 'pbpaste' executable seems to be able to find such
data. But the Pasteboard Peeker demo application can not, so I am pretty
sure this module is working OK. Unfortunately I was unable to find the
source for pbpaste online, so I am unable to verify what's going on.

=head3 kPasteboardFlavorSenderTranslated

This pasteboard flavor flag indicates that the flavor's data has been
translated in some way by the process that placed it on the clipboard,
and it will not be saved by the Finder in clipping files.

=head3 kPasteboardFlavorSystemTranslated

This pasteboard flavor flag indicates that the flavor's data must be
translated by the Translation Manager. This flag cannot be set
programmatically, and the Finder will not save this data in clipping
files.

=head2 Pasteboard and flavor names

=head3 defaultFlavor

This constant represents the name of the default flavor, either
C<'com.apple.traditional-mac-plain-text'> or
C<'public.utf8-plain-text'>, depending on what version of macOS you are
running and how this module was installed.

=head3 kPasteboardClipboard

This constant represents the name of the system clipboard,
C<'com.apple.pasteboard.clipboard'>.

=head3 kPasteboardFind

This constant represents the name of the find pasteboard,
C<'com.apple.pasteboard.find'>.

=head3 kPasteboardUniqueName

This constant specifies that a unique name be generated for the
pasteboard. Under Mac OS 10.4 (Tiger) or above, the generated name will

lib/Mac/Pasteboard.pm  view on Meta::CPAN

earlier versions.

=head1 SUPPORT

Support is by the author. Please file bug reports at
L<https://github.com/trwyant/perl-Mac-Pasteboard/issues/> or in
electronic mail to the author.

=head1 SEE ALSO

The B<Clipboard> module by Ryan King will access text on the clipboard
under most operating systems. Under macOS. recent versions use this
module; older ones shell out to the I<pbpaste> and I<pbcopy> executables.

The I<pbpaste> and I<pbcopy> executables themselves are available, and
described by their respective man pages.

The I<Pasteboard Manager Reference> is available online at
L<https://web.archive.org/web/20090718011220/http://developer.apple.com/documentation/Carbon/Reference/Pasteboard_Reference/Reference/reference.html>.
See also the I<Pasteboard Manager Programming Guide> at
L<https://web.archive.org/web/20090718063745/http://developer.apple.com/documentation/Carbon/Conceptual/Pasteboard_Prog_Guide/paste_intro/paste_intro.html>.

pbl.c  view on Meta::CPAN

	    if (!stat && pbref != NULL) {
		stat = pbl_clear (pbref);
		CFRelease (pbref);
	    }
	} else if (!strcmp (argv[1], "copy")) {
	    if (ARGUMENT(2) == NULL) {
		fprintf (stderr, "You must supply an argument to 'copy'\n");
	    } else {
		PasteboardRef pbref;
		stat = pbl_create(
			ARGUMENT_D( 4, "com.apple.pasteboard.clipboard" ),
			(void **) &pbref, NULL );
		if (!stat && pbref != NULL) {
		    stat = pbl_clear (pbref);
		    if (!stat)
			stat = pbl_copy (
				pbref,
				(const unsigned char *) ARGUMENT(2),
				strlen (ARGUMENT(2)),
				1, ARGUMENT(3), 0);
		    CFRelease (pbref);
		}
	    }
	} else if (!strcmp (argv[1], "create")) {
	    PasteboardRef pbref = NULL;
	    char *pbname = NULL;
	    stat = pbl_create (
		    ARGUMENT_D( 2, "com.apple.pasteboard.clipboard" ),
		    (void **) &pbref, &pbname);
	    if (pbname != NULL) {
		fprintf (stderr, "Created pasteboard \"%s\"\n", pbname);
		FREE ("main pbname", pbname);
	    }
	    if (pbref != NULL)
		CFRelease (pbref);
	} else if (!strcmp (argv[1], "paste")) {
	    PasteboardRef pbref;
	    stat = pbl_create(
		    ARGUMENT_D( 2, "com.apple.pasteboard.clipboard" ),
		    (void **) &pbref, NULL );
	    if (!stat && pbref != NULL) {
		unsigned char* data;
		size_t size;
		PB_FLAVOR_FLAGS flags;
		stat = pbl_paste( pbref, 1, 0UL, ARGUMENT( 3 ),
			&data, &size, &flags );
		if ( data != NULL ) {
		    data[size] = '\0';
		    printf( "data: '%s'\n", data );
		    printf( "size: %lu\n", size );
		    printf( "flags: %#lx\n", ( unsigned long ) flags );
		}
		CFRelease (pbref);
	    }
	} else if (!strcmp (argv[1], "pbl_all")) {
	    PasteboardRef pbref;
	    stat = pbl_create(
		    ARGUMENT_D( 2, "com.apple.pasteboard.clipboard" ),
		    (void **) &pbref, NULL );
	    if (!stat && pbref != NULL) {
		pbl_rqst_t rqst = {
		    1,
		    0,
		    NULL,
		    1,
		};
		pbl_resp_t *resp;
		size_t num_resp;

pbl.h  view on Meta::CPAN


/*
 * pbl_uti_tags returns the preferred tags associated with the given
 * UTI in the given structure. On return, the elements of the structure
 * will contain either strings (which must be freed), or NULL.
 */

void pbl_uti_tags (char * c_uti, pbl_uti_tags_t * tags);

/*
 * pbl_all returns everything on the clipboard, subject to the settings
 * in the rqst argument, to wit:
 *     if rqst.all is false, only data matching the given id is
 *         returned.
 *     if rqst.want_data is false, the actual flavor data is not
 *         returned.
 */

OSStatus pbl_all (
	void * pbref,
	pbl_rqst_t *rqst,

script/pbtool  view on Meta::CPAN

#!/usr/local/bin/perl

=head1 NAME

pbtool - Manipulate Mac OS X pasteboards/clipboards.

=head1 SYNOPSIS

 $ pbtool
 pbtool> paste
 Flags = 0 (kPasteboardFlavorNoFlags)
 Able was I ere I saw Elba.
 pbtool> clear
 pbtool> copy "Madam, I'm Adam."
 pbtool> exit

script/pbtool  view on Meta::CPAN

causes I<copy> to use item ID 1, and I<paste> to use the last item that
contains the desired flavor), specify -noid.

The default is -noid.

=head1 DETAILS

I<Pbtool> is a Perl script that acts as a wrapper for Mac::Pasteboard.
Most functions of the package are available through the script, and it
adds a couple on its own account. The commands in general operate on the
current pasteboard, which initially is the system clipboard. Commands
also exist for changing the script's notion of the current pasteboard.

Input is from standard in, using Term::ReadLine if that is available and
the input is a terminal.

Blank input lines and input lines whose first non-blank character is '#'
are ignored. Any lines left are broken into tokens on spaces, though
quoted text is kept together. Text::ParseWords does the heavy lifting
here.

script/pbtool  view on Meta::CPAN

}
-t and print "\n";

my $pb;

=head2 clear [name]

This command clears the current pasteboard. If a name is given, the
named pasteboard becomes the current pasteboard, and it is cleared. If
no name is given and there is no current pasteboard, the system
clipboard becomes the current pasteboard and is cleared.

=cut

sub clear {
    $pb or create (@_);
    $pb->clear ();
    return;
}

=head2 copy

 pbtool> copy data [flavor [flags]]

This command copies the given data to the current pasteboard as the
given flavor and the given flavor flags. The flavor flags default to 0,
and the flavor to 'com.apple.traditional-mac-plain-text'. If there is no
current pasteboard the system clipboard becomes the current pasteboard,
but you get an error anyway because you do not own it at this point, not
having cleared it.

If the I<id> setting is set to a number, your data is copied to the item
with that ID. If it is set to undef, it is copied to item id 1.

=cut

sub copy {
    $pb or create ();

script/pbtool  view on Meta::CPAN

    return;
}

=head2 copy_file

 pbtool> copy_file file_name [flavor [flags]]

This command copies the contents of the given file to the current
pasteboard as the given flavor and the given flavor flags. The flavor
flags default to 0, and the flavor to C<defaultFlavor>. If there is no
current pasteboard the system clipboard becomes the current pasteboard,
but you get an error anyway because you do not own it at this point, not
having cleared it.

The file's encoding is assumed to be that implied by the flavor, if any.

If the I<id> setting is set to a number, your data is copied to the item
with that ID. If it is set to undef, it is copied to item id 1.

=cut

script/pbtool  view on Meta::CPAN

    close $fh;
    $pb->copy( $data, $flavor, $flags );
    return;
}

=head2 create

 pbtool> create [name]

The named pasteboard is created if necessary, and becomes the current
pasteboard. If no name is specified, you get the system clipboard, named
'com.apple.pasteboard.clipboard'.

=cut

{
    my %cache;
    sub create {	## no critic (RequireArgUnpacking)
	my $name = defined $_[0] ? $_[0] : kPasteboardClipboard;
	$pb = $cache{$name} ||= Mac::Pasteboard->new( $name );
	$pb->set(
	    encode		=> ! $opt{binary},

script/pbtool  view on Meta::CPAN

    print $pb->get( 'default_flavor' ), "\n";
    defined $flavor
	and $pb->set( default_flavor => $flavor );
    return;
}

=head2 flavors

 pbtool> flavors [conforms_to]

This command dumps the flavors of data present on the clipboard which
conform to the given flavor, If no conforming flavor is given, all
flavors are dumped. If the I<id> is defined, only data from that
pasteboard item are dumped. The output is in YAML if module B<YAML>
can be loaded, or in B<Data::Dumper> format if that module can be
loaded.  Either way, what you actually get is an array of anonymous
hashes. Each hash has the following keys:

 flag_names: a reference to a list of the names of the flags set;
 flags: the flavor flags;
 flavor: the name of the flavor;

script/pbtool  view on Meta::CPAN

sub help {
    pod2usage( { -verbose => 2, -exitval => 'NOEXIT' } );
    return;
}

=head2 name

 pbtool> name

This command displays the name of the current pasteboard. If there is no
current pasteboard, the system clipboard is made the current pasteboard,
and its name is displayed.

=cut

sub name {
    $pb or create ();
    print $pb->get ('name'), "\n";
    return;
}

script/pbtool  view on Meta::CPAN


=head2 paste_all

 pbtool> paste_all [conforms_to]

This command displays all data on the current pasteboard conforming to
the given flavor. If no flavor is given, all flavors are displayed. If
the I<id> is set, only data from the corresponding item are displayed.

The output is the same as for L<flavors|/flavors>, but in addition the
'data' key holds the actual data. If there is no current clipboard, the
system pasteboard is made the current clipboard.

See
L<https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/understanding_utis/understand_utis_intro/understand_utis_intro.html>
for the concept of conformance.

=cut

sub paste_all {
    my @args = @_;
    $pb or create ();

script/pbtool  view on Meta::CPAN

    _expand_flavors( $pb, @flavors );
    print Dump( \@flavors );
    return;
}

=head2 pbpaste

 pbtool> pbpaste

This command is equivalent to I<paste>, but always uses the system
clipboard.

=cut

sub pbpaste {
    my @args = @_;
    my ($data, $flags) = Mac::Pasteboard::pbpaste (@args);
    defined $data or die "No data found\n";
    print STDERR "Flags = $flags (",
	scalar Mac::Pasteboard::flavor_flag_names ($flags), ")\n";
    print $data;

script/pbtool  view on Meta::CPAN

    @_ = ();
    goto &opt;
}

=head2 status

 pbtool> status [new_value]

This command displays the current status setting of the current
pasteboard, optionally setting it first to the given value.  If there is
no current pasteboard, the system clipboard is made the current
pasteboard, and its status is displayed.

=cut

sub status {
    my @args = @_;
    $pb or create ();
    @args and $pb->set (status => $args[0]);
    print $pb->get ('status'), "\n";
    return;
}

=head2 synch

 pbtool> synch [pasteboard_name]

This command synchronizes with the current pasteboard. If a name is
given, that pasteboard is made the current pasteboard, and it is
synchronized. If there is no current pasteboard and no name is given,
the system clipboard is made the current pasteboard.

The synchronization flags returned by the operation are written to
standard out.

=cut

sub synch {
    my @args = @_;
    (!$pb || @args) and create (@args);
    my $flags = $pb->synch ();

script/pbtool  view on Meta::CPAN

	} else {
	    push @rslt, $_;
	}
    }
    return @rslt;
}

#	@args = _options (@args);
#
#	This subroutine feeds its input to GetOptions. Anything left
#	over is assumed to be the name of a clipboard to make current.
#	An error results in the display of a brief error message.

sub _options {
    local @ARGV = @_;
    GetOptions( \%opt,
	qw{ binary! default_flavor|default-flavor=s echo! id=i },
	noid => sub { $opt{id} = undef },
	help => sub { pod2usage( { -verbose => 2 } ) },
    ) or pod2usage( { -verbose => 0 } );
    $pb and $pb->set(

t/pasteboard.t  view on Meta::CPAN


use strict;
use warnings;

use Test::More 0.96;	# Because of subtest();

note <<'EOD';

The tests were originally segregated into their own files because this
was the handiest way to segregate the tests of different functions. But
this causes fights over the clipboard when the tests are run in
parallel, resulting in test failures. Renaming the test files and then
just doing them seemed like the simplest way to serialize the whole
mess.

EOD

diag '';
foreach my $name ( qw{ LANG LC_ALL LC_COLLATE LC_CTYPE LC_MONETARY
    LC_NUMERIC LC_TIME LC_MESSAGES } ) {
    my_diag_value( $name, $ENV{$name} );

t/pasteboard.t  view on Meta::CPAN

    } or diag 'I18N::Langinfo unavailable';
}

if ( eval { require Mac::Pasteboard; 1 } ) {
    foreach my $sub ( qw{ defaultEncode defaultFlavor __variant } ) {
	my $code = Mac::Pasteboard->can( $sub );
	my_diag_value( $sub, $code->() );
    }
}

subtest 'Copy to clipboard' => sub {
    do './t/copy.tx';
};

subtest 'Error handling' => sub {
    do './t/error.tx';
};

subtest 'Miscellaneous' => sub {
    do './t/misc.tx';
};

subtest 'Paste from clipboard' => sub {
    do './t/paste.tx';
};

subtest 'Synch with clipboard' => sub {
    do './t/synch.tx';
};

done_testing;

sub my_diag_value {
    my ( $name, $value ) = @_;
    if ( defined $value ) {
	diag "$name='$value'";
    } else {



( run in 3.381 seconds using v1.01-cache-2.11-cpan-84e82930d8c )