Lone Wolf Development Forums

Lone Wolf Development Forums (http://forums.wolflair.com/index.php)
-   Realm Works Discussion (http://forums.wolflair.com/forumdisplay.php?f=67)
-   -   XML Transformation Script (PowerShell) (http://forums.wolflair.com/showthread.php?t=57418)

EightBitz January 15th, 2017 02:49 AM

XML Transformation Script (PowerShell)
 
New version as of August 14th
Version: 1.6

Fixes:
• Changed the method of loading the XML data so it allows for loading larger files.
• Fixed an issue with smart images where the script generated an error if the image was revealed, nothing was unmasked, and the export included only revealed content. Now, instead of generating an error, the script ignores the lack of image data.
• Fixed an issue with the GUI script where the Destination file dialog was not filtering for the proper extensions according to the selected output format (Word vs. HTML).


Please read the release notes.

https://github.com/EightBitz/RWExport

Bidmaron January 15th, 2017 04:53 AM

We are going to need a new sticky somewhere or an entirely new sub forum with user-donated tools, as I am sure this is just the first of the useful things that folks will generate to handle morphing exports.

Eightbitz, congrats on paving the way, and I pray you find a job.

rob January 15th, 2017 05:04 AM

Absolutely stick with Compact format. That's exactly what you should be using for stuff like this.

And I echo @Bidmaron's sentiments about best wishes for finding gainful employment quickly!

I'll need to talk to BJ about what we're going to do about user-created tools. That's something that I'd always expected once we got the export/import stuff into place, but it's also something I neglected give any serious thought to how we can facilitate. <sigh>

mazzy January 15th, 2017 07:24 AM

I love that you are choosing to share this with the community and appreciate the time and knowledge you spent coming up with a solution, thanks so much Eightbitz!

If you'd like a hand with the HTML and CSS portion, I'm willing to give it a go. Mind you, I'm out of practice, it's been about 5'ish years since I last did anything with HTML/CSS, so if you'd rather take up someone else's offer or do it yourself, I completely understand :)

I second Bidmaron's suggestion of a repository for these type of posts, I'm sure there will be plenty of users that come up with clever solutions for doing different things with our newly found powers ;) Github would be a fantastic way for users to contribute under the Lone Wolf umbrella, would allow for versioning control, not to mention a way to review and approve submissions. Just a suggestion, of course, I'm sure you guys will come up with a solution that works :)

daplunk January 15th, 2017 08:49 AM

Eventually... Does the content market have a category for things like this? Keep the forums clean.

AEIOU January 15th, 2017 10:55 AM

I think we'll need a forum for general user reviews. While I think the Grey Whatsits will be helpful, they could be overwhelmed. And it would be nice to have Grey Whatsits and community reviews all collected in the same place so we don't run all over looking for information. It may be that reviews and tools can live happily together to reduce the number of forums? And I could be totally offbase and the ability to do reviews will be tied directly to the files in the Marketplace. I'm just throwing noodles at the fridge to see what might stick.... ;)

rob January 16th, 2017 01:24 AM

Quote:

Originally Posted by daplunk (Post 241487)
Eventually... Does the content market have a category for things like this? Keep the forums clean.

Not really. Nor does the Content Market current design model allow us to READILY integrate something like this. So it will require some thought. Which means it probably won't get the attention it deserves for a few more weeks, since the immediate focus is on fixing bugs in export/import/conversion and then delivering content to everyone. That's not the answer I want to be giving here, but it looks to be the reality of the situation. So, at least in the interim, the forums will have to suffice.

EightBitz January 16th, 2017 02:03 AM

Quote:

Originally Posted by rob (Post 241570)
Not really. Nor does the Content Market current design model allow us to READILY integrate something like this. So it will require some thought. Which means it probably won't get the attention it deserves for a few more weeks, since the immediate focus is on fixing bugs in export/import/conversion and then delivering content to everyone. That's not the answer I want to be giving here, but it looks to be the reality of the situation. So, at least in the interim, the forums will have to suffice.

Thank you, thank you, thank you for the export! I can't say that enough. This is the one reason I've been holding off on really using RW.

Also, no offense, but I'm taking your name in vain while improving my PowerShell script. The fact that a plain-text snippet can have multiple ordered lists, multiple unordered lists, multiple tables, and multiple instances of unformatted text, in any order, has made my life difficult. :-p

As a user, of course, I love that flexibility. But dealing with that with the script has been challenging. I think I'm there, though. I've been able to parse out all the different blocks, in order, and now I'm parsing the blocks themselves.

I'm done with the plain text, the ordered lists, and the unordered lists, and just have to deal with the tables.

Mind you, this is all for the plain text transformation. When I'm done with that, I'll work on an HTML one which, I gather, would be easier in this regard as I can leave more of the HTML tags in place instead of tearing it apart like I am now for plain text.

Now YOU can laugh at MY pain.

Seriously and sincerely, though, thank you for this. And don't forget to take care of yourself! You're going to need your strength to deal with the angry mob outside who are just waiting to bust your chops about custom calendars. ;-D

davidp January 16th, 2017 06:21 AM

Quote:

Originally Posted by EightBitz (Post 241578)
Also, no offense, but I'm taking your name in vain while improving my PowerShell script. The fact that a plain-text snippet can have multiple ordered lists, multiple unordered lists, multiple tables, and multiple instances of unformatted text, in any order, has made my life difficult. :-p

The <contents> of a <snippet> should all be HTML and that would be the only place that the lists, tables, etc. would be present. You should be able to take the <contents> and just treat it as HTML and be done with it. Any more and you are making life hard on yourself. :)

The only slight gotcha is that we use encoding of the contents so that it isn't interpreted as XML tags, but that should be straight-forward to deal with.

Vargr January 16th, 2017 07:06 AM

@EightBitz:

Notice how we have been civil and not turned into an angry mob complete with touches and pitchforks - yet...

Nah, just kidding. :-)

EightBitz January 16th, 2017 07:13 AM

Quote:

Originally Posted by davidp (Post 241597)
The <contents> of a <snippet> should all be HTML and that would be the only place that the lists, tables, etc. would be present. You should be able to take the <contents> and just treat it as HTML and be done with it. Any more and you are making life hard on yourself. :)

The only slight gotcha is that we use encoding of the contents so that it isn't interpreted as XML tags, but that should be straight-forward to deal with.

Yep. For the HTML version, I'll be taking advantage of that. Right now, I just wanted a plain text version. No HTML or anything.

EightBitz January 16th, 2017 07:21 AM

OK, I'm not going to keep cluttering up the forum, but here's a much better version of the plain text export. No HTML or anything yet, just plain text.

I'll set up a DropBox or something soon.

I sorted out a few bugs and oversights, and added some functionality:

Version 0.5a
Added a -Prefix switch to optionally include a topic's prefix.
Added a -Suffix switch to optionally include a topic's suffix.
Added a -Sort option to sort topics by:
1 = Name
2 = Prefix, Name **Default**
3 = Category, Name
4 = Category, Prefix, Name

Choosing options 2 or 4 will sort by prefix, regardless of whether or not
the -Prefix switch is specified. Likewise, choosing options 1 or 3 will
sort by name, regardless of whether or not the -Prefix switch is specified.

Added parentage for topics.
Made the topic suffix parenthetical so it's consistent with the display in RW.

Better (I hope) parsing of snippets, and support for more types.

Supported snippet types include:
Text (Including lists and tables, but NOT including any formatting such as bold, italic, highlights, etc.)
GM Directions
Labeled Text
Tags
Calendar Date
Calendar Date Range
Numeric Value
Tags (MultiDomain)

Unsupported snippet types include:
Picture (Simple)
Smart Image (Map)
Statblock
Hero Lab Portfolio
Foreign Object
Any snippet types listed under "Documents and Media"

It now responds to the Get-Help cmdlet.

Get-Help RWExport-To-Text.ps1

Have fun!

Code:

# RWExport-To-Text.ps1 (formerly Format-RWExport.ps1)
# by EightBitz
# Version 0.5a
# 1/16/2017, 10:10 AM CST
#
# Copy this full length of code into a text file, and save it as Format-RWExport.ps1
#
# Open a Powershell window, and CD to where you saved it.
# For example, if you saved in C:\Users\EightBitz\Desktop, then in the Powershell window, you would type:
#    CD \Users\EightBitz\Desktop
#
# Then run the powershell script, specifying the source file (the compact export you created)
#    and the new destination file you wish to create or overwrite. Example:
# .\Format-RWExport.ps1 -Source "C:\Users\EightBitz\desktop\Export test.rwoutput" -Destination C:\Users\EightBitz\desktop\TransformedExportTest.txt
#
#
# CSS categories:
#    <title>
#    <topic>
#    <section>
#    <snippet>
#
# Track parentage and levels of topics and sections
#
# Sort topics
#

<#
.SYNOPSIS
RWExport-To-Text.ps1 transforms a RealmWorks export file into a plain text file.

By: EightBitz
Date: 1/16/2017
Version 0.5a

.DESCRIPTION
RWExport-To-Text.ps1 loads the XML file exported from Realm Works and transforms it into plain text so it can be printed or imported/pasted into other programs. The export from Realm Works must done with the Compact Output option.
.PARAMETER Source
Enter the full path and filename for the source file (the Realm Works export file).
.PARAMETER Destination
Enter the full path and filename for the destination file (the plain text output).
.PARAMETER Sort
Choose your preferred sort order for exported topics.
  1 = Name
  2 = Prefix, Name **Default**
  3 = Category, Name
  4 = Category, Prefix, Name
