Skip to content

Instantly share code, notes, and snippets.

@danwakefield
Created April 30, 2025 22:29
Show Gist options
  • Save danwakefield/7756c03a2a839d46130251033d46a871 to your computer and use it in GitHub Desktop.
Save danwakefield/7756c03a2a839d46130251033d46a871 to your computer and use it in GitHub Desktop.
require 'csv'
# This reads a CSV and outputs it in the Cross Stitch osx format
# For the life of me I could not get https://flosscross.com/ to actually read the output
# when run with `ruby csv_to_osx_cross_stitch_format.rb > output.osx`
# despite `file output.osx` telling me it was valid XML (and being the same as
# a file _exported_ from flosscross)
# Instead I created a new blank project in flosscross, exported that to OSX,
# Opened it, and replaced its `palette` and `fullstitches` sections with the
# ones from this generation.
# Maybe newlines or something with encoding but whatever it worked
class OsxFileFormat
attr_reader :height, :width, :csv_content, :colors
def initialize(csv_path, colors: {})
@csv_content = CSV.read(csv_path, headers: false)
@width = csv_content.first.length + 2
@height = csv_content.length + 2
@colors = colors
@colors.each.with_index(1) { |(_, v), index| v[:index] = index }
end
def palette_items
colors.map do |symbol, config|
<<~XML.gsub!(/[[:space:]]+/, " ")
<palette_item
index="#{config[:index]}"
number="#{config[:number]}"
name="#{config[:name]}"
color="#{config[:color]}"
printcolor="#{config[:color]}"
blendcolor="nil"
comments=""
strands="2"
symbol="#{config[:index]}"
dashpattern=""
bsstrands="1"
bscolor="ffffff"
misc1="" />
XML
end
end
def csv_to_stitches
a = []
csv_content.each.with_index(1) do |row, y_index|
row.each.with_index(1) do |cell, x_index|
c = colors[cell]
next if c.nil?
stitch_content = <<~XML.gsub!(/[[:space:]]+/, " ").strip!
<stitch x="#{x_index}" y="#{y_index}" palindex="#{c[:index]}" />
XML
a << stitch_content
end
end
a
end
def output_file
puts <<~XML
<?xml version="1.0" encoding="UTF-8"?>
<chart>
<format comments01="https://ursasoftware.com/OXSFormat/" />
<properties
oxsversion="1.0"
software="FlossCross"
software_version="104"
chartheight="#{height}"
chartwidth="#{width}"
charttitle="vampurr"
author=""
copyright=""
instructions=""
stitchesperinch="14"
stitchesperinch_y="14"
palettecount="#{palette_items.length}"
misc1="normal" misc2="" />
<palette>
<palette_item index="0" number="cloth" name="cloth" color="FFFFFF" printcolor="FFFFFF" blendcolor="nil" comments="" strands="2" symbol="100" dashpattern="" misc1="" bsstrands="2" bscolor="FFFFFF"/>
#{palette_items.join("\n")}
</palette>
<fullstitches>
#{csv_to_stitches.join("")}
</fullstitches>
<partstitches>
<partstitch />
</partstitches>
<backstitches>
<backstitch />
</backstitches>
<ornaments_inc_knots_and_beads>
<object />
</ornaments_inc_knots_and_beads>
<commentboxes />
</chart>
XML
end
end
colors = {
"." => { number: "DMC 782", name: "Gold", color: "7E5A23" },
"+" => { number: "DMC 817", name: "Mid Red", color: "AF2D27" },
"–" => { number: "DMC 4", name: "Dark Grey", color: "7F7A7F" },
"w" => { number: "DMC 816", name: "Dark Red", color: "812A32" },
"●" => { number: "DMC 310", name: "Black", color: "000000" },
"≈" => { number: "DMC 1", name: "Lightest Grey", color: "DDDDDD" },
"☆" => { number: "DMC 780", name: "Dark Gold", color: "A55B1A" },
"β" => { number: "DMC 535", name: "Grey", color: "585858" },
"s" => { number: "DMC 3799", name: "Darkest Grey", color: "303030" },
"▮" => { number: "DMC B5200", name: "Blanc", color: "FFFFF9" },
"⊗" => { number: "DMC 2", name: "Lighter Grey", color: "CAC9CC" },
"¶" => { number: "DMC 814", name: "Burgundy", color: "540F21" },
"%" => { number: "DMC 3", name: "Darker Grey", color: "A2A1A8" },
">" => { number: "DMC 3801", name: "Blood Red", color: "C93536" },
"▲" => { number: "DMC 815", name: "Maroon", color: "6C1D24" },
"λ" => { number: "DMC 783", name: "Yellow Gold", color: "D28922" },
"Z" => { number: "DMC 300", name: "Brown", color: "562811" },
}
OsxFileFormat.new("/tmp/x.csv", colors: colors).output_file
@danwakefield
Copy link
Author

danwakefield commented Apr 30, 2025

  1. I bought a pattern that was not compatible with PatternKeeper.
  2. Extracted the table inside it with https://ronnywang.github.io/pdf-table-extractor/
  3. Changed instances of empty columns to a single space - This allowed me to view it with a monospace font to quickly see if it had extracted properly.
  4. Fixed a missing sectio with vim block selection search and replace + some manual edits.
  5. Ran this on the file.
  6. Spent a bunch of time trying to get flosscross to read it until I found the workaround.
  7. Flosscross then did some math on the hexcodes and replaced the ones it didn't like??
  8. Manually fix the palette colors in flosscross
  9. Exported a new PatternKeeper compatible PDF to import.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment