Scrambling Text

From Scribus Wiki
Jump to navigation Jump to search
This article is part of the Scripts series.

I had a request to write a script that would take a text document with multiple frames and scramble its text. There was a desire to share the document regarding its layout, but not the actual content. While the text could have been replaced by lorem ipsum, this would have been time-consuming, and various styles would have to be recreated.

Here is the initial version of this script, which scrambles the text in a selected frame, which must of course be a text frame. The logic begins with the way that Autoquote takes apart the content of a frame, character by character, but skipping over the characters you're not looking for, leaving everything else, including the markers for character styles, intact.

Here we wish to change all aphabetical characters, but leave numbers, punctuation, carriage returns, and so on unchanged. It uses the python module random to generate a new random small letter and capital letter each time a new alphabetical character is found, then modifies according to the case of the found letter. This isn't like a cryptographic code using some algorithm for the process, it's as random as the random module can generate, so this will not be reversible. This isn't exactly true – there are some times when you can use Undo after running the script to see what I mean. Once you save the document and reopen, it's truly irreversible.

Todo?

  • Possibility to scrambled text of grouped text objects on master pages
    • Hmm, more challenging.
  • Scramble image names
    • Now this takes some research ... I think a barrier here will be that if you scramble image names with a script you create a missing image problem. Maybe easier just to remove all images?

Some notes on the versions

I've left all of these here because different users might have different needs.

  • replacetext.py – this one only operates on an individual selected text frame
  • replacetext3.py – this version scrambles the text in all text frames on all pages (but not Master Pages)
  • replacetext4.py – this version scrambles the text in all text frames on all pages AND on all Master Pages
  • replacetext2.py – this version works only on the current page

replacetext.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# File: replacetext.py
# © 2014.06.06 Gregory Pittman
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
"""
USAGE

This script will scramble text in text frames.
You must have a document open, and a text frame selected.
If you have more than one object or a non-text frame selected,
there will be an error generated, and the script will exit.


"""
import scribus
import random

if scribus.haveDoc():
    c = 0
        
else:
    scribus.messageBox('Usage Error', 'You need a Document open', scribus.ICON_WARNING, scribus.BUTTON_OK)
    sys.exit(2)

if scribus.selectionCount() == 0:
    scribus.messageBox('Scribus - Usage Error',
        "There is no object selected.\nPlease select a text frame and try again.",
        scribus.ICON_WARNING, scribus.BUTTON_OK)
    sys.exit(2)
if scribus.selectionCount() > 1:
    scribus.messageBox('Scribus - Usage Error',
        "You have more than one object selected.\nPlease select one text frame and try again.", scribus.ICON_WARNING, scribus.BUTTON_OK)
    sys.exit(2)

textbox = scribus.getSelectedObject()
pageitems = scribus.getPageItems()
boxcount = 1
for item in pageitems:
    if (item[0] == textbox):
        if (item[1] != 4):
            scribus.messageBox('Scribus - Usage Error', "This is not a textframe. Try again.", scribus.ICON_WARNING, scribus.BUTTON_OK)
            sys.exit(2)
contents = scribus.getTextLength(textbox)


while 1:
    if ((c == contents) or (c > contents)): break
    if ((c + 1) > contents - 1):
        nextchar = ' '
    else:
        scribus.selectText(c+1, 1, textbox)
        nextchar = scribus.getText(textbox)
    scribus.selectText(c, 1, textbox)
    char = scribus.getText(textbox)
    if (len(char) != 1):
        c += 1
        continue
    alpha = random.randint(1,26)
    letter = chr(alpha + 96)
    LETTER = chr(alpha + 64)
    if ((ord(char)>96)and(ord(char)<123)):
	scribus.deleteText(textbox)
	scribus.insertText(letter, c, textbox)
    if ((ord(char)>64)and(ord(char)<91)):
	scribus.deleteText(textbox)
	scribus.insertText(LETTER, c, textbox)

            
    c += 1
    contents = scribus.getTextLength(textbox)

scribus.setRedraw(1)
scribus.docChanged(1)
scribus.messageBox("Finished", "That should do it!",scribus.ICON_NONE,scribus.BUTTON_OK)

replacetext_v3.py

The next phase was to try not to simply alter all the text in all the text frames and all pages of a document. Here is the final result.