.PARAMETER Prefix
Include this parameter to display the prefix for each topic.
.PARAMETER Suffix
Include this parameter to display the suffix for each topic.
.EXAMPLE
RWExport-To-Text.ps1 -Source MyExport.rwoutput -Destination MyPlainTextFile.txt
.EXAMPLE
RWExport-To-Text.ps1 -Source MyExport.rwoutput -Destination MyPlainTextFile.txt -Sort 1
.EXAMPLE
RWExport-To-Text.ps1 -Source MyExport.rwoutput -Destination MyPlainTextFile.txt -Prefix -Suffix
.NOTES
The specified sort order will occur regardless of whether or not prefixes are included.
If you sort by prefix, but do not include the -Prefix parameter, your topics will still be sorted by prefix, even though the prefix won't be displayed.
Likewise, if you sort by Name, but do include the -Prefix parameter, your topics will still be sorted by name, even though the prefix will be displayed.

It appears that topics will always be sorted under their containers. In other words, topics that are not in containers will be sorted relative to each other. Contained topics will be sorted relative to their peers within that container.

Everything outputs in plain text. I do want to work on an HTML/CSS version, but first I had to get all of this done. This is my foundation for building a version with formatted output.
#>

param (
    # Source file path and name.
    [Parameter(Mandatory,Position=1)]
    [string]$Source,

    # Destination file path and name.
    [Parameter(Mandatory,Position=2)]
    [string]$Destination,

    # Sort topics by:
    #    1 = Name
    #    2 = Prefix, Name **Default**
    #    3 = Category, Name
    #    4 = Category, Prefix, Name
    #
    # Choosing options 2 or 4 will sort by prefix, regardless of whether or not
    #    the -Prefix switch is specified. Likewise, choosing options 1 or 3 will
    #    sort by name, regardless of whether or not the -Prefix switch is specified.
    [Parameter()]
    [int]$Sort = 2,

    # Include prefix in topic name ($true or $false)
    [switch]$Prefix,

    # Include suffix in topic name ($true or $false)
    [switch]$Suffix

) # param

Function ParseTopic($PassedTopic,$Outputfile,$Sort,$Prefix,$Suffix,$Parent) {
  $TopicName = $PassedTopic.public_name
  if ($Prefix -and $PassedTopic.Prefix) {$TopicName = $PassedTopic.Prefix + " - " + $TopicName}
  if ($Suffix -and $PassedTopic.Suffix) {$TopicName = $TopicName + " (" + $PassedTopic.Suffix + ")"}
  $TopicName = "Topic: $TopicName"
  $ParentName = "Parent Topic: $Parent"
  $CategoryName = "Category: " + $PassedTopic.category_name

  Add-Content -Path $outputfile -Value $TopicName
  Add-Content -Path $outputfile -Value $ParentName
  Add-Content -Path $outputfile -Value $CategoryName
  If ($PassedTopic.tag_assign) {ParseTags $PassedTopic $Outputfile}
  If ($PassedTopic.linkage) {ParseLinkage $PassedTopic $outputfile}
  Add-Content -Path $outputfile -Value ""

  foreach ($Section in $PassedTopic.section) {
      ParseSection $Section $Outputfile
  } # foreach ($Section in $PassedTopic.section)

  Switch ($Sort) {
      1 {$TopicList = $PassedTopic.topic | Sort-Object public_name}
      2 {$TopicList = $PassedTopic.topic | Sort-Object prefix,public_name}
      3 {$TopicList = $PassedTopic.topic | Sort-Object category_name,public_name}
      4 {$TopicList = $PassedTopic.topic | Sort-Object category_name,prefix,public_name}
      default {$TopicList = $PassedTopic.topic}
  }

  $Parent = $Parent + $PassedTopic.Public_Name + "/"
  foreach ($Topic in $TopicList) {
      ParseTopic $Topic $Outputfile $Sort $Prefix $Suffix $Parent
  } # foreach ($Topic in $PassedTopic.topic)
} # Function ParseTopic($PassedTopic)

Function ParseSection ($PassedSection,$Outputfile) {
  $SectionName = "Section: " + $PassedSection.name
  Add-Content -Path $outputfile -Value $SectionName
  Add-Content -Path $outputfile -Value ""
  foreach ($Snippet in $PassedSection.snippet) {
      ParseSnippet $Snippet $Outputfile
  } # foreach ($Snippet in $PassedSection.snippet)

  foreach ($Section in $PassedSection.section) {
      ParseSection $Section $Outputfile
  } # foreach ($Section in $PassedSection.section)
} # Function ParseSection ($PassedSection)

