0 votos

AppleScript: ¿Cómo modificar los datos HTML y RTF en el portapapeles?

Estoy escribiendo un archivo AppleScript .scpt, activado en todo el sistema por la combinación de teclas asignada en FastScripts.app, que añade paréntesis alrededor del texto editable seleccionado.

Si el texto seleccionado ya está envuelto en paréntesis, quiero que mi script elimine efectivamente los paréntesis de la selección. Aquí es donde necesito ayuda. No quiero quitar nada del formato del texto formateado.

Mi script funciona si la selección-con-paréntesis es de datos de texto plano, pero no si es de datos RTF o HTML.

Aquí está mi código completo:

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

(*
Get the selected text into an AppleScript, while preserving the original clipboard:
From: http://apple.stackexchange.com/questions/271161/how-to-get-the-selected-text-into-an-applescript-without-copying-the-text-to-th/
*)

-- Back up the original clipboard contents:
set savedClipboard to my fetchStorableClipboard()
set thePasteboard to current application's NSPasteboard's generalPasteboard()
set theCount to thePasteboard's changeCount()

-- Copy selected text to clipboard:
tell application "System Events" to keystroke "c" using {command down}

-- Check for changed clipboard:
repeat 20 times
    if thePasteboard's changeCount() is not theCount then exit repeat
    delay 0.1
end repeat

set firstCharacter to (character 1 of (the clipboard))
set lastCharacter to (character (length of (the clipboard))) of (the clipboard)

-- Remove the parentheses from the selection, if the selection is wrapped in parentheses:
if (firstCharacter is "(") and (lastCharacter is ")") then
    -- The selection already has parentheses.
    -- I must discern what class types are available for the clipboard content:
    tell current application
        set cbInfo to get (clipboard info) as string
        if cbInfo contains "RTF" then
            -- I need help here.
            -- Remove the first and last characters of the rich text, while retaining the rich text formatting.
        else if cbInfo contains "HTML" then
            -- I need help here.
            -- Remove the first and last characters of the HTML, while retaining formatting data.
        else
            -- The clipboard contains plain text.

            -- Remove the first and last character of a plain text string:
            set theSelectionWithoutParentheses to (text 2 thru -2 of (the clipboard))
            set the clipboard to theSelectionWithoutParentheses
            tell application "System Events" to keystroke "v" using {command down}
        end if
    end tell

else
    -- The selection needs parentheses.
    tell application "System Events" to keystroke "("
    delay 0.1
    tell application "System Events" to keystroke "v" using {command down}
    delay 0.1
    tell application "System Events" to keystroke ")"
end if

delay 1 -- Without this delay, may restore clipboard before pasting.

-- Restore clipboard:
my putOnClipboard:savedClipboard