I have added a warning message and an ability to opt out of the script in case you haven't saved the original.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# File: replacetext_v3.py
# © 2014.06.06 Gregory Pittman
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
"""
USAGE

You must have a document open.
WARNING: this script irreversibly scrambles your text on all pages.
You would be wise to work on a copy of the original to avoid 
accidentally saving this scrambled version only to lose the original.


"""
import scribus
import random

if scribus.haveDoc():
    c = 0
        
else:
    scribus.messageBox('Usage Error', 'You need a Document open',scribus.ICON_WARNING, scribus.BUTTON_OK)
    sys.exit(2)
scribus.messagebarText("Getting ready to process Page 1")  # a bit kludgey maybe, but gives an initial message about Page 1
scribus.redrawAll()

warnresult = scribus.valueDialog('Warning!', 'This script is going to irreversibly alter the text in your document.\nChange this default value to abort', 'Ok!')

if (warnresult != 'Ok!'):
    sys.exit(2)
    
page = 1
pagenum = scribus.pageCount()
while (page <= pagenum):
  scribus.gotoPage(page)
  scribus.messagebarText("Processing Page "+str(page)) # New Feature! - sends a message to message bar 
  scribus.redrawAll()                                  # this allows the message to show
    
  pageitems = scribus.getPageItems()

  for item in pageitems:
    if (item[1] == 4):
      c = 0
      textbox = item[0]
      scribus.selectObject(textbox)
      contents = scribus.getTextLength(textbox)

      while 1:
	  if ((c == contents) or (c > contents)): break
	  if ((c + 1) > contents - 1):
	      nextchar = ' '
	  else:
	      scribus.selectText(c+1, 1, textbox)
	      nextchar = scribus.getText(textbox)
	  scribus.selectText(c, 1, textbox)
	  char = scribus.getText(textbox)
	  if (len(char) != 1):   # here is where you skip over any nonprinting characters
	      c += 1
	      continue
	  alpha = random.randint(1,26)
	  letter = chr(alpha + 96)
	  LETTER = chr(alpha + 64)
	  if ((ord(char)>96)and(ord(char)<123)):
	      scribus.deleteText(textbox)
	      scribus.insertText(letter, c, textbox)
	  if ((ord(char)>64)and(ord(char)<91)):
	      scribus.deleteText(textbox)
	      scribus.insertText(LETTER, c, textbox)
 
	  c += 1
	  contents = scribus.getTextLength(textbox)
  page += 1

scribus.setRedraw(1)
scribus.docChanged(1)
scribus.messageBox("Finished", "That should do it!",scribus.ICON_NONE, scribus.BUTTON_OK)

replacetext_v4.py

Here is a version similar to number 3, but this one in addition will scramble the text in Master Pages. It can probably be made much neater by creating some functions which might be called for the replacement process at least. I decided to be a bit cautious and repetitive in case the Master Page editing didn't work right.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# File: replacetext_v4.py
# © 2014.07.04 Gregory Pittman
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
"""
USAGE

You must have a document open.
WARNING: this script irreversibly scrambles your text on all pages.
You would be wise to work on a copy of the original to avoid 
accidentally saving this scrambled version only to lose the original.

This version will also scramble the text in Master Pages.


"""
import scribus
import random

if scribus.haveDoc():
    c = 0
        
else:
    scribus.messageBox('Usage Error', 'You need a Document open', scribus.ICON_WARNING, scribus.BUTTON_OK)
    sys.exit(2)

warnresult = scribus.valueDialog('Warning!', 'This script is going to irreversibly alter the text in your document.\nChange this default value to abort', 'Ok!')

if (warnresult != 'Ok!'):
    sys.exit(2)
    
masterpages = scribus.masterPageNames()  # here is where you get a list of and then edit Master Pages
for mpage in masterpages:
  scribus.editMasterPage(mpage)
  pageitems = scribus.getPageItems()
  for item in pageitems:
    if (item[1] == 4):
      c = 0
      textbox = item[0]
      scribus.selectObject(textbox)
      contents = scribus.getTextLength(textbox)
      
      while 1:
	if ((c == contents) or (c > contents)): break
        if ((c + 1) > contents - 1): nextchar = ' '
        else:
	  scribus.selectText(c + 1, 1, textbox)
	  nextchar = scribus.getText(textbox)
	scribus.selectText(c, 1, textbox)
	char = scribus.getText(textbox)
	if (len(char) != 1):
	  c += 1
	  continue
	alpha = random.randint(1,26)
	letter = chr(alpha + 96)
	LETTER = chr(alpha + 64)
	if ((ord(char) > 96) and (ord(char) < 123)):
	  scribus.deleteText(textbox)
	  scribus.insertText(letter, c, textbox)
	if ((ord(char)>64)and(ord(char)<91)):
	  scribus.deleteText(textbox)
	  scribus.insertText(LETTER, c, textbox)
 
	c += 1
	contents = scribus.getTextLength(textbox)
  scribus.closeMasterPage()    #  note that there is no argument for this command inside the parentheses
	  