Function ParseSnippet ($PassedSnippet,$Outputfile) {
  switch ($PassedSnippet.type) {
      "Multi_Line" {
        if ($PassedSnippet.purpose -eq "directions_only") {
          $Text = ParseSnippetText $PassedSnippet.gm_directions
          $Text = "GM Directions: " + $Text
          Add-Content -Path $outputfile -Value $Text
        } elseif (($PassedSnippet.contents.contains("<ul")) -or ($PassedSnippet.contents.contains("<ol")) -or ($PassedSnippet.contents.contains("<table"))) {
          $Text = ParseFormattedStuff $PassedSnippet.contents
          Add-Content -Path $outputfile -Value $Text
        } else {
          $Text = ParseSnippetText $PassedSnippet.contents
          Add-Content -Path $outputfile -Value $Text
        } # if ($PassedSnippet.purpose -eq "directions_only")
        Add-Content -Path $outputfile -Value ""
      } # "Multi_Line"
     
      "Labeled_Text" {
        $Text = ParseSnippetText $PassedSnippet.contents
        $LabeledText = $PassedSnippet.Label + ": $Text"
        Add-Content -Path $outputfile -Value $LabeledText
        Add-Content -Path $outputfile -Value ""
      } # "Labeled_Text"
     
      "Tag_Standard" {
        ParseTags $PassedSnippet $outputfile
        Add-Content -Path $outputfile -Value ""
      } # "Tag_Standard"

      "Numeric" {
        $annotation = ParseSnippetText $PassedSnippet.annotation
        $Numeric = $PassedSnippet.contents + ", $annotation"
        Add-Content -Path $outputfile -Value $Numeric
        Add-Content -Path $outputfile -Value ""
      } # "Numeric"
     
      "Tag_Multi_Domain" {
        ParseTags $PassedSnippet $outputfile
        Add-Content -Path $outputfile -Value ""
      } # "Tag_Multi_Domain"

      "Date_Game" {
        $DateText = ParseSnippetText $PassedSnippet.annotation
        $DateText = $PassedSnippet.game_date.display + ", " + $DateText
        Add-Content -Path $outputfile -Value $DateText
        Add-Content -Path $outputfile -Value ""
      } # Date_Game

      "Date_Range" {
        $DateRange = $PassedSnippet.date_range.display_start + " to " + $PassedSnippet.date_range.display_end
        $annotation = ParseSnippetText $PassedSnippet.annotation
        $DateText = "$DateRange, $annotation"
        Add-Content -Path $outputfile -Value $DateText
        Add-Content -Path $outputfile -Value ""
      } # Date_Range

      default {
        # Do nothing here.
      } # default
  } # switch ($PassedSnippet.type)
} # Function ParseSnippet

Function ParseSnippetText ($PassedText) {
  if ($PassedText -ne $null) {
      $gt = $PassedText.Indexof('>')
  } else {
      $gt = -1
  } # if ($PassedText -ne $null)
  $Text = $null
  While ($gt -ge 0) {
      # $gt = $PassedText.Indexof(">",$RWSnippet)
      $lt = $PassedText.Indexof("<",$gt)
      $length = $lt-$gt
      if ($length -gt 1) {$Text = $Text + $PassedText.substring($gt+1,$length-1)}
      if ($lt -lt $gt) {
        $gt = -1
      } else {
        $PassedText = $PassedText.substring($lt,$PassedText.length-$lt)
        $gt = $PassedText.Indexof('>')
      } # if ($lt -lt $gt)
  } # While ($gt -ge 0)
  if ($Text.contains("&nbsp;")) {$Text = $Text.replace("&nbsp;","`r`n`r`n")}
  Return $Text
} # Function ParseSnippetText

Function ParseTags ($PassedTags,$OuptutFile) {
  # This block of code assumes that tags of the same domain will always be grouped together.
  $domain = 0
  $tagdomains = $PassedTags.tag_assign.domain_name
  $tagdomain = $tagdomains[$domain]
  $tagline = ""

  foreach ($tag in $PassedTags.tag_assign) {
      if ($tag.domain_name -eq $tagdomain) {
        if ($tagline -eq "") {$tagline = $tagdomain + ": " + $tag.tag_name} else {$tagline = $tagline + ", " + $tag.tag_name}
        $domain++
      } else {
        $tagdomain = $tag.domain_name
        $tagline = $tagline + "`r`n" + $tagdomain + ": " + $tag.tag_name
        $domain++
      } # if ($tag.domain_name -eq $tagdomain)
  } # foreach ($tag in $PassedSnippet.tag_assign)

  Add-Content -Path $outputfile -Value $tagline
  if ($PassedTags.annotation) {
      $annotation = ParseSnippetText $PassedTags.annotation
      Add-Content -Path $outputfile -Value "Annotation: $annotation"
  } # foreach ($tag in $PassedTags.tag_assign)
} # Function ParseTags

Function ParseLinkage ($PassedTopic,$OutputFile) {
  $Linkage = $PassedTopic.linkage
  $LinkList = ""
  foreach ($link in $linkage) {
      if ($linklist -eq "") {$linklist = "Linkage: " + $link.target_name} else {$linklist = $linklist + ", " + $link.target_name}
  } # foreach ($link in $linkage)
  Add-Content -Path $OutputFile -Value $Linklist
 
} # Function ParseLinkage