on fetchStorableClipboard()
    set aMutableArray to current application's NSMutableArray's array() -- used to store contents
    -- get the pasteboard and then its pasteboard items
    set thePasteboard to current application's NSPasteboard's generalPasteboard()
    -- loop through pasteboard items
    repeat with anItem in thePasteboard's pasteboardItems()
        -- make a new pasteboard item to store existing item's stuff
        set newPBItem to current application's NSPasteboardItem's alloc()'s init()
        -- get the types of data stored on the pasteboard item
        set theTypes to anItem's types()
        -- for each type, get the corresponding data and store it all in the new pasteboard item
        repeat with aType in theTypes
            set theData to (anItem's dataForType:aType)'s mutableCopy()
            if theData is not missing value then
                (newPBItem's setData:theData forType:aType)
            end if
        end repeat
        -- add new pasteboard item to array
        (aMutableArray's addObject:newPBItem)
    end repeat
    return aMutableArray
end fetchStorableClipboard

on putOnClipboard:theArray
    -- get pasteboard
    set thePasteboard to current application's NSPasteboard's generalPasteboard()
    -- clear it, then write new contents
    thePasteboard's clearContents()
    thePasteboard's writeObjects:theArray
end putOnClipboard:

1voto

rubik's sphere Puntos 31

Con algo de ayuda de

He ideado una solución para modificar el contenido del portapapeles del HTML o RTF, y luego poner este contenido modificado en el portapapeles.


Ya que estamos en ello, aquí hay otro método para eliminar los paréntesis externos de texto simple datos del portapapeles:

do shell script "pbpaste | tr -d '()' | pbcopy"

Para eliminar los paréntesis externos de HTML datos del portapapeles (conservando los datos de formato):

do shell script "osascript -e 'try' -e 'get the clipboard as «class HTML»' -e 'end try' | awk '{sub(/«data HTML/, \"3C68746D6C3E\") sub(/»/, \"3C2F68746D6C3E\")} {print}' | xxd -r -p | textutil -convert rtf -stdin -stdout | tr -d '()' | pbcopy"

Ambos Método 1 y Método 2 para eliminar los paréntesis exteriores de RTF datos del portapapeles (mientras se conservan los datos de formato), manipular el contenido del portapapeles RTF analizando estos datos como código hexadecimal:

Método 1:

try
    -- Get the RTF clipboard data in hexadecimal form:
    set theOriginalHexData to do shell script "osascript -e 'try' -e 'get the clipboard as «class RTF »' -e 'end try'"
on error eStr number eNum
    display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with icon caution
    error number -128 (* user cancelled *)
end try

-- I don't want any parentheses inside of the outer parentheses to be removed; they must be preserved. So...

-- Check to see if there is more than 1 instance of "(":    
set originalDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to "28" -- The hex code for the character: "("
set theHexInAListSeparatedByOpeningParentheses to text items of theOriginalHexData
set numberOfOpeningParentheses to ((count theHexInAListSeparatedByOpeningParentheses) - 1)
set AppleScript's text item delimiters to originalDelimiters

if numberOfOpeningParentheses is 1 then
-- There are zero inner opening-parentheses.
    set theModifiedHexData to (item 1 of theHexInAListSeparatedByOpeningParentheses) & (item 2 of theHexInAListSeparatedByOpeningParentheses)

else if numberOfOpeningParentheses is greater than 1 then
-- There is at least one inner opening-parenthesis.
    set theModifiedHexData to (item 1 of theHexInAListSeparatedByOpeningParentheses) & (item 2 of theHexInAListSeparatedByOpeningParentheses)
    set counter to 2
    repeat until (counter is greater than numberOfOpeningParentheses)
        -- Add the desired inner opening-parentheses back into the string:
        set theModifiedHexData to (theModifiedHexData & "28" & (item (counter + 1) of theHexInAListSeparatedByOpeningParentheses))
        set counter to counter + 1
    end repeat
end if

-- Check to see if there is more than 1 instance of ")":
set originalDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to "29" -- The hex code for the character: ")"
set theHexInAListSeparatedByClosingParentheses to text items of theModifiedHexData
set numberOfClosingParentheses to ((count theHexInAListSeparatedByClosingParentheses) - 1)
set AppleScript's text item delimiters to originalDelimiters

if numberOfClosingParentheses is 1 then
-- There are zero inner closing-parentheses.
    set theModifiedHexData to (item 1 of theHexInAListSeparatedByClosingParentheses) & (item 2 of theHexInAListSeparatedByClosingParentheses)

else if numberOfClosingParentheses is greater than 1 then
-- There is at least one inner closing-parenthesis.
    set theModifiedHexData to (item 1 of theHexInAListSeparatedByClosingParentheses)
    set counter to 2
    repeat until ((counter) is greater than numberOfClosingParentheses)
        -- Add the desired inner closing-parentheses back into the string:
        set theModifiedHexData to (theModifiedHexData & "29" & (item (counter) of theHexInAListSeparatedByClosingParentheses))
        set counter to counter + 1
    end repeat
    set theModifiedHexData to (theModifiedHexData & (item (counter) of theHexInAListSeparatedByClosingParentheses))
end if

-- Put the modified hex code onto the clipboard, as RTF data:
try
    -- Get the RTF clipboard data in hexadecimal form:
    do shell script "osascript -e 'set the clipboard to " & theModifiedHexData & "'"
on error eStr number eNum
    display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with icon caution
    error number -128 (* user cancelled *)
end try

El método 1 para modificar los datos del portapapeles de la RTF es posiblemente susceptible a falsos positivos, si el código hexagonal deseado se divide entre el segundo carácter de un par de hexágonos y el primer carácter del siguiente par de hexágonos.

Por ejemplo, 32 y 85 cuando están uno al lado del otro en el código hexagonal, también se interpreta como 28 a los ojos del Método 1. Claramente, esto es indeseable.

Método 2:

El método 2 resuelve el problema, haciendo imposible un falso positivo como este. Esto se debe a que, antes de que el método 2 analice el código hexagonal, primero organiza el código hexagonal text en un objeto de base binaria list objeto.

A diferencia del Método 1, el Método 2 interpreta el código hexagonal en pares. Por lo tanto, el Método 2 es técnicamente mejor:

global hexCodeInBinaryList

set firstCharacterHEX to "28"
set lastCharacterHEX to "29"

try
    -- Get the RTF clipboard data in hexadecimal form:
    set theOriginalHexData to do shell script "osascript -e 'try' -e 'get the clipboard as «class RTF »' -e 'end try'"
on error eStr number eNum
    display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with icon caution
    error number -128 (* user cancelled *)
end try

-- I don't want any parentheses inside of the outer parentheses to be removed; they must be preserved. So...

-- Make sure that any parentheses that come after the first parenthesis is preseved:
putStringIntoBinaryList(theOriginalHexData)
set counter to 1
set listContainingItemNumbers to {}

repeat until (counter is greater than (count of hexCodeInBinaryList))
    if ((item counter) of hexCodeInBinaryList) is firstCharacterHEX then
        set listContainingItemNumbers to listContainingItemNumbers & counter
    end if
    set counter to counter + 1
end repeat
set numberOfOpeningParentheses to (count of listContainingItemNumbers)

set theNewLocation to item 1 of listContainingItemNumbers
set theModifiedHexData to ((items 1 thru (theNewLocation - 1) of hexCodeInBinaryList) as text)
set theModifiedHexData to theModifiedHexData & ((items (theNewLocation + 1) thru (count of hexCodeInBinaryList) of hexCodeInBinaryList) as text)

-- Make sure that any parentheses that come before the last parenthesis is preseved:
putStringIntoBinaryList(theModifiedHexData)

set counter to 1
set listContainingItemNumbers to {}

repeat until (counter is greater than (count of hexCodeInBinaryList))
    if ((item counter) of hexCodeInBinaryList) is lastCharacterHEX then
        set listContainingItemNumbers to listContainingItemNumbers & counter
    end if
    set counter to counter + 1
end repeat
set numberOfClosingParentheses to (count of listContainingItemNumbers)

set theNewLocation to (item numberOfClosingParentheses of listContainingItemNumbers)
set theModifiedHexData to ((items 1 thru (theNewLocation - 1) of hexCodeInBinaryList) as text)
set theModifiedHexData to theModifiedHexData & ((items (theNewLocation + 1) thru (length of hexCodeInBinaryList) of hexCodeInBinaryList) as text)

try
    -- Put the modified hex code onto the clipboard, as RTF data:
    do shell script "osascript -e 'set the clipboard to " & theModifiedHexData & "'"
on error eStr number eNum
    display dialog eStr & " number " & eNum buttons {"OK"} default button 1 with icon caution
    error number -128 (* user cancelled *)
end try

on putStringIntoBinaryList(theStringToConvertToList)

    set totalNumberOfChracters to length of theStringToConvertToList

    set startPoint to 1
    set endPoint to 2
    set hexCodeInBinaryList to {}

    repeat until (endPoint is totalNumberOfChracters) or ((endPoint - 1) is totalNumberOfChracters)
        set hexCodeInBinaryList to hexCodeInBinaryList & (text startPoint thru endPoint of (the theStringToConvertToList))

        set startPoint to (startPoint + 2)
        set endPoint to (endPoint + 2)
    end repeat

    if ((endPoint - 1) is totalNumberOfChracters) then
        set hexCodeInBinaryList to hexCodeInBinaryList & (character (endPoint - 1) of (the theStringToConvertToList))
    end if

end putStringIntoBinaryList

Sin embargo, note que el Método 2 ha sido diseñado alrededor de la suposición de que usted está trabajando con un de dos caracteres delimitador de código hexagonal.

Si su delimitador de código hexagonal excede los 2 caracteres, (como el delimitador de código hexagonal de 8 caracteres para una comilla doble de apertura que es 5C273933 ), tendrías que reescribir el putStringIntoBinaryList() en consecuencia (o utilice en su lugar el Método 1, que probablemente sea seguro de utilizar en un delimitador de código hexadecimal largo de 8 caracteres).


AppleAyuda.com

AppleAyuda es una comunidad de usuarios de los productos de Apple en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X