page = 1
pagenum = scribus.pageCount()
while (page <= pagenum):
  scribus.gotoPage(page)
    
  pageitems = scribus.getPageItems()

  for item in pageitems:
    if (item[1] == 4):
      c = 0
      textbox = item[0]
      scribus.selectObject(textbox)
      contents = scribus.getTextLength(textbox)

      while 1:
	  if ((c == contents) or (c > contents)): break
	  if ((c + 1) > contents - 1):
	      nextchar = ' '
	  else:
	      scribus.selectText(c+1, 1, textbox)
	      nextchar = scribus.getText(textbox)
	  scribus.selectText(c, 1, textbox)
	  char = scribus.getText(textbox)
	  if (len(char) != 1):   # here is where you skip over any nonprinting characters
	      c += 1
	      continue
	  alpha = random.randint(1,26)
	  letter = chr(alpha + 96)
	  LETTER = chr(alpha + 64)
	  if ((ord(char)>96)and(ord(char)<123)):
	      scribus.deleteText(textbox)
	      scribus.insertText(letter, c, textbox)
	  if ((ord(char)>64)and(ord(char)<91)):
	      scribus.deleteText(textbox)
	      scribus.insertText(LETTER, c, textbox)
 
	  c += 1
	  contents = scribus.getTextLength(textbox)
  page += 1

scribus.setRedraw(1)
scribus.docChanged(1)
scribus.messageBox("Finished", "That should do it!",scribus.ICON_NONE, scribus.BUTTON_OK)

replacetext_v2.py

This is another version which only modifies a page at a time.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# File: replacetext_v2.py
# © 2014.06.06 Gregory Pittman
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
"""
USAGE

You must have a document open.
WARNING: this script irreversibly scrambles your text


"""
import scribus
import random

if scribus.haveDoc():
    c = 0
        
else:
    scribus.messageBox('Usage Error', 'You need a Document open', scribus.ICON_WARNING, scribus.BUTTON_OK)
    sys.exit(2)

warnresult = scribus.valueDialog('Warning!', 'This script is going to irreveribly alter the text in your document.\nChange this default value to abort', 'Ok!')

if (warnresult != 'Ok!'):
    sys.exit(2)
    
pageitems = scribus.getPageItems()

for item in pageitems:
    if (item[1] == 4):
      c = 0
      textbox = item[0]
      scribus.selectObject(textbox)
      contents = scribus.getTextLength(textbox)


      while 1:
	  if ((c == contents) or (c > contents)): break
	  if ((c + 1) > contents - 1):
	      nextchar = ' '
	  else:
	      scribus.selectText(c+1, 1, textbox)
	      nextchar = scribus.getText(textbox)
	  scribus.selectText(c, 1, textbox)
	  char = scribus.getText(textbox)
	  if (len(char) != 1):
	      c += 1
	      continue
	  alpha = random.randint(1,26)
	  letter = chr(alpha + 96)
	  LETTER = chr(alpha + 64)
	  if ((ord(char)>96)and(ord(char)<123)):
	      scribus.deleteText(textbox)
	      scribus.insertText(letter, c, textbox)
	  if ((ord(char)>64)and(ord(char)<91)):
	      scribus.deleteText(textbox)
	      scribus.insertText(LETTER, c, textbox)

		  
	  c += 1
	  contents = scribus.getTextLength(textbox)

scribus.setRedraw(1)
scribus.docChanged(1)
scribus.messageBox("Finished", "That should do it!", scribus.ICON_NONE, scribus.BUTTON_OK)

Side Effects

The biggest one seems to be that in most if not all cases your text will take up more lines than it did before scrambling. This is due to the random nature of these changes, in which case the occurrence of wide letters is much more common than in the anyone's original text. There could certainly be the possibility of considering character frequencies and making adjustments, but this would reduce randomness somewhat.

Also note that as written this changes only the limited Latin character set, a-z, and A-Z, but you should be able to alter this script to include other character sets. One might also scramble numbers to random numbers if that seemed desirable.