Function ParseFormattedStuff ($PassedSnippet) {
  $TagLocations = GetTagLocations $PassedSnippet
  [array]$FullText = $null

  foreach ($tag in $TagLocations) {
      $tagtext = $PassedSnippet.substring($tag.start,$tag.end-$tag.start+1)
      Switch ($tag.tag) {
        "text" {
            $Text = ParseSnippetText $tagtext
            if ($Text) {
              if ($Text.contains("&#xd;")) {$Text = $Text.Replace("&#xd;"," ")}
              if ($Text.contains("&nbsp;")) {$Text = $Text.Replace("&nbsp;"," ")}
              $Text = $Text.Trim()
              Add-Content -Path $outputfile -Value $Text
            }
        } # "text"
           
        "ul" {
            $ordered = $false
            $Text = ParseList $tagtext $ordered
            Add-Content -Path $outputfile -Value $Text
        } # "ul"

        "ol" {
            $ordered = $true
            $Text = ParseList $tagtext $ordered
            Add-Content -Path $outputfile -Value $Text
        } # "ol"

        "table" {
            $table = ParseTable $tagtext
            $Text = $table | Out-String
            $Text = $Text.TrimEnd()
            Add-Content -Path $outputfile -Value $Text
        } # "table"

      } # Switch ($tag.tag)
  } # foreach ($tag in $TagLocations)
  Add-Content -Path $outputfile -Value ""
} # Function ParseFormattedStuff

Function GetTagLocations ($PassedSnippet) {
  $taglist = $null
  # Get the locations of all the start tags.
  $ul = $PassedSnippet.IndexOf("<ul")
  $ol = $PassedSnippet.IndexOf("<ol")
  $table = $PassedSnippet.IndexOf("<table")

  While (($ul -ge 0) -or ($ol -ge 0) -or ($table -ge 0)) {
      if ($ul -ge 0) {
        $properties = @{
            'Tag'='ul';
            'Start'=$ul;
            'End'=0
        } # $properties

        $TagLocation = New-Object –TypeName PSObject –Prop $properties
        [array]$Taglist = $Taglist + $TagLocation
      } # if ($ul -ge 0)

      if ($ol -ge 0) {
        $properties = @{
            'Tag'='ol';
            'Start'=$ol;
            'End'=0
        } # $properties

        $TagLocation = New-Object –TypeName PSObject –Prop $properties
        [array]$Taglist = $Taglist + $TagLocation
      } # if ($ol -ge 0)

      if ($table -ge 0) {
        $properties = @{
            'Tag'='table';
            'Start'=$table;
            'End'=0
        } # $properties

        $TagLocation = New-Object –TypeName PSObject –Prop $properties
        [array]$Taglist = $Taglist + $TagLocation
      } # if ($table -ge 0)

      $Taglist = $Taglist | Sort-Object Start
      $HighTag = $Taglist.Count-1
      $StartPosition = $Taglist[$HighTag].Start + 1

      $ul = $PassedSnippet.IndexOf("<ul",$StartPosition)
      $ol = $PassedSnippet.IndexOf("<ol",$StartPosition)
      $table = $PassedSnippet.IndexOf("<table",$StartPosition)
  } # While (($ul -ge 0) -or ($ol -ge 0) -or ($table -ge 0))

  # Get all the end tags.
  foreach ($tag in $Taglist) {
      $endtag = "</" + $tag.tag
      $endtag = $PassedSnippet.IndexOf($endtag,$tag.Start + 1)
      $Tag.end = $endtag+4
  } # foreach ($tag in $Taglist)

  # Fill in the gaps, if there are any.
  $firstitem = 0
  $firstpos = 0
  $lastitem = $Taglist.count -1
  for ($item=$firstitem; $item -le $lastitem; $item++) {
      if (($firstpos -lt $Taglist[$item].start)) {
        $endpos = $Taglist[$item].start - 1

        $properties = @{
            'Tag'='text';
            'Start'=$firstpos;
            'End'=$endpos
        } # $properties

        $TagLocation = New-Object –TypeName PSObject –Prop $properties
        [array]$gaplist = $gaplist + $TagLocation

      } # if (($firstpos -lt $Taglist[$item].start))

      if ($item -eq $lastitem) {
        $firstpos = $Taglist[$item].end + 1
        $endpos = $PassedSnippet.length - 1

        $properties = @{
            'Tag'='text';
            'Start'=$firstpos;
            'End'=$endpos
        } # $properties

        $TagLocation = New-Object –TypeName PSObject –Prop $properties
        [array]$gaplist = $gaplist + $TagLocation
      } # if ($item -eq $lastitem)
      $firstpos = $taglist[$item].end+1
  } # for ($item=$firstitem; $item -le $lastitem; $item++)

  $Taglist = $Taglist + $gaplist
  $Taglist = $Taglist | Sort-Object start
  Return $Taglist
} # GetTagLocations

Function ParseList ($PassedText,$Ordered) {
  if ($PassedText -ne $null) {
      $openli = $PassedText.Indexof('<li')
  } else {
      $openli = -1
  }
  [array]$List = $null
  $ItemNumber = 1
  While ($openli -ge 0) {
      $closeli = $PassedText.Indexof("</li",$openli)
      $length = $closeli-$openli
      if ($length -gt 1) {
        $LineItem = $PassedText.substring($openli+1,$length-1)
        $LineItem = ParseSnippetText $LineItem
        if ($Ordered) {$LineItem = $ItemNumber.ToString() + ". " + $LineItem} else {$LineItem = "* $LineItem"}
        [array]$List = [array]$List + $LineItem
      } # if ($length -gt 1)

      if ($closeli -lt $openli) {
        $openli = -1
      } else {
        $PassedText = $PassedText.substring($closeli,$PassedText.length-$closeli)
        $openli = $PassedText.Indexof('<li')
      } # if ($closeli -lt $openli)
      $ItemNumber++
  } # While ($openli -ge 0)
  Return $List
} # Funcion ParseList

Function ParseTable ($PassedText) {
  if ($PassedText -ne $null) {
      $opentr = $PassedText.Indexof('<tr')
  } else {
      $opentr = -1
  }
  [array]$RowList = $null
  While ($opentr -ge 0) {
      $closetr = $PassedText.Indexof("</tr",$opentr)
      $length = $closetr-$opentr
      if ($length -gt 1) {
        $Row = $PassedText.substring($opentr+1,$length-1)
        $Row = $Row.Replace("tr>&#xd;","")
        $Row = $Row.Trim()
        [array]$RowList = [array]$RowList + $Row
      }

      if ($closetr -lt $opentr) {
        $opentr = -1
      } else {
        $PassedText = $PassedText.substring($closetr,$PassedText.length-$closetr)
        $opentr = $PassedText.Indexof('<tr')
      }
  } # While ($opentr -ge 0)

  [array]$Table = $null
  foreach ($Row in $RowList) {
      if ($Row -ne $null) {
        $opentd = $Row.Indexof('<td')
      } else {
        $opentd = -1
      }
      $ItemList = new-object psobject
      $ItemNumber = 1
      While ($opentd -ge 0) {
        $closetd = $Row.Indexof("</td",$opentd)
        $length = $closetd-$opentd
        if ($length -gt 1) {
            $Item = $Row.substring($opentd+1,$length-1)
            $Item = ParseSnippetText $Item
            $ItemList | Add-Member -Name "Item $ItemNumber" -Type noteproperty -value $Item
        }

        if ($closetd -lt $opentd) {
            $opentd = -1
        } else {
            $Row = $Row.substring($closetd,$Row.length-$closetd)
            $opentd = $Row.Indexof('<td')
        }
        $ItemNumber++
      } # While ($opentd -ge 0)
      [array]$Table = [array]$Table + $ItemList
  } # foreach ($Row in $RowList)
  $Text = $Table | Format-Table -HideTableHeaders -AutoSize -Wrap
  Return $Text
} # Function ParseTable

# Main {
  $inputfile = $Source
  $outputfile = $Destination
  [xml]$Export = Get-Content -Path $inputfile
  $Title = "Title: " + $Export.Output.definition.details.name
  Set-Content -Path $outputfile -Value $Title
  Add-Content -Path $outputfile -Value ""
  $Contents = $Export.output.contents

  Switch ($Sort) {
      1 {$TopicList = $Contents.topic | Sort-Object public_name}
      2 {$TopicList = $Contents.topic | Sort-Object prefix,public_name}
      3 {$TopicList = $Contents.topic | Sort-Object category_name,public_name}
      4 {$TopicList = $Contents.topic | Sort-Object category_name,prefix,public_name}
      default {$TopicList = $Contents.topic}
  }

  $Parent = "/"
  foreach ($Topic in $TopicList) {
      ParseTopic $Topic $outputfile $Sort $Prefix $Suffix $Parent
  } # foreach ($Topic in $Contents.topic)
# } Main


AEIOU January 16th, 2017 09:46 AM

You aren't cluttering. This is really interesting to see the project develop and it's a critical need for the community. Thank you for tackling this huge need.

And all the best in your job searching.

davidp January 16th, 2017 09:52 AM

As far as a place to store code, github or gitlab could be good options. You can create free accounts on either and then check in code, getting versioning as part of it. git client is free. If someone wants to grab code but not install git, they can do so from the web interface. Anyone who wants to contribute to it can also submit changes that you could review and incorporate into your code base pretty easily.

rob January 16th, 2017 12:06 PM

Quote:

Originally Posted by Vargr (Post 241599)
@EightBitz:

Notice how we have been civil and not turned into an angry mob complete with touches and pitchforks - yet...

Nah, just kidding. :-)

I'm onto you guys. This is just the calm before the storm, when the pitchforks and torches are properly gathered and the villagers summoned from their homes to be properly equipped and whipped into a frenzy in the days ahead. Right? :)

kbs666 January 16th, 2017 03:30 PM

I'm way too old school D&D for simple pitchforks. Guisarmes and Glaives for the peasants or there will be no mob.

rob January 16th, 2017 03:51 PM

Quote:

Originally Posted by EightBitz (Post 241601)
OK, I'm not going to keep cluttering up the forum, but here's a much better version of the plain text export. No HTML or anything yet, just plain text.

This is NOT clutter. Please keep posting updates as you evolve this tool. There are going to be plenty of users who will find it valuable. :)

ShadowChemosh January 16th, 2017 03:55 PM

My only suggestion would be to keep the "First" page updated with the latest script. As this thread grows it can be hard for someone new to find the "latest" script. :)

This forum lets you edit any post you have ever done no matter how long ago.

Silveras January 16th, 2017 03:58 PM

Quote:

Originally Posted by kbs666 (Post 241649)
I'm way too old school D&D for simple pitchforks. Guisarmes and Glaives for the peasants or there will be no mob.

Be careful of the Speed Factor on those...

daplunk January 16th, 2017 04:18 PM

Put the script in a GitHub. It's worth it as you can track changes and people can submit bug reports etc. Update the front page with a link to the GitHub.

AEIOU January 16th, 2017 05:14 PM

Hey! I had a pitchfork LAST time. You promised me I could have a torch!!!

Oh, and github is beautiful. Even for us non-coders. The 5e HL community stuff is there and it's easy to download and provide edits.

EightBitz January 17th, 2017 06:02 AM

Quote:

Originally Posted by davidp (Post 241597)
The <contents> of a <snippet> should all be HTML and that would be the only place that the lists, tables, etc. would be present. You should be able to take the <contents> and just treat it as HTML and be done with it. Any more and you are making life hard on yourself. :)

The only slight gotcha is that we use encoding of the contents so that it isn't interpreted as XML tags, but that should be straight-forward to deal with.

Nope, I still have to tear things apart for HTML. There's another gotcha.

I'm adding an option to indent nested topics and sections. And it works great for everything except tables. Mind you, I'm still very much an amateur coder, but here's basically what's happening.

In order to indent blocks of text, and not just the first line, I'm using margin-left. For pre-formatted HTML text in the contents node, I'm inserting it right after the "<p " tag. And I add it to every instance, otherwise only some parts of some snippets will indent, and the rest will not.

Adding it to every instance of <p means it's also adding the margin to the table cells.

I could just write code that says if this snippet includes a table tag, then don't insert a margin, but if I have a combo snippet with text and lists and tables, that's not going to work.

So I have to divide things up into components again so I can have better control of where I insert the margins.

The GOOD news is that I already have that code. :-)

The bad news is that I've been up all night, and I'm tired.

But the GOOD news is that everything else is looking good, and this is hopefully the last thing I have to tackle.

The bad news is that I can't do it right now.

But the GOOD news is that ... uhhh ... oh, never mind.

Vargr January 17th, 2017 10:57 AM

Quote:

Originally Posted by rob (Post 241630)
I'm onto you guys. This is just the calm before the storm, when the pitchforks and torches are properly gathered and the villagers summoned from their homes to be properly equipped and whipped into a frenzy in the days ahead. Right? :)

Darn! Well, seems we will have to switch to plan B

EightBitz January 17th, 2017 02:12 PM

New version up. Handles HTML. See the first message in this thread or go here:

https://gist.github.com/EightBitz/c2...52ba593a9735a1

daplunk January 18th, 2017 04:15 AM

Bloody nice work EightBitz! I tried it tonight and managed it with little to no issues. Had to modify the security settings on my PC to enable the script to run and I had removed the full stop from the start of the script thinking that was just to break any auto-formatting (I do that in Excel alot).

To make this a bit easier for new users to pick up here's a video on the process of setting up and running the script.

Realm Works - How To Print Your Realm

EightBitz January 18th, 2017 04:55 AM

Doh! I forgot about the security issue.
The Powershell command you need is:
Set-ExecutionPolicy RemoteSigned

That SHOULD do the trick.

EightBitz January 18th, 2017 05:07 AM

Quote:

Originally Posted by daplunk (Post 241887)
Bloody nice work EightBitz! I tried it tonight and managed it with little to no issues. Had to modify the security settings on my PC to enable the script to run and I had removed the full stop from the start of the script thinking that was just to break any auto-formatting (I do that in Excel alot).

To make this a bit easier for new users to pick up here's a video on the process of setting up and running the script.

Realm Works - How To Print Your Realm

OK, that was an awesome video, but you overlooked one very important detail.

There's a file called "main.css" that should have been included in the zip download. Put the HTML file in the same folder as the main.css file, and you will see a world of difference.

EightBitz January 18th, 2017 05:12 AM

And if you want nested topics and sections indented, you can add -Indent to the end of the command line.

If you type:
Get-Help .\RWExport-To-HTML.ps1 -full

You will see all the options available, with examples of how to use them on the command line.

EightBitz January 18th, 2017 06:21 AM

2 Attachment(s)
I just wanted to show people what the output SHOULD look like in your web browser, if you have the html and css files together, and if you use the -Indent option.

Playground1.pdf is the way I have things setup by default.
In Playground2.pdf, I just made a few changes to the css file to change the formatting, but it's the exact same HTML file.

EDIT: Note this is also from a newer version of the script that now displays images inline, and has links for statblocks. I want to smooth a few things out before I put this new version up, but the key here is to showcase the formatting.

daplunk January 18th, 2017 10:30 AM

Excellent! Have to admit... I was excited that I figured out how to get it working at all.... Will put a full video together showing this next step.

ShadowChemosh January 18th, 2017 11:16 AM

Nice EightBitz. Very nice! :)

tmilktoast January 18th, 2017 12:17 PM

First of all, thank you EightBitz, for the great script.

I figure you would want a report of any errors, so here goes. I've done some testing and believe I have it narrowed down to it choking on bullet lists (which I use heavily, unfortunately).

Interestingly, it works fine if I change the Snippet Type to Labeled Text and leave the bullet lists as-is. It also seems to work if I use the bullet list under "Styles" rather than under "Paragraphs". (Does anyone know why we have two sets of bullets, and other, lists?)

Code:

New-Object : Cannot find type [â€TypeName PSObject â€Prop]: verify that the assembly containing this type is loaded.
At D:\owncloud\rpg\utilities\rwexport\RWExport-To-HTML.ps1:388 char:25
+ ...    $TagLocation = New-Object –TypeName PSObject –Prop $properties
+                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidType: (:) [New-Object], PSArgumentException
    + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

Cannot index into a null array.
At D:\owncloud\rpg\utilities\rwexport\RWExport-To-HTML.ps1:416 char:7
+      $StartPosition = $Taglist[$HighTag].Start + 1
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

Edit: To expand on this, it looks like it hits a bullet list then gets stuck and just continually spits out these errors. The HTML file is still created, but ends at the snippet above the one with the bullet list.

EightBitz January 18th, 2017 12:23 PM

Quote:

Originally Posted by tmilktoast (Post 241932)
First of all, thank you EightBitz, for the great script.

I figure you would want a report of any errors, so here goes. I've done some testing and believe I have it narrowed down to it choking on bullet lists (which I use heavily, unfortunately).

Interestingly, it works fine if I change the Snippet Type to Labeled Text and leave the bullet lists as-is. It also seems to work if I use the bullet list under "Styles" rather than under "Paragraphs". (Does anyone know why we have two sets of bullets, and other, lists?)

Code:

New-Object : Cannot find type [â€TypeName PSObject â€Prop]: verify that the assembly containing this type is loaded.
At D:\owncloud\rpg\utilities\rwexport\RWExport-To-HTML.ps1:388 char:25
+ ...    $TagLocation = New-Object –TypeName PSObject –Prop $properties
+                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidType: (:) [New-Object], PSArgumentException
    + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

Cannot index into a null array.
At D:\owncloud\rpg\utilities\rwexport\RWExport-To-HTML.ps1:416 char:7
+      $StartPosition = $Taglist[$HighTag].Start + 1
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

Edit: To expand on this, it looks like it hits a bullet list then gets stuck and just continually spits out these errors. The HTML file is still created, but ends at the snippet above the one with the bullet list.

Would you be able to export just that one topic and copy and and paste the xml code in a private message to me?

tmilktoast January 18th, 2017 12:41 PM

Email sent.

EightBitz January 18th, 2017 12:46 PM

Quote:

Originally Posted by tmilktoast (Post 241934)
Email sent.

Thanks! I will take a look.

EightBitz January 18th, 2017 08:15 PM

To Rob, Davidp, daplunk, ShadowChemosh, and everyone else, I want to thank you for the kind words and moral support.

And daplunk, thank you for a mostly awesome video ... until the very end where you pulled up the unformatted HTML and said, "Well that's it!" And my jaw dropped with incredulity as I mentally screamed, "No, that is certainly NOT it!"

But still, you seem to have a very good method in making your videos. When I gear up to start entering my own data, I plan to watch more of them.

Right now, though, yes I am still updating the script. As you've seen in the PDF files that I posted, I'm adding support for stat blocks and images, and I've going to see what I can do with other snippet types. No guarantees on anything at this point.

daplunk January 18th, 2017 08:25 PM

:D Will update it tonight.

EightBitz January 19th, 2017 04:00 AM

New version up. See the first post in this thread.

daplunk January 20th, 2017 12:16 AM

Just thinking out loud here...

Powershell scripts can be run via .net applications right?

Which means it should be relatively simple to build a UI into this thing...

kbs666 January 20th, 2017 02:09 AM

Quote:

Originally Posted by daplunk (Post 242057)
Just thinking out loud here...

Powershell scripts can be run via .net applications right?

Which means it should be relatively simple to build a UI into this thing...

LOL

Can be and relatively simple not same thing.


All times are GMT -8. The time now is 04:42 PM.

Powered by vBulletin® - Copyright ©2000 - 2024, vBulletin Solutions, Inc.
wolflair.com copyright ©1998-2016 Lone Wolf Development, Inc. View our Privacy Policy here.